diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f6abc42 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/submodules/ElementEngine"] + path = src/submodules/ElementEngine + url = https://github.com/pandepic/ElementEngine.git diff --git a/src/MiMap.Viewer.DesktopGL/Components/GuiMapViewer.ImGui.cs b/src/MiMap.Viewer.DesktopGL/Components/GuiMapViewer.ImGui.cs new file mode 100644 index 0000000..fe7c4f2 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Components/GuiMapViewer.ImGui.cs @@ -0,0 +1,446 @@ +using System; +using System.Numerics; +using Microsoft.Xna.Framework; +using ImGuiNET; +using OpenAPI.WorldGenerator.Generators.Biomes; +using static ImGuiNET.ImGui; + +namespace MiMap.Viewer.DesktopGL.Components +{ + public partial class GuiMapViewer + { + private Point _cursorPosition; + private int[] _cursorBlock = new int[3]; + private BiomeBase _cursorBlockBiome; + private int[] _cursorChunk = new int[2]; + private int[] _cursorRegion = new int[2]; + private int _cursorBlockBiomeId; + + private void DrawImGui_StyleEditor() + { + if (Begin("ImGui Style Editor")) + { + if (BeginTableEx("styleeditor", 2)) + { + var io = GetIO(); + io.ConfigWindowsResizeFromEdges = true; + var style = GetStyle(); + + TableRowEditor(nameof(style.Alpha), ref style.Alpha); + TableRowEditor(nameof(style.AntiAliasedFill), ref style.AntiAliasedFill); + TableRowEditor(nameof(style.AntiAliasedLines), ref style.AntiAliasedLines); + TableRowEditor(nameof(style.AntiAliasedLinesUseTex), ref style.AntiAliasedLinesUseTex); + TableRowEditor(nameof(style.ButtonTextAlign), ref style.ButtonTextAlign); + TableRowEditor(nameof(style.CellPadding), ref style.CellPadding); + TableRowEditor(nameof(style.ChildBorderSize), ref style.ChildBorderSize); + TableRowEditor(nameof(style.ChildRounding), ref style.ChildRounding); + TableRowEditor(nameof(style.CircleTessellationMaxError), ref style.CircleTessellationMaxError); + // TableRowEditor(nameof(style.ColorButtonPosition), ref style.ColorButtonPosition); + // TableRowEditor(nameof(style.Colors), ref style.Colors); + TableRowEditor(nameof(style.ColumnsMinSpacing), ref style.ColumnsMinSpacing); + TableRowEditor(nameof(style.CurveTessellationTol), ref style.CurveTessellationTol); + TableRowEditor(nameof(style.DisabledAlpha), ref style.DisabledAlpha); + TableRowEditor(nameof(style.DisplaySafeAreaPadding), ref style.DisplaySafeAreaPadding); + TableRowEditor(nameof(style.FrameBorderSize), ref style.FrameBorderSize); + TableRowEditor(nameof(style.FramePadding), ref style.FramePadding); + TableRowEditor(nameof(style.FrameRounding), ref style.FrameRounding); + TableRowEditor(nameof(style.GrabMinSize), ref style.GrabMinSize); + TableRowEditor(nameof(style.GrabRounding), ref style.GrabRounding); + TableRowEditor(nameof(style.IndentSpacing), ref style.IndentSpacing); + TableRowEditor(nameof(style.ItemInnerSpacing), ref style.ItemInnerSpacing); + TableRowEditor(nameof(style.ItemSpacing), ref style.ItemSpacing); + TableRowEditor(nameof(style.LogSliderDeadzone), ref style.LogSliderDeadzone); + TableRowEditor(nameof(style.MouseCursorScale), ref style.MouseCursorScale); + TableRowEditor(nameof(style.PopupBorderSize), ref style.PopupBorderSize); + TableRowEditor(nameof(style.PopupRounding), ref style.PopupRounding); + TableRowEditor(nameof(style.ScrollbarRounding), ref style.ScrollbarRounding); + TableRowEditor(nameof(style.ScrollbarSize), ref style.ScrollbarSize); + TableRowEditor(nameof(style.SelectableTextAlign), ref style.SelectableTextAlign); + TableRowEditor(nameof(style.TabBorderSize), ref style.TabBorderSize); + TableRowEditor(nameof(style.TabMinWidthForCloseButton), ref style.TabMinWidthForCloseButton); + TableRowEditor(nameof(style.TabRounding), ref style.TabRounding); + TableRowEditor(nameof(style.TouchExtraPadding), ref style.TouchExtraPadding); + TableRowEditor(nameof(style.WindowBorderSize), ref style.WindowBorderSize); + TableRowEditor(nameof(style.WindowPadding), ref style.WindowPadding); + TableRowEditor(nameof(style.WindowRounding), ref style.WindowRounding); + TableRowEditor(nameof(style.WindowMinSize), ref style.WindowMinSize); + TableRowEditor(nameof(style.WindowTitleAlign), ref style.WindowTitleAlign); + TableRowEditor(nameof(style.DisplayWindowPadding), ref style.DisplayWindowPadding); + + EndTable(); + } + + End(); + } + } + + private bool BeginTableEx(string name, int columns, ImGuiTableFlags flags = ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInner | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.SizingFixedFit) + { + if (BeginTable(name, columns, flags)) + { + TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed); + for (int i = 0; i < columns; i++) + { + TableSetupColumn("", ImGuiTableColumnFlags.WidthStretch); + } + + return true; + } + + return false; + } + + private void TableRowEditor(string label, ref string value) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + TableNextColumn(); + InputText(null, ref value, 0); + } + + private void TableRowEditor(string label, ref int value) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + TableNextColumn(); + InputInt(null, ref value); + } + + private void TableRowEditor(string label, ref float value) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + TableNextColumn(); + DragFloat(null, ref value); + } + + private void TableRowEditor(string label, ref bool value) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + TableNextColumn(); + Checkbox(null, ref value); + } + + private void TableRowEditor(string label, ref System.Numerics.Vector2 value) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + TableNextColumn(); + DragFloat2(null, ref value); + } + + private void TableRowEditor(string label, ref System.Numerics.Vector3 value) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + TableNextColumn(); + DragFloat3(null, ref value); + } + + private void TableRowEx(string label, params Action[] columns) + { + TableNextRow(); + TableNextColumn(); + Text(label); + + for (int i = 0; i < columns.Length; i++) + { + TableNextColumn(); + columns[i].Invoke(); + } + } + + private void TableRowEx(params Action[] columns) + { + TableNextRow(); + for (int i = 0; i < columns.Length; i++) + { + TableNextColumn(); + columns[i].Invoke(); + } + } + + private void DrawImGui() + { + try + { + var io = GetIO(); + io.ConfigFlags |= ImGuiConfigFlags.DockingEnable; + io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable; + io.ConfigFlags |= ImGuiConfigFlags.DpiEnableScaleViewports; + io.ConfigFlags |= ImGuiConfigFlags.DpiEnableScaleFonts; + io.ConfigWindowsResizeFromEdges = true; + + //DrawImGui_StyleEditor(); + + DockSpaceOverViewport(GetMainViewport(), ImGuiDockNodeFlags.NoDockingInCentralNode | ImGuiDockNodeFlags.PassthruCentralNode); + + if (Begin("Map Viewer", ImGuiWindowFlags.DockNodeHost)) + { + if (BeginTable("mapviewtable", 2, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingStretchProp)) + { + _mapPositions[0] = _mapPosition.X; + _mapPositions[1] = _mapPosition.Y; + TableNextRow(); + TableNextColumn(); + Text("Map Position"); + TableNextColumn(); + InputInt2("##value", ref _mapPositions[0]); + if (IsItemEdited()) + { + MapPosition = new Point(_mapPositions[0], _mapPositions[1]); + } + + var bounds = MapBounds; + var boundsValues = new int[] { bounds.X, bounds.Y, bounds.Width, bounds.Height }; + + TableNextRow(); + TableNextColumn(); + Text("Map Bounds"); + TableNextColumn(); + InputInt4("##value", ref boundsValues[0], ImGuiInputTextFlags.ReadOnly); + + TableNextRow(); + TableNextColumn(); + Text("Scale"); + TableNextColumn(); + SliderFloat("##value", ref _scale, MinScale, MaxScale); + if (IsItemEdited()) + { + RecalculateTransform(); + } + + TableNextRow(); + + // PopID(); + EndTable(); + } + + Spacing(); + + if (BeginTable("mapviewtable", 2, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingStretchProp)) + { + var cursorPositionValues = new int[] + { + _cursorPosition.X, + _cursorPosition.Y + }; + TableNextRow(); + TableNextColumn(); + Text("Cursor Position"); + TableNextColumn(); + InputInt2("##value", ref cursorPositionValues[0], ImGuiInputTextFlags.ReadOnly); + + + EndTable(); + } + + SetNextItemOpen(true, ImGuiCond.FirstUseEver); + if (TreeNodeEx("Graphics")) + { + if (BeginTable("graphics", 2, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInner | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.SizingFixedFit)) + { + TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed); + TableSetupColumn("", ImGuiTableColumnFlags.WidthStretch); + + var v = GraphicsDevice.Viewport; + var viewportValues = new int[] + { + v.X, + v.Y, + v.Width, + v.Height + }; + + TableNextRow(); + TableNextColumn(); + Text("Viewport"); + TableNextColumn(); + InputInt4("##value", ref viewportValues[0], ImGuiInputTextFlags.ReadOnly); + + EndTable(); + } + + TreePop(); + } + + SetNextItemOpen(true, ImGuiCond.FirstUseEver); + if (TreeNodeEx("Window")) + { + if (BeginTable("window", 2, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInner | ImGuiTableFlags.BordersOuter | ImGuiTableFlags.SizingFixedFit)) + { + TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed); + TableSetupColumn("", ImGuiTableColumnFlags.WidthStretch); + + var p = Game.Window.Position; + var windowPositionValues = new int[] + { + p.X, + p.Y + }; + TableNextRow(); + TableNextColumn(); + Text("Position"); + TableNextColumn(); + InputInt2("##value", ref windowPositionValues[0], ImGuiInputTextFlags.ReadOnly); + + + var c = Game.Window.ClientBounds; + var windowClientBoundsValues = new int[] + { + c.X, + c.Y, + c.Width, + c.Height + }; + TableNextRow(); + TableNextColumn(); + Text("Client Bounds"); + TableNextColumn(); + InputInt4("##value", ref windowClientBoundsValues[0], ImGuiInputTextFlags.ReadOnly); + + + EndTable(); + } + + TreePop(); + } + + End(); + } + + if (Begin("Info")) + { + Text("At Cursor"); + InputInt3("Block", ref _cursorBlock[0], ImGuiInputTextFlags.ReadOnly); + InputInt2("Chunk", ref _cursorChunk[0], ImGuiInputTextFlags.ReadOnly); + InputInt2("Region", ref _cursorRegion[0], ImGuiInputTextFlags.ReadOnly); + + SetNextItemOpen(true, ImGuiCond.FirstUseEver); + if (TreeNode("Biome Info")) + { + var biome = _cursorBlockBiome; + + var biomeId = biome?.Id ?? _cursorBlockBiomeId; + var biomeName = biome?.Name ?? string.Empty; + var biomeDefinitionName = biome?.DefinitionName ?? string.Empty; + var biomeMinHeight = biome?.MinHeight ?? 0; + var biomeMaxHeight = biome?.MaxHeight ?? 0; + var biomeTemperature = biome?.Temperature ?? 0; + var biomeDownfall = biome?.Downfall ?? 0; + + InputInt("ID", ref biomeId, 0, 0, ImGuiInputTextFlags.ReadOnly); + InputText("Name", ref biomeName, 0, ImGuiInputTextFlags.ReadOnly); + InputText("Definition Name", ref biomeDefinitionName, 0, ImGuiInputTextFlags.ReadOnly); + InputFloat("Min Height", ref biomeMinHeight, 0, 0, null, ImGuiInputTextFlags.ReadOnly); + InputFloat("Max Height", ref biomeMaxHeight, 0, 0, null, ImGuiInputTextFlags.ReadOnly); + InputFloat("Temperature", ref biomeTemperature, 0, 0, null, ImGuiInputTextFlags.ReadOnly); + InputFloat("Downfall", ref biomeDownfall, 0, 0, null, ImGuiInputTextFlags.ReadOnly); + + SetNextItemOpen(true, ImGuiCond.FirstUseEver); + if (TreeNode("Config")) + { + var cfg = biome?.Config; + BeginDisabled(); + + var cfgIsEdgeBiome = cfg?.IsEdgeBiome ?? false; + var cfgAllowRivers = cfg?.AllowRivers ?? false; + var cfgAllowScenicLakes = cfg?.AllowScenicLakes ?? false; + var cfgSurfaceBlendIn = cfg?.SurfaceBlendIn ?? false; + var cfgSurfaceBlendOut = cfg?.SurfaceBlendOut ?? false; + var cfgWeight = cfg?.Weight ?? 0; + + Checkbox("Is Edge Biome", ref cfgIsEdgeBiome); + Checkbox("Allow Rivers", ref cfgAllowRivers); + Checkbox("Allow Scenic Lakes", ref cfgAllowScenicLakes); + Checkbox("Surface Blend In", ref cfgSurfaceBlendIn); + Checkbox("Surface Blend Out", ref cfgSurfaceBlendOut); + InputInt("Weight", ref cfgWeight); + + EndDisabled(); + + TreePop(); + } + + + TreePop(); + } + + End(); + } + + if (Begin("Biome Colors")) + { + if (BeginTable("biomeclr", 3, ImGuiTableFlags.Resizable | ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingStretchProp)) + { + foreach (var c in Map.BiomeRegistry.Biomes) + { + TableNextRow(); + TableNextColumn(); + Text(c.Id.ToString()); + TableNextColumn(); + Text(c.Name); + TableNextColumn(); + TableSetBgColor(ImGuiTableBgTarget.CellBg, GetColor(c.Color ?? System.Drawing.Color.Transparent)); + Text(" "); + } + + EndTable(); + } + + End(); + } + } + catch (Exception ex) + { + Log.Error(ex, $"Drawing exception."); + } + } + + private void OnCursorMove_ImGui(Point cursorPosition, Point previousCursorPosition, bool isCursorDown) + { + var cursorBlockPos = Unproject(cursorPosition); + // var cursorBlockPos = Vector3.Transform(new Vector3(cursorPosition.X, cursorPosition.Y, 0f), Transform*_effect.View); + _cursorBlock[0] = cursorBlockPos.X; + _cursorBlock[2] = cursorBlockPos.Y; + _cursorChunk[0] = _cursorBlock[0] >> 4; + _cursorChunk[1] = _cursorBlock[2] >> 4; + _cursorRegion[0] = _cursorBlock[0] >> 9; + _cursorRegion[1] = _cursorBlock[2] >> 9; + + var cursorBlockRegion = Map.GetRegion(new Point(_cursorRegion[0], _cursorRegion[1])); + if (cursorBlockRegion?.IsComplete ?? false) + { + var cursorBlockChunk = cursorBlockRegion[_cursorChunk[0] % 32, _cursorChunk[1] % 32]; + var x = _cursorBlock[0] % 16; + var z = _cursorBlock[2] % 16; + _cursorBlock[1] = (int)cursorBlockChunk.GetHeight(x, z); + _cursorBlockBiomeId = (int)cursorBlockChunk.GetBiome(x, z); + _cursorBlockBiome = Map.BiomeRegistry.GetBiome(_cursorBlockBiomeId); + } + } + + private static uint GetColor(System.Drawing.Color color) + { + return (uint)( + 0xFF << 24 + | ((color.B & 0xFF) << 16) + | ((color.G & 0xFF) << 8) + | ((color.R & 0xFF) << 0) + ); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Components/GuiMapViewer.cs b/src/MiMap.Viewer.DesktopGL/Components/GuiMapViewer.cs new file mode 100644 index 0000000..11ca130 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Components/GuiMapViewer.cs @@ -0,0 +1,417 @@ +// /* +// * MiMap.Viewer.DesktopGL +// * +// * Copyright (c) 2020 Dan Spiteri +// * +// * / + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ImGuiNET; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using MiMap.Viewer.DesktopGL.Graphics; +using NLog; +using OpenAPI.WorldGenerator.Generators.Biomes; +using OpenAPI.WorldGenerator.Utils; + +namespace MiMap.Viewer.DesktopGL.Components +{ + public partial class GuiMapViewer : DrawableGameComponent + { + public const float MinScale = 0.2f; + public const float MaxScale = 8f; + private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); + + private Rectangle _visibleChunkBounds; + private Rectangle _mapBounds; + private float _scale = 0.5f; + private ConcurrentQueue _pendingRegions; + private ConcurrentQueue _pendingChunks; + private bool _chunksDirty = false; + private Map _map; + private Point _mapPosition; + private Matrix _transform; + private int _drawOrder = 1; + private bool _visible = true; + private IRegionMeshManager _regionMeshManager; + private BasicEffect _effect; + private BasicEffect _spriteBatchEffect; + private VertexBuffer _vertexBuffer; + private IndexBuffer _indexBuffer; + private ImGuiRenderer _gui; + private Rectangle _bounds; + private MouseState _mouseState; + + private SpriteBatch _spriteBatch; + + public Rectangle Bounds + { + get => _bounds; + set + { + if (_bounds == value) return; + _bounds = value; + RecalculateMapBounds(); + } + } + + public Point MapPosition + { + get => _mapPosition; + set + { + if (_mapPosition == value) return; + _mapPosition = value; + RecalculateTransform(); + } + } + + public Rectangle MapBounds + { + get => _mapBounds; + private set + { + if (_mapBounds == value) + return; + + _mapBounds = value; + OnMapBoundsChanged(); + } + } + + public Map Map + { + get => _map; + set + { + if (_map != default) + { + _map.RegionGenerated -= OnRegionGenerated; + _map.ChunkGenerated -= OnChunkGenerated; + } + + _map = value; + + if (_map != default) + { + _map.RegionGenerated += OnRegionGenerated; + _map.ChunkGenerated += OnChunkGenerated; + } + } + } + + + public Matrix Transform + { + get => _transform; + private set + { + _transform = value; + InverseTransform = Matrix.Invert(value); + } + } + + private Matrix InverseTransform { get; set; } + + public GuiMapViewer(MiMapViewer game, Map map) : base(game) + { + Map = map; + _pendingRegions = new ConcurrentQueue(); + _pendingChunks = new ConcurrentQueue(); + _regionMeshManager = game.RegionMeshManager; + _gui = game.ImGuiRenderer; + } + + public override void Initialize() + { + base.Initialize(); + + _spriteBatch = new SpriteBatch(GraphicsDevice); + _spriteBatchEffect = new BasicEffect(GraphicsDevice) + { + TextureEnabled = true, + VertexColorEnabled = true + }; + _effect = new BasicEffect(GraphicsDevice) + { + TextureEnabled = true, + VertexColorEnabled = false + }; + + Game.GraphicsDevice.DeviceReset += (s, o) => UpdateBounds(); + Game.Activated += (s, o) => UpdateBounds(); + Game.Window.ClientSizeChanged += (s, o) => UpdateBounds(); + UpdateBounds(); + + RecalculateTransform(); + _vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionTexture), 4, BufferUsage.WriteOnly); + _indexBuffer = new IndexBuffer(GraphicsDevice, typeof(ushort), 6, BufferUsage.WriteOnly); + _vertexBuffer.SetData(new[] + { + new VertexPositionTexture(new Vector3(0f, 0f, 0f), new Vector2(0f, 0f)), + new VertexPositionTexture(new Vector3(512f, 0f, 0f), new Vector2(1f, 0f)), + new VertexPositionTexture(new Vector3(0f, 512f, 0f), new Vector2(0f, 1f)), + new VertexPositionTexture(new Vector3(512f, 512f, 0f), new Vector2(1f, 1f)), + }); + _indexBuffer.SetData(new ushort[] + { + 0, 1, 2, + 1, 3, 2 + }); + } + + public override void Update(GameTime gameTime) + { + UpdateBounds(); + UpdateMouseInput(); + + if (Map == default) return; + + if (_chunksDirty) + { + Map.EnqueueChunks(_mapBounds); // generate the visible chunks + + _chunksDirty = false; + } + + if (!_pendingChunks.IsEmpty || !_pendingRegions.IsEmpty) + { + var sw = Stopwatch.StartNew(); + MapRegion r; + while (_pendingRegions.TryDequeue(out r) && sw.ElapsedMilliseconds < 36f) + { + //_regions.Add(_regionMeshManager.CacheRegion(r)); + } + + MapChunk c; + while (_pendingChunks.TryDequeue(out c) && sw.ElapsedMilliseconds < 50f) + { + _regionMeshManager.CacheRegionChunk(c); +// _chunks.Add(_regionMeshManager.CacheChunk(c)); + } + } + } + + #region Drawing + + public override void Draw(GameTime gameTime) + { + base.Draw(gameTime); + + if (Map != default) + { + DrawMap(gameTime); + } + + _gui.BeforeLayout(gameTime); + + DrawImGui(); + + _gui.AfterLayout(); + } + + private void DrawMap(GameTime gameTime) + { + DrawMap_Region(gameTime); + } + + private static readonly Point RegionSize = new Point(512, 512); + private static readonly Rectangle ChunkSize = new Rectangle(0, 0, 16, 16); + private int[] _mapPositions = new int[2]; + + // private void DrawMap_Chunk(GameTime gameTime) + // { + // _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullNone, _spriteBatchEffect, Transform); + // + // foreach (var chunk in _chunks) + // { + // _spriteBatch.Draw(chunk.Texture, chunk.Position.ToVector2(), Color.White); + // // _spriteBatch.Draw(chunk.Texture, chunk.Position.ToVector2(), ChunkSize, Color.White, 0, Vector2.Zero, _scale,SpriteEffects.FlipVertically, 0); + // } + // + // _spriteBatch.DrawRectangle(new Rectangle(_cursorBlock[0], _cursorBlock[2], 1, 1), Color.White); + // _spriteBatch.DrawRectangle(new Rectangle(_cursorChunk[0] << 4, _cursorChunk[1] << 4, 16, 16), Color.Yellow); + // _spriteBatch.DrawRectangle(new Rectangle(_cursorRegion[0] << 9, _cursorRegion[1] << 9, 512, 512), Color.PaleGreen); + // + // _spriteBatch.End(); + // } + + private void DrawMap_Region(GameTime gameTime) + { + using (var cxt = GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.None, RasterizerState.CullNone, SamplerState.LinearClamp)) + { + foreach (var region in _regionMeshManager.Regions) + { + cxt.GraphicsDevice.SetVertexBuffer(_vertexBuffer); + cxt.GraphicsDevice.Indices = _indexBuffer; + + _effect.World = region.World * Transform; + _effect.Texture = region.Texture; + + foreach (var p in _effect.CurrentTechnique.Passes) + { + p.Apply(); + cxt.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 2); + } + } + } + + _spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone, _spriteBatchEffect, Transform); + + _spriteBatch.DrawRectangle(new Rectangle(_cursorBlock[0], _cursorBlock[2], 1, 1), Color.White); + _spriteBatch.DrawRectangle(new Rectangle(_cursorChunk[0] << 4, _cursorChunk[1] << 4, 16, 16), Color.Yellow); + _spriteBatch.DrawRectangle(new Rectangle(_cursorRegion[0] << 9, _cursorRegion[1] << 9, 512, 512), Color.PaleGreen); + + _spriteBatch.DrawLine(1f, new Vector2(_mapBounds.X, _cursorBlock[2]), new Vector2(_mapBounds.X + _mapBounds.Width, _cursorBlock[2]), Color.DarkSlateGray * 0.65f); + _spriteBatch.DrawLine(1f, new Vector2(_cursorBlock[0], _mapBounds.Y), new Vector2(_cursorBlock[0], _mapBounds.Y + _mapBounds.Height), Color.DarkSlateGray * 0.65f); + + _spriteBatch.End(); + } + + #endregion + + private Rectangle _previousClientBounds = Rectangle.Empty; + + private void UpdateBounds() + { + var clientBounds = Game.Window.ClientBounds; + if (clientBounds != _previousClientBounds) + { + Bounds = new Rectangle(Point.Zero, Game.Window.ClientBounds.Size); + RecalculateMapBounds(); + } + } + + private void RecalculateTransform() + { + if (_scale < MinScale) _scale = MinScale; + if (_scale > MaxScale) _scale = MaxScale; + + var p = MapPosition; + Transform = Matrix.Identity + // * Matrix.CreateRotationX(MathHelper.Pi) + * Matrix.CreateTranslation(-p.X, -p.Y, 0) + * Matrix.CreateScale(_scale, _scale, 1f) + ; + + RecalculateMapBounds(); + } + + private void RecalculateMapBounds() + { + var screenBounds = _bounds; + + // var bbSize = (_bounds.Size.ToVector2()) / _scale; + // var bbCenter = (_mapPosition.ToVector2() + _bounds.Center.ToVector2()) / _scale; + // var bbMin = _mapPosition.ToVector2() / _scale; + var bbMin = Unproject(Point.Zero); + var bbSize = Unproject(_bounds.Size) - bbMin; + + MapBounds = new Rectangle(bbMin, bbSize); + + if (_effect != default) + { + _effect.World = Transform; + _effect.View = Matrix.CreateLookAt(Vector3.Backward, Vector3.Zero, Vector3.Up); + _effect.Projection = Matrix.CreateOrthographicOffCenter(0, screenBounds.Width, screenBounds.Height, 0, 0f, 1f); + } + + if (_spriteBatchEffect != default) + { + _spriteBatchEffect.World = Transform; + _spriteBatchEffect.View = Matrix.CreateLookAt(Vector3.Backward, Vector3.Zero, Vector3.Up); + _spriteBatchEffect.Projection = Matrix.CreateOrthographicOffCenter(0, screenBounds.Width, screenBounds.Height, 0, 0f, 1f); + } + } + + private void OnMapBoundsChanged() + { + //_chunksDirty = true; + var b = _mapBounds; + var visibleChunkBounds = new Rectangle(b.X >> 4, b.Y >> 4, b.Width >> 4, b.Height >> 4); + if (visibleChunkBounds != _visibleChunkBounds) + { + _chunksDirty = true; + } + + _visibleChunkBounds = visibleChunkBounds; + + Log.Info($"Map bounds changed: {_mapBounds.X:000}, {_mapBounds.Y:000} => {_mapBounds.Width:0000} x {_mapBounds.Height:0000}"); + } + + private void OnRegionGenerated(object sender, Point regionPosition) + { + //_chunksDirty = true; + // var region = Map.GetRegion(regionPosition); + // if (region != null) + // { + // _regions.Add(_regionMeshManager.CacheRegion(region)); + // + // Log.Info($"Region generated: {regionPosition.X:000}, {regionPosition.Y:000}"); + // } + } + + private void OnChunkGenerated(object sender, Point chunkPosition) + { + // _chunksDirty = true; + var chunk = Map.GetChunk(chunkPosition); + if (chunk != null) + { + _pendingChunks.Enqueue(chunk); + + Log.Debug($"Chunk generated: {chunkPosition.X:000}, {chunkPosition.Y:000}"); + } + } + + #region Mouse Events + + private void UpdateMouseInput() + { + if (ImGui.GetIO().WantCaptureMouse) + return; + + var newState = Mouse.GetState(); + + if (_mouseState.Position != newState.Position) + { + var bounds = Game.Window.ClientBounds; + // var currPos = new Point(newState.X - bounds.X, bounds.Height - (newState.Y - bounds.Y)); + // var prevPos = new Point(_mouseState.X - bounds.X, bounds.Height - (_mouseState.Y - bounds.Y)); + //var currPos = new Point(newState.X - bounds.X, newState.Y - bounds.Y); + //var prevPos = new Point(_mouseState.X - bounds.X, _mouseState.Y - bounds.Y); + + var currPos = new Point(newState.X, newState.Y); + var prevPos = new Point(_mouseState.X, _mouseState.Y); + var isPressed = _mouseState.LeftButton == ButtonState.Pressed; + + OnCursorMove_ImGui(currPos, prevPos, isPressed); + OnCursorMove(currPos, prevPos, isPressed); + _cursorPosition = currPos; + } + + _mouseState = newState; + } + + private Point Unproject(Point cursor) + { + return (cursor.ToVector2() / _scale).ToPoint() + _mapPosition; + } + + private void OnCursorMove(Point cursorPosition, Point previousCursorPosition, bool isCursorDown) + { + + if (isCursorDown) + { + var p = ((cursorPosition - previousCursorPosition).ToVector2() / _scale).ToPoint(); + MapPosition += new Point(-p.X, -p.Y); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Content/Content.mgcb b/src/MiMap.Viewer.DesktopGL/Content/Content.mgcb new file mode 100644 index 0000000..e69de29 diff --git a/src/MiMap.Viewer.DesktopGL/Globals.cs b/src/MiMap.Viewer.DesktopGL/Globals.cs new file mode 100644 index 0000000..5bd7f41 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Globals.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace MiMap.Viewer.DesktopGL +{ + public static class Globals + { + public static readonly Color[] BiomeColors; + + static Globals() + { + BiomeColors = new Color[byte.MaxValue]; + + byte i = 0; + BiomeColors[i++] = new Color(0xFF700000); + BiomeColors[i++] = new Color(0xFF60B38D); + BiomeColors[i++] = new Color(0xFF1894FA); + BiomeColors[i++] = new Color(0xFF606060); + BiomeColors[i++] = new Color(0xFF216605); + BiomeColors[i++] = new Color(0xFF59660B); + BiomeColors[i++] = new Color(0xFFB2F907); + BiomeColors[i++] = new Color(0xFFFF0000); + BiomeColors[i++] = new Color(0xFF0000FF); + BiomeColors[i++] = new Color(0xFFFF8080); + BiomeColors[i++] = new Color(0xFFA09090); + BiomeColors[i++] = new Color(0xFFFFA0A0); + BiomeColors[i++] = new Color(0xFFFFFFFF); + BiomeColors[i++] = new Color(0xFFA0A0A0); + BiomeColors[i++] = new Color(0xFFFF00FF); + BiomeColors[i++] = new Color(0xFFFF00A0); + BiomeColors[i++] = new Color(0xFF55DEFA); + BiomeColors[i++] = new Color(0xFF125FD2); + BiomeColors[i++] = new Color(0xFF1C5522); + BiomeColors[i++] = new Color(0xFF333916); + BiomeColors[i++] = new Color(0xFF9A7872); + BiomeColors[i++] = new Color(0xFF097B53); + BiomeColors[i++] = new Color(0xFF05422C); + BiomeColors[i++] = new Color(0xFF178B62); + BiomeColors[i++] = new Color(0xFF300000); + BiomeColors[i++] = new Color(0xFF84A2A2); + BiomeColors[i++] = new Color(0xFFC0F0FA); + BiomeColors[i++] = new Color(0xFF447430); + BiomeColors[i++] = new Color(0xFF325F1F); + BiomeColors[i++] = new Color(0xFF1A5140); + BiomeColors[i++] = new Color(0xFF4A5531); + BiomeColors[i++] = new Color(0xFF363F24); + BiomeColors[i++] = new Color(0xFF516659); + BiomeColors[i++] = new Color(0xFF3E5F54); + BiomeColors[i++] = new Color(0xFF507050); + BiomeColors[i++] = new Color(0xFF5FB2BD); + BiomeColors[i++] = new Color(0xFF649DA7); + BiomeColors[i++] = new Color(0xFF1545D9); + BiomeColors[i++] = new Color(0xFF6597B0); + BiomeColors[i++] = new Color(0xFF658CCA); + + for (; i < BiomeColors.Length; i++) + { + BiomeColors[i] = Color.Transparent; + } + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/CachedChunkMesh.cs b/src/MiMap.Viewer.DesktopGL/Graphics/CachedChunkMesh.cs new file mode 100644 index 0000000..06bdb41 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/CachedChunkMesh.cs @@ -0,0 +1,68 @@ +using System; +using System.Runtime.CompilerServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + public class CachedChunkMesh : ITile, IDisposable + { + // FaceGroupOptimizer + // FaceGroupUtil + + public int X { get; } + public int Z { get; } + public Point Position { get; } + public Texture2D Texture { get; private set; } + + public Matrix World { get; private set; } + + public CachedChunkMesh(GraphicsDevice graphics, MapChunk chunk) + { + X = chunk.X; + Z = chunk.Z; + Position = new Point(X << 4, Z << 4); + + var t = new Texture2D(graphics, 1 << 4, 1 << 4); + var d = new Color[(1 << 4) * (1 << 4)]; + + int x, y, z; + byte b; + Color c; + + for (int bx = 0; bx < 16; bx++) + for (int bz = 0; bz < 16; bz++) + { + b = chunk.GetBiome(bx, bz); + y = chunk.GetHeight(bx, bz); + c = chunk.GetColor(bx, bz); + + SetData(d, bx, bz, c); + } + + + // for (int i = 0; i < 16; i++) + // for (int j = 0; j < 1; j++) + // { + // SetData(d, i, j, Color.Red * 0.5f); + // SetData(d, j, i, Color.Blue * 0.5f); + // } + + t.SetData(d); + Texture = t; + World = Matrix.CreateWorld(new Vector3((chunk.X << 4), (chunk.Z << 4), 0f), Vector3.Forward, Vector3.Up); + + } + + public void Dispose() + { + Texture?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SetData(Color[] data, int blockX, int blockZ, Color color) + { + data[(blockZ * 16) + blockX] = color; + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/CachedRegionMesh.cs b/src/MiMap.Viewer.DesktopGL/Graphics/CachedRegionMesh.cs new file mode 100644 index 0000000..254e830 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/CachedRegionMesh.cs @@ -0,0 +1,115 @@ +using System; +using System.Runtime.CompilerServices; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + public interface ITile + { + int X { get; } + int Z { get; } + Point Position { get; } + Texture2D Texture { get; } + Matrix World { get; } + } + public class CachedRegionMesh : ITile, IDisposable + { + // FaceGroupOptimizer + // FaceGroupUtil + + public int X { get; } + public int Z { get; } + public Point Position { get; } + public Texture2D Texture { get; } + + public Matrix World { get; } + + public CachedRegionMesh(GraphicsDevice graphics, int x, int z) + { + X = x; + Z = z; + Position = new Point(X << 9, Z << 9); + var t = new Texture2D(graphics, 1 << 9, 1 << 9); + + Texture = t; + World = Matrix.CreateWorld(new Vector3(Position.X, Position.Y, 0f), Vector3.Forward, Vector3.Up); + } + + public CachedRegionMesh(GraphicsDevice graphics, MapRegion region) : this(graphics, region.X, region.Z) + { + var d = new Color[(1 << 9) * (1 << 9)]; + + int x, y, z; + byte b; + Color c; + + for (int cx = 0; cx < 32; cx++) + for (int cz = 0; cz < 32; cz++) + { + var chunk = region[cx, cz]; + if (chunk != default) + { + for (int bx = 0; bx < 16; bx++) + for (int bz = 0; bz < 16; bz++) + { + x = (cx << 4) + bx; + z = (cz << 4) + bz; + b = chunk.GetBiome(bx, bz); + y = chunk.GetHeight(bx, bz); + c = chunk.GetColor(bx, bz); + + SetData(d, x, z, c); + } + } + } + + for (int i = 0; i < 512; i++) + for (int j = 0; j < 5; j++) + { + SetData(d, i, j, Color.Red); + SetData(d, j, i, Color.Blue); + } + + Texture.SetData(d); + } + + public void UpdateChunk(MapChunk chunk) + { + if (chunk == default) + return; + + var cx = chunk.X & 0x1F; + var cz = chunk.Z & 0x1F; + + int x, y, z; + byte b; + Color c; + var d = new Color[(1 << 4) * (1 << 4)]; + + for (int bx = 0; bx < 16; bx++) + for (int bz = 0; bz < 16; bz++) + { + b = chunk.GetBiome(bx, bz); + y = chunk.GetHeight(bx, bz); + c = chunk.GetColor(bx, bz); + + d[(bz * 16) + bx] = c; + } + + var r = new Rectangle(cx << 4, cz << 4, 16, 16); + Texture.SetData(0, r, d, 0, 16*16); + } + + public void Dispose() + { + Texture?.Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void SetData(Color[] data, int blockX, int blockZ, Color color) + { + data[(blockZ * 512) + blockX] = color; + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/DrawVertDeclaration.cs b/src/MiMap.Viewer.DesktopGL/Graphics/DrawVertDeclaration.cs new file mode 100644 index 0000000..61e1841 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/DrawVertDeclaration.cs @@ -0,0 +1,30 @@ +using ImGuiNET; +using Microsoft.Xna.Framework.Graphics; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + public static class DrawVertDeclaration + { + public static readonly VertexDeclaration Declaration; + + public static readonly int Size; + + static DrawVertDeclaration() + { + unsafe { Size = sizeof(ImDrawVert); } + + Declaration = new VertexDeclaration( + Size, + + // Position + new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0), + + // UV + new VertexElement(8, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0), + + // Color + new VertexElement(16, VertexElementFormat.Color, VertexElementUsage.Color, 0) + ); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/GraphicsContext.cs b/src/MiMap.Viewer.DesktopGL/Graphics/GraphicsContext.cs new file mode 100644 index 0000000..59df02f --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/GraphicsContext.cs @@ -0,0 +1,263 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + public class GraphicsContext : IDisposable + { + public EventHandler Disposed; + + public GraphicsDevice GraphicsDevice { get; } + + #region Properties + + private readonly PropertyState _viewportProperty; + private readonly PropertyState _blendStateProperty; + private readonly PropertyState _depthStencilStateProperty; + private readonly PropertyState _rasterizerStateProperty; + private readonly PropertyState _samplerStateProperty; + private readonly PropertyState _scissorRectangleProperty; + private readonly PropertyState _blendFactorProperty; + + public Viewport Viewport + { + get => _viewportProperty; + set => _viewportProperty.Set(value); + } + public BlendState BlendState + { + get => _blendStateProperty; + set => _blendStateProperty.Set(value); + } + public DepthStencilState DepthStencilState + { + get => _depthStencilStateProperty; + set => _depthStencilStateProperty.Set(value); + } + public RasterizerState RasterizerState + { + get => _rasterizerStateProperty; + set => _rasterizerStateProperty.Set(value); + } + public SamplerState SamplerState + { + get => _samplerStateProperty; + set => _samplerStateProperty.Set(value); + } + public Rectangle ScissorRectangle + { + get => _scissorRectangleProperty; + set => _scissorRectangleProperty.Set(value); + } + public Color BlendFactor + { + get => _blendFactorProperty; + set => _blendFactorProperty.Set(value); + } + + #endregion + + public GraphicsContext(GraphicsDevice graphicsDevice) + { + GraphicsDevice = graphicsDevice; + + _viewportProperty = CreateState(g => g.Viewport, (g, v) => g.Viewport = v); + _blendStateProperty = CreateState(g => g.BlendState, (g, v) => g.BlendState = v); + _depthStencilStateProperty = CreateState(g => g.DepthStencilState, (g, v) => g.DepthStencilState = v); + _rasterizerStateProperty = CreateState(g => g.RasterizerState, (g, v) => g.RasterizerState = v); + _samplerStateProperty = CreateState(g => g.SamplerStates[0], (g, v) => g.SamplerStates[0] = v); + _scissorRectangleProperty = CreateState(g => g.ScissorRectangle, (g, v) => g.ScissorRectangle = v); + _blendFactorProperty = CreateState(g => g.BlendFactor, (g, v) => g.BlendFactor = v); + } + + public GraphicsContext CreateContext(BlendState blendState = null, DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, SamplerState samplerState = null) + { + return CreateContext(GraphicsDevice, blendState, depthStencilState, rasterizerState, samplerState); + } + + public void RestoreState() + { + ForEachProperty(p => p.RestoreInitialValue()); + } + + private void ForEachProperty(Action> action) + { + if (action == null) return; + + action.Invoke(_viewportProperty); + action.Invoke(_blendStateProperty); + action.Invoke(_depthStencilStateProperty); + action.Invoke(_rasterizerStateProperty); + action.Invoke(_samplerStateProperty); + action.Invoke(_scissorRectangleProperty); + action.Invoke(_blendFactorProperty); + } + + public static GraphicsContext CreateContext(GraphicsDevice graphicsDevice, + BlendState blendState = null, + DepthStencilState depthStencilState = null, + RasterizerState rasterizerState = null, + SamplerState samplerState = null) + { + var context = new GraphicsContext(graphicsDevice); + + if (blendState != null) + { + context.BlendState = blendState; + } + + if (depthStencilState != null) + { + context.DepthStencilState = depthStencilState; + } + + if (rasterizerState != null) + { + context.RasterizerState = rasterizerState; + } + + if (samplerState != null) + { + context.SamplerState = samplerState; + } + + return context; + } + + #region Dispose + + private bool _isDisposed = false; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + ForEachProperty(p => p.Dispose()); + + Disposed?.Invoke(this, null); + } + + _isDisposed = true; + } + } + + public void Dispose() + { + Dispose(true); + } + + #endregion + + + private PropertyState CreateState(Func getProperty, Action setProperty) + { + return new PropertyState(GraphicsDevice, getProperty(GraphicsDevice), setProperty); + } + + public interface IPropertyState : IDisposable + { + void RestoreInitialValue(); + } + + public class PropertyState : IPropertyState + { + public TPropertyType InitialValue { get; } + public TPropertyType Value + { + get => _currentValue; + set => Set(value); + } + + private bool _dirty; + private TPropertyType _currentValue; + private readonly TPropertyOwner _owner; + + private Func _getValueFunc; + private readonly Action _setValueFunc; + + + public PropertyState(TPropertyOwner owner, TPropertyType initialValue, Action setValueFunc) + { + _owner = owner; + _setValueFunc = setValueFunc; + + InitialValue = initialValue; + _currentValue = initialValue; + } + + public TPropertyType Set(TPropertyType newValue) + { + _dirty = true; + _currentValue = newValue; + + _setValueFunc(_owner, _currentValue); + + return Value; + } + + public void RestoreInitialValue() + { + if (_dirty) + { + _setValueFunc(_owner, InitialValue); + } + } + + public void Dispose() + { + RestoreInitialValue(); + } + + public static implicit operator TPropertyType(PropertyState propertyState) + { + return propertyState.Value; + } + } + + + } + + public static class GraphicsContextExtensions + { + public static IDisposable PushRenderTarget(this GraphicsDevice graphicsDevice, RenderTarget2D renderTarget, Viewport viewport) + { + var currentRenderTargets = graphicsDevice.GetRenderTargets(); + graphicsDevice.SetRenderTarget(renderTarget); + + var currentViewport = graphicsDevice.Viewport; + graphicsDevice.Viewport = viewport; + + return new ContextDisposable(() => + { + graphicsDevice.SetRenderTargets(currentRenderTargets); + graphicsDevice.Viewport = currentViewport; + }); + } + + public static IDisposable PushRenderTarget(this GraphicsDevice graphicsDevice, RenderTarget2D renderTarget) + { + var current = graphicsDevice.GetRenderTargets(); + graphicsDevice.SetRenderTarget(renderTarget); + return new ContextDisposable(() => graphicsDevice.SetRenderTargets(current)); + } + } + + + internal class ContextDisposable : IDisposable + { + private readonly Action _onDisposeAction; + + public ContextDisposable(Action onDisposeAction) + { + _onDisposeAction = onDisposeAction; + } + + public void Dispose() + { + _onDisposeAction?.Invoke(); + } + } +} diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/ImGuiRenderer.cs b/src/MiMap.Viewer.DesktopGL/Graphics/ImGuiRenderer.cs new file mode 100644 index 0000000..d5b41e2 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/ImGuiRenderer.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using ImGuiNET; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + + /// + /// ImGui renderer for use with XNA-likes (FNA & MonoGame) + /// + public class ImGuiRenderer + { + private Game _game; + + // Graphics + private GraphicsDevice _graphicsDevice; + + private BasicEffect _effect; + private RasterizerState _rasterizerState; + + private byte[] _vertexData; + private VertexBuffer _vertexBuffer; + private int _vertexBufferSize; + + private byte[] _indexData; + private IndexBuffer _indexBuffer; + private int _indexBufferSize; + + // Textures + private Dictionary _loadedTextures; + + private int _textureId; + private IntPtr? _fontTextureId; + + // Input + private int _scrollWheelValue; + + private List _keys = new List(); + + public ImGuiRenderer(Game game) + { + var context = ImGui.CreateContext(); + ImGui.SetCurrentContext(context); + + _game = game ?? throw new ArgumentNullException(nameof(game)); + _graphicsDevice = game.GraphicsDevice; + + _loadedTextures = new Dictionary(); + + _rasterizerState = new RasterizerState() + { + CullMode = CullMode.None, + DepthBias = 0, + FillMode = FillMode.Solid, + MultiSampleAntiAlias = false, + ScissorTestEnable = true, + SlopeScaleDepthBias = 0 + }; + + SetupInput(); + } + + #region ImGuiRenderer + + /// + /// Creates a texture and loads the font data from ImGui. Should be called when the is initialized but before any rendering is done + /// + public virtual unsafe void RebuildFontAtlas() + { + // Get font texture from ImGui + var io = ImGui.GetIO(); + io.Fonts.GetTexDataAsRGBA32(out byte* pixelData, out int width, out int height, out int bytesPerPixel); + + // Copy the data to a managed array + var pixels = new byte[width * height * bytesPerPixel]; + unsafe { Marshal.Copy(new IntPtr(pixelData), pixels, 0, pixels.Length); } + + // Create and register the texture as an XNA texture + var tex2d = new Texture2D(_graphicsDevice, width, height, false, SurfaceFormat.Color); + tex2d.SetData(pixels); + + // Should a texture already have been build previously, unbind it first so it can be deallocated + if (_fontTextureId.HasValue) UnbindTexture(_fontTextureId.Value); + + // Bind the new texture to an ImGui-friendly id + _fontTextureId = BindTexture(tex2d); + + // Let ImGui know where to find the texture + io.Fonts.SetTexID(_fontTextureId.Value); + io.Fonts.ClearTexData(); // Clears CPU side texture data + } + + /// + /// Creates a pointer to a texture, which can be passed through ImGui calls such as . That pointer is then used by ImGui to let us know what texture to draw + /// + public virtual IntPtr BindTexture(Texture2D texture) + { + var id = new IntPtr(_textureId++); + + _loadedTextures.Add(id, texture); + + return id; + } + + /// + /// Removes a previously created texture pointer, releasing its reference and allowing it to be deallocated + /// + public virtual void UnbindTexture(IntPtr textureId) + { + _loadedTextures.Remove(textureId); + } + + /// + /// Sets up ImGui for a new frame, should be called at frame start + /// + public virtual void BeforeLayout(GameTime gameTime) + { + ImGui.GetIO().DeltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; + + UpdateInput(); + + ImGui.NewFrame(); + } + + /// + /// Asks ImGui for the generated geometry data and sends it to the graphics pipeline, should be called after the UI is drawn using ImGui.** calls + /// + public virtual void AfterLayout() + { + ImGui.Render(); + + unsafe { RenderDrawData(ImGui.GetDrawData()); } + } + + #endregion ImGuiRenderer + + #region Setup & Update + + /// + /// Maps ImGui keys to XNA keys. We use this later on to tell ImGui what keys were pressed + /// + protected virtual void SetupInput() + { + var io = ImGui.GetIO(); + + _keys.Add(io.KeyMap[(int)ImGuiKey.Tab] = (int)Keys.Tab); + _keys.Add(io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Keys.Left); + _keys.Add(io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Keys.Right); + _keys.Add(io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Keys.Up); + _keys.Add(io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Keys.Down); + _keys.Add(io.KeyMap[(int)ImGuiKey.PageUp] = (int)Keys.PageUp); + _keys.Add(io.KeyMap[(int)ImGuiKey.PageDown] = (int)Keys.PageDown); + _keys.Add(io.KeyMap[(int)ImGuiKey.Home] = (int)Keys.Home); + _keys.Add(io.KeyMap[(int)ImGuiKey.End] = (int)Keys.End); + _keys.Add(io.KeyMap[(int)ImGuiKey.Delete] = (int)Keys.Delete); + _keys.Add(io.KeyMap[(int)ImGuiKey.Backspace] = (int)Keys.Back); + _keys.Add(io.KeyMap[(int)ImGuiKey.Enter] = (int)Keys.Enter); + _keys.Add(io.KeyMap[(int)ImGuiKey.Escape] = (int)Keys.Escape); + _keys.Add(io.KeyMap[(int)ImGuiKey.Space] = (int)Keys.Space); + _keys.Add(io.KeyMap[(int)ImGuiKey.A] = (int)Keys.A); + _keys.Add(io.KeyMap[(int)ImGuiKey.C] = (int)Keys.C); + _keys.Add(io.KeyMap[(int)ImGuiKey.V] = (int)Keys.V); + _keys.Add(io.KeyMap[(int)ImGuiKey.X] = (int)Keys.X); + _keys.Add(io.KeyMap[(int)ImGuiKey.Y] = (int)Keys.Y); + _keys.Add(io.KeyMap[(int)ImGuiKey.Z] = (int)Keys.Z); + + // MonoGame-specific ////////////////////// + _game.Window.TextInput += (s, a) => + { + if (a.Character == '\t') return; + + io.AddInputCharacter(a.Character); + }; + /////////////////////////////////////////// + + // FNA-specific /////////////////////////// + //TextInputEXT.TextInput += c => + //{ + // if (c == '\t') return; + + // ImGui.GetIO().AddInputCharacter(c); + //}; + /////////////////////////////////////////// + + ImGui.GetIO().Fonts.AddFontDefault(); + } + + /// + /// Updates the to the current matrices and texture + /// + protected virtual Effect UpdateEffect(Texture2D texture) + { + _effect = _effect ?? new BasicEffect(_graphicsDevice); + + var io = ImGui.GetIO(); + + _effect.World = Matrix.Identity; + _effect.View = Matrix.Identity; + _effect.Projection = Matrix.CreateOrthographicOffCenter(0f, io.DisplaySize.X, io.DisplaySize.Y, 0f, -1f, 1f); + _effect.TextureEnabled = true; + _effect.Texture = texture; + _effect.VertexColorEnabled = true; + + return _effect; + } + + /// + /// Sends XNA input state to ImGui + /// + protected virtual void UpdateInput() + { + var io = ImGui.GetIO(); + + var mouse = Mouse.GetState(); + var keyboard = Keyboard.GetState(); + + for (int i = 0; i < _keys.Count; i++) + { + io.KeysDown[_keys[i]] = keyboard.IsKeyDown((Keys)_keys[i]); + } + + io.KeyShift = keyboard.IsKeyDown(Keys.LeftShift) || keyboard.IsKeyDown(Keys.RightShift); + io.KeyCtrl = keyboard.IsKeyDown(Keys.LeftControl) || keyboard.IsKeyDown(Keys.RightControl); + io.KeyAlt = keyboard.IsKeyDown(Keys.LeftAlt) || keyboard.IsKeyDown(Keys.RightAlt); + io.KeySuper = keyboard.IsKeyDown(Keys.LeftWindows) || keyboard.IsKeyDown(Keys.RightWindows); + + io.DisplaySize = new System.Numerics.Vector2(_graphicsDevice.PresentationParameters.BackBufferWidth, _graphicsDevice.PresentationParameters.BackBufferHeight); + io.DisplayFramebufferScale = new System.Numerics.Vector2(1f, 1f); + + io.MousePos = new System.Numerics.Vector2(mouse.X, mouse.Y); + + io.MouseDown[0] = mouse.LeftButton == ButtonState.Pressed; + io.MouseDown[1] = mouse.RightButton == ButtonState.Pressed; + io.MouseDown[2] = mouse.MiddleButton == ButtonState.Pressed; + + var scrollDelta = mouse.ScrollWheelValue - _scrollWheelValue; + io.MouseWheel = scrollDelta > 0 ? 1 : scrollDelta < 0 ? -1 : 0; + _scrollWheelValue = mouse.ScrollWheelValue; + } + + #endregion Setup & Update + + #region Internals + + /// + /// Gets the geometry as set up by ImGui and sends it to the graphics device + /// + private void RenderDrawData(ImDrawDataPtr drawData) + { + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers + var lastViewport = _graphicsDevice.Viewport; + var lastScissorBox = _graphicsDevice.ScissorRectangle; + + _graphicsDevice.BlendFactor = Color.White; + _graphicsDevice.BlendState = BlendState.NonPremultiplied; + _graphicsDevice.RasterizerState = _rasterizerState; + _graphicsDevice.DepthStencilState = DepthStencilState.DepthRead; + + // Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays) + drawData.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale); + + // Setup projection + _graphicsDevice.Viewport = new Viewport(0, 0, _graphicsDevice.PresentationParameters.BackBufferWidth, _graphicsDevice.PresentationParameters.BackBufferHeight); + + UpdateBuffers(drawData); + + RenderCommandLists(drawData); + + // Restore modified state + _graphicsDevice.Viewport = lastViewport; + _graphicsDevice.ScissorRectangle = lastScissorBox; + } + + private unsafe void UpdateBuffers(ImDrawDataPtr drawData) + { + if (drawData.TotalVtxCount == 0) + { + return; + } + + // Expand buffers if we need more room + if (drawData.TotalVtxCount > _vertexBufferSize) + { + _vertexBuffer?.Dispose(); + + _vertexBufferSize = (int)(drawData.TotalVtxCount * 1.5f); + _vertexBuffer = new VertexBuffer(_graphicsDevice, DrawVertDeclaration.Declaration, _vertexBufferSize, BufferUsage.None); + _vertexData = new byte[_vertexBufferSize * DrawVertDeclaration.Size]; + } + + if (drawData.TotalIdxCount > _indexBufferSize) + { + _indexBuffer?.Dispose(); + + _indexBufferSize = (int)(drawData.TotalIdxCount * 1.5f); + _indexBuffer = new IndexBuffer(_graphicsDevice, IndexElementSize.SixteenBits, _indexBufferSize, BufferUsage.None); + _indexData = new byte[_indexBufferSize * sizeof(ushort)]; + } + + // Copy ImGui's vertices and indices to a set of managed byte arrays + int vtxOffset = 0; + int idxOffset = 0; + + for (int n = 0; n < drawData.CmdListsCount; n++) + { + ImDrawListPtr cmdList = drawData.CmdListsRange[n]; + + fixed (void* vtxDstPtr = &_vertexData[vtxOffset * DrawVertDeclaration.Size]) + fixed (void* idxDstPtr = &_indexData[idxOffset * sizeof(ushort)]) + { + Buffer.MemoryCopy((void*)cmdList.VtxBuffer.Data, vtxDstPtr, _vertexData.Length, cmdList.VtxBuffer.Size * DrawVertDeclaration.Size); + Buffer.MemoryCopy((void*)cmdList.IdxBuffer.Data, idxDstPtr, _indexData.Length, cmdList.IdxBuffer.Size * sizeof(ushort)); + } + + vtxOffset += cmdList.VtxBuffer.Size; + idxOffset += cmdList.IdxBuffer.Size; + } + + // Copy the managed byte arrays to the gpu vertex- and index buffers + _vertexBuffer.SetData(_vertexData, 0, drawData.TotalVtxCount * DrawVertDeclaration.Size); + _indexBuffer.SetData(_indexData, 0, drawData.TotalIdxCount * sizeof(ushort)); + } + + private unsafe void RenderCommandLists(ImDrawDataPtr drawData) + { + _graphicsDevice.SetVertexBuffer(_vertexBuffer); + _graphicsDevice.Indices = _indexBuffer; + + int vtxOffset = 0; + int idxOffset = 0; + + for (int n = 0; n < drawData.CmdListsCount; n++) + { + ImDrawListPtr cmdList = drawData.CmdListsRange[n]; + + for (int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++) + { + ImDrawCmdPtr drawCmd = cmdList.CmdBuffer[cmdi]; + + if (drawCmd.ElemCount == 0) + { + continue; + } + + if (!_loadedTextures.ContainsKey(drawCmd.TextureId)) + { + throw new InvalidOperationException($"Could not find a texture with id '{drawCmd.TextureId}', please check your bindings"); + } + + _graphicsDevice.ScissorRectangle = new Rectangle( + (int)drawCmd.ClipRect.X, + (int)drawCmd.ClipRect.Y, + (int)(drawCmd.ClipRect.Z - drawCmd.ClipRect.X), + (int)(drawCmd.ClipRect.W - drawCmd.ClipRect.Y) + ); + + var effect = UpdateEffect(_loadedTextures[drawCmd.TextureId]); + + foreach (var pass in effect.CurrentTechnique.Passes) + { + pass.Apply(); + +#pragma warning disable CS0618 // // FNA does not expose an alternative method. + _graphicsDevice.DrawIndexedPrimitives( + primitiveType: PrimitiveType.TriangleList, + baseVertex: (int)drawCmd.VtxOffset + vtxOffset, + minVertexIndex: 0, + numVertices: cmdList.VtxBuffer.Size, + startIndex: (int)drawCmd.IdxOffset + idxOffset, + primitiveCount: (int)drawCmd.ElemCount / 3 + ); +#pragma warning restore CS0618 + } + } + + vtxOffset += cmdList.VtxBuffer.Size; + idxOffset += cmdList.IdxBuffer.Size; + } + } + + #endregion Internals + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/RegionMeshManager.cs b/src/MiMap.Viewer.DesktopGL/Graphics/RegionMeshManager.cs new file mode 100644 index 0000000..99d2cf9 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/RegionMeshManager.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + public interface IRegionMeshManager : IDisposable + { + ICollection Regions { get; } + CachedRegionMesh CacheRegion(MapRegion region); + void CacheRegionChunk(MapChunk chunk); + CachedChunkMesh CacheChunk(MapChunk chunk); + } + + public class RegionMeshManager : IRegionMeshManager + { + private GraphicsDevice _graphics; + + private IDictionary _regionCache = new Dictionary(); + private IDictionary _chunkCache = new Dictionary(); + + public ICollection Regions => _regionCache.Values; + + public RegionMeshManager(GraphicsDevice graphics) + { + _graphics = graphics; + } + + public CachedRegionMesh CacheRegion(MapRegion region) + { + var i = new Point(region.X, region.Z); + CachedRegionMesh cached; + if (_regionCache.TryGetValue(i, out cached)) return cached; + cached = new CachedRegionMesh(_graphics, region); + _regionCache[i] = cached; + return cached; + } + + public void CacheRegionChunk(MapChunk chunk) + { + var i = new Point(chunk.X >> 5, chunk.Z >> 5); + + CachedRegionMesh cachedRegion; + if (!_regionCache.TryGetValue(i, out cachedRegion)) + { + cachedRegion = new CachedRegionMesh(_graphics, i.X, i.Y); + _regionCache[i] = cachedRegion; + } + + cachedRegion.UpdateChunk(chunk); + } + + public CachedChunkMesh CacheChunk(MapChunk chunk) + { + var i = new Point(chunk.X, chunk.Z); + CachedChunkMesh cached; + if (_chunkCache.TryGetValue(i, out cached)) + return cached; + cached = new CachedChunkMesh(_graphics, chunk); + _chunkCache[i] = cached; + return cached; + } + + public void Dispose() + { + var points = _regionCache.Keys.ToArray(); + foreach (var p in points) + { + if (_regionCache.Remove(p, out var c)) + { + c.Dispose(); + } + } + points = _chunkCache.Keys.ToArray(); + foreach (var p in points) + { + if (_chunkCache.Remove(p, out var c)) + { + c.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Graphics/SpriteBatchExtensions.cs b/src/MiMap.Viewer.DesktopGL/Graphics/SpriteBatchExtensions.cs new file mode 100644 index 0000000..ce7bf81 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Graphics/SpriteBatchExtensions.cs @@ -0,0 +1,122 @@ +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace MiMap.Viewer.DesktopGL.Graphics +{ + public static class SpriteBatchExtensions + { + private static Texture2D WhiteTexture { get; set; } + + + public static void Init(GraphicsDevice gd) + { + // TODO + WhiteTexture = new Texture2D(MiMapViewer.Instance.GraphicsDevice, 1, 1); + WhiteTexture.SetData(new Color[] {Color.White}); + } + + public static void Dispose() + { + WhiteTexture?.Dispose(); + } + + + /// + /// Draw a line between the two supplied points. + /// + /// Starting point. + /// End point. + /// The draw color. + public static void DrawLine(this SpriteBatch sb, float thickness, Vector2 start, Vector2 end, Color color) + => DrawLine(sb, thickness, start, end, color, Vector2.One, 0); + + /// + /// Draw a line between the two supplied points. + /// + /// Starting point. + /// End point. + /// The draw color. + public static void DrawLine(this SpriteBatch sb, float thickness, Vector2 start, Vector2 end, Color color, Vector2 scale, float layerdepth) + { + float length = (end - start).Length(); + float rotation = (float)Math.Atan2(end.Y - start.Y, end.X - start.X); + sb.Draw(WhiteTexture, start, null, color, rotation, Vector2.Zero, new Vector2(scale.X * length, scale.Y * (thickness)), SpriteEffects.None, layerdepth); + } + + /// + /// Draw a rectangle. + /// + /// The rectangle to draw. + /// The draw color. + public static void DrawRectangle(this SpriteBatch sb, Rectangle rectangle, Color color) + { + sb.Draw(WhiteTexture, new Rectangle(rectangle.Left, rectangle.Top, rectangle.Width, 1), color); + sb.Draw(WhiteTexture, new Rectangle(rectangle.Left, rectangle.Bottom, rectangle.Width, 1), color); + sb.Draw(WhiteTexture, new Rectangle(rectangle.Left, rectangle.Top, 1, rectangle.Height), color); + sb.Draw(WhiteTexture, new Rectangle(rectangle.Right, rectangle.Top, 1, rectangle.Height + 1), color); + } + + /// + /// Fill a rectangle. + /// + /// The rectangle to fill. + /// The fill color. + public static void FillRectangle(this SpriteBatch sb, Rectangle rectangle, Color color) + { + sb.Draw(WhiteTexture, rectangle, color); + } + + public static void FillRectangle(this SpriteBatch sb, Rectangle rectangle, Color color, float layerDepth) + { + sb.Draw(WhiteTexture, rectangle, null, color, 0f, Vector2.Zero, SpriteEffects.None, layerDepth); + } + + public static string GetBytesReadable(long i) + { + // Get absolute value + long absoluteI = (i < 0 ? -i : i); + // Determine the suffix and readable value + string suffix; + double readable; + if (absoluteI >= 0x1000000000000000) // Exabyte + { + suffix = "EB"; + readable = (i >> 50); + } + else if (absoluteI >= 0x4000000000000) // Petabyte + { + suffix = "PB"; + readable = (i >> 40); + } + else if (absoluteI >= 0x10000000000) // Terabyte + { + suffix = "TB"; + readable = (i >> 30); + } + else if (absoluteI >= 0x40000000) // Gigabyte + { + suffix = "GB"; + readable = (i >> 20); + } + else if (absoluteI >= 0x100000) // Megabyte + { + suffix = "MB"; + readable = (i >> 10); + } + else if (absoluteI >= 0x400) // Kilobyte + { + suffix = "KB"; + readable = i; + } + else + { + return i.ToString("0 B"); // Byte + } + // Divide by 1024 to get fractional value + readable = (readable / 1024); + // Return formatted number with suffix + return readable.ToString("0.### ") + suffix; + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Icon.bmp b/src/MiMap.Viewer.DesktopGL/Icon.bmp new file mode 100644 index 0000000..4af43af Binary files /dev/null and b/src/MiMap.Viewer.DesktopGL/Icon.bmp differ diff --git a/src/MiMap.Viewer.DesktopGL/Icon.ico b/src/MiMap.Viewer.DesktopGL/Icon.ico new file mode 100644 index 0000000..2b3de01 Binary files /dev/null and b/src/MiMap.Viewer.DesktopGL/Icon.ico differ diff --git a/src/MiMap.Viewer.DesktopGL/MiMap.Viewer.DesktopGL.csproj b/src/MiMap.Viewer.DesktopGL/MiMap.Viewer.DesktopGL.csproj new file mode 100644 index 0000000..4171eaf --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/MiMap.Viewer.DesktopGL.csproj @@ -0,0 +1,57 @@ + + + + Exe + net6.0 + false + false + latest + Release;Debug + AnyCPU;x64;x86 + true + + + app.manifest + Icon.ico + + + + + + + + Always + + + Always + + + + + + + + + + + false + + + + + + + %(Filename).cs + + + + + + + + + + diff --git a/src/MiMap.Viewer.DesktopGL/MiMapViewer.cs b/src/MiMap.Viewer.DesktopGL/MiMapViewer.cs new file mode 100644 index 0000000..a2ee3e8 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/MiMapViewer.cs @@ -0,0 +1,184 @@ +using System; +using System.IO; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using MiMap.Viewer.DesktopGL.Components; +using MiMap.Viewer.DesktopGL.Graphics; +using MiMap.Viewer.DesktopGL.Models; +using MiNET.Worlds; +using OpenAPI.WorldGenerator.Generators; + +namespace MiMap.Viewer.DesktopGL +{ + public class MiMapViewer : Game + { + public static MiMapViewer Instance { get; private set; } + + private readonly GraphicsDeviceManager _graphics; + public ImGuiRenderer ImGuiRenderer { get; private set; } + private Texture2D _xnaTexture; + private IntPtr _imGuiTexture; + + public IWorldGenerator WorldGenerator { get; private set; } + public Map Map { get; private set; } + public IRegionMeshManager RegionMeshManager { get; private set; } + private GuiMapViewer _mapViewer; + + public MiMapViewer() : base() + { + Instance = this; + _graphics = new GraphicsDeviceManager(this) + { + PreferredBackBufferHeight = 720, + PreferredBackBufferWidth = 1280, + PreferMultiSampling = true, + GraphicsProfile = GraphicsProfile.HiDef + }; + _graphics.PreparingDeviceSettings += (sender, args) => + { + //_graphics.PreferredBackBufferFormat = SurfaceFormat.Color; + _graphics.PreferMultiSampling = true; + _graphics.PreferredBackBufferHeight = 720; + _graphics.PreferredBackBufferWidth = 1280; + _graphics.GraphicsProfile = GraphicsProfile.HiDef; + }; + + Content.RootDirectory = "Content"; + IsMouseVisible = true; + Window.AllowUserResizing = true; + + WorldGenerator = new OverworldGeneratorV2(); + Map = new Map(WorldGenerator); + } + + protected override void Initialize() + { + // _graphics.GraphicsProfile = GraphicsProfile.HiDef; + // _graphics.SynchronizeWithVerticalRetrace = false; +// _graphics.PreferMultiSampling = true; + IsFixedTimeStep = false; + Window.AllowUserResizing = true; + + UpdateViewport(); + Window.ClientSizeChanged += (s, o) => UpdateViewport(); + + SpriteBatchExtensions.Init(GraphicsDevice); + + ImGuiRenderer = new ImGuiRenderer(this); + ImGuiRenderer.RebuildFontAtlas(); + + RegionMeshManager = new RegionMeshManager(GraphicsDevice); + _mapViewer = new GuiMapViewer(this, Map); + _mapViewer.Initialize(); + Components.Add(_mapViewer); + + + // Initialize biome colors + InitializeBiomeColors(); + + } + + private void InitializeBiomeColors() + { + var json = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "biomeColors.json")); + var map = BiomeColors.FromJson(json); + + foreach (var mapping in map.ColorMap) + { + var biome = Map.BiomeRegistry.GetBiome(mapping.BiomeId); + if (biome != default) + { + biome.Color = System.Drawing.Color.FromArgb(mapping.JColor.R, mapping.JColor.G, mapping.JColor.B); + } + } + } + + private void UpdateViewport(bool apply = true) + { + var bounds = Window.ClientBounds; + + if ( + _graphics.PreferredBackBufferWidth != bounds.Width + || + _graphics.PreferredBackBufferHeight != bounds.Height) + { + if (_graphics.IsFullScreen) + { + _graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width; + _graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height; + } + else + { + _graphics.PreferredBackBufferWidth = bounds.Width; + _graphics.PreferredBackBufferHeight = bounds.Height; + } + + if (apply) + _graphics.ApplyChanges(); + } + + ImGuiRenderer?.RebuildFontAtlas(); + //_graphics.GraphicsDevice.Viewport = new Viewport(bounds); + } + + protected override void LoadContent() + { + // First, load the texture as a Texture2D (can also be done using the XNA/FNA content pipeline) + _xnaTexture = CreateTexture(GraphicsDevice, 300, 150, pixel => + { + var red = (pixel % 300) / 2; + return new Color(red, 1, 1); + }); + + // Then, bind it to an ImGui-friendly pointer, that we can use during regular ImGui.** calls (see below) + _imGuiTexture = ImGuiRenderer.BindTexture(_xnaTexture); + + base.LoadContent(); + + UpdateViewport(); + } + + protected override void Update(GameTime gameTime) + { + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || + Keyboard.GetState().IsKeyDown(Keys.Escape)) + Exit(); + + base.Update(gameTime); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + _mapViewer?.Dispose(); + RegionMeshManager?.Dispose(); + Map?.Dispose(); + } + + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.HotPink); + base.Draw(gameTime); + } + + private static Texture2D CreateTexture(GraphicsDevice device, int width, int height, Func paint) + { + //initialize a texture + var texture = new Texture2D(device, width, height); + + //the array holds the color for each pixel in the texture + Color[] data = new Color[width * height]; + for(var pixel = 0; pixel < data.Length; pixel++) + { + //the function applies the color according to the specified pixel + data[pixel] = paint( pixel ); + } + + //set the color + texture.SetData( data ); + + return texture; + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Models/BiomeColors.cs b/src/MiMap.Viewer.DesktopGL/Models/BiomeColors.cs new file mode 100644 index 0000000..2aa934e --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Models/BiomeColors.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace MiMap.Viewer.DesktopGL.Models +{ + public partial class BiomeColors + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("colorMap")] + public BiomeColorMapping[] ColorMap { get; set; } + + public static BiomeColors FromJson(string json) => JsonConvert.DeserializeObject(json); + public string ToJson(BiomeColors self) => JsonConvert.SerializeObject(self); + } + + [JsonConverter(typeof(BiomeColorMappingConverter))] + public partial class BiomeColorMapping + { + public byte BiomeId { get; set; } + + public JColor JColor { get; set; } + } + + public partial class JColor + { + [JsonProperty("r")] + public int R { get; set; } + + [JsonProperty("g")] + public int G { get; set; } + + [JsonProperty("b")] + public int B { get; set; } + } + + public class BiomeColorMappingConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, BiomeColorMapping value, JsonSerializer serializer) + { + writer.WriteStartArray(); + writer.WriteValue(value.BiomeId); + serializer.Serialize(writer, value.JColor); + writer.WriteEndArray(); + } + + public override BiomeColorMapping ReadJson(JsonReader reader, Type objectType, BiomeColorMapping existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.StartArray) + return null; + + var v = existingValue ?? new BiomeColorMapping(); + v.BiomeId = (byte)(reader.ReadAsInt32() ?? 0); + reader.Read(); + v.JColor = serializer.Deserialize(reader); + + reader.Read(); // end array + return v; + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Models/Map.cs b/src/MiMap.Viewer.DesktopGL/Models/Map.cs new file mode 100644 index 0000000..42ceb2f --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Models/Map.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using MiMap.Viewer.DesktopGL.Utilities; +using MiNET.Utils.Vectors; +using MiNET.Worlds; +using NLog; +using OpenAPI.WorldGenerator.Generators.Biomes; + +namespace MiMap.Viewer.DesktopGL +{ + public class Map : IDisposable + { + private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); + + private readonly IWorldGenerator _worldGenerator; + public Dictionary Regions { get; } + + public event EventHandler RegionGenerated; + public event EventHandler ChunkGenerated; + + private Thread _thread; + private ConcurrentQueue _regionsToGenerate; + private AutoResetEvent _trigger; + private bool _running; + + public readonly BiomeRegistry BiomeRegistry = new BiomeRegistry(); + + public Map(IWorldGenerator worldGenerator) + { + _worldGenerator = worldGenerator; + Regions = new Dictionary(); + _regionsToGenerate = new ConcurrentQueue(); + _trigger = new AutoResetEvent(false); + _thread = new Thread(Run) + { + Name = "WorldGenerator", + IsBackground = false + }; + _thread.Start(); + } + + public void Run() + { + _running = true; + + while (_running) + { + if (_trigger.WaitOne(10000)) + { + while (_regionsToGenerate.TryPeek(out var r)) + { + if (Regions.ContainsKey(r)) + { + _regionsToGenerate.TryDequeue(out _); + continue; + } + + Log.Info($"Generating Region: {r.X:000}, {r.Y:000}"); + var sw = Stopwatch.StartNew(); + Regions[r] = new MapRegion(r.X, r.Y); + _regionsToGenerate.TryDequeue(out _); + var chunkGenTimes = new float[32 * 32]; + Parallel.For(0, 32 * 32, new ParallelOptions() + { + MaxDegreeOfParallelism = Environment.ProcessorCount / 2, + TaskScheduler = TaskScheduler.Default + }, + (i) => + { + var cx = (int)Math.Floor(i / 32f); + var cz = i % 32; + var chunkPosition = new ChunkCoordinates((r.X << 5) + cx, (r.Y << 5) + cz); + var csw = Stopwatch.StartNew(); + using var chunk = _worldGenerator.GenerateChunkColumn(chunkPosition); + csw.Stop(); + var t1 = csw.ElapsedMilliseconds; + csw.Restart(); + Regions[r][cx, cz] = ExtractChunkData(chunk); + csw.Stop(); + chunkGenTimes[(cx * 32) + cz] = t1; + Log.Debug($"Completed Chunk {chunkPosition.X:000}, {chunkPosition.Z:000} in {t1:N3} ms (generation: {t1:N3} ms, dataExtraction: {csw.ElapsedMilliseconds:N3} ms)"); + ChunkGenerated?.Invoke(this, new Point(chunkPosition.X, chunkPosition.Z)); + }); + sw.Stop(); + var ctMin = chunkGenTimes.Min(); + var ctMax = chunkGenTimes.Max(); + var ctAvg = chunkGenTimes.Average(); + Log.Info($"Generated Region: {r.X:000}, {r.Y:000} in {sw.ElapsedMilliseconds:N3} ms (ChunkGen: min = {ctMin:N3}, max = {ctMax:N3}, avg = {ctAvg:N3})"); + Regions[r].IsComplete = true; + RegionGenerated?.Invoke(this, r); + } + } + } + } + + private void EnqueueRegion(Point regionCoords) + { + if (Regions.ContainsKey(regionCoords) || _regionsToGenerate.Contains(regionCoords)) + return; + + Log.Info($"Enqueue Region: {regionCoords.X:000}, {regionCoords.Y:000}"); + _regionsToGenerate.Enqueue(regionCoords); + _trigger.Set(); + } + + private MapChunk ExtractChunkData(ChunkColumn chunk) + { + var mapChunk = new MapChunk(chunk.X, chunk.Z); + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { + mapChunk.SetHeight(x, z, chunk.GetHeight(x, z)); + mapChunk.SetBiome(x, z, chunk.GetBiome(x, z)); + } + + return mapChunk; + } + + public MapRegion GetRegion(Point regionPosition) + { + if (Regions.TryGetValue(regionPosition, out var region)) + return region; + return null; + } + + public MapChunk GetChunk(Point chunkPosition) + { + var regionPosition = new Point(chunkPosition.X >> 5, chunkPosition.Y >> 5); + if (Regions.TryGetValue(regionPosition, out var region)) + return region[chunkPosition.X % 32,chunkPosition.Y % 32]; + return null; + } + + public IEnumerable GetRegions() + { + var v = Regions.Values.ToArray(); + foreach (var region in v) + { + yield return region; + } + } + + public IEnumerable GetRegions(Rectangle blockBounds) + { + var regionMin = new Point((blockBounds.X >> 9) - 1, (blockBounds.Y >> 9) - 1); + var regionMax = new Point(((blockBounds.X + blockBounds.Width) >> 9) + 1, ((blockBounds.Y + blockBounds.Height) >> 9) + 1); + + for (int x = regionMin.X; x <= regionMax.X; x++) + for (int z = regionMin.Y; z <= regionMax.Y; z++) + { + var p = new Point(x, z); + if (Regions.TryGetValue(p, out var region)) + { + yield return region; + } + else + { + EnqueueRegion(p); + } + } + } + + public void EnqueueChunks(Rectangle blockBounds) + { + _regionsToGenerate.Clear(); + + var regionMin = new Point(blockBounds.X >> 9, blockBounds.Y >> 9 ); + var regionMax = new Point((blockBounds.X + blockBounds.Width) >> 9, (blockBounds.Y + blockBounds.Height) >> 9); + + foreach(var p in Spiral.FillRegionFromCenter(new Rectangle(regionMin - new Point(1,1), (regionMax - regionMin) + new Point(2,2)))) + { + if (!Regions.ContainsKey(p)) + { + EnqueueRegion(p); + } + } + } + + public IEnumerable GetChunks(Rectangle blockBounds) + { + var regionMin = new Point((blockBounds.X >> 9), (blockBounds.Y >> 9) ); + var regionMax = new Point(((blockBounds.X + blockBounds.Width) >> 9), ((blockBounds.Y + blockBounds.Height) >> 9)); + + var chunkMin = new Point((blockBounds.X >> 4), (blockBounds.Y >> 4)); + var chunkMax = new Point(((blockBounds.X + blockBounds.Width) >> 4), ((blockBounds.Y + blockBounds.Height) >> 4)); + + for (int rx = regionMin.X; rx <= regionMax.X; rx++) + for (int rz = regionMin.Y; rz <= regionMax.Y; rz++) + { + var p = new Point(rx, rz); + if (Regions.TryGetValue(p, out var region)) + { + for (int cx = 0; cx < 32; cx++) + for (int cz = 0; cz < 32; cz++) + { + var x = (rx << 5) + cx; + var z = (rz << 5) + cz; + if (x >= chunkMin.X && x <= chunkMax.X && + z >= chunkMin.Y && z <= chunkMax.Y) + { + var chunk = region[cx, cz]; + if (chunk != default) + yield return chunk; + } + } + } + else + { + EnqueueRegion(p); + } + } + } + + public void Dispose() + { + _running = false; + _trigger.Set(); + _thread.Join(); + _trigger?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Models/MapChunk.cs b/src/MiMap.Viewer.DesktopGL/Models/MapChunk.cs new file mode 100644 index 0000000..e591910 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Models/MapChunk.cs @@ -0,0 +1,75 @@ +using System; +using Microsoft.Xna.Framework; +using OpenAPI.WorldGenerator.Utils; + +namespace MiMap.Viewer.DesktopGL +{ + public class MapChunk + { + public readonly int X; + public readonly int Z; + + public readonly int[] Heights; + public readonly byte[] Biomes; + public readonly Color[] Colors; + + public MapChunk(int x, int z) + { + X = x; + Z = z; + Heights = new int[16 * 16]; + Biomes = new byte[16 * 16]; + Colors = new Color[16 * 16]; + } + + public int GetHeight(int x, int z) + { + return Heights[GetIndex(x, z)]; + } + + public byte GetBiome(int x, int z) + { + return Biomes[GetIndex(x, z)]; + } + + public Color GetColor(int x, int z) + { + return Colors[GetIndex(x, z)]; + } + + public void SetHeight(int x, int z, int height) + { + var i = GetIndex(x, z); + Heights[i] = height; + UpdateColor(i); + } + + public void SetBiome(int x, int z, byte biome) + { + var i = GetIndex(x, z); + Biomes[i] = biome; + UpdateColor(i); + } + + private void UpdateColor(int i) + { + var biome = Biomes[i]; + var height = Heights[i]; + var hIntensity = MathHelper.Clamp((height % (255f / 25f)) / 25f, 0f, 1f) / 2; + + // var c1 = Globals.BiomeColors[biome]; + var c1d = MiMapViewer.Instance.Map.BiomeRegistry.GetBiome(biome)?.Color; + var c1 = c1d.HasValue + ? new Color((byte)c1d.Value.R, c1d.Value.G, c1d.Value.B, c1d.Value.A) + : Color.HotPink; + var c2 = Color.Black; + // Colors[i] = Color.Lerp(c1, c2, hIntensity); + Colors[i] = c1; + } + + private int GetIndex(int x, int z) + { + return ((x & 0x0F) * 16) + (z & 0x0F); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Models/MapRegion.cs b/src/MiMap.Viewer.DesktopGL/Models/MapRegion.cs new file mode 100644 index 0000000..94baac4 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Models/MapRegion.cs @@ -0,0 +1,34 @@ +namespace MiMap.Viewer.DesktopGL +{ + public class MapRegion + { + public readonly int X; + public readonly int Z; + + public readonly MapChunk[] Chunks; + public bool IsComplete { get; internal set; } + + public MapRegion(int x, int z) + { + X = x; + Z = z; + Chunks = new MapChunk[32 * 32]; + } + + public void SetChunk(int cx, int cz, MapChunk chunk) + { + Chunks[GetIndex(cx, cz)] = chunk; + } + + public MapChunk this[int cx, int cz] + { + get => Chunks[GetIndex(cx, cz)]; + set => Chunks[GetIndex(cx, cz)] = value; + } + + private int GetIndex(int x, int z) + { + return ((x & 0x1F) * 32) + (z & 0x1F); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/NLog.config b/src/MiMap.Viewer.DesktopGL/NLog.config new file mode 100644 index 0000000..cbe4ef3 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/NLog.config @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/MiMap.Viewer.DesktopGL/Program.cs b/src/MiMap.Viewer.DesktopGL/Program.cs new file mode 100644 index 0000000..f574251 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Program.cs @@ -0,0 +1,62 @@ +using System; +using System.IO; +using System.Reflection; +using Microsoft.Xna.Framework; +using MiMap.Viewer.DesktopGL.Graphics; +using NLog; + +namespace MiMap.Viewer.DesktopGL +{ + public static class Program + { + private static ILogger Log; + [STAThread] + static void Main(string[] args) + { + ConfigureNLog(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))); + + try + { + Log.Info($"Starting {Assembly.GetExecutingAssembly().GetName().Name}"); + + using (var game = new MiMapViewer()) + { + game.Run(GameRunBehavior.Synchronous); + } + } + catch (Exception ex) + { + // NLog: catch any exception and log it. + Log.Error(ex, "Stopped program because of exception"); + throw; + } + finally + { + SpriteBatchExtensions.Dispose(); + // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) + LogManager.Shutdown(); + } + } + + private static void ConfigureNLog(string baseDir) + { + string loggerConfigFile = Path.Combine(baseDir, "NLog.config"); + + string logsDir = Path.Combine(baseDir, "logs"); + if (!Directory.Exists(logsDir)) + { + Directory.CreateDirectory(logsDir); + } + + LogManager.ThrowConfigExceptions = false; + LogManager.LoadConfiguration(loggerConfigFile); +// LogManager.Configuration = new XmlLoggingConfiguration(loggerConfigFile); + LogManager.Configuration.Variables["basedir"] = baseDir; + + Log = LogManager.GetCurrentClassLogger(); + + AppDomain.CurrentDomain.FirstChanceException += (sender, args) => Log.Error(args.Exception, "FirstChanceException"); + AppDomain.CurrentDomain.UnhandledException += (sender, args) => Log.Error(args.ExceptionObject as Exception, "Unhandled exception"); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/Utilities/Spiral.cs b/src/MiMap.Viewer.DesktopGL/Utilities/Spiral.cs new file mode 100644 index 0000000..2b18ee4 --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/Utilities/Spiral.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; + +namespace MiMap.Viewer.DesktopGL.Utilities +{ + public static class Spiral + { + + public static IEnumerable FillRegionFromCenter(Rectangle region) + { + var cX = region.Center.X; + var cY = region.Center.Y; + var r = (int)Math.Max(region.Width, region.Height); + + var x = 0; + var y = 0; + var t = r; + var dx = 0; + var dy = -1; + var p = Point.Zero; + + for (var i = 0; i < (r * r); i++) + { + if ((-r / 2 <= x) && (x <= r / 2) && (-r / 2 <= y) && (y <= r / 2)) + { + p = new Point(cX + x, cY + y); + if (region.RegionContainsInclusive(p)) + yield return p; + } + + if ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1 - y))) + { + t = dx; + dx = -dy; + dy = t; + } + + x += dx; + y += dy; + } + } + + private static bool RegionContainsInclusive(this Rectangle region, Point point) + => point.X >= region.X && point.X <= (region.X + region.Width) && + point.Y >= region.Y && point.Y <= (region.Y + region.Height); + + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.DesktopGL/app.manifest b/src/MiMap.Viewer.DesktopGL/app.manifest new file mode 100644 index 0000000..b77e67a --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/app.manifest @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + + diff --git a/src/MiMap.Viewer.DesktopGL/biomeColors.json b/src/MiMap.Viewer.DesktopGL/biomeColors.json new file mode 100644 index 0000000..2ab037e --- /dev/null +++ b/src/MiMap.Viewer.DesktopGL/biomeColors.json @@ -0,0 +1,83 @@ +{ + "name":"test", + "colorMap":[ + [ 0, { "r":0, "g":0, "b":112 } ], + [ 1, { "r":141, "g":179, "b":96 } ], + [ 2, { "r":250, "g":148, "b":24 } ], + [ 3, { "r":96, "g":96, "b":96 } ], + [ 4, { "r":5, "g":102, "b":33 } ], + [ 5, { "r":11, "g":2, "b":89 } ], + [ 6, { "r":7, "g":249, "b":178 } ], + [ 7, { "r":0, "g":0, "b":255 } ], + [ 8, { "r":255, "g":0, "b":0 } ], + [ 9, { "r":128, "g":128, "b":255 } ], + [ 10, { "r":112, "g":112, "b":214 } ], + [ 11, { "r":160, "g":160, "b":255 } ], + [ 12, { "r":255, "g":255, "b":255 } ], + [ 13, { "r":160, "g":160, "b":160 } ], + [ 14, { "r":255, "g":0, "b":255 } ], + [ 15, { "r":160, "g":0, "b":255 } ], + [ 16, { "r":250, "g":222, "b":85 } ], + [ 17, { "r":210, "g":95, "b":18 } ], + [ 18, { "r":34, "g":85, "b":28 } ], + [ 19, { "r":22, "g":57, "b":51 } ], + [ 20, { "r":114, "g":120, "b":154 } ], + [ 21, { "r":83, "g":123, "b":9 } ], + [ 22, { "r":44, "g":66, "b":5 } ], + [ 23, { "r":98, "g":139, "b":23 } ], + [ 24, { "r":0, "g":0, "b":48 } ], + [ 25, { "r":162, "g":162, "b":132 } ], + [ 26, { "r":250, "g":240, "b":192 } ], + [ 27, { "r":48, "g":116, "b":68 } ], + [ 28, { "r":31, "g":5, "b":50 } ], + [ 29, { "r":64, "g":81, "b":26 } ], + [ 30, { "r":49, "g":85, "b":74 } ], + [ 31, { "r":36, "g":63, "b":54 } ], + [ 32, { "r":89, "g":102, "b":81 } ], + [ 33, { "r":69, "g":7, "b":62 } ], + [ 34, { "r":80, "g":112, "b":80 } ], + [ 35, { "r":189, "g":18, "b":95 } ], + [ 36, { "r":167, "g":157, "b":100 } ], + [ 37, { "r":217, "g":69, "b":21 } ], + [ 38, { "r":17, "g":151, "b":101 } ], + [ 39, { "r":202, "g":140, "b":101 } ], + [ 40, { "r":128, "g":128, "b":255 } ], + [ 41, { "r":128, "g":128, "b":255 } ], + [ 42, { "r":128, "g":128, "b":255 } ], + [ 43, { "r":128, "g":128, "b":255 } ], + [ 44, { "r":0, "g":0, "b":172 } ], + [ 45, { "r":0, "g":0, "b":144 } ], + [ 46, { "r":32, "g":32, "b":112 } ], + [ 47, { "r":0, "g":0, "b":80 } ], + [ 48, { "r":0, "g":0, "b":64 } ], + [ 49, { "r":32, "g":32, "b":56 } ], + [ 50, { "r":64, "g":64, "b":144 } ], + [ 127, { "r":0, "g":0, "b":0 } ], + [ 129, { "r":181, "g":219, "b":136 } ], + [ 130, { "r":255, "g":188, "b":64 } ], + [ 131, { "r":136, "g":136, "b":136 } ], + [ 132, { "r":45, "g":142, "b":73 } ], + [ 133, { "r":51, "g":142, "b":19 } ], + [ 134, { "r":47, "g":255, "b":18 } ], + [ 140, { "r":180, "g":20, "b":220 } ], + [ 149, { "r":123, "g":13, "b":49 } ], + [ 151, { "r":138, "g":179, "b":63 } ], + [ 155, { "r":88, "g":156, "b":108 } ], + [ 156, { "r":71, "g":15, "b":90 } ], + [ 157, { "r":104, "g":121, "b":66 } ], + [ 158, { "r":89, "g":125, "b":114 } ], + [ 160, { "r":129, "g":142, "b":121 } ], + [ 161, { "r":109, "g":119, "b":102 } ], + [ 162, { "r":120, "g":52, "b":120 } ], + [ 163, { "r":229, "g":218, "b":135 } ], + [ 164, { "r":207, "g":197, "b":140 } ], + [ 165, { "r":255, "g":109, "b":61 } ], + [ 166, { "r":216, "g":191, "b":141 } ], + [ 167, { "r":242, "g":180, "b":141 } ], + [ 168, { "r":118, "g":142, "b":20 } ], + [ 169, { "r":59, "g":71, "b":10 } ], + [ 170, { "r":82, "g":41, "b":33 } ], + [ 171, { "r":221, "g":8, "b":8 } ], + [ 172, { "r":73, "g":144, "b":123 } ] + ] +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/Game.cs b/src/MiMap.Viewer.Element/Game.cs new file mode 100644 index 0000000..000523c --- /dev/null +++ b/src/MiMap.Viewer.Element/Game.cs @@ -0,0 +1,57 @@ +using ElementEngine; +using ElementEngine.EndlessTiles; +using ElementEngine.Tiled; +using MiMap.Viewer.Element.MiMapTiles; +using MiNET.Worlds; +using OpenAPI.WorldGenerator.Generators; +using Veldrid; + +namespace MiMap.Viewer.Element +{ + public class Game : BaseGame + { + public IWorldGenerator WorldGenerator; + public MiMapTilesWorld TilesWorld; + public Texture2D TilesTexture; + public MiMapTilesRenderer TilesRenderer; + public Camera2D BackgroundCamera; + + public override void Load() + { + SettingsManager.LoadFromPath("Settings.xml"); + + var windowRect = new ElementEngine.Rectangle() + { + X = SettingsManager.GetSetting("Window", "X"), + Y = SettingsManager.GetSetting("Window", "Y"), + Width = SettingsManager.GetSetting("Window", "Width"), + Height = SettingsManager.GetSetting("Window", "Height") + }; + + var graphicsBackend = GraphicsBackend.Vulkan; + +#if OPENGL + graphicsBackend = GraphicsBackend.OpenGL; +#endif + + SetupWindow(windowRect, "Captain Shostakovich", graphicsBackend); + SetupAssets(); + + ClearColor = RgbaFloat.Black; + + InputManager.LoadGameControls(); + + WorldGenerator = new OverworldGeneratorV2(); + + TilesWorld = new MiMapTilesWorld(WorldGenerator); + + //todo: TilesTexture + TilesRenderer = new MiMapTilesRenderer(TilesWorld, TilesTexture); + + BackgroundCamera = new Camera2D(new ElementEngine.Rectangle(0, 0, ElementGlobals.TargetResolutionWidth, ElementGlobals.TargetResolutionHeight)) + { + Zoom = 3 + }; + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMap.Viewer.Element.csproj b/src/MiMap.Viewer.Element/MiMap.Viewer.Element.csproj new file mode 100644 index 0000000..12b8f2a --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMap.Viewer.Element.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + + + + + + + + + + + + diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesRenderer.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesRenderer.cs new file mode 100644 index 0000000..c3cd504 --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesRenderer.cs @@ -0,0 +1,38 @@ +using ElementEngine; + +namespace MiMap.Viewer.Element.MiMapTiles +{ + // MiMapTilesRendererChunk + + public class MiMapTilesRenderer + { + public MiMapTilesWorld World { get; set; } + public Texture2D Tilesheet { get; set; } + public Dictionary Chunks { get; set; } = new Dictionary(); + + public MiMapTilesRenderer(MiMapTilesWorld world, Texture2D tilesheet) + { + World = world; + Tilesheet = tilesheet; + + foreach (var (_, chunk) in world.Chunks) + Chunks.Add(chunk.Position, new MiMapTilesRendererChunk(chunk, tilesheet)); + } + + public void Update(GameTimer gameTimer) + { + foreach (var (_, chunk) in Chunks) + chunk.TileBatch.Update(gameTimer); + } + + public void DrawLayers(int start, int end, Camera2D camera) + { + foreach (var (pos, chunk) in Chunks) + { + if (camera.ScaledView.Intersects(chunk.ChunkRect)) + chunk.TileBatch.DrawLayers(start, end, camera, chunk.ChunkRect.LocationF); + } + } // DrawLayers + + } // MiMapTilesRenderer +} diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesRendererChunk.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesRendererChunk.cs new file mode 100644 index 0000000..1dbda45 --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesRendererChunk.cs @@ -0,0 +1,47 @@ +using ElementEngine; + +namespace MiMap.Viewer.Element.MiMapTiles +{ + public class MiMapTilesRendererChunk + { + public MiMapTilesWorldChunk ChunkData { get; set; } + public Rectangle ChunkRect; + public TileBatch2D TileBatch { get; set; } + + public MiMapTilesRendererChunk(MiMapTilesWorldChunk chunkData, Texture2D tilesheet) + { + ChunkData = chunkData; + ChunkRect = new Rectangle( + ChunkData.Position * (ChunkData.World.TileSize * ChunkData.World.ChunkSize), + ChunkData.World.TileSize * ChunkData.World.ChunkSize); + + TileBatch = new TileBatch2D(ChunkData.World.ChunkSize.X, ChunkData.World.ChunkSize.Y, ChunkData.World.TileSize.X, ChunkData.World.TileSize.Y, tilesheet, TileBatch2DWrapMode.None, ChunkData.World.TileAnimations); + + TileBatch.BeginBuild(); + + foreach (var layer in ChunkData.Layers) + BuildTilebatchLayer(layer); + + TileBatch.EndBuild(); + } + + public void BuildTilebatchLayer(MiMapTilesWorldLayer layer) + { + for (int y = 0; y < ChunkData.World.ChunkSize.Y; y++) + { + for (int x = 0; x < ChunkData.World.ChunkSize.X; x++) + { + var tileID = layer.Tiles[x + ChunkData.World.ChunkSize.X * y]; + + if (tileID == 0) + continue; + + TileBatch.SetTileAtPosition(x, y, tileID); + } + } + + TileBatch.EndLayer(); + layer.ClearTiles(); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorld.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorld.cs new file mode 100644 index 0000000..97ae9c6 --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorld.cs @@ -0,0 +1,255 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using ElementEngine; +using MiNET.Utils.Vectors; +using MiNET.Worlds; +using NLog; + +namespace MiMap.Viewer.Element.MiMapTiles +{ + public class ConcurrentWorkItemQueue : IDisposable + { + public event EventHandler ItemCompleted; + public event EventHandler ItemStarted; + + private readonly int _threadCount; + private readonly Action _processWorkItem; + private object _chunksSync = new object(); + private Queue _pending; + private HashSet _inProgress; + private HashSet _completed; + + private Thread[] _threads; + private AutoResetEvent _trigger; + private bool _running; + + public ConcurrentWorkItemQueue(Action processWorkItem) : this(Environment.ProcessorCount, processWorkItem) + { + } + + public ConcurrentWorkItemQueue(int threadCount, Action processWorkItem) + { + _threadCount = threadCount; + _processWorkItem = processWorkItem; + _pending = new Queue(); + _inProgress = new HashSet(); + _completed = new HashSet(); + _trigger = new AutoResetEvent(false); + _threads = new Thread[threadCount]; + for (int i = 0; i < threadCount; i++) + { + _threads[i] = new Thread(WorkItemThreadRun) + { + Name = "WorldGenerator", + IsBackground = false + }; + _threads[i].Start(); + } + } + + public void Start() + { + _running = true; + for (int i = 0; i < _threadCount; i++) + { + _threads[i].Start(); + } + } + + public void Stop() + { + _running = false; + _trigger.Set(); + for (int i = 0; i < _threads.Length; i++) + { + _threads[i].Join(); + } + _trigger?.Dispose(); + } + + public bool TryEnqueue(T item) + { + lock (_chunksSync) + { + if (_pending.Contains(item) || _inProgress.Contains(item) || _completed.Contains(item)) + return false; + + _pending.Enqueue(item); + return true; + } + } + + private bool TryDequeue(int timeout, out T item) + { + item = default; + if (!Monitor.TryEnter(_chunksSync, timeout)) + return false; + try + { + if (_pending.TryDequeue(out item)) + { + _inProgress.Add(item); + return true; + } + + return false; + } + finally + { + Monitor.Exit(_chunksSync); + } + } + + private void MarkComplete(T item) + { + lock (_chunksSync) + { + _inProgress.Remove(item); + _completed.Add(item); + } + } + + public void WorkItemThreadRun() + { + while (_running) + { + _trigger.WaitOne(1000); + + while (_running && TryDequeue(50, out var c)) + { + ItemStarted?.Invoke(this, c); + + _processWorkItem.Invoke(c); + + MarkComplete(c); + ItemCompleted?.Invoke(this, c); + } + + } + } + + public void ClearQueue() + { + lock (_chunksSync) + { + _pending.Clear(); + } + } + + public void Reset() + { + lock (_chunksSync) + { + _pending.Clear(); + _inProgress.Clear(); + _completed.Clear(); + } + } + + public void Dispose() + { + Reset(); + Stop(); + _trigger?.Dispose(); + } + } + + public class MiMapTilesWorld : IDisposable + { + private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); + public IWorldGenerator WorldGenerator { get; } + public const int BLANK_TILE = -1; + + public Vector2I TileSize { get; set; } + public Vector2I ChunkSize { get; set; } + public Dictionary Chunks { get; set; } = new Dictionary(); + public Dictionary TileAnimations { get; set; } = new Dictionary(); + + private ConcurrentWorkItemQueue _workItemQueue; + private ConcurrentBag _newChunks; + + public MiMapTilesWorld(IWorldGenerator worldGenerator) + { + WorldGenerator = worldGenerator; + ChunkSize = new Vector2I(512, 512); + TileSize = new Vector2I(16, 16); + _workItemQueue = new ConcurrentWorkItemQueue(GenerateChunk); + _newChunks = new ConcurrentBag(); + } + + public void EnqueueChunk(Vector2I chunkCoords) + { + if (!_workItemQueue.TryEnqueue(chunkCoords)) + return; + + Log.Info($"Enqueue Chunk: {chunkCoords.X:N2}, {chunkCoords.Y:N2}"); + } + + private void GenerateChunk(Vector2I chunkCoords) + { + Log.Info($"Generating Chunk: {chunkCoords.X:N2}, {chunkCoords.Y:N2}"); + var csw = Stopwatch.StartNew(); + var chunkPosition = new ChunkCoordinates(chunkCoords.X, chunkCoords.Y); + using var chunk = WorldGenerator.GenerateChunkColumn(chunkPosition); + csw.Stop(); + var t1 = csw.ElapsedMilliseconds; + csw.Restart(); + var worldChunk = ExtractWorldChunk(chunkCoords, chunk); + _newChunks.Add(worldChunk); + csw.Stop(); + Log.Debug($"Completed Chunk {chunkPosition.X:N2}, {chunkPosition.Z:N2} in {t1:N3} ms (generation: {t1:N3} ms, dataExtraction: {csw.ElapsedMilliseconds:N3} ms)"); + } + + private MiMapTilesWorldChunk ExtractWorldChunk(Vector2I coords, ChunkColumn chunkColumn) + { + var chunk = new MiMapTilesWorldChunk(coords, this); + + var biomeTiles = new int[chunk.TotalTiles]; + var heightTiles = new int[chunk.TotalTiles]; + // var temperatureTiles = new int[chunk.TotalTiles]; + // var humidityTiles = new int[chunk.TotalTiles]; + + int i = 0; + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { + i = (z << 4) + x; + + biomeTiles[i] = (int)chunkColumn.biomeId[i]; + heightTiles[i] = chunkColumn.height[i]; + } + + chunk.UpdateLayerTiles(MiMapTilesWorldLayerType.Biome, biomeTiles); + chunk.UpdateLayerTiles(MiMapTilesWorldLayerType.Height, heightTiles); + chunk.UpdateLayerTiles(MiMapTilesWorldLayerType.Humidity, biomeTiles); + chunk.UpdateLayerTiles(MiMapTilesWorldLayerType.Temperature, biomeTiles); + + return chunk; + } + + public void Update() + { + while (_newChunks.TryTake(out var chunk)) + { + Chunks.Add(chunk.Position, chunk); + } + } + + public void ClearTiles() + { + foreach (var (_, chunk) in Chunks) + chunk.ClearTiles(); + } + + public void ResetTiles() + { + foreach (var (_, chunk) in Chunks) + chunk.ResetTiles(); + } + + public void Dispose() + { + _workItemQueue.Dispose(); + } + } // EndlessTilesWorld +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldChunk.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldChunk.cs new file mode 100644 index 0000000..3bef2d3 --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldChunk.cs @@ -0,0 +1,51 @@ +using ElementEngine; + +namespace MiMap.Viewer.Element.MiMapTiles +{ + public enum MiMapTilesWorldLayerType : int + { + Biome = 0, + Height = 1, + Temperature = 2, + Humidity = 3 + } + + public class MiMapTilesWorldChunk + { + private static readonly int MiMapTilesWorldLayerTypeCount = Enum.GetNames(typeof(MiMapTilesWorldLayerType)).Length; + + public MiMapTilesWorld World { get; set; } + public Vector2I Position { get; set; } + public MiMapTilesWorldLayer[] Layers { get; } + + public int TotalTiles => World.ChunkSize.X * World.ChunkSize.Y; + + public MiMapTilesWorldChunk(Vector2I position, MiMapTilesWorld world) + { + Position = position; + World = world; + Layers = new MiMapTilesWorldLayer[MiMapTilesWorldLayerTypeCount]; + for (int i = 0; i < Layers.Length; i++) + { + Layers[i] = new MiMapTilesWorldLayer((MiMapTilesWorldLayerType)i, this); + } + } + + public void UpdateLayerTiles(MiMapTilesWorldLayerType type, int[] tiles) + { + Layers[(int)type].ResetTiles(tiles); + } + + public void ClearTiles() + { + foreach (var layer in Layers) + layer.ClearTiles(); + } + + public void ResetTiles() + { + foreach (var layer in Layers) + layer.ResetTiles(); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldChunkData.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldChunkData.cs new file mode 100644 index 0000000..6b8d141 --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldChunkData.cs @@ -0,0 +1,11 @@ +using ElementEngine; + +namespace MiMap.Viewer.Element.MiMapTiles +{ + public class MiMapTilesWorldChunkData + { + public Vector2I Position { get; set; } + public List Layers { get; set; } + + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldData.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldData.cs new file mode 100644 index 0000000..f5f5e6e --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldData.cs @@ -0,0 +1,16 @@ +using ElementEngine; + +namespace MiMap.Viewer.Element.MiMapTiles +{ + public class MiMapTilesWorldData + { + public string Name { get; set; } + public string TilesheetPath { get; set; } + public string TilesheetName { get; set; } + public Vector2I TileSize { get; set; } + public Vector2I ChunkSize { get; set; } + + public Dictionary SavedChunks { get; set; } + public Dictionary TileAnimations { get; set; } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldLayer.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldLayer.cs new file mode 100644 index 0000000..2c1bd88 --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldLayer.cs @@ -0,0 +1,44 @@ +namespace MiMap.Viewer.Element.MiMapTiles +{ + public class MiMapTilesWorldLayer + { + public MiMapTilesWorldChunk Chunk { get; } + + public MiMapTilesWorldLayerType Type { get; } + public int[] Tiles { get; set; } + + public bool TilesLoaded => Tiles != null; + + public MiMapTilesWorldLayer(MiMapTilesWorldLayerType type, MiMapTilesWorldChunk chunk) + { + Type = type; + Tiles = null; + Chunk = chunk; + } + public MiMapTilesWorldLayer(MiMapTilesWorldLayerType type, int[] tiles, MiMapTilesWorldChunk chunk) : this(type, chunk) + { + Tiles = tiles; + } + + public void ClearTiles() + { + Tiles = null; + } + + public void ResetTiles(int[] newTiles) + { + if (newTiles.Length != Chunk.TotalTiles) + throw new IndexOutOfRangeException(nameof(newTiles)); + + Tiles = newTiles; + } + + public void ResetTiles() + { + Tiles = new int[Chunk.TotalTiles]; + for (var i = 0; i < Chunk.TotalTiles; i++) + Tiles[i] = MiMapTilesWorld.BLANK_TILE; + } + + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldLayerData.cs b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldLayerData.cs new file mode 100644 index 0000000..75c866d --- /dev/null +++ b/src/MiMap.Viewer.Element/MiMapTiles/MiMapTilesWorldLayerData.cs @@ -0,0 +1,8 @@ +namespace MiMap.Viewer.Element.MiMapTiles +{ + public class MiMapTilesWorldLayerData + { + public int Index { get; set; } + public string CompressedTiles { get; set; } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/Program.cs b/src/MiMap.Viewer.Element/Program.cs new file mode 100644 index 0000000..5275247 --- /dev/null +++ b/src/MiMap.Viewer.Element/Program.cs @@ -0,0 +1,11 @@ +namespace MiMap.Viewer.Element +{ + class Program + { + static void Main(string[] args) + { + using (var game = new Game()) + game.Run(); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/Utilities/MathUtil.cs b/src/MiMap.Viewer.Element/Utilities/MathUtil.cs new file mode 100644 index 0000000..cfb3d0f --- /dev/null +++ b/src/MiMap.Viewer.Element/Utilities/MathUtil.cs @@ -0,0 +1,41 @@ +using System.Runtime.CompilerServices; + +namespace MiMap.Viewer.Core.Utilities +{ + public static class MathUtil + { + #region Clamp + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float Clamp(this float v, float min, float max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double Clamp(this double v, double min, double max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte Clamp(this sbyte v, sbyte min, sbyte max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(this byte v, byte min, byte max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Clamp(this short v, short min, short max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort Clamp(this ushort v, ushort min, ushort max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Clamp(this int v, int min, int max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Clamp(this uint v, uint min, uint max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Clamp(this long v, long min, long max) => v < min ? min : v > max ? max : v; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Clamp(this ulong v, ulong min, ulong max) => v < min ? min : v > max ? max : v; + + #endregion + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/Utilities/RectangleExtensions.cs b/src/MiMap.Viewer.Element/Utilities/RectangleExtensions.cs new file mode 100644 index 0000000..e67423e --- /dev/null +++ b/src/MiMap.Viewer.Element/Utilities/RectangleExtensions.cs @@ -0,0 +1,12 @@ +using Veldrid; + +namespace MiMap.Viewer.Core.Utilities +{ + public static class RectangleExtensions + { + public static Point Center(this Rectangle rectangle) + { + return new Point(rectangle.X + (rectangle.Width / 2), rectangle.Y + (rectangle.Height / 2)); + } + } +} \ No newline at end of file diff --git a/src/MiMap.Viewer.Element/Utilities/Spiral.cs b/src/MiMap.Viewer.Element/Utilities/Spiral.cs new file mode 100644 index 0000000..b53b913 --- /dev/null +++ b/src/MiMap.Viewer.Element/Utilities/Spiral.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using MiMap.Viewer.Core.Utilities; +using Veldrid; + +namespace MiMap.Viewer.DesktopGL.Utilities +{ + public static class Spiral + { + + public static IEnumerable FillRegionFromCenter(Rectangle region) + { + var c = region.Center(); + var cX = c.X; + var cY = c.Y; + var r = (int)Math.Max(region.Width, region.Height); + + var x = 0; + var y = 0; + var t = r; + var dx = 0; + var dy = -1; + var p = new Point(); + + for (var i = 0; i < (r * r); i++) + { + if ((-r / 2 <= x) && (x <= r / 2) && (-r / 2 <= y) && (y <= r / 2)) + { + p = new Point(cX + x, cY + y); + if (region.RegionContainsInclusive(p)) + yield return p; + } + + if ((x == y) || ((x < 0) && (x == -y)) || ((x > 0) && (x == 1 - y))) + { + t = dx; + dx = -dy; + dy = t; + } + + x += dx; + y += dy; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool RegionContainsInclusive(this Rectangle region, Point point) + => point.X >= region.X && point.X <= (region.X + region.Width) && + point.Y >= region.Y && point.Y <= (region.Y + region.Height); + + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/DebugWorldProvider.cs b/src/OpenAPI.WorldGenerator/DebugWorldProvider.cs index ba0a338..486d13f 100644 --- a/src/OpenAPI.WorldGenerator/DebugWorldProvider.cs +++ b/src/OpenAPI.WorldGenerator/DebugWorldProvider.cs @@ -12,14 +12,14 @@ namespace OpenAPI.WorldGenerator { public class DebugWorldProvider : IWorldProvider, ICachingWorldProvider { - private readonly ConcurrentDictionary _chunkCache = new ConcurrentDictionary(); - public bool IsCaching { get; private set; } + private readonly ConcurrentDictionary _chunkCache = new ConcurrentDictionary(); + public bool IsCaching => true; public IWorldGenerator Generator { get; } public DebugWorldProvider(IWorldGenerator worldGenerator) { Generator = worldGenerator; - IsCaching = true; + // IsCaching = true; } public void Initialize() @@ -61,7 +61,7 @@ public long GetDayTime() public string GetName() { - return "Cool world"; + return "OpenMiNET.WorldGenerator World Provider"; } public int SaveChunks() @@ -93,7 +93,7 @@ public int UnloadChunks(MiNET.Player[] players, ChunkCoordinates spawn, double m { int removed = 0; - lock (_chunkCache) + // lock (_chunkCache) { List coords = new List {spawn}; @@ -109,13 +109,13 @@ public int UnloadChunks(MiNET.Player[] players, ChunkCoordinates spawn, double m if (!keep) { _chunkCache.TryRemove(chunkColumn.Key, out ChunkColumn waste); - if (waste != null) { foreach (var chunk in waste) { - chunk.PutPool(); + chunk.Dispose(); } + waste.Dispose(); } Interlocked.Increment(ref removed); diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeBase.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeBase.cs index 4460c7f..3a7c3e1 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeBase.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeBase.cs @@ -59,65 +59,32 @@ public float TerrainNoise(OverworldGeneratorV2 generator, int x, int y, float bo } river = 1f - (1f - borderForRiver) * (1f - river); - return Terrain.GenerateNoise(generator, x, y, border, river); + return Terrain.GenerateNoise(generator, x, y, border, 1f); } float lakePressure = LakePressure(generator, x, y, border, generator.LakeFrequency, OverworldGeneratorV2.LakeBendSizeLarge, OverworldGeneratorV2.LakeBendSizeMedium, OverworldGeneratorV2.LakeBendSizeSmall); float lakeFlattening = LakeFlattening(lakePressure, generator.LakeShoreLevel, generator.LakeDepressionLevel); - /*if (!this.Config.AllowScenicLakes) - { - return Terrain.GenerateNoise(generator, x, y, border, river); - }*/ - // combine rivers and lakes - if ((river < 1) && (lakeFlattening < 1)) - { + if ((river < 1) && (lakeFlattening < 1)) { river = (1f - river) / river + (1f - lakeFlattening) / lakeFlattening; river = (1f / (river + 1f)); } - else if (lakeFlattening < river) - { + else if (lakeFlattening < river) { river = lakeFlattening; } // smooth the edges on the top river = 1f - river; - river *= (river / (river + 0.05f) * (1.05f)); + river = river * (river / (river + 0.05f) * (1.05f)); river = 1f - river; // make the water areas flat for water features float riverFlattening = river * (1f + OverworldGeneratorV2.RiverFlatteningAddend) - OverworldGeneratorV2.RiverFlatteningAddend; - if (riverFlattening < 0) - { + if (riverFlattening < 0) { riverFlattening = 0; } - - /*float riverFlattening = river*1.25f-0.25f; - if (riverFlattening <0) riverFlattening = 0; - if ((river<1)&&(lakeFlattening<1)) - { - riverFlattening = (1f-riverFlattening)/riverFlattening+(1f-lakeFlattening)/lakeFlattening; - riverFlattening = (1f/(riverFlattening+1f)); - } - else if (lakeFlattening < riverFlattening) - { - riverFlattening = lakeFlattening; - } - - // the lakes have to have a little less flattening to avoid the rocky edges - lakeFlattening = LakeFlattening(lakePressure, generator.LakeWaterLevel, generator.LakeDepressionLevel); - - if ((river<1)&&(lakeFlattening<1)) - { - river = (1f-river)/river+(1f-lakeFlattening)/lakeFlattening; - river = (1f/(river+1f)); - } - else if (lakeFlattening < river) - { - river = lakeFlattening; - }*/ // flatten terrain to set up for the water features float terrainNoise = Terrain.GenerateNoise(generator, x, y, border, riverFlattening); @@ -153,7 +120,7 @@ public float ErodedNoise(OverworldGeneratorV2 generator, int x, int y, float riv // river of actualRiverProportions now maps to 1; float riverFlattening = 1f - river; - riverFlattening = riverFlattening - (1f - OverworldGeneratorV2.ActualRiverProportion); + riverFlattening -= (1f - OverworldGeneratorV2.ActualRiverProportion); // return biomeHeight if no river effect if (riverFlattening < 0) { @@ -186,7 +153,7 @@ public float LakePressure(OverworldGeneratorV2 generator, int x, int y, float bo float pX = x; float pY = y; - ISimplexData2D jitterData = SimplexData2D.NewDisk(); + ISimplexData2D jitterData = new SimplexData2D(); generator.SimplexInstance(1).GetValue(x / 240.0d, y / 240.0d, jitterData); pX += jitterData.GetDeltaX() * largeBendSize; @@ -206,8 +173,6 @@ public float LakePressure(OverworldGeneratorV2 generator, int x, int y, float bo public float LakeFlattening(float pressure, float shoreLevel, float topLevel) { - if (!this.Config.AllowScenicLakes) - return 1f; // adjusts the lake pressure to the river numbers. The lake shoreLevel is mapped // to become equivalent to actualRiverProportion if (pressure > topLevel) @@ -227,21 +192,10 @@ public float LakeFlattening(float pressure, float shoreLevel, float topLevel) return 1; } - /*public void Replace(ChunkColumn primer, BlockCoordinates blockPos, int x, int y, int depth, OverworldGeneratorV2 generator, - float[] noise, float river, BiomeBase[] biomes) - { - Replace(primer, blockPos.X, blockPos.Z, x, y, depth, generator, noise, river, biomes); - }*/ - - public void Replace(ChunkColumn primer, int blockX, int blockZ, int x, int z, int depth, OverworldGeneratorV2 generator, + public virtual void Replace(ChunkColumn primer, int blockX, int blockZ, int x, int z, int depth, OverworldGeneratorV2 generator, float[] noise, float river, BiomeBase[] biomes) { - /* if (RTG.surfacesDisabled() || this.getConfig().DISABLE_RTG_SURFACES.get()) - { - return; - } -*/ - if (this.Surface == null) + if (this.Surface == null) return; float riverRegion = !this.Config.AllowRivers ? 0f : river; @@ -263,7 +217,7 @@ protected void ReplaceWithRiver(ChunkColumn primer, int i, int j, int x, int y, float riverRegion = !this.Config.AllowRivers ? 0f : river; this.Surface.PaintTerrain(primer, i, j, x, y, depth, generator, noise, riverRegion, biomes); - //this.surfaceRiver.paintTerrain(primer, i, j, x, y, depth, generator, noise, riverRegion, biomes); + /* if (RTGConfig.lushRiverbanksInDesert()) { this.surfaceRiver.paintTerrain(primer, i, j, x, y, depth, generator, noise, riverRegion, biomes); diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeRegistry.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeRegistry.cs index dad2c24..9bf8a88 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeRegistry.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/BiomeRegistry.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; @@ -53,7 +54,7 @@ public BiomeRegistry() new ExtremeHillsPlusMBiome(),*/ new FlowerForestBiome(), new ForestBiome(), - new ForestHillsBiome(), + new WoodedHillsBiome(), new FrozenOceanBiome(), // new IceMountainsBiome(), new IcePlainsBiome(), @@ -164,7 +165,7 @@ public BiomeRegistry() byId.TryAdd(biome.Id, biome); } - foreach (var group in Biomes.Where(x => !x.Config.IsEdgeBiome).GroupBy(x => x.Temperature * x.Downfall * x.Config.Weight)) + foreach (var group in Biomes.Where(x => !x.Config.IsEdgeBiome).GroupBy(x => (1f + x.Temperature) * (1f + x.Downfall) * x.Config.Weight)) { if (group.Count() > 1) { @@ -196,57 +197,54 @@ public BiomeBase[] GetBiomes() public int GetBiome(float temperature, float downfall, float selector) { var biomes = GetBiomes(); - // selector = Math.Clamp(selector, 0, 1f); + var weights = ArrayPool.Shared.Rent(biomes.Length); - - float[] weights = new float[biomes.Length]; - - float minDifference = float.MaxValue; - float sum = 0f; - - for (int i = 0; i < biomes.Length; i++) + try { - var temperatureDifference = MathF.Abs((biomes[i].Temperature - temperature)); - var humidityDifference = MathF.Abs(biomes[i].Downfall - downfall); + float sum = 0f; - // temperatureDifference *= 5.5f; - // humidityDifference *= 1.5f; + for (int i = 0; i < biomes.Length; i++) + { + var temperatureDifference = (biomes[i].Temperature - temperature); + var humidityDifference = (biomes[i].Downfall - downfall); - var difference = - (temperatureDifference * temperatureDifference + humidityDifference * humidityDifference) / 3f; + //temperatureDifference *= 7.5f; + //humidityDifference *= 2.5f; - var w = (float)biomes[i].Config.Weight; - /*if (biomes[i].MaxHeight < height || biomes[i].MinHeight > height) - { - var mdifference = MathF.Min( - MathF.Abs(biomes[i].MaxHeight - height), MathF.Abs(biomes[i].MinHeight - height)) * 4f; - - w -= mdifference; - }*/ - weights[i] = w * (1f + difference); + var weight = (float) biomes[i].Config.Weight * MathF.Abs( + (temperatureDifference * temperatureDifference + humidityDifference * humidityDifference)); - sum += weights[i]; - } + if (weight > 0f) + { + weights[i] = weight; - selector *= sum; + sum += weight; + } + } - int result = 0; + selector *= sum; - float currentWeightIndex = 0; - for (int i = 0; i < weights.Length; i++) - { - var value = weights[i]; + float currentWeightIndex = 0; - if (value > 0f) + for (int i = 0; i < biomes.Length; i++) { - currentWeightIndex += value; + var value = weights[i]; + + if (value > 0f) + { + currentWeightIndex += value; - if (currentWeightIndex >= selector) - return biomes[i].Id; + if (currentWeightIndex >= selector) + return biomes[i].Id; + } } - } - return biomes[result].Id; + return biomes[0].Id; + } + finally + { + ArrayPool.Shared.Return(weights, true); + } } public BiomeBase GetBiome(int id) diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Config/BiomeConfig.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Config/BiomeConfig.cs index 02620e1..fb26d32 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Config/BiomeConfig.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Config/BiomeConfig.cs @@ -11,6 +11,8 @@ public class BiomeConfig public bool IsEdgeBiome { get; set; } = false; public int Weight { get; set; } = Weights.Common; + + public float TreeDensity = 0.6f; public BiomeConfig() { diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Desert/DesertHillsBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Desert/DesertHillsBiome.cs index 456f8f3..c0c2449 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Desert/DesertHillsBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Desert/DesertHillsBiome.cs @@ -15,13 +15,13 @@ public DesertHillsBiome() Downfall = 0.0f; MinHeight = 0.2f; MaxHeight = 0.7f; - + + Config.AllowRivers = false; + Config.AllowScenicLakes = false; + Terrain = new DesertHillsTerrain(10f, 80f, 68f, 200f); Surface = new SurfaceBase(Config, new Sand(), new Sandstone()); - Config.AllowScenicLakes = false; - Config.AllowRivers = false; - Color = ColorUtils.FromHtml("#D25F12"); } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestBiome.cs index 1b4f52f..966296a 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestBiome.cs @@ -15,7 +15,7 @@ public BirchForestBiome() MinHeight = 0.1f; MaxHeight = 0.2f; Terrain = new BirchForestTerrain(); - Config.Weight = Weights.Common; + Config.Weight = Weights.Common - 1; Color = ColorUtils.FromHtml("#307444"); } diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestHillsMBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestHillsMBiome.cs index 8382cc8..82daeb7 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestHillsMBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestHillsMBiome.cs @@ -2,8 +2,8 @@ using OpenAPI.WorldGenerator.Generators.Terrain; using OpenAPI.WorldGenerator.Utils; -namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest; - +namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest +{ public class BirchForestHillsMBiome : BiomeBase { public BirchForestHillsMBiome() @@ -14,9 +14,14 @@ public BirchForestHillsMBiome() Downfall = 0.6f; MinHeight = 0.35f; MaxHeight = 0.45f; + + Config.AllowRivers = false; + Config.AllowScenicLakes = false; + Terrain = new BirchForestHillsMTerrain(); Config.Weight = Weights.Rare; Color = ColorUtils.FromHtml("#47875A"); } +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestMBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestMBiome.cs index f344cdf..6f6de2a 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestMBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/BirchForestMBiome.cs @@ -2,8 +2,8 @@ using OpenAPI.WorldGenerator.Generators.Terrain; using OpenAPI.WorldGenerator.Utils; -namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest; - +namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest +{ public class BirchForestMBiome : BiomeBase { public BirchForestMBiome() @@ -19,4 +19,5 @@ public BirchForestMBiome() Color = ColorUtils.FromHtml("#589C6C"); } +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/FlowerForestBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/FlowerForestBiome.cs index 584f2cb..767eb12 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/FlowerForestBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/FlowerForestBiome.cs @@ -2,8 +2,8 @@ using OpenAPI.WorldGenerator.Generators.Terrain; using OpenAPI.WorldGenerator.Utils; -namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest; - +namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest +{ public class FlowerForestBiome : BiomeBase { public FlowerForestBiome() @@ -20,4 +20,5 @@ public FlowerForestBiome() Color = ColorUtils.FromHtml("#2D8E49"); } +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/ForestHillsBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/WoodedHillsBiome.cs similarity index 75% rename from src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/ForestHillsBiome.cs rename to src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/WoodedHillsBiome.cs index 97c1d54..172d12d 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/ForestHillsBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Forest/WoodedHillsBiome.cs @@ -4,9 +4,9 @@ namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Forest { - public class ForestHillsBiome : BiomeBase + public class WoodedHillsBiome : BiomeBase { - public ForestHillsBiome() + public WoodedHillsBiome() { Id = 18; Name = "Wooded Hills"; @@ -14,6 +14,10 @@ public ForestHillsBiome() Downfall = 0.8f; MinHeight = 0.2f; MaxHeight = 0.6f; + + Config.AllowRivers = false; + Config.AllowScenicLakes = false; + Terrain = new ForestHillsTerrain(); Config.Weight = Weights.Common; diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleBiome.cs index 7b3508d..b586e05 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleBiome.cs @@ -15,10 +15,13 @@ public JungleBiome() Downfall = 0.9f; MinHeight = 0.1f; MaxHeight = 0.4f; - + + Config.Weight = Weights.Common; + Config.TreeDensity = 0.4f; + Terrain = new JungleTerrain(); Surface = new JungleSurface(Config, new Grass(), new Dirt(), 0f, 1.5f, 60f, 65f, 1.5f, new Podzol(), 0.09f); - Config.Weight = Weights.Common; + Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#537B09"); // Config.WeightMultiplier = 1.f; diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleEdgeBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleEdgeBiome.cs index 1c3d057..f1e58ca 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleEdgeBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleEdgeBiome.cs @@ -13,6 +13,8 @@ public JungleEdgeBiome() MinHeight = 0.1f; MaxHeight = 0.2f; + Config.TreeDensity = 0.65f; + Terrain = new JungleEdgeTerrain(); Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#628B17"); } diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleHillsBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleHillsBiome.cs index 5924700..c126b1e 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleHillsBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Jungle/JungleHillsBiome.cs @@ -13,10 +13,15 @@ public JungleHillsBiome() Downfall = 0.9f; MinHeight = 0.2f; MaxHeight = 1.8f; - - Terrain = new JungleHillsTerrain(72f, 40f); + + Config.AllowRivers = false; + Config.AllowScenicLakes = false; + Config.Weight = Weights.Uncommon; + Config.Weight = Weights.Common; + Config.TreeDensity = 0.4f; + Terrain = new JungleHillsTerrain(72f, 40f); Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#2C4205"); } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandBiome.cs index ed6ae36..a266c20 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandBiome.cs @@ -1,3 +1,8 @@ +using MiNET.Blocks; +using OpenAPI.WorldGenerator.Generators.Biomes.Config; +using OpenAPI.WorldGenerator.Generators.Surfaces.Mushroom; +using OpenAPI.WorldGenerator.Generators.Terrain; + namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Mushroom { public class MushroomIslandBiome : BiomeBase @@ -10,8 +15,13 @@ public MushroomIslandBiome() Downfall = 1.0f; MinHeight = 0.2f; MaxHeight = 1f; - + Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#FF00FF"); + Config.AllowScenicLakes = false; + Config.Weight = Weights.SuperRare; + + Surface = new MushroomSurface(Config, new Mycelium(), new Stone(), 0f); + Terrain = new MushroomIslandTerrain(); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandShoreBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandShoreBiome.cs index 1cb6f9e..66c6160 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandShoreBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Mushroom/MushroomIslandShoreBiome.cs @@ -1,3 +1,8 @@ +using MiNET.Blocks; +using OpenAPI.WorldGenerator.Generators.Biomes.Config; +using OpenAPI.WorldGenerator.Generators.Surfaces.Mushroom; +using OpenAPI.WorldGenerator.Generators.Terrain; + namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Mushroom { public class MushroomIslandShoreBiome : BiomeBase @@ -12,6 +17,11 @@ public MushroomIslandShoreBiome() MaxHeight = 0.1f; Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#A000FF"); + Config.AllowScenicLakes = false; + Config.Weight = Weights.SuperRare; + + Surface = new MushroomSurface(Config, new Mycelium(), new Stone(), 0f); + Terrain = new MushroomIslandShoreTerrain(); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Plains/IcePlainsBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Plains/IcePlainsBiome.cs index 1a195a8..30be056 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Plains/IcePlainsBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Plains/IcePlainsBiome.cs @@ -1,3 +1,5 @@ +using MiNET.Blocks; +using OpenAPI.WorldGenerator.Generators.Surfaces.Plains; using OpenAPI.WorldGenerator.Generators.Terrain; namespace OpenAPI.WorldGenerator.Generators.Biomes.Vanilla.Plains @@ -14,7 +16,7 @@ public IcePlainsBiome() MaxHeight = 0.5f; Terrain = new IcePlainsTerrain(); - + Surface = new IcePlainsSurface(Config, new Snow(), new Dirt(), new Snow(), new Dirt()); Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#FFFFFF"); } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Taiga/TaigaHillsBiome.cs b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Taiga/TaigaHillsBiome.cs index 6a4e594..195a5bf 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Taiga/TaigaHillsBiome.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Biomes/Vanilla/Taiga/TaigaHillsBiome.cs @@ -15,10 +15,12 @@ public TaigaHillsBiome() MinHeight = 0.2f; MaxHeight = 0.7f; + Config.AllowRivers = false; + Config.AllowScenicLakes = false; + Terrain = new TaigaHillsTerrain(); Surface = new TaigaSurface(Config, new Grass(), new Dirt()); - - Config.AllowRivers = false; + Color = OpenAPI.WorldGenerator.Utils.ColorUtils.FromHtml("#163933"); } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Decorators/FoliageDecorator.cs b/src/OpenAPI.WorldGenerator/Generators/Decorators/FoliageDecorator.cs index a8debd0..1c6c173 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Decorators/FoliageDecorator.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Decorators/FoliageDecorator.cs @@ -5,6 +5,7 @@ using OpenAPI.WorldGenerator.Generators.Biomes; using OpenAPI.WorldGenerator.Generators.Structures; using OpenAPI.WorldGenerator.Utils.Noise; +using OpenAPI.WorldGenerator.Utils.Noise.Modules; using OpenAPI.WorldGenerator.Utils.Noise.Primitives; using Biome = OpenAPI.WorldGenerator.Generators.Biomes.Biome; using Structure = OpenAPI.WorldGenerator.Generators.Structures.Structure; @@ -53,25 +54,20 @@ public override void Decorate(ChunkColumn column, if (surface && y >= Preset.SeaLevel) { var m = Math.Min(biome.Downfall * 0.32f, 0.03f); - var noise = Simplex.GetValue(rx * m, rz * m); + var noise = MathF.Abs( Math.Clamp(Simplex.GetValue(rx * m, rz * m), 0f, 1f)); if (x >= 3 && x <= 13 && z >= 3 && z <= 13) { Structure tree = null; - //if (biome.Config.) - if (biome.Downfall <= 0f && biome.Temperature >= 2f) + if (biome.Downfall >= 0 && (noise > ((biome.Config.TreeDensity) + (y / 512f)))) { - if (GetRandom(64) == 16) + if (currentTemperature >= 2f && biome.Downfall <= 0f) { var randValue = GetRandom(3); tree = new CactusStructure(randValue); } - } - - if (tree == null && biome.Downfall > 0 && (noise > (0.5f + (y / 512f)))) - { - if (currentTemperature >= 1f && biome.Downfall >= 0.4f) + else if (currentTemperature >= 1f && biome.Downfall >= 0.4f) { if (GetRandom(8) == 4) { @@ -111,13 +107,14 @@ public override void Decorate(ChunkColumn column, { if (y + 1 < 254) { - if (tree.CanCreate(column, x, y, z)) + StructurePlan plan = new StructurePlan(); + tree.Create(plan, x, y + 1, z); + + if (plan.TryExecute(column)) { - tree.Create(column, x, y + 1, z); + generated = true; } } - - generated = true; } } @@ -243,6 +240,7 @@ private int GetRandom(int max) protected override void InitSeed(int seed) { Simplex = new SimplexPerlin(seed, NoiseQuality.Fast); + Simplex = new ScaledNoiseModule(Simplex) {ScaleX = 16f, ScaleY = 1f, ScaleZ = 16f}; // Simplex.SetScale(1.5); Rnd = new FastRandom(seed); diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/GroundEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/GroundEffect.cs index fa66bd0..97f7d1c 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/GroundEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/GroundEffect.cs @@ -7,19 +7,16 @@ namespace OpenAPI.WorldGenerator.Generators.Effects */ public class GroundEffect : HeightEffect { - - // the standard ground effect - private float amplitude; + private float Amplitude { get; } public GroundEffect(float amplitude) { - this.amplitude = amplitude; + this.Amplitude = amplitude; } public override float Added(OverworldGeneratorV2 generator, float x, float y) { - return TerrainBase.GetGroundNoise(generator, x, y, amplitude); + return TerrainBase.GetGroundNoise(generator, x, y, Amplitude); } - } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/HeightEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/HeightEffect.cs index a48fccc..10505e7 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/HeightEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/HeightEffect.cs @@ -1,35 +1,12 @@ namespace OpenAPI.WorldGenerator.Generators.Effects { - /** - * @author Zeno410 - */ - public abstract class HeightEffect { - - public abstract float Added(OverworldGeneratorV2 generator, float x, float y); - - public HeightEffect Plus(HeightEffect added) { - - return new SummedHeightEffect(this, added); - } - } - - public class SummedHeightEffect : HeightEffect + public abstract class HeightEffect { + public abstract float Added(OverworldGeneratorV2 generator, float x, float y); - private HeightEffect _one; - private HeightEffect _two; - - public SummedHeightEffect(HeightEffect one, HeightEffect two) - { - - this._one = one; - this._two = two; - } - - public override float Added(OverworldGeneratorV2 generator, float x, float y) + public HeightEffect Plus(HeightEffect added) { - return _one.Added(generator, x, y) + _two.Added(generator, x, y); + return new SummedHeightEffect(this, added); } } - } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/JitterEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/JitterEffect.cs index a40fda1..a3f16ef 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/JitterEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/JitterEffect.cs @@ -6,24 +6,24 @@ namespace OpenAPI.WorldGenerator.Generators.Effects { public class JitterEffect : HeightEffect { - public float Amplitude = int.MaxValue; - public float Wavelength = 0; - public HeightEffect Jittered; + public float Amplitude { get; set; } = int.MaxValue; + public float Wavelength { get; set; } = 0; + public HeightEffect Jittered { get; set; } public JitterEffect() { } - public JitterEffect(float amplitude, float wavelength, HeightEffect toJitter) { - + public JitterEffect(float amplitude, float wavelength, HeightEffect toJitter) + { this.Amplitude = amplitude; this.Wavelength = wavelength; this.Jittered = toJitter; } - + public override float Added(OverworldGeneratorV2 generator, float x, float y) { - ISimplexData2D jitterData = SimplexData2D.NewDisk(); + ISimplexData2D jitterData = new SimplexData2D(); generator.SimplexInstance(1).GetValue(x / Wavelength, y / Wavelength, jitterData); int pX = (int) Math.Round(x + jitterData.GetDeltaX() * Amplitude); int pY = (int) Math.Round(y + jitterData.GetDeltaY() * Amplitude); diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/RaiseEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/RaiseEffect.cs index cd17444..70552ad 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/RaiseEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/RaiseEffect.cs @@ -7,7 +7,6 @@ namespace OpenAPI.WorldGenerator.Generators.Effects */ public class RaiseEffect : HeightEffect { - // just adds a number public readonly float Height; @@ -18,7 +17,6 @@ public RaiseEffect(float height) public override float Added(OverworldGeneratorV2 generator, float x, float y) { - return Height; } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/SpikeEverywhereEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/SpikeEverywhereEffect.cs index 2e2508c..4209a40 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/SpikeEverywhereEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/SpikeEverywhereEffect.cs @@ -3,30 +3,26 @@ namespace OpenAPI.WorldGenerator.Generators.Effects { - /** - * This creates a spiky multiplier going from 0 to 1 - * - * @author Zeno410 - */ + /// + /// Creates a spiky multiplier going from 0 to 1 + /// public class SpikeEverywhereEffect : HeightEffect { - // not going to bother to set up a creator shell to make sure everything is set // set defaults to absurd values to crash if they're not set // a trio of parameters frequently used together - public float Wavelength = 0; + public float Wavelength { get; set; } = 0; - public float MinimumSimplex = int.MaxValue; // normal range is -1 to 1; + public float MinimumSimplex { get; set; } = int.MaxValue; // normal range is -1 to 1; //usually numbers above 0 are often preferred to avoid dead basins - public int Octave; - public float Power = 1.6f; // usually a range of 1 to 2 - public HeightEffect Spiked; + public int Octave { get; set; } + public float Power { get; set; } = 1.6f; // usually a range of 1 to 2 + public HeightEffect Spiked { get; set; } public override float Added(OverworldGeneratorV2 generator, float x, float y) { - float noise = generator.SimplexInstance(Octave).GetValue(x / Wavelength, y / Wavelength); noise = MathF.Abs(noise); noise = TerrainBase.BlendedHillHeight(noise, MinimumSimplex); diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/SummedHeightEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/SummedHeightEffect.cs new file mode 100644 index 0000000..c200bb9 --- /dev/null +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/SummedHeightEffect.cs @@ -0,0 +1,18 @@ +namespace OpenAPI.WorldGenerator.Generators.Effects; + +public class SummedHeightEffect : HeightEffect +{ + private HeightEffect _one; + private HeightEffect _two; + + public SummedHeightEffect(HeightEffect one, HeightEffect two) + { + this._one = one; + this._two = two; + } + + public override float Added(OverworldGeneratorV2 generator, float x, float y) + { + return _one.Added(generator, x, y) + _two.Added(generator, x, y); + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiBorderEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiBorderEffect.cs index 3a4ac79..bb10bbd 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiBorderEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiBorderEffect.cs @@ -4,14 +4,16 @@ namespace OpenAPI.WorldGenerator.Generators.Effects { public class VoronoiBorderEffect : HeightEffect { - public float PointWavelength = 0; - public float Floor = float.MaxValue; - public float MinimumDivisor = 0;//low divisors can produce excessive rates of change - - public override float Added(OverworldGeneratorV2 generator, float x, float y) { + public float PointWavelength { get; set; } = 0; + public float Floor { get; set; } = float.MaxValue; + public float MinimumDivisor { get; set; } = 0;//low divisors can produce excessive rates of change + + public override float Added(OverworldGeneratorV2 generator, float x, float y) + { VoronoiResult points = generator.CellularInstance(1).Eval2D(x / PointWavelength, y / PointWavelength); float raise = (float) (points.InteriorValue); raise = 1.0f - raise; + //raise = TerrainBase.blendedHillHeight(raise, floor); return raise; } diff --git a/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiPlateauEffect.cs b/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiPlateauEffect.cs index 1a4fab5..a8a5c98 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiPlateauEffect.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Effects/VoronoiPlateauEffect.cs @@ -6,9 +6,9 @@ namespace OpenAPI.WorldGenerator.Generators.Effects { public class VoronoiPlateauEffect : HeightEffect { - public float PointWavelength = 200; - public float MinimumDivisor = 0;//low divisors can produce excessive rates of change - public float AdjustmentRadius = 3f; + public float PointWavelength { get; set; } = 200; + public float MinimumDivisor { get; set; } = 0;//low divisors can produce excessive rates of change + public float AdjustmentRadius { get; set; } = 3f; public override float Added(OverworldGeneratorV2 generator, float x, float y) diff --git a/src/OpenAPI.WorldGenerator/Generators/OverworldGeneratorV2.cs b/src/OpenAPI.WorldGenerator/Generators/OverworldGeneratorV2.cs index 4bc347a..41cc01a 100644 --- a/src/OpenAPI.WorldGenerator/Generators/OverworldGeneratorV2.cs +++ b/src/OpenAPI.WorldGenerator/Generators/OverworldGeneratorV2.cs @@ -64,7 +64,7 @@ public OverworldGeneratorV2() // Preset = JsonConvert.DeserializeObject( // "{\"useCaves\":true,\"useStrongholds\":true,\"useVillages\":true,\"useMineShafts\":true,\"useTemples\":true,\"useRavines\":true,\"useMonuments\":true,\"useMansions\":true,\"useLavaOceans\":false,\"useWaterLakes\":true,\"useLavaLakes\":true,\"useDungeons\":true,\"fixedBiome\":-3,\"biomeSize\":4,\"seaLevel\":63,\"riverSize\":4,\"waterLakeChance\":4,\"lavaLakeChance\":80,\"dungeonChance\":8,\"dirtSize\":33,\"dirtCount\":10,\"dirtMinHeight\":0,\"dirtMaxHeight\":255,\"gravelSize\":33,\"gravelCount\":8,\"gravelMinHeight\":0,\"gravelMaxHeight\":255,\"graniteSize\":33,\"graniteCount\":10,\"graniteMinHeight\":0,\"graniteMaxHeight\":80,\"dioriteSize\":33,\"dioriteCount\":10,\"dioriteMinHeight\":0,\"dioriteMaxHeight\":80,\"andesiteSize\":33,\"andesiteCount\":10,\"andesiteMinHeight\":0,\"andesiteMaxHeight\":80,\"coalSize\":17,\"coalCount\":20,\"coalMinHeight\":0,\"coalMaxHeight\":128,\"ironSize\":9,\"ironCount\":20,\"ironMinHeight\":0,\"ironMaxHeight\":64,\"goldSize\":9,\"goldCount\":2,\"goldMinHeight\":0,\"goldMaxHeight\":32,\"redstoneSize\":8,\"redstoneCount\":8,\"redstoneMinHeight\":0,\"redstoneMaxHeight\":16,\"diamondSize\":8,\"diamondCount\":1,\"diamondMinHeight\":0,\"diamondMaxHeight\":16,\"lapisSize\":7,\"lapisCount\":1,\"lapisMinHeight\":0,\"lapisMaxHeight\":32,\"coordinateScale\":684,\"heightScale\":684,\"mainNoiseScaleX\":80,\"mainNoiseScaleY\":160,\"mainNoiseScaleZ\":80,\"depthNoiseScaleX\":200,\"depthNoiseScaleZ\":200,\"depthNoiseScaleExponent\":0.5,\"biomeDepthWeight\":1,\"biomeDepthOffset\":0,\"biomeScaleWeight\":1,\"biomeScaleOffset\":1,\"lowerLimitScale\":512,\"upperLimitScale\":512,\"baseSize\":8.5,\"stretchY\":12,\"lapisCenterHeight\":16,\"lapisSpread\":16}");; - int seed = 3566635; + int seed = 356556635; _seed = seed; InitBiomeProviders(seed); @@ -76,8 +76,6 @@ public OverworldGeneratorV2() this._cellularNoiseInstances[i] = new SpacedCellularNoise(seed * i); } - // BiomeList = new BiomeBase[256]; - for (int i = 0; i < _weightings.Length; i++) { _weightings[i] = new float[256]; @@ -109,15 +107,13 @@ public OverworldGeneratorV2() BeachBiome = new bool[256]; LandBiomes = new bool[256]; WaterBiomes= new bool[256]; + foreach (var biome in totalBiomes) { BeachBiome[biome.Id] = ((biome.Type & BiomeType.Beach) != 0); LandBiomes[biome.Id] = (biome.Type & BiomeType.Land) != 0; WaterBiomes[biome.Id] = (biome.Type & BiomeType.Ocean) != 0 || (biome.Type & BiomeType.River) != 0; } - // BeachBiome = totalBiomes.Select(x => (x.Type.HasFlag(BiomeType.Beach))).ToArray(); - // LandBiome = totalBiomes.Select(x => (x.Type.HasFlag(BiomeType.Land))).ToArray(); - // OceanBiome = totalBiomes.Select(x => (x.Type.HasFlag(BiomeType.Ocean))).ToArray(); } public float RiverSeperation => RiverSeparationBase / Preset.RiverFrequency; @@ -150,98 +146,49 @@ public SpacedCellularNoise CellularInstance(int index) { private ChunkDecorator[] Decorators { get; set; } private void InitBiomeProviders(int seed) { + var distortNoise = new MultiFractalNoiseModule(new SimplexPerlin(seed ^ 32, NoiseQuality.Fast)); + + BiomeRegistry = new BiomeRegistry(); var biomeScale = 16f * Preset.BiomeSize; - INoiseModule temperaturePrimitive = new SimplexPerlin(seed ^ 3, NoiseQuality.Fast); - INoiseModule temperatureNoise = temperaturePrimitive; - - temperatureNoise = new VoronoiNoseModule() + INoiseModule temperatureNoise = new VoronoiNoseModule(new SimplexPerlin(seed ^ 3, NoiseQuality.Fast)) { - Primitive = temperatureNoise, - Distance = false, - Frequency = 0.0525644f, - OctaveCount = 6, - // Size = 8, - - Displacement = 2f, - Gain = 1.3f, - Lacunarity = 1.2f, - SpectralExponent = 0.6f + Distance = false, Frequency = 0.0325644f, Displacement = 2f }; - temperatureNoise = new TurbulenceNoiseModule(temperatureNoise, - new SimplexPerlin(seed ^ 6, NoiseQuality.Fast), - new SimplexPerlin(seed ^ 12, NoiseQuality.Fast), - new SimplexPerlin(seed ^ 18, NoiseQuality.Fast), 2f); + temperatureNoise = new TurbulenceNoiseModule(temperatureNoise, distortNoise, distortNoise, distortNoise, 3.5f); - temperatureNoise = new ScaledNoiseModule(temperatureNoise) + TemperatureNoise = new ScaledNoiseModule(temperatureNoise) { ScaleX = 1f / biomeScale, ScaleY = 1f / biomeScale, ScaleZ = 1f / biomeScale }; - INoiseModule rainNoise = new SimplexPerlin(seed * seed ^ 2, NoiseQuality.Fast); - rainNoise = new VoronoiNoseModule() + RainfallNoise = new ScaledNoiseModule(new VoronoiNoseModule(new SimplexPerlin(seed * seed ^ 2, NoiseQuality.Fast)) { - Primitive = rainNoise, - Distance = true, - Frequency = 0.0122776f, - OctaveCount = 6, - - Displacement = 1f, - Gain = 1.3f, - Lacunarity = 1.1f, - SpectralExponent = 0.8f - }; - - rainNoise = new TurbulenceNoiseModule(rainNoise, - new SimplexPerlin((seed * seed) ^ 6, NoiseQuality.Fast), - new SimplexPerlin((seed * seed) ^ 12, NoiseQuality.Fast), - new SimplexPerlin((seed * seed) ^ 18, NoiseQuality.Fast), 2f); - - rainNoise = new ScaledNoiseModule(rainNoise) + Distance = false, + Frequency = 0.022776f, + Displacement = 1f + }) { ScaleX = 1f / biomeScale, ScaleY = 1f / biomeScale, ScaleZ = 1f / biomeScale }; - // rainNoise = new TurbulenceNoiseModule(rainNoise, distortionY, distortionX, distortionX, 16f); - - BiomeRegistry = new BiomeRegistry(); - - - INoiseModule selectorNoise = new SimplexPerlin(seed, NoiseQuality.Fast); - - - selectorNoise = new VoronoiNoseModule() + INoiseModule selectorNoise = new VoronoiNoseModule(new SimplexPerlin(seed * 69, NoiseQuality.Fast)) { - Primitive = selectorNoise, - Distance = false, - Frequency = 0.1245776f, - OctaveCount = 3, - - Displacement = 1f, - Gain = 2.3f, - Lacunarity = 1.3f, - SpectralExponent = 0.8f - };// new SimplexPerlin(seed * seed ^ 2, NoiseQuality.Fast);} - - selectorNoise = new TurbulenceNoiseModule(selectorNoise, - new SimplexPerlin(seed * seed ^ 32, NoiseQuality.Fast), - new SimplexPerlin(seed * seed ^ 64, NoiseQuality.Fast), - new SimplexPerlin(seed * seed ^ 128, NoiseQuality.Fast), 1.5f); + Distance = false, Frequency = 0.245776f, Displacement = 1f + }; - selectorNoise = new ScaledNoiseModule(selectorNoise) + selectorNoise = new TurbulenceNoiseModule(selectorNoise, distortNoise, distortNoise, distortNoise, 4f); + + SelectorNoise = new ScaledNoiseModule( + selectorNoise) { - ScaleX = 1f / 128f, - ScaleY = 1f / 128f, - ScaleZ = 1f / 128f + ScaleX = 1f / Preset.MainNoiseScaleX, ScaleY = 1f / Preset.MainNoiseScaleY, ScaleZ = 1f / Preset.MainNoiseScaleZ }; var d = new FoliageDecorator(Preset); d.SetSeed(seed); - - RainfallNoise = rainNoise; - TemperatureNoise = temperatureNoise; - SelectorNoise = selectorNoise; + Decorators = new ChunkDecorator[] {d}; } @@ -260,73 +207,17 @@ public ChunkColumn GenerateChunkColumn(ChunkCoordinates chunkCoordinates) ChunkColumn column = new ChunkColumn() {X = chunkCoordinates.X, Z = chunkCoordinates.Z}; ChunkLandscape landscape = new ChunkLandscape(); - var biomes = CalculateBiomes(chunkCoordinates, landscape); - + CalculateBiomes(chunkCoordinates, landscape); + GenerateTerrain(column, landscape.Noise); - FixBiomes(column, landscape, biomes); + BlendBiomes(column, landscape); - SetBiomeBlocks(column, landscape); + // SetBiomeBlocks(column, landscape); return column; } - private float RiverAdjusted(float top, float river) { - if (river >= 1) { - return top; - } - float erodedRiver = river / ActualRiverProportion; - if (erodedRiver <= 1f) { - top = top * (1 - erodedRiver) + (Preset.SeaLevel - 1f) * erodedRiver; - } - top = top * (1 - river) + (Preset.SeaLevel - 1f) * river; - return top; - } - - private void SetBiomeBlocks(ChunkColumn chunk, ChunkLandscape landscape) - { - int worldX = chunk.X * 16; - int worldZ = chunk.Z * 16; - - var coords = new BlockCoordinates(worldX, 0, worldZ); - - for (int x = 0; x < 16; x++) - { - coords.X = worldX + x; - for (int z = 0; z < 16; z++) - { - coords.Z = worldZ + z; - - var index = NoiseMap.GetIndex(x, z); - var biome = landscape.Biome[index]; - var river = landscape.River[index]; - int depth = -1; - - biome.Replace(chunk, coords.X, coords.Z, x, z, depth, this, landscape.Noise, river, landscape.Biome); - chunk.biomeId[index] = (byte) biome.Id; - } - } - - for (int x = 0; x < 16; x++) - { - for (int z = 0; z < 16; z++) - { - var index = NoiseMap.GetIndex(x, z); - - var biome = landscape.Biome[index]; - var river = landscape.River[index]; - biome.Decorate(chunk, landscape.Noise[index], river); - - foreach (var decorator in Decorators) - { - decorator.Decorate( - chunk, chunk.X, chunk.Z, BiomeRegistry.GetBiome(chunk.biomeId[index]), new float[0], x, - chunk.height[index], z, true, false); - } - } - } - } - #region Biome Selection private long _totalLookups = 0; @@ -342,19 +233,14 @@ private void SetBiomeBlocks(ChunkColumn chunk, ChunkLandscape landscape) public float MaxHeight = float.MinValue; public float MinHeight = float.MaxValue; - // private ThreadSafeList _uniqueBiomes = new ThreadSafeList(); - private int[] CalculateBiomes(ChunkCoordinates coordinates, ChunkLandscape landscape) + private void CalculateBiomes(ChunkCoordinates coordinates, ChunkLandscape landscape) { - // var biomes = BiomeProvider.GetBiomes().ToArray(); - int worldX = coordinates.X << 4; int worldZ = coordinates.Z << 4; float[] weightedBiomes = new float[256]; int[] biomes = new int[SampleArraySize * SampleArraySize]; - - var availableBiomes = BiomeRegistry.GetBiomes(); for (int x = -SampleSize; x < SampleSize + 5; x++) { for (int z = -SampleSize; z < SampleSize + 5; z++) @@ -369,49 +255,42 @@ private int[] CalculateBiomes(ChunkCoordinates coordinates, ChunkLandscape lands //We do this by getting the absolute value (0 to 1) and multiplying it by 2. // temp = MathF.Abs(temp) * 2f; + /* if (temp > 2f) { var remainder = temp - 2f; temp = 2f - remainder; - } + }*/ MaxTemp = MathF.Max(temp, MaxTemp); MinTemp = MathF.Min(temp, MinTemp); + /* if (rain > 1f) { var remainder = rain - 1f; rain = 1f - remainder; - } + }*/ MaxRain = MathF.Max(rain, MaxRain); MinRain = MathF.Min(rain, MinRain); _totalLookups++; - var selector =MathF.Abs(SelectorNoise.GetValue(xx, zz)); - if (selector > 1f) + var selector = MathF.Abs(SelectorNoise.GetValue(xx, zz)); + /*if (selector > 1f) { var remainder = selector - 1f; selector = 1f - remainder; - } + }*/ MaxSelector = MathF.Max(selector, MaxSelector); MinSelector = MathF.Min(selector, MinSelector); - /*var height = MathF.Abs(HeightNoise.GetValue(xx, zz)); - height -= 1f; - - MaxHeight = MathF.Max(height, MaxHeight); - MinHeight = MathF.Min(height, MinHeight); - - height = Math.Clamp(height, -1.8f, 1.8f);*/ biomes[(x + SampleSize) * SampleArraySize + (z + SampleSize)] = (BiomeRegistry.GetBiome( (float) temp, (float) rain, selector)); } } - // var availableBiomes = BiomeProvider.GetBiomes(); - for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { @@ -461,169 +340,116 @@ private int[] CalculateBiomes(ChunkCoordinates coordinates, ChunkLandscape lands } } } - - //landscape.Biome[index] = weightedBiomes.; - - // landscape.Biome[index] = BiomeProvider.GetBiome(i); - //landscape.Biome[index] = BiomeProvider.GetBiome(); } } - - return biomes; } public const int MAX_BIOMES = 256; - private void FixBiomes(ChunkColumn column, ChunkLandscape landscape, int[] neighboring) + private void BlendBiomes(ChunkColumn column, ChunkLandscape landscape) { - // return; - // landscape.Biome - - ISimplexData2D jitterData = SimplexData2D.NewDisk(); + /* + ISimplexData2D jitterData = new SimplexData2D(); BiomeBase[] jitteredBiomes = new BiomeBase[256]; - var riverStrength = landscape.River; - var noise = landscape.Noise; + BiomeBase jitterbiome, actualbiome; - for (int i = 0; i < 16; i++) + for (int x = 0; x < 16; x++) { - for (int j = 0; j < 16; j++) + for (int z = 0; z < 16; z++) { - int x = (column.X * 16) + i; - int z = (column.Z * 16) + j; - this.SimplexInstance(0).GetValue(x, z, jitterData); - int pX = (int) Math.Round(x + jitterData.GetDeltaX() * BlendRadius); - int pZ = (int) Math.Round(z + jitterData.GetDeltaY() * BlendRadius); - actualbiome = landscape.Biome[(x & 15) * 16 + (z & 15)]; + int realX = (column.X * 16) + x; + int realZ = (column.Z * 16) + z; + this.SimplexInstance(0).GetValue(realX, realZ, jitterData); + int pX = (int) Math.Round(realX + jitterData.GetDeltaX() * BlendRadius); + int pZ = (int) Math.Round(realZ + jitterData.GetDeltaY() * BlendRadius); + actualbiome = landscape.Biome[(realX & 15) * 16 + (realZ & 15)]; jitterbiome = landscape.Biome[(pX & 15) * 16 + (pZ & 15)]; - jitteredBiomes[i * 16 + j] = + jitteredBiomes[x * 16 + z] = (actualbiome.Config.SurfaceBlendIn && jitterbiome.Config.SurfaceBlendOut) ? jitterbiome : actualbiome; - - //riverStrength[i * 16 + j] = RiverAdjusted(Preset.SeaLevel, riverStrength[i * 16 + j]); } } + */ + SetBiomeBlocks(column, landscape, landscape.Biome); + } + + private void SetBiomeBlocks(ChunkColumn chunk, ChunkLandscape landscape, BiomeBase[] jitteredBiomes) + { + int worldX = chunk.X * 16; + int worldZ = chunk.Z * 16; - BiomeBase realisticBiome; - - // currently just stuffs the genLayer into the jitter; - for (int i = 0; i < MAX_BIOMES; i++) + for (int x = 0; x < 16; x++) { - realisticBiome = landscape.Biome[i]; - - var targetHeight = noise[i]; + var cx = worldX + x; + for (int z = 0; z < 16; z++) + { + var cz = worldZ + z; - bool canBeRiver = riverStrength[i] > 0.7 /* && targetHeight >= (Preset.SeaLevel * 0.5f) - && targetHeight <= Preset.SeaLevel + 1*/; + var index = NoiseMap.GetIndex(x, z); + var localRiver = landscape.River[index]; + int depth = -1; - if (targetHeight > Preset.SeaLevel) - { - // replace - jitteredBiomes[i] = realisticBiome; - } - else - { - // check for river - if (canBeRiver && (realisticBiome.Type & BiomeType.Ocean) == 0) - { - // make river - jitteredBiomes[i] = realisticBiome.GetRiverBiome(); - } - else if (targetHeight < Preset.SeaLevel) - { - jitteredBiomes[i] = realisticBiome; - } + jitteredBiomes[index].Replace(chunk, cx, cz, x, z, depth, this, landscape.Noise, localRiver, landscape.Biome); } } - - BiomeSearch waterSearch = new BiomeSearch(WaterBiomes); - BiomeSearch landSearch = new BiomeSearch(LandBiomes); - - waterSearch.SetNotHunted(); - waterSearch.SetAbsent(); - - landSearch.SetNotHunted(); - landSearch.SetAbsent(); - // beachSearch.Hunt(neighboring); - // landSearch.Hunt(neighboring); - - float beachTop = Preset.SeaLevel + 1.5f; // 64.5f; - float beachBottom = Preset.SeaLevel - 1.5f; - var b = jitteredBiomes.Select(x => x.Id).ToArray(); - for (int i = 0; i < MAX_BIOMES; i++) + + + for (int x = 0; x < 16; x++) { - if (noise[i] < beachBottom || noise[i] > beachTop) - { - continue; // this block isn't beach level - } - - if ((jitteredBiomes[i].Type & BiomeType.Swamp) != 0 || ((jitteredBiomes[i].Type & BiomeType.Ocean) == 0 && (jitteredBiomes[i].Type & BiomeType.River) == 0)) + var cx = worldX + x; + for (int z = 0; z < 16; z++) { - continue; - } - - - // if (waterSearch.IsNotHunted()) - // { - // waterSearch.Hunt(neighboring); - // } - - // int foundWaterBiome = waterSearch.Biomes[i]; + var cz = worldZ + z; + var index = NoiseMap.GetIndex(x, z); - // if (foundWaterBiome != -1) - { - if (landSearch.IsNotHunted()) + var biome = landscape.Biome[index]; + var river = landscape.River[index]; + + if (river > 0.9f && biome.Config.AllowRivers) { - landSearch.Hunt(neighboring); + chunk.biomeId[index] = (byte) biome.GetRiverBiome().Id; + biome.GetRiverBiome().Decorate(chunk, landscape.Noise[index], river); } - - // var nearestWaterBiome = BiomeProvider.GetBiome(foundWaterBiome); - int nearestLandBiome = landSearch.Biomes[i]; - - if (nearestLandBiome > -1) + else { - var foundBiome = BiomeRegistry.GetBiome(nearestLandBiome).GetBeachBiome(); + chunk.biomeId[index] = (byte) landscape.Biome[index].Id; + biome.Decorate(chunk, landscape.Noise[index], river); + } - if (foundBiome != null) - { - jitteredBiomes[i] = foundBiome; - } + foreach (var decorator in Decorators) + { + decorator.Decorate( + chunk, chunk.X, chunk.Z, biome, Array.Empty(), x, + chunk.height[index], z, true, false); } } } - - for (int i = 0; i < jitteredBiomes.Length; i++) - { - landscape.Biome[i] = jitteredBiomes[i]; - } } #endregion + private static int _stoneId = new Stone().GetRuntimeId(); + private static int _waterId = new Water().GetRuntimeId(); private void GenerateTerrain(ChunkColumn column, float[] noiseMap) { - int stoneId = new Stone().GetRuntimeId(); - var waterId = new Water().GetRuntimeId(); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { var height = (int) noiseMap[NoiseMap.GetIndex(x, z)]; - for (int y = 0; (y <= height || y <= Preset.SeaLevel) && y < 255; y++) + for (int y = 0; (y <= Math.Max(height, Preset.SeaLevel)) && y < 255; y++) { - if (y > height) + if (y <= height) { - if (y <= Preset.SeaLevel) - { - column.SetBlockByRuntimeId(x, y, z, waterId); - } + column.SetBlockByRuntimeId(x, y, z, _stoneId); } - else + else if (y < Preset.SeaLevel) { - column.SetBlockByRuntimeId(x, y, z, stoneId); + column.SetBlockByRuntimeId(x, y, z, _waterId); } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Preset.cs b/src/OpenAPI.WorldGenerator/Generators/Preset.cs index 4ca32e6..85f1da4 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Preset.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Preset.cs @@ -215,10 +215,12 @@ public class WorldGeneratorPreset [JsonProperty("lapisSpread")] public float LapisSpread { get; set; } - - public float RiverFrequency { get; set; } = 1.0f; - public float RiverBendMult { get; set; } = 1.0f; + public float RiverSizeMult { get; set; } = 1.0f; + public float RiverFrequency { get; set; } = 0.5f; + public float RiverBendMult { get; set; } = 1.0f; + public float RiverCutOffAmpl { get; set; } = 0.5f; + public float RiverCutOffScale { get; set; } = 350.0f; public float RTGlakeSizeMult { get; set; } = 1f; // RTG public float RTGlakeFreqMult { get; set; } = 1f; // RTG diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/AcaciaTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/AcaciaTree.cs index 40b199e..2964041 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/AcaciaTree.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/AcaciaTree.cs @@ -13,11 +13,6 @@ public override string Name get { return "AcaciaTree"; } } - public override int MaxHeight - { - get { return 8; } - } - private static readonly int WoodId = new Wood() { WoodType = "acacia" @@ -29,15 +24,16 @@ public override int MaxHeight }.GetRuntimeId(); private readonly int _leafRadius = 2; - public override void Create(ChunkColumn column, int x, int y, int z) + public override void Create(StructurePlan plan, int x, int y, int z) { var location = new Vector3(x, y, z); - if (!ValidLocation(location, _leafRadius)) return; + CheckFloor(plan, x, y - 1, z); + // if (!ValidLocation(location, _leafRadius)) return; int height = Rnd.Next(5, 6); - GenerateColumn(column, location, height, WoodId); - GenerateVanillaLeaves(column, location + new Vector3(0, height, 0), _leafRadius, LeafId); + GenerateColumn(plan, location, height, WoodId); + GenerateVanillaLeaves(plan, location + new Vector3(0, height, 0), _leafRadius, LeafId); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/BirchTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/BirchTree.cs index 877eb9f..7394f4c 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/BirchTree.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/BirchTree.cs @@ -11,11 +11,6 @@ public override string Name get { return "BirchTree"; } } - public override int MaxHeight - { - get { return 7; } - } - private const int LeafRadius = 2; private static readonly int WoodId = new Wood() { @@ -27,18 +22,17 @@ public override int MaxHeight OldLeafType = "birch" }.GetRuntimeId(); - public override void Create(ChunkColumn column, int x, int y, int z) + public override void Create(StructurePlan plan, int x, int y, int z) { // var block = blocks[OverworldGenerator.GetIndex(x, y - 1, z)]; // if (block != 2 && block != 3) return; - + CheckFloor(plan, x, y - 1, z); var location = new Vector3(x, y, z); - if (!ValidLocation(location, LeafRadius)) return; - int height = Rnd.Next(4, MaxHeight); - GenerateColumn(column, location, height, WoodId); + int height = Rnd.Next(4, 7); + GenerateColumn(plan, location, height, WoodId); Vector3 leafLocation = location + new Vector3(0, height, 0); - GenerateVanillaLeaves(column, leafLocation, LeafRadius, LeafId); + GenerateVanillaLeaves(plan, leafLocation, LeafRadius, LeafId); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/CactusStructure.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/CactusStructure.cs index f936290..23ed05f 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/CactusStructure.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/CactusStructure.cs @@ -20,40 +20,21 @@ public CactusStructure(int height) } private static readonly int CactusBlockId = new Cactus().GetRuntimeId(); - public override void Create(ChunkColumn chunk, int x, int y, int z) + public override void Create(StructurePlan plan, int x, int y, int z) { + plan.RequireBlock(x,y - 1,z, new Sand()); //if (blocks[OverworldGenerator.GetIndex(x, y - 1, z)] != 12) return; //Not sand, do not generate. - var growth = Rnd.Next(0x1, 0x15); + //var growth = Rnd.Next(0x1, 0x15); for (int modifiedY = y; modifiedY < y + _height; modifiedY++) { - if (!CheckSafe(chunk, x, modifiedY, z)) break; + //if (!CheckSafe(plan, x, modifiedY, z)) break; //blocks[OverworldGenerator.GetIndex(x, modifiedY, z)] = 81; //metadata[OverworldGenerator.GetIndex(x, modifiedY, z)] = (byte) growth; - chunk.SetBlockByRuntimeId(x, modifiedY, z, CactusBlockId); //Cactus block + plan.PlaceBlock(x, modifiedY, z, CactusBlockId); //Cactus block //chunk.SetMetadata(x, modifiedY, z, (byte)growth); } } - - private bool CheckSafe(ChunkColumn chunk, int x, int y, int z) - { - if (!chunk.IsAir(x - 1, y, z)) return false; - if (!chunk.IsAir(x + 1, y, z)) return false; - if (!chunk.IsAir(x, y, z - 1)) return false; - if (!chunk.IsAir(x, y, z + 1)) return false; - - return true; - } - - private bool CheckSafe(Level level, int x, int y, int z) - { - if (level.IsAir(new BlockCoordinates(x - 1, y, z))) return false; - if (level.IsAir(new BlockCoordinates(x + 1, y, z))) return false; - if (level.IsAir(new BlockCoordinates(x, y, z - 1))) return false; - if (level.IsAir(new BlockCoordinates(x, y, z + 1))) return false; - - return true; - } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/LargeJungleTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/LargeJungleTree.cs index 81d29b9..cafa02f 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/LargeJungleTree.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/LargeJungleTree.cs @@ -13,10 +13,6 @@ public override string Name get { return "JungleTree"; } } - public override int MaxHeight - { - get { return 30; } - } private int BaseSize = 9; private int Roots = 3; @@ -35,51 +31,46 @@ public override int MaxHeight { OldLeafType = "jungle" }.GetRuntimeId(); - - public override void Create(ChunkColumn chunk, int x, int y, int z) - { - if (x < 6 || x > 10 || z < 6 || z > 10) return; - - //var block = blocks[OverworldGenerator.GetIndex(x, y - 1, z)]; - // if (block != 2 && block != 3) return; - - BaseSize = 9 + Rnd.Next(5); + public override void Create(StructurePlan plan, int x, int y, int z) + { + CheckFloor(plan, x, y - 1, z); + var baseSize = 9 + Rnd.Next(5); if (Roots > 0f) { for (int k = 0; k < 3; k++) { - GenerateBranch(chunk, x, y + Roots, z, (120 * k) - 40 + Rnd.Next(80), 1.6f + (float)Rnd.NextDouble() * 0.1f, Roots * 1.7f, 1f, - WoodId); + var horRootDir = (120 * k) - 40 + Rnd.Next(80); + var verRootDir = 1.6f + (float) Rnd.NextDouble() * 0.1f; + + GenerateBranch(plan, x, y + Roots, z, horRootDir, verRootDir, Roots * 1.7f, 1f, WoodId); } } - for (int i = y + Roots; i < y + BaseSize; i++) + for (int i = y + Roots; i < y + baseSize; i++) { - chunk.SetBlockByRuntimeId(x, i, z, WoodId); + plan.PlaceBlock(x, i, z, WoodId); } - + float horDir, verDir; int eX, eY, eZ; + for (int j = 0; j < Branches; j++) { horDir = (120 * j) - 60 + Rnd.Next(120); - verDir = VerticalStart + (float)Rnd.NextDouble() * VerticalRand; + verDir = VerticalStart + (float) Rnd.NextDouble() * VerticalRand; - eX = x + (int)(Math.Cos(horDir * Math.PI / 180D) * verDir * BranchLength); - eZ = z + (int)(Math.Sin(horDir * Math.PI / 180D) * verDir * BranchLength); - eY = y + BaseSize + (int)((1f - verDir) * BranchLength); + eX = x + (int) (Math.Cos(horDir * Math.PI / 180D) * verDir * BranchLength); + eZ = z + (int) (Math.Sin(horDir * Math.PI / 180D) * verDir * BranchLength); + eY = y + baseSize + (int) ((1f - verDir) * BranchLength); - if (CanGenerateBranch(x, y + BaseSize, z, horDir, verDir, BranchLength, 1f, 4f, 1.5f) && CanGenerateLeaves(eX, eY, eZ, 4f, 1.5f)) + //if (CanGenerateBranch(x, y + BaseSize, z, horDir, verDir, BranchLength, 1f/*, 4f, 1.5f*/) && CanGenerateLeaves(eX, eY, eZ, 4f, 1.5f)) { - GenerateBranch(chunk, x, y + BaseSize, z, horDir, verDir, BranchLength, 1f, - WoodId); + GenerateBranch(plan, x, y + baseSize, z, horDir, verDir, BranchLength, 1f, WoodId); for (int m = 0; m < 1; m++) { - GenerateLeaves(chunk, eX, eY, eZ, 4f, 1.5f, - LeavesId, - WoodId); + GenerateLeaves(plan, eX, eY, eZ, 4f, 1.5f, LeavesId, WoodId); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/OakTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/OakTree.cs index 199606d..95037b2 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/OakTree.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/OakTree.cs @@ -17,11 +17,6 @@ public override string Name get { return "OakTree"; } } - public override int MaxHeight - { - get { return 7; } - } - private static readonly int WoodId = new Wood() { WoodType = "oak" @@ -33,15 +28,15 @@ public override int MaxHeight }.GetRuntimeId(); private readonly int _leafRadius = 2; - public override void Create(ChunkColumn chunk, int x, int y, int z) + public override void Create(StructurePlan plan, int x, int y, int z) { + CheckFloor(plan, x, y - 1, z); var location = new Vector3(x, y, z); - if (!ValidLocation(location, _leafRadius)) return; int height = Rnd.Next(4, 5); - GenerateColumn(chunk, location, height, WoodId); + GenerateColumn(plan, location, height, WoodId); Vector3 leafLocation = location + new Vector3(0, height, 0); - GenerateVanillaLeaves(chunk, leafLocation, _leafRadius, LeafId); + GenerateVanillaLeaves(plan, leafLocation, _leafRadius, LeafId); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/PineTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/PineTree.cs index bfd7a01..9b381cc 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/PineTree.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/PineTree.cs @@ -15,11 +15,6 @@ public override string Name get { return "PineTree"; } } - public override int MaxHeight - { - get { return 10; } - } - private static readonly int WoodId = new Wood() { WoodType = "oak" @@ -30,43 +25,40 @@ public override int MaxHeight OldLeafType = "oak" }.GetRuntimeId(); - public override void Create(ChunkColumn column, int x, int y, int z) + public override void Create(StructurePlan plan, int x, int y, int z) { - //var block = blocks[OverworldGenerator.GetIndex(x, y - 1, z)]; - //if (block != 2 && block != 3) return; - + CheckFloor(plan, x, y - 1, z); var location = new Vector3(x, y, z); - if (!ValidLocation(location, _leafRadius)) return; var height = Rnd.Next(7, 8); - GenerateColumn(column, location, height, WoodId); + GenerateColumn(plan, location, height, WoodId); for (var Y = 1; Y < height; Y++) { if (Y % 2 == 0) { - GenerateVanillaCircle(column, location + new Vector3(0, Y + 1, 0), _leafRadius - 1, LeafId, 1); + GenerateVanillaCircle(plan, location + new Vector3(0, Y + 1, 0), _leafRadius - 1, LeafId, 1); continue; } - GenerateVanillaCircle(column, location + new Vector3(0, Y + 1, 0), _leafRadius, LeafId, 1); + GenerateVanillaCircle(plan, location + new Vector3(0, Y + 1, 0), _leafRadius, LeafId, 1); } - GenerateTopper(column, location + new Vector3(0, height, 0), 0x1); + GenerateTopper(plan, location + new Vector3(0, height, 0), 0x1); } - protected void GenerateTopper(ChunkColumn chunk, Vector3 location, byte type = 0x0) + protected void GenerateTopper(StructurePlan plan, Vector3 location, byte type = 0x0) { var sectionRadius = 1; - GenerateCircle(chunk, location, sectionRadius, LeafId); + GenerateCircle(plan, location, sectionRadius, LeafId); var top = location + new Vector3(0, 1, 0); var x = (int)location.X; var y = (int)location.Y + 1; var z = (int)location.Z; - chunk.SetBlockByRuntimeId(x, y, z, LeafId); + plan.PlaceBlock(x, y, z, LeafId); //chunk.SetMetadata(x, y, z, 1); if (type == 0x1 && y < 256) - GenerateVanillaCircle(chunk, new Vector3(x, y, z), sectionRadius, LeafId); + GenerateVanillaCircle(plan, new Vector3(x, y, z), sectionRadius, LeafId); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/SmallJungleTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/SmallJungleTree.cs index 6841a39..c17accc 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/SmallJungleTree.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/SmallJungleTree.cs @@ -13,11 +13,6 @@ public override string Name get { return "JungleTree"; } } - public override int MaxHeight - { - get { return 6; } - } - private int BaseSize = 5; private int Roots = 0; private float BranchLength = 5f; @@ -36,28 +31,26 @@ public override int MaxHeight OldLeafType = "jungle" }.GetRuntimeId(); - public override void Create(ChunkColumn chunk, int x, int y, int z) + public override void Create(StructurePlan plan, int x, int y, int z) { + CheckFloor(plan, x, y - 1, z); //var block = blocks[OverworldGenerator.GetIndex(x, y - 1, z)]; //if (block != 2 && block != 3) return; + BaseSize = 3 + Rnd.Next(2); - BaseSize = 3 + Rnd.Next(2); if (Roots > 0f) { for (int k = 0; k < 3; k++) { - GenerateBranch(chunk, x, y + Roots, z, (120 * k) - 40 + Rnd.Next(80), 1.6f + (float)Rnd.NextDouble() * 0.1f, Roots * 1.7f, 1f, WoodId); + GenerateBranch(plan, x, y + Roots, z, (120 * k) - 40 + Rnd.Next(80), 1.6f + (float)Rnd.NextDouble() * 0.1f, Roots * 1.7f, 1f, WoodId); } } for (int i = y + Roots; i < y + BaseSize; i++) { - //blocks[OverworldGenerator.GetIndex(x, i, z)] = 17; - //metadata[OverworldGenerator.GetIndex(x, i, z)] = 3; - chunk.SetBlockByRuntimeId(x, i, z, WoodId); - // chunk.SetMetadata(x, i, z, 3); + plan.PlaceBlock(x, i, z, WoodId); } - + float horDir, verDir; int eX, eY, eZ; for (int j = 0; j < Branches; j++) @@ -69,27 +62,16 @@ public override void Create(ChunkColumn chunk, int x, int y, int z) eZ = z + (int)(Math.Sin(horDir * Math.PI / 180D) * verDir * BranchLength); eY = y + BaseSize + (int)((1f - verDir) * BranchLength); - if (CanGenerateBranch(x, y + BaseSize, z, horDir, verDir, BranchLength, 1f, 4f, 1.5f) && CanGenerateLeaves(eX, eY, eZ, 4f, 1.5f)) + //if (CanGenerateBranch(x, y + BaseSize, z, horDir, verDir, BranchLength, 1f) && CanGenerateLeaves(eX, eY, eZ, 4f, 1.5f)) { - GenerateBranch(chunk, x, y + BaseSize, z, horDir, verDir, BranchLength, 1f, WoodId); + GenerateBranch(plan, x, y + BaseSize, z, horDir, verDir, BranchLength, 1f, WoodId); for (int m = 0; m < 1; m++) { - GenerateLeaves(chunk, eX, eY, eZ, 4f, 1.5f, LeavesId, WoodId); + GenerateLeaves(plan, eX, eY, eZ, 4f, 1.5f, LeavesId, WoodId); } } } - - /*var location = new Vector3(x, y, z); - if (!ValidLocation(location, 2)) return; - - int height = Math.Max(4, Rnd.Next(MaxHeight)); - - GenerateColumn(chunk, location, height, 17, 3); - Vector3 leafLocation = location + new Vector3(0, height, 0); - GenerateVanillaLeaves(chunk, leafLocation, 2, 18, 3); - */ - // base.Create(chunk, x, y, z); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/SpruceTree.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/SpruceTree.cs deleted file mode 100644 index cc80be9..0000000 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/SpruceTree.cs +++ /dev/null @@ -1,21 +0,0 @@ -using MiNET.Blocks; -using MiNET.Utils; -using MiNET.Utils.Vectors; -using MiNET.Worlds; - -namespace OpenAPI.WorldGenerator.Generators.Structures -{ - public class SpruceTree : TreeStructure - { - public override string Name - { - get { return "SpruceTree"; } - } - - public override int MaxHeight - { - get { return 8; } - } - } -} - diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/StructureClass.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/StructureClass.cs index cb5c264..1dce8ce 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/StructureClass.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/StructureClass.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using MiNET.Blocks; using MiNET.Utils; using MiNET.Utils.Vectors; @@ -14,25 +16,115 @@ public virtual string Name get { return null; } } - public virtual int MaxHeight { get { return 0; } } + public virtual void Create(StructurePlan plan, int x, int y, int z) + { + + } + } - public virtual void Create(ChunkColumn column, int x, int y, int z) + public class StructurePlan + { + private LinkedList Actions { get; } + + public StructurePlan() { - + Actions = new LinkedList(); + } + + public void AddAction(StructureAction action) + { + Actions.AddLast(action); + } + + public void PlaceBlock(int x, int y, int z, int runtimeId) + { + AddAction(new PlaceBlockAction(x, y, z, runtimeId)); + } + + public void RequireBlock(int x, int y, int z, params Block[] validBlocks) + { + AddAction(new RequireBlockAction(x, y, z, validBlocks)); } - public virtual bool CanCreate(ChunkColumn column, int x, int y, int z) + public bool TryExecute(ChunkColumn column) { + foreach (var action in Actions) + { + if (!action.Validate(column)) + return false; + } + + foreach (var action in Actions) + { + action.Execute(column); + } + return true; } - protected Block GetBlockObject(int runtimeId) + public class RequireBlockAction : StructureAction + { + public int X { get; } + public int Y { get; } + public int Z { get; } + public Type[] RuntimeIds { get; } + + public RequireBlockAction(int x, int y, int z, params Block[] runtimeIds) + { + X = x; + Y = y; + Z = z; + RuntimeIds = runtimeIds.Select(x => x.GetType()).ToArray(); + } + + /// + public override bool Validate(ChunkColumn column) + { + var blockAtPosition = column.GetBlockObject(X, Y, Z); + var blockType = blockAtPosition.GetType(); + + return RuntimeIds.Any(x => x.IsAssignableFrom(blockType)); + } + + /// + public override void Execute(ChunkColumn column) + { + + } + } + + public class PlaceBlockAction : StructureAction + { + public int X { get; } + public int Y { get; } + public int Z { get; } + public int RuntimeId { get; } + + public PlaceBlockAction(int x, int y, int z, int runtimeId) + { + X = x; + Y = y; + Z = z; + RuntimeId = runtimeId; + } + + /// + public override bool Validate(ChunkColumn column) + { + return X >= 0 && X <= 15 && Z >= 0 && Z <= 15; + } + + /// + public override void Execute(ChunkColumn column) + { + column.SetBlockByRuntimeId(X, Y, Z, RuntimeId); + } + } + + public abstract class StructureAction { - BlockStateContainer blockStateContainer = BlockFactory.BlockPalette[runtimeId]; - Block blockById = BlockFactory.GetBlockById(blockStateContainer.Id); - blockById.SetState(blockStateContainer.States); - blockById.Metadata = (byte) blockStateContainer.Data; - return blockById; + public abstract bool Validate(ChunkColumn column); + public abstract void Execute(ChunkColumn column); } } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Structures/TreeStructure.cs b/src/OpenAPI.WorldGenerator/Generators/Structures/TreeStructure.cs index 30b6509..9ff3f18 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Structures/TreeStructure.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Structures/TreeStructure.cs @@ -12,22 +12,31 @@ namespace OpenAPI.WorldGenerator.Generators.Structures //And from https://github.com/SirCmpwn/TrueCraft public class TreeStructure : Structure { - protected void GenerateVanillaLeaves(ChunkColumn chunk, Vector3 location, int radius, int blockRuntimeId) + private static readonly Block[] ValidBlocks = new Block[] + { + new Grass(), new Dirt(), new Farmland() + }; + + protected void CheckFloor(StructurePlan plan, int x, int y, int z) + { + plan.RequireBlock(x,y,z, ValidBlocks); + } + protected void GenerateVanillaLeaves(StructurePlan plan, Vector3 location, int radius, int blockRuntimeId) { - if (location.X > 16 || location.X < 0 || location.Z > 16 || location.Z < 0) return; var radiusOffset = radius; for (var yOffset = -radius; yOffset <= radius; yOffset = (yOffset + 1)) { var y = location.Y + yOffset; if (y > 255) continue; - GenerateVanillaCircle(chunk, new Vector3(location.X, y, location.Z), radiusOffset, blockRuntimeId); + + GenerateVanillaCircle(plan, new Vector3(location.X, y, location.Z), radiusOffset, blockRuntimeId); if (yOffset != -radius && yOffset % 2 == 0) radiusOffset--; } } - protected void GenerateVanillaCircle(ChunkColumn chunk, Vector3 location, int radius, int blockRuntimeId, + protected void GenerateVanillaCircle(StructurePlan plan, Vector3 location, int radius, int blockRuntimeId, double corner = 0) { for (var I = -radius; I <= radius; I = (I + 1)) @@ -48,13 +57,13 @@ protected void GenerateVanillaCircle(ChunkColumn chunk, Vector3 location, int ra if (x < 0 || z > 16) continue; if (z < 0 || z > 16) continue; - chunk.SetBlockByRuntimeId((int)x, (int)location.Y, (int)z, blockRuntimeId); + plan.PlaceBlock((int)x, (int)location.Y, (int)z, blockRuntimeId); } } } } - public static void GenerateColumn(ChunkColumn chunk, Vector3 location, int height, int blockRuntimeId) + public static void GenerateColumn(StructurePlan plan, Vector3 location, int height, int blockRuntimeId) { for (var o = 0; o < height; o++) { @@ -62,11 +71,11 @@ public static void GenerateColumn(ChunkColumn chunk, Vector3 location, int heigh var y = (int)location.Y + o; var z = (int)location.Z; - chunk.SetBlockByRuntimeId(x, y, z, blockRuntimeId); + plan.PlaceBlock(x, y, z, blockRuntimeId); } } - protected void GenerateCircle(ChunkColumn chunk, Vector3 location, int radius, int blockRuntimeId) + protected void GenerateCircle(StructurePlan plan, Vector3 location, int radius, int blockRuntimeId) { for (var I = -radius; I <= radius; I = (I + 1)) { @@ -85,13 +94,13 @@ protected void GenerateCircle(ChunkColumn chunk, Vector3 location, int radius, i var y = (int)location.Y; var z = (int)Z; - chunk.SetBlockByRuntimeId(x, y, z, blockRuntimeId); + plan.PlaceBlock(x, y, z, blockRuntimeId); } } } } - protected bool CanGenerateBranch(float x, float y, float z, float horDir, float verDir, float branchLength, float speed, float size, float width) + /*protected bool CanGenerateBranch(float x, float y, float z, float horDir, float verDir, float branchLength, float speed/*, float size, float width#1#) { if (verDir < 0f) { @@ -120,7 +129,7 @@ protected bool CanGenerateBranch(float x, float y, float z, float horDir, float if (x < 0 || x >= 16 || z < 0 || z >= 16) return false; } - int i, j, k, s = (int)(size - 1f), w = (int)((size - 1f) * width); + /*int i, j, k, s = (int)(size - 1f), w = (int)((size - 1f) * width); for (i = -w; i <= w; i++) { for (j = -s; j <= s; j++) @@ -130,12 +139,12 @@ protected bool CanGenerateBranch(float x, float y, float z, float horDir, float if (x + i < 0 || x + i >= 16 || z + k < 0 || z + k >= 16) return false; } } - } + }#1# return true; - } + }*/ - protected void GenerateBranch(ChunkColumn chunk, float x, float y, float z, double horDir, float verDir, float length, float speed, int blockRuntimeId) + protected void GenerateBranch(StructurePlan plan, float x, float y, float z, double horDir, float verDir, float length, float speed, int blockRuntimeId) { if (verDir < 0f) { @@ -155,7 +164,8 @@ protected void GenerateBranch(ChunkColumn chunk, float x, float y, float z, doub while (c < length) { - chunk.SetBlockByRuntimeId((int)x, (int)y, (int)z, blockRuntimeId); + plan.PlaceBlock((int)x, (int)y, (int)z, blockRuntimeId); + //chunk.SetBlockByRuntimeId((int)x, (int)y, (int)z, blockRuntimeId); x += velX; y += velY; @@ -165,7 +175,7 @@ protected void GenerateBranch(ChunkColumn chunk, float x, float y, float z, doub } } - protected bool CanGenerateLeaves(int x, int y, int z, float size, float width) + /*protected bool CanGenerateLeaves(int x, int y, int z, float size, float width) { float dist; int i, j, k, s = (int)(size - 1f), w = (int)((size - 1f) * width); @@ -187,9 +197,9 @@ protected bool CanGenerateLeaves(int x, int y, int z, float size, float width) } } return true; - } + }*/ - protected void GenerateLeaves(ChunkColumn chunk, int x, int y, int z, float size, float width, int leafBlockRuntimeId, int woodRuntimeId) + protected void GenerateLeaves(StructurePlan plan, int x, int y, int z, float size, float width, int leafBlockRuntimeId, int woodRuntimeId) { float dist; int i, j, k, s = (int)(size - 1f), w = (int)((size - 1f) * width); @@ -204,27 +214,27 @@ protected void GenerateLeaves(ChunkColumn chunk, int x, int y, int z, float size { if (dist < 0.6f) { - chunk.SetBlockByRuntimeId(x + i, y + j, z + k, woodRuntimeId); + plan.PlaceBlock(x + i, y + j, z + k, woodRuntimeId); } - chunk.SetBlockByRuntimeId(x + i, y + j, z + k, leafBlockRuntimeId); + plan.PlaceBlock(x + i, y + j, z + k, leafBlockRuntimeId); } } } } } - public bool ValidLocation(Vector3 location, int leafRadius) + /*public bool ValidLocation(Vector3 location, int leafRadius) { return !(location.X - leafRadius < 0) && !(location.X + leafRadius >= 16) && !(location.Z - leafRadius < 0) && !(location.Z + leafRadius >= 16); - } + }*/ private static int[] ValidBlockRuntimeIds = new int[] { new Grass().GetRuntimeId(), new Dirt().GetRuntimeId(), new Farmland().GetRuntimeId() }; - /// + /*/// public override bool CanCreate(ChunkColumn column, int x, int y, int z) { var block = column.GetBlockObject(x, y, z); @@ -235,6 +245,6 @@ public override bool CanCreate(ChunkColumn column, int x, int y, int z) return false; return base.CanCreate(column, x, y, z); - } + }*/ } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Surfaces/Mushroom/MushroomSurface.cs b/src/OpenAPI.WorldGenerator/Generators/Surfaces/Mushroom/MushroomSurface.cs new file mode 100644 index 0000000..3bdccf6 --- /dev/null +++ b/src/OpenAPI.WorldGenerator/Generators/Surfaces/Mushroom/MushroomSurface.cs @@ -0,0 +1,117 @@ +using MiNET.Blocks; +using MiNET.Worlds; +using OpenAPI.WorldGenerator.Generators.Biomes; +using OpenAPI.WorldGenerator.Generators.Biomes.Config; +using OpenAPI.WorldGenerator.Generators.Terrain; + +namespace OpenAPI.WorldGenerator.Generators.Surfaces.Mushroom; + +public class MushroomSurface : SurfaceBase +{ + private float min; + + private float sCliff = 1.5f; + private float sHeight = 60f; + private float sStrength = 65f; + private float cCliff = 1.5f; + + /// + public MushroomSurface(BiomeConfig config, Block top, Block filler, float minCliff, float stoneCliff, float stoneHeight, float stoneStrength, float clayCliff) : this(config, top, filler, minCliff) + { + sCliff = stoneCliff; + sHeight = stoneHeight; + sStrength = stoneStrength; + cCliff = clayCliff; + } + + /// + public MushroomSurface(BiomeConfig config, Block top, Block filler, float minCliff) : base(config, top, filler) + { + min = minCliff; + } + + /// + public override void PaintTerrain(ChunkColumn column, + int blockX, + int blockZ, + int x, + int z, + int depth, + OverworldGeneratorV2 generator, + float[] noise, + float river, + BiomeBase[] biomes) + { + var rand = Rnd; + var simplex = generator.SimplexInstance(0); + float c = TerrainBase.CalcCliff(x, z, noise); + int cliff = 0; + + int b; + + for (int k = 255; k > -1; k--) + { + b = column.GetBlockId(x, k, z); + + if (b == AirId) + { + depth = -1; + } + else if (b == StoneId) + { + depth++; + + if (depth == 0) + { + float p = simplex.GetValue(x / 8f, z / 8f, k / 8f) * 0.5f; + + if (c > min && c > sCliff - ((k - sHeight) / sStrength) + p) + { + cliff = 1; + } + + if (c > cCliff) + { + cliff = 2; + } + + if (cliff == 1) + { + if (rand.Next(3) == 0) + { + column.SetBlockByRuntimeId(x, k, z, CliffCobbleBlock); + } + else + { + + column.SetBlockByRuntimeId(x, k, z, CliffStoneBlock); + } + } + else if (cliff == 2) + { + column.SetBlockByRuntimeId(x, k, z, ShadowStoneBlock); + } + else + { + column.SetBlockByRuntimeId(x, k, z, TopBlock); + } + } + else if (depth < 6) + { + if (cliff == 1) + { + column.SetBlockByRuntimeId(x, k, z, CliffStoneBlock); + } + else if (cliff == 2) + { + column.SetBlockByRuntimeId(x, k, z, ShadowStoneBlock); + } + else + { + column.SetBlockByRuntimeId(x, k, z, FillerBlock); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Surfaces/Plains/IcePlainsSurface.cs b/src/OpenAPI.WorldGenerator/Generators/Surfaces/Plains/IcePlainsSurface.cs new file mode 100644 index 0000000..df04e1b --- /dev/null +++ b/src/OpenAPI.WorldGenerator/Generators/Surfaces/Plains/IcePlainsSurface.cs @@ -0,0 +1,74 @@ +using MiNET.Blocks; +using MiNET.Worlds; +using OpenAPI.WorldGenerator.Generators.Biomes; +using OpenAPI.WorldGenerator.Generators.Biomes.Config; +using OpenAPI.WorldGenerator.Generators.Terrain; + +namespace OpenAPI.WorldGenerator.Generators.Surfaces.Plains; + +public class IcePlainsSurface : SurfaceBase +{ + private int CliffBlock1 { get; } + private int CliffBlock2 { get; } + /// + public IcePlainsSurface(BiomeConfig config, Block top, Block filler, Block cliff1, Block cliff2) : base(config, top, filler) + { + CliffBlock1 = cliff1.GetRuntimeId(); + CliffBlock2 = cliff2.GetRuntimeId(); + } + + /// + public override void PaintTerrain(ChunkColumn column, + int blockX, + int blockZ, + int x, + int z, + int depth, + OverworldGeneratorV2 generator, + float[] noise, + float river, + BiomeBase[] biomes) + { + float c = TerrainBase.CalcCliff(x, z, noise); + bool cliff = c > 1.4f; + + int b; + + for (int y = 255; y > -1; y--) + { + b = column.GetBlockId(x, y, z); + + if (b == AirId) + { + depth = -1; + } + else if (b == StoneId) + { + depth++; + + if (cliff) + { + if (depth > -1 && depth < 2) + { + column.SetBlockByRuntimeId(x, y, z, CliffBlock1); + } + else if (depth < 10) + { + column.SetBlockByRuntimeId(x, y, z, CliffBlock2); + } + } + else + { + if (depth == 0 && y > 61) + { + column.SetBlockByRuntimeId(x, y, z, TopBlock); + } + else if (depth < 4) + { + column.SetBlockByRuntimeId(x, y, z, FillerBlock); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Surfaces/SurfaceBase.cs b/src/OpenAPI.WorldGenerator/Generators/Surfaces/SurfaceBase.cs index 551481b..3161eb2 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Surfaces/SurfaceBase.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Surfaces/SurfaceBase.cs @@ -25,6 +25,7 @@ public class SurfaceBase protected static readonly int AirId = new Air().Id; protected static readonly int StoneId = new Stone().Id; protected static readonly int WaterId = new Water().Id; + protected static readonly int SandRuntimeId = new Sand().GetRuntimeId(); public SurfaceBase(BiomeConfig config, Block top, Block filler) { @@ -42,7 +43,7 @@ public SurfaceBase(BiomeConfig config, Block top, Block filler) public virtual void PaintTerrain(ChunkColumn column, int blockX, int blockZ, int x, int z, int depth, OverworldGeneratorV2 generator, float[] noise, float river, BiomeBase[] biomes) { - // float c = TerrainBase.CalcCliff(x, z, noise); + //float c = TerrainBase.CalcCliff(x, z, noise); // bool cliff = c > 1.4f; for (int y = 255; y > -1; y--) { @@ -53,8 +54,12 @@ public virtual void PaintTerrain(ChunkColumn column, int blockX, int blockZ, int } else if (b == StoneId) { depth++; - - if (depth == 0 && y > generator.Preset.SeaLevel - 2) { + + if (depth < 4 && river > 0.7) + { + column.SetBlockByRuntimeId(x,y, z, SandRuntimeId); + } + else if (depth == 0 && y > generator.Preset.SeaLevel - 2) { column.SetBlockByRuntimeId(x, y, z, TopBlock); } else if (depth < 4) { diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsMTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsMTerrain.cs index 7ce19ea..5dbe35b 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsMTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsMTerrain.cs @@ -1,5 +1,5 @@ -namespace OpenAPI.WorldGenerator.Generators.Terrain; - +namespace OpenAPI.WorldGenerator.Generators.Terrain +{ public class BirchForestHillsMTerrain : TerrainBase { private float HillStrength { get; set; } = 70f; @@ -15,6 +15,7 @@ public BirchForestHillsMTerrain(float baseHeight, float hillStrength) : base(bas public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight /*- generator.Preset.SeaLevel*/); + return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight - generator.Preset.SeaLevel); } +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsTerrain.cs index 115a360..41acf90 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestHillsTerrain.cs @@ -15,7 +15,7 @@ public BirchForestHillsTerrain(float baseHeight, float hillStrength) : base(base public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight /*- generator.Preset.SeaLevel*/); + return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight - generator.Preset.SeaLevel); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestMTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestMTerrain.cs index 0c37ebd..a87bcc3 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestMTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestMTerrain.cs @@ -1,14 +1,18 @@ using OpenAPI.WorldGenerator.Generators.Effects; -namespace OpenAPI.WorldGenerator.Generators.Terrain; - +namespace OpenAPI.WorldGenerator.Generators.Terrain +{ public class BirchForestMTerrain : TerrainBase { - private GroundEffect GroundEffect { get; set; }= new GroundEffect(4f); - + public BirchForestMTerrain() + { + HeightEffect = new GroundEffect(4f); + } + public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainPlains(generator, x, y, river, 160f, 10f, 60f, 80f, 65f); - return TerrainForest(generator, x, y, river, generator.Preset.SeaLevel + 3 + GroundEffect.Added(generator, x, y)); + return TerrainPlains(generator, x, y, river, 160f, 10f, 60f, 80f, generator.Preset.SeaLevel + 1); + //return TerrainForest(generator, x, y, river, generator.Preset.SeaLevel + 3 + GroundEffect.Added(generator, x, y)); } +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestTerrain.cs index 772636e..14e40e4 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/BirchForestTerrain.cs @@ -4,11 +4,15 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class BirchForestTerrain : TerrainBase { - private GroundEffect GroundEffect { get; set; }= new GroundEffect(4f); + public BirchForestTerrain() + { + HeightEffect = new GroundEffect(4f); + } public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainForest(generator, x, y, river, generator.Preset.SeaLevel + 3 + GroundEffect.Added(generator, x, y)); + return TerrainPlains(generator, x, y, river, 160f, 10f, 60f, 80f, 65f); + return TerrainForest(generator, x, y, river, generator.Preset.SeaLevel + 3 + HeightEffect.Added(generator, x, y)); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/DesertHillsTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/DesertHillsTerrain.cs index 836ef0f..f89c596 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/DesertHillsTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/DesertHillsTerrain.cs @@ -2,20 +2,20 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class DesertHillsTerrain : TerrainBase { - private float _start; - private float _height; - private float _width; + private float _hillStart; + private float _landHeight; + private float _hillWidth; public DesertHillsTerrain(float hillStart, float landHeight, float baseHeight, float hillWidth) : base(baseHeight) { - _start = hillStart; - _height = landHeight; - _width = hillWidth; + _hillStart = hillStart; + _landHeight = landHeight; + _hillWidth = hillWidth; } public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainHighland(generator, x, y, river, _start, _width, _height, BaseHeight /*- generator.Preset.SeaLevel*/); + return TerrainHighland(generator, x, y, river, _hillStart, _hillWidth, _landHeight, BaseHeight - generator.Preset.SeaLevel); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestHillsTerrains.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestHillsTerrains.cs index 53a19ee..b0ab638 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestHillsTerrains.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestHillsTerrains.cs @@ -4,7 +4,7 @@ public class ForestHillsTerrain : TerrainBase { public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainHighland(generator, x, y, river, 10f, 68f, 30f, BaseHeight /*- generator.Preset.SeaLevel*/); + return TerrainHighland(generator, x, y, river, 10f, 68f, 30f, BaseHeight - generator.Preset.SeaLevel); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestTerrain.cs index e7fafb2..7259610 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/ForestTerrain.cs @@ -6,14 +6,6 @@ public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y { GroundNoise = GetGroundNoise(generator, x, y, GroundVariation); return TerrainForest(generator, x, y, river, generator.Preset.SeaLevel + 3 + GroundNoise); - // return TerrainForest(generator, x, y, river, ge) - /*GroundNoise = GetGroundNoise(x, y, GroundVariation, generator); - - float m = Hills(x, y, 10f, generator); - - float floNoise = generator.Preset.SeaLevel + 3 + GroundNoise + m; - - return Riverized(generator, floNoise, river);*/ } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/IcePlainsTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/IcePlainsTerrain.cs new file mode 100644 index 0000000..94cbf82 --- /dev/null +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/IcePlainsTerrain.cs @@ -0,0 +1,14 @@ +namespace OpenAPI.WorldGenerator.Generators.Terrain; + +public class IcePlainsTerrain : TerrainBase +{ + public IcePlainsTerrain() : base(65f) + { + + } + + public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) + { + return TerrainPlains(generator, x, y, river, 160f, 10f, 60f, 200f, BaseHeight); + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleEdgeTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleEdgeTerrain.cs index cddae79..7b00ec0 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleEdgeTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleEdgeTerrain.cs @@ -4,11 +4,14 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class JungleEdgeTerrain : TerrainBase { - private GroundEffect GroundEffect { get; set; } = new GroundEffect(4f); + public JungleEdgeTerrain() + { + HeightEffect = new GroundEffect(4f); + } public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return Riverized(generator, generator.Preset.SeaLevel + 3 + GroundEffect.Added(generator, x, y), river); + return Riverized(generator, generator.Preset.SeaLevel + 3 + HeightEffect.Added(generator, x, y), river); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleHillsTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleHillsTerrain.cs index 6e5a8e1..8271e5a 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleHillsTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleHillsTerrain.cs @@ -11,7 +11,7 @@ public JungleHillsTerrain(float baseHeight, float hillStrength) : base(baseHeigh public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight/* - generator.Preset.SeaLevel*/); + return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight - generator.Preset.SeaLevel); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleTerrain.cs index df7b379..8bb18f1 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/JungleTerrain.cs @@ -2,9 +2,13 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class JungleTerrain : TerrainBase { + public JungleTerrain(float baseHeight = 66f) : base(baseHeight) + { + + } public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainFlatLakes(generator, x, y, river, 66f); + return TerrainFlatLakes(generator, x, y, river, BaseHeight); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaPlateauTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaPlateauTerrain.cs index a3775d4..3fd5712 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaPlateauTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaPlateauTerrain.cs @@ -26,7 +26,7 @@ public MesaPlateauTerrain(float baseHeight) : base(baseHeight) public override float GenerateNoise(OverworldGeneratorV2 generator, int passedX, int passedY, float border, float river) { - var jitterData = SimplexData2D.NewDisk(); + var jitterData = new SimplexData2D(); generator.SimplexInstance(1).GetValue(passedX / _jitterWavelength, passedY / _jitterWavelength, jitterData); float x = (float) (passedX + jitterData.GetDeltaX() * _jitterAmplitude); float y = (float) (passedY + jitterData.GetDeltaY() * _jitterAmplitude); diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaTerrain.cs index ffaa67c..ea169af 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/MesaTerrain.cs @@ -4,8 +4,6 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class MesaTerrain : TerrainBase { - // private GroundEffect GroundEffect { get; set; }= new GroundEffect(4f); - public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { return TerrainMesa(generator, x, y, river, border);// Riverized(generator, BaseHeight + GroundEffect.Added(generator, x, y), river); diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/MushroomIslandShoreTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/MushroomIslandShoreTerrain.cs new file mode 100644 index 0000000..7990214 --- /dev/null +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/MushroomIslandShoreTerrain.cs @@ -0,0 +1,14 @@ +namespace OpenAPI.WorldGenerator.Generators.Terrain; + +public class MushroomIslandShoreTerrain : TerrainBase +{ + public MushroomIslandShoreTerrain() : base(61.5f) + { + + } + /// + public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) + { + return TerrainMarsh(generator, x, y, BaseHeight, river); + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/MushroomIslandTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/MushroomIslandTerrain.cs new file mode 100644 index 0000000..91639e1 --- /dev/null +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/MushroomIslandTerrain.cs @@ -0,0 +1,10 @@ +namespace OpenAPI.WorldGenerator.Generators.Terrain; + +public class MushroomIslandTerrain : TerrainBase +{ + /// + public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) + { + return TerrainGrasslandFlats(generator, x, y, river, 40f, BaseHeight); + } +} \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/PlainsTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/PlainsTerrain.cs index b9bd66b..2f7b688 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/PlainsTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/PlainsTerrain.cs @@ -4,20 +4,15 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class PlainsTerrain : TerrainBase { - private GroundEffect _groundEffect = new GroundEffect(4f); - - public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) + public PlainsTerrain() : base(65f) { - return Riverized(generator, 65f + _groundEffect.Added(generator, x, y), river); - - return TerrainPlains(generator, x, y, river, 160f, 10f, 60f, generator.Preset.HeightScale, BaseHeight); + HeightEffect = new GroundEffect(4f); } - } - - public class IcePlainsTerrain : TerrainBase - { + public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { + return Riverized(generator, BaseHeight + HeightEffect.Added(generator, x, y), river); + return TerrainPlains(generator, x, y, river, 160f, 10f, 60f, generator.Preset.HeightScale, BaseHeight); } } diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/SavannaTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/SavannaTerrain.cs index 5f11aa4..ca981cc 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/SavannaTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/SavannaTerrain.cs @@ -4,11 +4,14 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class SavannaTerrain : TerrainBase { - private GroundEffect GroundEffect { get; set; }= new GroundEffect(4f); + public SavannaTerrain() + { + HeightEffect = new GroundEffect(4f); + } public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return Riverized(generator, generator.Preset.SeaLevel + 3 + GroundEffect.Added(generator, x, y), river); + return Riverized(generator, generator.Preset.SeaLevel + 3 + HeightEffect.Added(generator, x, y), river); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/TaigaHillsTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/TaigaHillsTerrain.cs index 334e3a5..e8435ea 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/TaigaHillsTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/TaigaHillsTerrain.cs @@ -2,20 +2,15 @@ namespace OpenAPI.WorldGenerator.Generators.Terrain { public class TaigaHillsTerrain : TerrainBase { - private float HillStrength { get; set; } = 30f; - public TaigaHillsTerrain() : this(72f, 30f) - { - - } - - public TaigaHillsTerrain(float baseHeight, float hillStrength) : base(baseHeight) + private float HillStrength { get; set; } + public TaigaHillsTerrain(float baseHeight = 72f, float hillStrength = 30f) : base(baseHeight) { HillStrength = hillStrength; } public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y, float border, float river) { - return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight /*- generator.Preset.SeaLevel*/); + return TerrainHighland(generator, x, y, river, 10f, 68f, HillStrength, BaseHeight - generator.Preset.SeaLevel); } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/TerrainBase.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/TerrainBase.cs index 3d8b30e..d21a5fe 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/TerrainBase.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/TerrainBase.cs @@ -1,6 +1,7 @@ using System; using MiNET.Utils; using MiNET.Utils.Vectors; +using OpenAPI.WorldGenerator.Generators.Effects; using OpenAPI.WorldGenerator.Utils.Noise; namespace OpenAPI.WorldGenerator.Generators.Terrain @@ -19,6 +20,8 @@ private static float protected float BaseHeight; // added as most terrains have this; protected float GroundNoise; + protected HeightEffect HeightEffect { get; set; } + public TerrainBase(float baseHeight) { @@ -96,14 +99,13 @@ public static float Hills(OverworldGeneratorV2 generator, float x, float y, floa return m * hillStrength; } - public static float GetTerrainBase(OverworldGeneratorV2 generator, float river) { - + public static float GetTerrainBase(OverworldGeneratorV2 generator, float river) + { return generator.Preset.GetTerrainBase() * river; } public static float GetGroundNoise(OverworldGeneratorV2 generator, int x, int y, float amplitude) { - float h = BlendedHillHeight(generator.SimplexInstance(0).GetValue(x / 49f, y / 49f), 0.2f) * amplitude; h += BlendedHillHeight(generator.SimplexInstance(1).GetValue(x / 23f, y / 23f), 0.2f) * amplitude / 2f; h += BlendedHillHeight(generator.SimplexInstance(2).GetValue(x / 11f, y / 11f), 0.2f) * amplitude / 4f; @@ -112,7 +114,6 @@ public static float GetGroundNoise(OverworldGeneratorV2 generator, int x, int y, public static float GetGroundNoise(OverworldGeneratorV2 generator, float x, float y, float amplitude) { - float h = BlendedHillHeight(generator.SimplexInstance(0).GetValue(x / 49f, y / 49f), 0.2f) * amplitude; h += BlendedHillHeight(generator.SimplexInstance(1).GetValue(x / 23f, y / 23f), 0.2f) * amplitude / 2f; h += BlendedHillHeight(generator.SimplexInstance(2).GetValue(x / 11f, y / 11f), 0.2f) * amplitude / 4f; @@ -137,7 +138,7 @@ public static float MountainCap(float m) public static float Riverized(OverworldGeneratorV2 generator, float height, float river) { - var baseH = generator.Preset.SeaLevel + 0.45f; + var baseH = generator.Preset.SeaLevel - 0.55f; if (height < baseH) { return height; @@ -257,12 +258,11 @@ public static float TerrainGrasslandMountains(OverworldGeneratorV2 generator, in return Riverized(generator, baseHeight + h + m, river); } - public static float TerrainHighland(OverworldGeneratorV2 generator, float x, float y, float river, float start, - float width, float height, float baseAdjust) + public static float TerrainHighland(OverworldGeneratorV2 generator, float x, float y, float river, float hillStart, + float hillWidth, float hillHeight, float baseAdjust) { - - float h = generator.SimplexInstance(0).GetValue(x / width, y / width) * height * river; //-140 to 140 - h = h < start ? start + ((h - start) / 4.5f) : h; + float h = generator.SimplexInstance(0).GetValue(x / hillWidth, y / hillWidth) * hillHeight * river; //-140 to 140 + h = h < hillStart ? hillStart + ((h - hillStart) / 4.5f) : h; if (h < 0f) { @@ -273,7 +273,7 @@ public static float TerrainHighland(OverworldGeneratorV2 generator, float x, flo { float st = h * 1.5f > 15f ? 15f : h * 1.5f; // 0 to 15 h += generator.SimplexInstance(4).GetValue(x / 70f, y / 70f, 1f) * st; // 0 to 155 - h = h * river; + h *= river; } h += BlendedHillHeight(generator.SimplexInstance(0).GetValue(x / 20f, y / 20f), 0f) * 4f; @@ -282,7 +282,7 @@ public static float TerrainHighland(OverworldGeneratorV2 generator, float x, flo if (h < 0) { - h = h / 2f; + h /= 2f; } if (h < -3) @@ -290,7 +290,7 @@ public static float TerrainHighland(OverworldGeneratorV2 generator, float x, flo h = (h + 3f) / 2f - 3f; } - return (GetTerrainBase(generator, river)) + (h + baseAdjust) * river; + return (GetTerrainBase(generator, river)) + ((h + baseAdjust) * river); } public static float TerrainLonelyMountain(OverworldGeneratorV2 generator, int x, int y, float river, @@ -618,7 +618,7 @@ public static float GetRiverStrength(OverworldGeneratorV2 generator, int x, int int worldZ = z; float pX = worldX; float pZ = worldZ; - var jitterData = SimplexData2D.NewDisk(); + var jitterData = new SimplexData2D(); //New river curve function. No longer creates worldwide curve correlations along cardinal axes. generator.SimplexInstance(1).GetValue((float) worldX / 240.0f, (float) worldZ / 240.0f, jitterData); pX += jitterData.GetDeltaX() * generator.RiverLargeBendSize; diff --git a/src/OpenAPI.WorldGenerator/Generators/Terrain/VanillaMesaPlateauTerrain.cs b/src/OpenAPI.WorldGenerator/Generators/Terrain/VanillaMesaPlateauTerrain.cs index b41de55..5657ec5 100644 --- a/src/OpenAPI.WorldGenerator/Generators/Terrain/VanillaMesaPlateauTerrain.cs +++ b/src/OpenAPI.WorldGenerator/Generators/Terrain/VanillaMesaPlateauTerrain.cs @@ -1,5 +1,5 @@ -namespace OpenAPI.WorldGenerator.Generators.Terrain; - +namespace OpenAPI.WorldGenerator.Generators.Terrain +{ public class VanillaMesaPlateauTerrain : TerrainBase { private readonly float[] _height; @@ -23,4 +23,5 @@ public override float GenerateNoise(OverworldGeneratorV2 generator, int x, int y { return TerrainPlateau(generator, x, y, river, _height, border, _strength, _heightLength, 100f, false); } +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/OpenAPI.WorldGenerator.csproj b/src/OpenAPI.WorldGenerator/OpenAPI.WorldGenerator.csproj index 21d87ba..e1acddb 100644 --- a/src/OpenAPI.WorldGenerator/OpenAPI.WorldGenerator.csproj +++ b/src/OpenAPI.WorldGenerator/OpenAPI.WorldGenerator.csproj @@ -2,6 +2,12 @@ net6.0 + 10 + + + + + true diff --git a/src/OpenAPI.WorldGenerator/Utils/ColorUtils.cs b/src/OpenAPI.WorldGenerator/Utils/ColorUtils.cs index c94d27f..7be867d 100644 --- a/src/OpenAPI.WorldGenerator/Utils/ColorUtils.cs +++ b/src/OpenAPI.WorldGenerator/Utils/ColorUtils.cs @@ -1,20 +1,21 @@ using System; using System.Drawing; -namespace OpenAPI.WorldGenerator.Utils; - -public static class ColorUtils +namespace OpenAPI.WorldGenerator.Utils { - public static Color FromHtml(string hex) + public static class ColorUtils { - if (hex.StartsWith("#")) - hex = hex.Substring(1); + public static Color FromHtml(string hex) + { + if (hex.StartsWith("#")) + hex = hex.Substring(1); - if (hex.Length != 6) throw new Exception("Color not valid"); + if (hex.Length != 6) throw new Exception("Color not valid"); - return Color.FromArgb( - int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber), - int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber), - int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber)); + return Color.FromArgb( + int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber), + int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber), + int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber)); + } } } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Utils/Noise/Api/ISimplexData2D.cs b/src/OpenAPI.WorldGenerator/Utils/Noise/Api/ISimplexData2D.cs index 96071b5..c47d2c4 100644 --- a/src/OpenAPI.WorldGenerator/Utils/Noise/Api/ISimplexData2D.cs +++ b/src/OpenAPI.WorldGenerator/Utils/Noise/Api/ISimplexData2D.cs @@ -16,7 +16,7 @@ public interface ISimplexData2D { void Clear(); - IDataRequest Request(); + void Request(float attn, float extrapolation, float gx, float gy, int giSph2, float dx, float dy); public interface IDataRequest { diff --git a/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/FilterNoiseModule.cs b/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/FilterNoiseModule.cs index 9931a9b..691deea 100644 --- a/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/FilterNoiseModule.cs +++ b/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/FilterNoiseModule.cs @@ -33,7 +33,7 @@ public float Frequency get { return this._frequency; } set { this._frequency = value; } } - + /// /// A multiplier that determines how quickly the frequency increases for each successive octave in a Perlin-noise function. /// diff --git a/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/VoronoiNoseModule.cs b/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/VoronoiNoseModule.cs index b9b0f95..a725aab 100644 --- a/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/VoronoiNoseModule.cs +++ b/src/OpenAPI.WorldGenerator/Utils/Noise/Modules/VoronoiNoseModule.cs @@ -4,19 +4,21 @@ namespace OpenAPI.WorldGenerator.Utils.Noise.Modules { - public class VoronoiNoseModule : FilterNoise, INoiseModule + public class VoronoiNoseModule : INoiseModule { public const float DefaultDisplacement = 1.0f; - public VoronoiNoseModule() + private INoiseModule Source { get; } + + public VoronoiNoseModule(INoiseModule source) { - + Source = source; } private float _displacement = DefaultDisplacement; private bool _distance; - + protected float _frequency = 1f; /// /// This noise module assigns each Voronoi cell with a random constant @@ -43,8 +45,17 @@ public bool Distance set { _distance = value; } } + /// + /// The number of cycles per unit length that a specific coherent-noise function outputs. + /// + [Modifier] + public float Frequency + { + get { return this._frequency; } + set { this._frequency = value; } + } + public int Size { get; set; } = 2; - public float GetValue(float x, float y) { x *= _frequency; @@ -66,10 +77,11 @@ public float GetValue(float x, float y) { // Calculate the position and distance to the seed point inside of // this unit cube. - var off = _source.GetValue(xCur, yCur); - float xPos = xCur + off;//_source2D.GetValue(xCur, yCur); - float yPos = yCur + off;//_source2D.GetValue(xCur, yCur); - + var off = Source.GetValue(xCur, yCur); + + float xPos = xCur + off; + float yPos = yCur + off; + float xDist = xPos - x; float yDist = yPos - y; float dist = xDist * xDist + yDist * yDist; @@ -99,7 +111,7 @@ public float GetValue(float x, float y) value = 0.0f; // Return the calculated distance with the displacement value applied. - return value + (_displacement * _source.GetValue( + return value + (_displacement * Source.GetValue( MathF.Floor(xCandidate), MathF.Floor(yCandidate)) ); @@ -139,9 +151,11 @@ public float GetValue(float x, float y, float z) { // Calculate the position and distance to the seed point inside of // this unit cube. - float xPos = xCur + _source.GetValue(xCur, yCur, zCur); - float yPos = yCur + _source.GetValue(xCur, yCur, zCur); - float zPos = zCur + _source.GetValue(xCur, yCur, zCur); + var off = Source.GetValue(xCur, yCur, zCur); + + float xPos = xCur + off; + float yPos = yCur + off; + float zPos = zCur + off; float xDist = xPos - x; float yDist = yPos - y; @@ -176,7 +190,7 @@ public float GetValue(float x, float y, float z) value = 0.0f; // Return the calculated distance with the displacement value applied. - return value + (_displacement*_source.GetValue( + return value + (_displacement*Source.GetValue( (MathF.Floor(xCandidate)), (MathF.Floor(yCandidate)), (MathF.Floor(zCandidate))) diff --git a/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/NoiseQuality.cs b/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/NoiseQuality.cs index f660fc4..fda5947 100644 --- a/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/NoiseQuality.cs +++ b/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/NoiseQuality.cs @@ -1,8 +1,9 @@ -namespace OpenAPI.WorldGenerator.Utils.Noise.Primitives; - +namespace OpenAPI.WorldGenerator.Utils.Noise.Primitives +{ public enum NoiseQuality : byte { Fast, Standard, Best, +} } \ No newline at end of file diff --git a/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/SimplexNoise.cs b/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/SimplexNoise.cs index ff32744..5538802 100644 --- a/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/SimplexNoise.cs +++ b/src/OpenAPI.WorldGenerator/Utils/Noise/Primitives/SimplexNoise.cs @@ -771,7 +771,8 @@ public virtual void GetValue(double x, double y, ISimplexData2D data) double gy = Gradients2D[gi + 1]; double extrp = gx * dx + gy * dy; int giSph2 = _perm2DSph2[giP]; - data.Request().Apply((float) attn, (float) extrp, (float) gx, (float) gy, giSph2, (float) dx, (float) dy); + + data.Request((float) attn, (float) extrp, (float) gx, (float) gy, giSph2, (float) dx, (float) dy); } } diff --git a/src/OpenAPI.WorldGenerator/Utils/Noise/SimplexData2D.cs b/src/OpenAPI.WorldGenerator/Utils/Noise/SimplexData2D.cs index 705e98a..71c7f16 100644 --- a/src/OpenAPI.WorldGenerator/Utils/Noise/SimplexData2D.cs +++ b/src/OpenAPI.WorldGenerator/Utils/Noise/SimplexData2D.cs @@ -4,67 +4,44 @@ namespace OpenAPI.WorldGenerator.Utils.Noise { - public abstract class SimplexData2D : ISimplexData2D + public class SimplexData2D : ISimplexData2D { + private float _deltaX; + private float _deltaY; - private float deltaX; - private float deltaY; - - private SimplexData2D() + public SimplexData2D() { this.Clear(); } - /** - * Gets a new {@link SimplexData2D.Disk} multi-evaluation data object for use in generating jitter effects. - * - * @return a new instance of SimplexData2D.Disk - * @since 1.0.0 - */ - public static ISimplexData2D NewDisk() - { - return new Disk(); - } - - /** - * Gets a new {@link SimplexData2D.Derivative} multi-evaluation data object for use in generating jitter effects. - * - * @return a new instance of SimplexData2D.Derivative - * @since 1.0.0 - */ - public static ISimplexData2D NewDerivative() - { - return new Derivative(); - } - public float GetDeltaX() { - return this.deltaX; + return this._deltaX; } public void SetDeltaX(float deltaX) { - this.deltaX = deltaX; + this._deltaX = deltaX; } public float GetDeltaY() { - return this.deltaY; + return this._deltaY; } public void SetDeltaY(float deltaY) { - this.deltaY = deltaY; + this._deltaY = deltaY; } public void AddToDeltaX(float val) { - this.deltaX += val; + this._deltaX += val; } public void AddToDeltaY(float val) { - this.deltaY += val; + this._deltaY += val; } public void Clear() @@ -73,74 +50,12 @@ public void Clear() this.SetDeltaY(0.0f); } - public virtual ISimplexData2D.IDataRequest Request() + public virtual void Request(float attn, float extrapolation, float gx, float gy, int giSph2, float dx, float dy) { - return new Disk.DiskDataRequest(this); - } - - public class Disk : SimplexData2D - { - - public Disk() : base() - { - - } - - public override ISimplexData2D.IDataRequest Request() - { - return new DiskDataRequest(this); - } - - public class DiskDataRequest : ISimplexData2D.IDataRequest - { - private SimplexData2D Data { get; } - - public DiskDataRequest(SimplexData2D data2D) - { - Data = data2D; - } - - public void Apply(float attn, float extrapolation, float gx, float gy, int gi_sph2, float dx, - float dy) - { - float attnSq = attn * attn; - float extrap = attnSq * attnSq * extrapolation; - Data.AddToDeltaX((float) (extrap * SimplexPerlin.GradientsSph2[gi_sph2])); - Data.AddToDeltaY((float) (extrap * SimplexPerlin.GradientsSph2[gi_sph2 + 1])); - } - } - } - - public class Derivative : SimplexData2D - { - - public Derivative() - { - - } - - public override ISimplexData2D.IDataRequest Request() - { - return new DerivativeDataRequest(this); - } - - public class DerivativeDataRequest : ISimplexData2D.IDataRequest - { - private SimplexData2D Data { get; } - - public DerivativeDataRequest(SimplexData2D data2D) - { - Data = data2D; - } - - public void Apply(float attn, float extrapolation, float gx, float gy, int gi_sph2, float dx, - float dy) - { - double attnSq = attn * attn; - Data.AddToDeltaX((float) ((gx * attn - 8f * dx * extrapolation) * attnSq * attn)); - Data.AddToDeltaY((float) ((gy * attn - 8f * dy * extrapolation) * attnSq * attn)); - } - } + float attnSq = attn * attn; + float extrap = attnSq * attnSq * extrapolation; + AddToDeltaX((float) (extrap * SimplexPerlin.GradientsSph2[giSph2])); + AddToDeltaY((float) (extrap * SimplexPerlin.GradientsSph2[giSph2 + 1])); } } } \ No newline at end of file diff --git a/src/WorldGenerator.Tweaking/Program.cs b/src/WorldGenerator.Tweaking/Program.cs index dfae97f..2589c09 100644 --- a/src/WorldGenerator.Tweaking/Program.cs +++ b/src/WorldGenerator.Tweaking/Program.cs @@ -3,83 +3,143 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.Xna.Framework; using MiNET.Utils.Vectors; using OpenAPI.WorldGenerator.Generators; +using OpenAPI.WorldGenerator.Utils.Noise; +using OpenAPI.WorldGenerator.Utils.Noise.Modules; +using OpenAPI.WorldGenerator.Utils.Noise.Primitives; +using OpenAPI.WorldGenerator.Utils.Noise.Transformers; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; namespace WorldGenerator.Tweaking { class Program { - private static TestGame Game { get; set; } - public const int Radius = 372; + private static Game Game { get; set; } + public const int Radius = 512; public const int Resolution = 1; - //private static ConcurrentQueue Finished = new ConcurrentQueue(); + static void Main(string[] args) { WorldGen = new OverworldGeneratorV2(); - Game = new TestGame(WorldGen, Radius, Resolution); - // gen.ApplyBlocks = true; + // RunNoiseTest(); + RunPreviewer(); + //Console.WriteLine($"Min Height: {gen.MinHeight} Max Height: {gen.MaxHeight}"); + } + + private static void RunNoiseTest() + { + var seed = 124; + var biomeScale = 4 * 16f; + + INoiseModule temperatureNoise = new VoronoiNoseModule(new AverageSelectorModule(new SimplexPerlin(seed ^ 3, NoiseQuality.Fast), new SimplexPerlin(seed ^ 64, NoiseQuality.Fast))) + { + Distance = true, + Frequency = 0.0325644f, + Displacement = 2f + }; + + temperatureNoise = new ScaledNoiseModule(temperatureNoise) + { + ScaleX = 1f / biomeScale, ScaleY = 1f / biomeScale, ScaleZ = 1f / biomeScale + }; + + Image output = new Image(Radius, Radius); + for (int x = 0; x < Radius; x++) + { + for (int z = 0; z < Radius; z++) + { + var temp = MathF.Abs(temperatureNoise.GetValue(x * 16f, z* 16f)); + // var rain = WorldGen.RainfallNoise.GetValue(x* 16f, z* 16f); + + output[x, z] = new Rgba32((1f / 2f) * temp, 0f, 0f); + } + } + output.SaveAsPng("test.png"); + + return; + } + + private static void RunPreviewer() + { + var game = new TestGame(WorldGen, Radius, Resolution); + Game = game; bool done = false; - // ChunkColumn[] generatedChunks = new ChunkColumn[chunks * chunks]; + long average = 0; long min = long.MaxValue; long max = long.MinValue; - int chunskGenerated = 0; - // threads[0] = new Thread(() => { GenerateBiomeMap(chunks); }); + int chunskGenerated = 0; List gen = new List(); - for (int z = 0; z < Radius; z+= Resolution) + + for (int z = 0; z < Radius; z++) { - for(int x= 0; x < Radius; x+= Resolution) + for (int x = 0; x < Radius; x++) { - gen.Add(new ChunkCoordinates(x, z)); + var cc = new ChunkCoordinates((x), (z)); + gen.Add(cc); } } + var cancellationToken = new CancellationTokenSource(); - new Thread(() => - { - Stopwatch timer = Stopwatch.StartNew(); - Parallel.ForEach( - gen, new ParallelOptions() - { - CancellationToken = cancellationToken.Token - }, coords => - { - Stopwatch timing = new Stopwatch(); - timing.Restart(); - - Game.Add(WorldGen.GenerateChunkColumn(coords)); - - chunskGenerated++; - - timing.Stop(); - - average += timing.ElapsedMilliseconds; - - if (timing.ElapsedMilliseconds < min) - min = timing.ElapsedMilliseconds; - - if (timing.ElapsedMilliseconds > max) - max = timing.ElapsedMilliseconds; - }); - - timer.Stop(); - - Console.Clear(); - - Console.WriteLine($"Generating {Radius * Radius} chunks took: {timer.Elapsed}"); - }).Start(); - + + new Thread( + () => + { + int total = gen.Count; + Stopwatch timer = Stopwatch.StartNew(); + + Parallel.ForEach( + gen, new ParallelOptions() {CancellationToken = cancellationToken.Token}, coords => + { + try + { + Stopwatch timing = new Stopwatch(); + timing.Restart(); + + game.Add(WorldGen.GenerateChunkColumn(coords)); + + timing.Stop(); + + average += timing.ElapsedMilliseconds; + + if (timing.ElapsedMilliseconds < min) + min = timing.ElapsedMilliseconds; + + if (timing.ElapsedMilliseconds > max) + max = timing.ElapsedMilliseconds; + + if (Interlocked.Increment(ref chunskGenerated) % 500 == 0) + { + float progress = (1f / total) * chunskGenerated; + Console.WriteLine($"[{progress:P000}] Generated {chunskGenerated} of {total} chunks. Average={(average / chunskGenerated):F2}ms"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Ehhh: {ex}"); + } + }); + + timer.Stop(); + + Console.Clear(); + + Console.WriteLine($"Generating {chunskGenerated} chunks took: {timer.Elapsed}"); + }).Start(); + Game.Run(); - + cancellationToken.Cancel(); - //Console.WriteLine($"Min Height: {gen.MinHeight} Max Height: {gen.MaxHeight}"); } - + public static OverworldGeneratorV2 WorldGen { get; set; } } } \ No newline at end of file diff --git a/src/WorldGenerator.Tweaking/TestGame.cs b/src/WorldGenerator.Tweaking/TestGame.cs index 2eeea48..9c8e01a 100644 --- a/src/WorldGenerator.Tweaking/TestGame.cs +++ b/src/WorldGenerator.Tweaking/TestGame.cs @@ -7,6 +7,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; +using MiNET.Utils.Vectors; using MiNET.Worlds; using Newtonsoft.Json; using OpenAPI.WorldGenerator.Generators; @@ -21,16 +22,12 @@ public class TestGame : Game private GraphicsDeviceManager _graphics; private OverworldGeneratorV2 _worldGen; - private Dictionary _blockColors; public TestGame(OverworldGeneratorV2 worldGen, int chunks, int resolution) { _worldGen = worldGen; _chunks = chunks; _resolution = resolution; _graphics = new GraphicsDeviceManager(this); - - _blockColors = JsonConvert.DeserializeObject>( - File.ReadAllText(Path.Combine("Resources", "blockColors.json"))); } /// @@ -79,6 +76,7 @@ public void Add(ChunkColumn chunk) private MouseState _mouseState = new MouseState(); + private int CursorHeight { get; set; } = 0; private BiomeBase ClickedBiome { get; set; } = null; private Point CursorPos { get; set; } = Point.Zero; @@ -107,6 +105,7 @@ protected override void Update(GameTime gameTime) (float) stageData.Texture.Width / width, (float) stageData.Texture.Height / height); pos = new Point((int) (pos.X * scale.X), (int) (pos.Y * scale.Y)); + //var chunkPos = new ChunkCoordinates(pos.X >> 4, pos.Y >> 4); var color = stageData.GetColorAt(pos.X, pos.Y); var biome = _worldGen.BiomeRegistry.Biomes.FirstOrDefault(x => { @@ -114,11 +113,26 @@ protected override void Update(GameTime gameTime) return c.R == color.R && c.B == color.B && c.G == color.G; }); + ClickedBiome = biome; CursorPos = pos; } } + else if (pos.X >= width && pos.X <= width * 2f && pos.Y >= height && pos.Y <= height * 2f) + { + pos.X -= width; + pos.Y -= height; + if (_stageDatas.TryGetValue(RenderStage.Height, out var stageData)) + { + var scale = new Vector2( + (float) stageData.Texture.Width / width, (float) stageData.Texture.Height / height); + + pos = new Point((int) (pos.X * scale.X), (int) (pos.Y * scale.Y)); + var color = stageData.GetColorAt(pos.X, pos.Y); + CursorHeight = color.R; + } + } } _biomeInfoSw.Restart(); @@ -146,7 +160,12 @@ protected override void Draw(GameTime gameTime) stage.Value.AddChunk(chunk); } - chunk?.Dispose(); + foreach (var subChunk in chunk) + { + subChunk.Dispose(); + } + + chunk.Dispose(); updates++; } @@ -163,11 +182,13 @@ protected override void Draw(GameTime gameTime) var size = _spriteFont.MeasureString(text); _spriteBatch.DrawString(_spriteFont, text, new Vector2(width - size.X, 10), Color.White); } + + DrawHeight(width * 2f, height + 10f); var updateText = $"Updates: {updates:000}\n" + $"Temp: min={_worldGen.MinTemp:F3} max={_worldGen.MaxTemp:F3} range={(_worldGen.MaxTemp - _worldGen.MinTemp):F3}\n" + $"Rain: min={_worldGen.MinRain:F3} max={_worldGen.MaxRain:F3} range={(_worldGen.MaxRain - _worldGen.MinRain):F3}\n" - + $"Selector: min={_worldGen.MinSelector:F3} max={_worldGen.MaxSelector:F3} range={(_worldGen.MaxSelector - _worldGen.MinSelector):F3/*}\n"/* + + $"Selector: min={_worldGen.MinSelector:F3} max={_worldGen.MaxSelector:F3} range={(_worldGen.MaxSelector - _worldGen.MinSelector):F3}\n"/* + $"Height: min={_worldGen.MinHeight:F3} max={_worldGen.MaxHeight:F3} range={(_worldGen.MaxHeight - _worldGen.MinHeight):F3}\n"*/; var updateTextSize = _spriteFont.MeasureString(updateText); _spriteBatch.DrawString(_spriteFont, updateText, new Vector2(GraphicsDevice.Viewport.Width - (updateTextSize.X + 5), 5), Color.White); @@ -177,6 +198,13 @@ protected override void Draw(GameTime gameTime) base.Draw(gameTime); } + private void DrawHeight(float x, float y) + { + string text = $"Height: {CursorHeight:000}"; + var size = _spriteFont.MeasureString(text); + _spriteBatch.DrawString(_spriteFont, text, new Vector2(x - size.X + 10, y), Color.White); + } + private void DrawBorder(Rectangle rectangleToDraw, int thicknessOfBorder, Color borderColor) { // Draw top line @@ -236,11 +264,10 @@ public enum RenderStage { Temperature, Height, - Biomes, - Blocks + Biomes } - private class StageData + private class StageData { private readonly RenderStage _stage; private readonly GraphicsDevice _device; @@ -380,17 +407,6 @@ private void DrawChunk(ChunkColumn data) var c = biome.Color.GetValueOrDefault(); _spriteBatch.Draw(_pixel, pixelPosition, new Color(c.R, c.G, c.B)); } break; - - case RenderStage.Blocks: - { - var block = data.GetBlockObject(x, data.GetHeight(x,y), y); - - if (_parent._blockColors.TryGetValue(block.Name, out uint color)) - { - int height = data.GetHeight(x, y); - _spriteBatch.Draw(_pixel, pixelPosition, ChangeColorBrightness(new Color(color), -(1f / 255f) * height)); - } - } break; } } } diff --git a/src/WorldGenerator.Tweaking/WorldGenerator.Tweaking.csproj b/src/WorldGenerator.Tweaking/WorldGenerator.Tweaking.csproj index 8b92158..0b7c573 100644 --- a/src/WorldGenerator.Tweaking/WorldGenerator.Tweaking.csproj +++ b/src/WorldGenerator.Tweaking/WorldGenerator.Tweaking.csproj @@ -3,6 +3,7 @@ Exe net6.0 + 10 diff --git a/src/WorldGenerator.sln b/src/WorldGenerator.sln index 64d5ad0..cdab117 100644 --- a/src/WorldGenerator.sln +++ b/src/WorldGenerator.sln @@ -4,6 +4,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAPI.WorldGenerator", "O EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorldGenerator.Tweaking", "WorldGenerator.Tweaking\WorldGenerator.Tweaking.csproj", "{2AB15C83-8F60-4D20-A8E0-2158B1C01E25}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiMap.Viewer.DesktopGL", "MiMap.Viewer.DesktopGL\MiMap.Viewer.DesktopGL.csproj", "{EE6B11E1-77BF-4CD6-B98B-4B01F2B68163}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MiMap.Viewer", "MiMap.Viewer", "{DB562C1B-F240-4832-8355-F6B3AA19608D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiMap.Viewer.Element", "MiMap.Viewer.Element\MiMap.Viewer.Element.csproj", "{6568E523-7999-4751-926D-DAF38F3EA62D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElementEngine", "submodules\ElementEngine\ElementEngine.csproj", "{7EC00713-86EC-4E58-8AC7-3904B830557F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -18,5 +26,22 @@ Global {2AB15C83-8F60-4D20-A8E0-2158B1C01E25}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AB15C83-8F60-4D20-A8E0-2158B1C01E25}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AB15C83-8F60-4D20-A8E0-2158B1C01E25}.Release|Any CPU.Build.0 = Release|Any CPU + {EE6B11E1-77BF-4CD6-B98B-4B01F2B68163}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE6B11E1-77BF-4CD6-B98B-4B01F2B68163}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE6B11E1-77BF-4CD6-B98B-4B01F2B68163}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE6B11E1-77BF-4CD6-B98B-4B01F2B68163}.Release|Any CPU.Build.0 = Release|Any CPU + {6568E523-7999-4751-926D-DAF38F3EA62D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6568E523-7999-4751-926D-DAF38F3EA62D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6568E523-7999-4751-926D-DAF38F3EA62D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6568E523-7999-4751-926D-DAF38F3EA62D}.Release|Any CPU.Build.0 = Release|Any CPU + {7EC00713-86EC-4E58-8AC7-3904B830557F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EC00713-86EC-4E58-8AC7-3904B830557F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EC00713-86EC-4E58-8AC7-3904B830557F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EC00713-86EC-4E58-8AC7-3904B830557F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EE6B11E1-77BF-4CD6-B98B-4B01F2B68163} = {DB562C1B-F240-4832-8355-F6B3AA19608D} + {6568E523-7999-4751-926D-DAF38F3EA62D} = {DB562C1B-F240-4832-8355-F6B3AA19608D} + {7EC00713-86EC-4E58-8AC7-3904B830557F} = {DB562C1B-F240-4832-8355-F6B3AA19608D} EndGlobalSection EndGlobal diff --git a/src/submodules/ElementEngine b/src/submodules/ElementEngine new file mode 160000 index 0000000..67b1866 --- /dev/null +++ b/src/submodules/ElementEngine @@ -0,0 +1 @@ +Subproject commit 67b1866f32815f7b8b822c9a2e932a302f6b5a57