Big refactor and Entity Inspector

- Split core ECS wrapper into gaemstone.ECS project
  - Module loading part of gaemstone, still
  - Remove ComponentHooks (for now)
- Fix IL for nullable references in IterActionGenerator
- Move from Silk.NET.Maths to System.Numerics
- Add Flecs.Core.Component and .Identifier
- Add Flecs.Doc module
- Add gaemstone.Doc module
- Add [Symbol] to register entities with symbols
  This decision will probably be reversed.
- World generator picks random blocks, more color!

Changes relating to ImGui:
- Use custom ImGUI.NET version with local changes to get
  access to internal functions. Currently waiting for
  upstream to implement this functionality.
- Add font support, and the fonts OpenSans and ForkAwesome
- Add an entity inspector window to browse entities, see
  their contents and find references to other entities.
wip/source-generators
copygirl 11 months ago
parent 0c6d63af21
commit 5268d97828
  1. 4
      .editorconfig
  2. 6
      .gitmodules
  3. 103
      gaemstone.sln
  4. 1
      src/Immersion/Immersion.csproj
  5. 2
      src/Immersion/ObserverTest.cs
  6. 17
      src/Immersion/Program.cs
  7. 1
      src/flecs-cs
  8. 16
      src/gaemstone.Bloxel/BlockFacing.cs
  9. 8
      src/gaemstone.Bloxel/BlockPos.cs
  10. 8
      src/gaemstone.Bloxel/ChunkPos.cs
  11. 34
      src/gaemstone.Bloxel/Client/Systems/ChunkMeshGenerator.cs
  12. 4
      src/gaemstone.Bloxel/Components/CoreComponents.cs
  13. 4
      src/gaemstone.Bloxel/Neighbor.cs
  14. 28
      src/gaemstone.Bloxel/Systems/BasicWorldGenerator.cs
  15. 2
      src/gaemstone.Bloxel/Utility/ChunkedOctree.cs
  16. 5
      src/gaemstone.Bloxel/gaemstone.Bloxel.csproj
  17. 88
      src/gaemstone.Client/Color.cs
  18. 6
      src/gaemstone.Client/Components/CameraComponents.cs
  19. 20
      src/gaemstone.Client/Components/InputComponents.cs
  20. 16
      src/gaemstone.Client/Components/RenderingComponents.cs
  21. 6
      src/gaemstone.Client/Components/ResourceComponents.cs
  22. BIN
      src/gaemstone.Client/Resources/ForkAwesome.ttf
  23. 95
      src/gaemstone.Client/Resources/LICENSE_ForkAwesome.txt
  24. 93
      src/gaemstone.Client/Resources/LICENSE_OpenSans.txt
  25. BIN
      src/gaemstone.Client/Resources/OpenSans.Bold.ttf
  26. BIN
      src/gaemstone.Client/Resources/OpenSans.Italic.ttf
  27. BIN
      src/gaemstone.Client/Resources/OpenSans.ttf
  28. 763
      src/gaemstone.Client/Systems/EntityInspector.cs
  29. 38
      src/gaemstone.Client/Systems/FreeCameraController.cs
  30. 20
      src/gaemstone.Client/Systems/ImGuiDemoWindow.cs
  31. 233
      src/gaemstone.Client/Systems/ImGuiInputDebug.cs
  32. 96
      src/gaemstone.Client/Systems/ImGuiManager.cs
  33. 24
      src/gaemstone.Client/Systems/InputManager.cs
  34. 10
      src/gaemstone.Client/Systems/MeshManager.cs
  35. 32
      src/gaemstone.Client/Systems/Renderer.cs
  36. 6
      src/gaemstone.Client/Systems/Windowing.cs
  37. 819
      src/gaemstone.Client/Utility/ForkAwesome.cs
  38. 55
      src/gaemstone.Client/Utility/ImGuiUtility.cs
  39. 5
      src/gaemstone.Client/gaemstone.Client.csproj
  40. 1
      src/gaemstone.ECS
  41. 12
      src/gaemstone/Components/TransformComponents.cs
  42. 86
      src/gaemstone/Doc.cs
  43. 53
      src/gaemstone/ECS/Attributes.cs
  44. 36
      src/gaemstone/ECS/Component.cs
  45. 90
      src/gaemstone/ECS/ComponentHooks.cs
  46. 48
      src/gaemstone/ECS/Entity.cs
  47. 48
      src/gaemstone/ECS/EntityBase.cs
  48. 116
      src/gaemstone/ECS/EntityBuilder.cs
  49. 233
      src/gaemstone/ECS/EntityPath.cs
  50. 169
      src/gaemstone/ECS/EntityRef.cs
  51. 25
      src/gaemstone/ECS/EntityType.cs
  52. 87
      src/gaemstone/ECS/Filter.cs
  53. 16
      src/gaemstone/ECS/FilterExtensions.cs
  54. 2
      src/gaemstone/ECS/Game.cs
  55. 54
      src/gaemstone/ECS/Identifier.cs
  56. 41
      src/gaemstone/ECS/IdentifierRef.cs
  57. 116
      src/gaemstone/ECS/Iterator.cs
  58. 6
      src/gaemstone/ECS/Module.cs
  59. 32
      src/gaemstone/ECS/Observer.cs
  60. 46
      src/gaemstone/ECS/Query.cs
  61. 22
      src/gaemstone/ECS/Relation.cs
  62. 31
      src/gaemstone/ECS/Rule.cs
  63. 71
      src/gaemstone/ECS/System.cs
  64. 116
      src/gaemstone/ECS/Term.cs
  65. 63
      src/gaemstone/ECS/Universe+Lookup.cs
  66. 51
      src/gaemstone/ECS/Universe.cs
  67. 53
      src/gaemstone/Flecs/Core.cs
  68. 4
      src/gaemstone/Flecs/DeletionEvent.cs
  69. 81
      src/gaemstone/Flecs/Doc.cs
  70. 36
      src/gaemstone/Flecs/FlecsException.cs
  71. 2
      src/gaemstone/Flecs/ObserverEvent.cs
  72. 2
      src/gaemstone/Flecs/Pipeline.cs
  73. 2
      src/gaemstone/Flecs/SystemPhase.cs
  74. 4
      src/gaemstone/Flecs/Systems/Monitor.cs
  75. 6
      src/gaemstone/Flecs/Systems/Rest.cs
  76. 188
      src/gaemstone/Universe+Modules.cs
  77. 35
      src/gaemstone/Universe.cs
  78. 167
      src/gaemstone/Utility/Allocators.cs
  79. 34
      src/gaemstone/Utility/CStringExtensions.cs
  80. 14
      src/gaemstone/Utility/CallbackContextHelper.cs
  81. 6
      src/gaemstone/Utility/CollectionExtensions.cs
  82. 78
      src/gaemstone/Utility/IL/IterActionGenerator.cs
  83. 19
      src/gaemstone/Utility/SpanToRef.cs
  84. 4
      src/gaemstone/gaemstone.csproj

@ -14,8 +14,10 @@ indent_size = 4
dotnet_diagnostic.IDE0005.severity = suggestion
# IDE0047: Parentheses can be removed
dotnet_diagnostic.IDE0047.severity = none
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = none
[src/{FastNoiseLite,flecs-cs}/**]
[src/{FastNoiseLite,ImGui.NET}/**]
# Suppress compiler and analyer warnings in dependencies.
dotnet_analyzer_diagnostic.severity = none
dotnet_diagnostic.IDE0005.severity = none

6
.gitmodules vendored

@ -1,6 +1,6 @@
[submodule "src/flecs-cs"]
path = src/flecs-cs
url = https://github.com/flecs-hub/flecs-cs
[submodule "src/FastNoiseLite"]
path = src/FastNoiseLite
url = https://github.com/Auburn/FastNoiseLite.git
[submodule "src/gaemstone.ECS"]
path = src/gaemstone.ECS
url = https://git.mcft.net/copygirl/gaemstone.ECS.git

@ -1,48 +1,55 @@
๏ปฟ
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{599B7E67-7F73-4301-A9C6-E8DF286A2625}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.Bloxel", "src\gaemstone.Bloxel\gaemstone.Bloxel.csproj", "{7A80D49C-6768-4803-9866-691C7AD80817}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.Client", "src\gaemstone.Client\gaemstone.Client.csproj", "{67B9B2D4-FCB7-4642-B584-A0186CAB2969}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone", "src\gaemstone\gaemstone.csproj", "{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Immersion", "src\Immersion\Immersion.csproj", "{4B9C20F6-0793-4E85-863A-2E14230A028F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7A80D49C-6768-4803-9866-691C7AD80817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A80D49C-6768-4803-9866-691C7AD80817}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A80D49C-6768-4803-9866-691C7AD80817}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A80D49C-6768-4803-9866-691C7AD80817}.Release|Any CPU.Build.0 = Release|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Release|Any CPU.Build.0 = Release|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Release|Any CPU.Build.0 = Release|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7A80D49C-6768-4803-9866-691C7AD80817} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{67B9B2D4-FCB7-4642-B584-A0186CAB2969} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{4B9C20F6-0793-4E85-863A-2E14230A028F} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
EndGlobalSection
EndGlobal
๏ปฟ
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{599B7E67-7F73-4301-A9C6-E8DF286A2625}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.Bloxel", "src\gaemstone.Bloxel\gaemstone.Bloxel.csproj", "{7A80D49C-6768-4803-9866-691C7AD80817}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.Client", "src\gaemstone.Client\gaemstone.Client.csproj", "{67B9B2D4-FCB7-4642-B584-A0186CAB2969}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone.ECS", "src\gaemstone.ECS\gaemstone.ECS.csproj", "{EB4F82C0-1BDF-4404-84FB-F0A4E1E4DA67}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gaemstone", "src\gaemstone\gaemstone.csproj", "{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Immersion", "src\Immersion\Immersion.csproj", "{4B9C20F6-0793-4E85-863A-2E14230A028F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7A80D49C-6768-4803-9866-691C7AD80817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A80D49C-6768-4803-9866-691C7AD80817}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A80D49C-6768-4803-9866-691C7AD80817}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A80D49C-6768-4803-9866-691C7AD80817}.Release|Any CPU.Build.0 = Release|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67B9B2D4-FCB7-4642-B584-A0186CAB2969}.Release|Any CPU.Build.0 = Release|Any CPU
{EB4F82C0-1BDF-4404-84FB-F0A4E1E4DA67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB4F82C0-1BDF-4404-84FB-F0A4E1E4DA67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB4F82C0-1BDF-4404-84FB-F0A4E1E4DA67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB4F82C0-1BDF-4404-84FB-F0A4E1E4DA67}.Release|Any CPU.Build.0 = Release|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0}.Release|Any CPU.Build.0 = Release|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B9C20F6-0793-4E85-863A-2E14230A028F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7A80D49C-6768-4803-9866-691C7AD80817} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{67B9B2D4-FCB7-4642-B584-A0186CAB2969} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{EB4F82C0-1BDF-4404-84FB-F0A4E1E4DA67} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{7744A8A5-7D9A-474C-BC24-1CF0A8CB7EC0} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
{4B9C20F6-0793-4E85-863A-2E14230A028F} = {599B7E67-7F73-4301-A9C6-E8DF286A2625}
EndGlobalSection
EndGlobal

@ -15,6 +15,7 @@
<ItemGroup>
<ProjectReference Include="../gaemstone.Bloxel/gaemstone.Bloxel.csproj" />
<ProjectReference Include="../gaemstone.Client/gaemstone.Client.csproj" />
<ProjectReference Include="../gaemstone.ECS/gaemstone.ECS.csproj" />
</ItemGroup>
<ItemGroup>

@ -11,7 +11,7 @@ namespace Immersion;
public class ObserverTest
{
[Observer<ObserverEvent.OnSet>]
[Expression("[in] Chunk, [none] (Mesh, *)")]
[Expression("[in] Chunk, [none] (MeshHandle, *)")]
public static void DoObserver(in Chunk chunk)
=> Console.WriteLine($"Chunk at {chunk.Position} now has a Mesh!");
}

@ -1,12 +1,12 @@
๏ปฟusing System;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Threading;
using gaemstone;
using gaemstone.Bloxel;
using gaemstone.ECS;
using gaemstone.Flecs;
using gaemstone.Utility;
using Silk.NET.Maths;
using Silk.NET.Windowing;
using static gaemstone.Bloxel.Components.CoreComponents;
using static gaemstone.Client.Components.CameraComponents;
@ -20,9 +20,8 @@ var culture = CultureInfo.InvariantCulture;
Thread.CurrentThread.CurrentCulture = culture;
CultureInfo.DefaultThreadCurrentCulture = culture;
FlecsAbortException.SetupHook();
var universe = new Universe();
var game = universe.LookupOrThrow<Game>();
var game = universe.LookupByTypeOrThrow<Game>();
universe.Modules.Register<gaemstone.Flecs.Systems.Rest>();
universe.Modules.Register<gaemstone.Flecs.Systems.Monitor>();
@ -43,6 +42,7 @@ universe.Modules.Register<gaemstone.Components.TransformComponents>();
universe.Modules.Register<gaemstone.Client.Components.RenderingComponents>();
universe.Modules.Register<gaemstone.Client.Systems.Renderer>();
universe.Modules.Register<gaemstone.Client.Systems.ImGuiManager>();
universe.Modules.Register<gaemstone.Client.Systems.ImGuiDemoWindow>();
universe.Modules.Register<gaemstone.Client.Components.ResourceComponents>();
universe.Modules.Register<gaemstone.Client.Systems.TextureManager>();
@ -51,6 +51,7 @@ universe.Modules.Register<gaemstone.Client.Systems.MeshManager>();
universe.Modules.Register<gaemstone.Client.Components.InputComponents>();
universe.Modules.Register<gaemstone.Client.Systems.InputManager>();
universe.Modules.Register<gaemstone.Client.Systems.ImGuiInputDebug>();
universe.Modules.Register<gaemstone.Client.Systems.EntityInspector>();
universe.Modules.Register<gaemstone.Client.Components.CameraComponents>();
universe.Modules.Register<gaemstone.Client.Systems.FreeCameraController>();
@ -60,7 +61,7 @@ game.Set(new GameWindow(window));
universe.New("MainCamera")
.Set(Camera.Default3D)
.Set((GlobalTransform) Matrix4X4.CreateTranslation(0.0F, 2.0F, 0.0F))
.Set((GlobalTransform)Matrix4x4.CreateTranslation(0.0F, 2.0F, 0.0F))
.Set(new CameraController { MouseSensitivity = 12.0F })
.Build();
@ -71,8 +72,8 @@ var entities = universe.New("Entities").Build();
var rnd = new Random();
for (var x = -12; x <= 12; x++)
for (var z = -12; z <= 12; z++) {
var position = Matrix4X4.CreateTranslation(x * 2, 0.0F, z * 2);
var rotation = Matrix4X4.CreateRotationY(rnd.NextFloat(MathF.PI * 2));
var position = Matrix4x4.CreateTranslation(x * 2, 0.0F, z * 2);
var rotation = Matrix4x4.CreateRotationY(rnd.NextFloat(MathF.PI * 2));
var (type, mesh) = rnd.Pick(("Heart", heartMesh), ("Sword", swordMesh));
entities.NewChild()
.Set((GlobalTransform)(rotation * position))
@ -98,7 +99,7 @@ for (var cz = -sizeH; cz < sizeH; cz++) {
var pos = new ChunkPos(cx, cy - 2, cz);
var storage = new ChunkStoreBlocks();
chunks.NewChild()
.Set((GlobalTransform)Matrix4X4.CreateTranslation(pos.GetOrigin()))
.Set((GlobalTransform)Matrix4x4.CreateTranslation(pos.GetOrigin()))
.Set(new Chunk(pos))
.Set(storage)
.Add<Texture>(texture)

@ -1 +0,0 @@
Subproject commit a2047983917aa462a8c2f34d5315aea48502f4d8

@ -1,6 +1,6 @@
using System;
using System.Collections.Immutable;
using Silk.NET.Maths;
using System.Numerics;
namespace gaemstone.Bloxel;
@ -47,14 +47,14 @@ public static class BlockFacingExtensions
public static BlockFacing GetOpposite(this BlockFacing self)
=> (BlockFacing)((int)self ^ 0b1);
public static Vector3D<float> ToVector3(this BlockFacing self)
public static Vector3 ToVector3(this BlockFacing self)
=> self switch {
BlockFacing.East => Vector3D<float>.UnitX,
BlockFacing.West => -Vector3D<float>.UnitX,
BlockFacing.Up => Vector3D<float>.UnitY,
BlockFacing.Down => -Vector3D<float>.UnitY,
BlockFacing.South => Vector3D<float>.UnitZ,
BlockFacing.North => -Vector3D<float>.UnitZ,
BlockFacing.East => Vector3.UnitX,
BlockFacing.West => -Vector3.UnitX,
BlockFacing.Up => Vector3.UnitY,
BlockFacing.Down => -Vector3.UnitY,
BlockFacing.South => Vector3.UnitZ,
BlockFacing.North => -Vector3.UnitZ,
_ => throw new ArgumentException(
$"'{self}' is not a valid BlockFacing", nameof(self))
};

@ -1,5 +1,5 @@
using System;
using Silk.NET.Maths;
using System.Numerics;
namespace gaemstone.Bloxel;
@ -15,8 +15,8 @@ public readonly struct BlockPos
public BlockPos(int x, int y, int z) => (X, Y, Z) = (x, y, z);
public void Deconstruct(out int x, out int y, out int z) => (x, y, z) = (X, Y, Z);
public Vector3D<float> GetOrigin() => new(X, Y, Z);
public Vector3D<float> GetCenter() => new(X + 0.5f, Y + 0.5f, Z + 0.5f);
public Vector3 GetOrigin() => new(X, Y, Z);
public Vector3 GetCenter() => new(X + 0.5f, Y + 0.5f, Z + 0.5f);
public BlockPos Add(int x, int y, int z) => new(X + x, Y + y, Z + z);
@ -70,6 +70,6 @@ public readonly struct BlockPos
public static class BlockPosExtensions
{
public static BlockPos ToBlockPos(this Vector3D<float> self)
public static BlockPos ToBlockPos(this Vector3 self)
=> new((int)MathF.Floor(self.X), (int)MathF.Floor(self.Y), (int)MathF.Floor(self.Z));
}

@ -1,5 +1,5 @@
using System;
using Silk.NET.Maths;
using System.Numerics;
using static gaemstone.Bloxel.Constants;
namespace gaemstone.Bloxel;
@ -16,9 +16,9 @@ public readonly struct ChunkPos
public ChunkPos(int x, int y, int z) => (X, Y, Z) = (x, y, z);
public void Deconstruct(out int x, out int y, out int z) => (x, y, z) = (X, Y, Z);
public Vector3D<float> GetOrigin() => new(
public Vector3 GetOrigin() => new(
X << ChunkBitShift, Y << ChunkBitShift, Z << ChunkBitShift);
public Vector3D<float> GetCenter() => new(
public Vector3 GetCenter() => new(
(X << ChunkBitShift) + ChunkLength / 2,
(Y << ChunkBitShift) + ChunkLength / 2,
(Z << ChunkBitShift) + ChunkLength / 2);
@ -66,7 +66,7 @@ public readonly struct ChunkPos
public static class ChunkPosExtensions
{
public static ChunkPos ToChunkPos(this Vector3D<float> pos) => new(
public static ChunkPos ToChunkPos(this Vector3 pos) => new(
(int)MathF.Floor(pos.X) >> ChunkBitShift,
(int)MathF.Floor(pos.Y) >> ChunkBitShift,
(int)MathF.Floor(pos.Z) >> ChunkBitShift);

@ -1,7 +1,7 @@
using System;
using System.Numerics;
using gaemstone.Client.Systems;
using gaemstone.ECS;
using Silk.NET.Maths;
using static gaemstone.Bloxel.Components.CoreComponents;
using static gaemstone.Client.Components.RenderingComponents;
using static gaemstone.Client.Components.ResourceComponents;
@ -14,13 +14,13 @@ public class ChunkMeshGenerator
{
private const int StartingCapacity = 1024;
private static readonly Vector3D<float>[][] OffsetPerFacing = {
new Vector3D<float>[]{ new(1,1,1), new(1,0,1), new(1,0,0), new(1,1,0) }, // East (+X)
new Vector3D<float>[]{ new(0,1,0), new(0,0,0), new(0,0,1), new(0,1,1) }, // West (-X)
new Vector3D<float>[]{ new(1,1,0), new(0,1,0), new(0,1,1), new(1,1,1) }, // Up (+Y)
new Vector3D<float>[]{ new(1,0,1), new(0,0,1), new(0,0,0), new(1,0,0) }, // Down (-Y)
new Vector3D<float>[]{ new(0,1,1), new(0,0,1), new(1,0,1), new(1,1,1) }, // South (+Z)
new Vector3D<float>[]{ new(1,1,0), new(1,0,0), new(0,0,0), new(0,1,0) } // North (-Z)
private static readonly Vector3[][] OffsetPerFacing = {
new Vector3[]{ new(1,1,1), new(1,0,1), new(1,0,0), new(1,1,0) }, // East (+X)
new Vector3[]{ new(0,1,0), new(0,0,0), new(0,0,1), new(0,1,1) }, // West (-X)
new Vector3[]{ new(1,1,0), new(0,1,0), new(0,1,1), new(1,1,1) }, // Up (+Y)
new Vector3[]{ new(1,0,1), new(0,0,1), new(0,0,0), new(1,0,0) }, // Down (-Y)
new Vector3[]{ new(0,1,1), new(0,0,1), new(1,0,1), new(1,1,1) }, // South (+Z)
new Vector3[]{ new(1,1,0), new(1,0,0), new(0,0,0), new(0,1,0) } // North (-Z)
};
private static readonly int[] TriangleIndices
@ -28,9 +28,9 @@ public class ChunkMeshGenerator
private ushort[] _indices = new ushort[StartingCapacity];
private Vector3D<float>[] _vertices = new Vector3D<float>[StartingCapacity];
private Vector3D<float>[] _normals = new Vector3D<float>[StartingCapacity];
private Vector2D<float>[] _uvs = new Vector2D<float>[StartingCapacity];
private Vector3[] _vertices = new Vector3[StartingCapacity];
private Vector3[] _normals = new Vector3[StartingCapacity];
private Vector2[] _uvs = new Vector2[StartingCapacity];
[System]
[Expression("[in] Chunk, ChunkStoreBlocks, HasBasicWorldGeneration, !(Mesh, *)")]
@ -48,8 +48,8 @@ public class ChunkMeshGenerator
// TODO: We'll need a way to get neighbors again.
// var storages = new ChunkStoreBlocks[3, 3, 3];
// foreach (var (x, y, z) in Neighbors.ALL.Prepend(Neighbor.None))
// if (_chunkStore.TryGetEntityID(chunkPos.Add(x, y, z), out var neighborID))
// if (_storageStore.TryGet(neighborID, out var storage))
// if (_chunkStore.TryGetEntityId(chunkPos.Add(x, y, z), out var neighborId))
// if (_storageStore.TryGet(neighborId, out var storage))
// storages[x+1, y+1, z+1] = storage;
// var centerStorage = storages[1, 1, 1];
@ -61,11 +61,11 @@ public class ChunkMeshGenerator
for (var x = 0; x < 16; x++)
for (var y = 0; y < 16; y++)
for (var z = 0; z < 16; z++) {
var block = universe.Lookup(centerBlocks[x, y, z]);
var block = universe.LookupAlive(centerBlocks[x, y, z]);
if (block == null) continue;
var blockVertex = new Vector3D<float>(x, y, z);
var textureCell = block.Get<TextureCoords4>();
var blockVertex = new Vector3(x, y, z);
var textureCell = block.GetOrThrow<TextureCoords4>();
foreach (var facing in BlockFacings.All) {
if (!IsNeighborEmpty(storages, x, y, z, facing)) continue;
@ -99,7 +99,7 @@ public class ChunkMeshGenerator
}
// TODO: Should dynamically generating meshes require getting GL this way?
var GL = universe.LookupOrThrow<Game>().Get<Canvas>().GL;
var GL = universe.LookupByTypeOrThrow<Game>().GetOrThrow<Canvas>().GL;
return (indexCount > 0)
? MeshManager.Create(GL,
_indices.AsSpan(0, indexCount), _vertices.AsSpan(0, vertexCount),

@ -5,14 +5,14 @@ namespace gaemstone.Bloxel.Components;
[Module]
public partial class CoreComponents
{
[Component]
[Symbol, Component]
public readonly struct Chunk
{
public ChunkPos Position { get; }
public Chunk(ChunkPos pos) => Position = pos;
}
[Component]
[Symbol, Component]
public class ChunkStoreBlocks
: ChunkPaletteStorage<Entity>
{

@ -1,7 +1,7 @@
using System;
using System.Collections.Immutable;
using System.Numerics;
using System.Text;
using Silk.NET.Maths;
namespace gaemstone.Bloxel;
@ -174,7 +174,7 @@ public static class NeighborExtensions
public static BlockPos ToProperPos(this Neighbor self)
{ var (x, y, z) = self; return new(x, y, z); }
public static Vector3D<float> ToVector3(this Neighbor self)
public static Vector3 ToVector3(this Neighbor self)
{ var (x, y, z) = self; return new(x, y, z); }

@ -1,5 +1,6 @@
using System;
using gaemstone.ECS;
using gaemstone.Utility;
using static gaemstone.Bloxel.Components.CoreComponents;
using static gaemstone.Bloxel.Constants;
@ -10,6 +11,7 @@ namespace gaemstone.Bloxel.Systems;
public class BasicWorldGenerator
{
private readonly FastNoiseLite _noise;
private readonly Random _rnd = new();
public BasicWorldGenerator()
{
@ -20,24 +22,26 @@ public class BasicWorldGenerator
_noise.SetFractalGain(0.6f);
}
[Tag]
[Symbol, Tag]
public struct HasBasicWorldGeneration { }
[System]
public void Populate(Universe universe, EntityRef entity,
public void Populate(World world, EntityRef entity,
in Chunk chunk, ChunkStoreBlocks blocks,
[Not] HasBasicWorldGeneration _)
{
var stone = universe.LookupOrThrow("Stone");
for (var lx = 0; lx < ChunkLength; lx++)
for (var ly = 0; ly < ChunkLength; ly++)
for (var lz = 0; lz < ChunkLength; lz++) {
var gx = chunk.Position.X << ChunkBitShift | lx;
var gy = chunk.Position.Y << ChunkBitShift | ly;
var gz = chunk.Position.Z << ChunkBitShift | lz;
var bias = Math.Clamp(gy / 32.0f + 1.0f, 0.0f, 1.0f);
if (_noise.GetNoise(gx, gy, gz) > bias)
blocks[lx, ly, lz] = stone;
var stone = world.LookupByPathOrThrow("Stone");
var dirt = world.LookupByPathOrThrow("Dirt");
var grass = world.LookupByPathOrThrow("Grass");
for (var localX = 0; localX < ChunkLength; localX++)
for (var localY = 0; localY < ChunkLength; localY++)
for (var localZ = 0; localZ < ChunkLength; localZ++) {
var globalX = chunk.Position.X << ChunkBitShift | localX;
var globalY = chunk.Position.Y << ChunkBitShift | localY;
var globalZ = chunk.Position.Z << ChunkBitShift | localZ;
var bias = Math.Clamp((globalY / 32.0f + 1.0f), 0.0f, 1.0f);
if (_noise.GetNoise(globalX, globalY, globalZ) > bias)
blocks[localX, localY, localZ] = _rnd.Pick(stone, dirt, grass);
}
entity.Add<HasBasicWorldGeneration>();
}

@ -72,7 +72,7 @@ public class ChunkedOctree<T>
while (enumerator.MoveNext()) yield return enumerator.Current;
}
public sealed class Enumerator
public class Enumerator
: IEnumerator<(ChunkPos ChunkPos, T Value, float Weight)>
{
private readonly ChunkedOctree<T> _octree;

@ -14,6 +14,11 @@
<ItemGroup>
<ProjectReference Include="../gaemstone/gaemstone.csproj" />
<ProjectReference Include="../gaemstone.Client/gaemstone.Client.csproj" />
<ProjectReference Include="../gaemstone.ECS/gaemstone.ECS.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Silk.NET" Version="2.16.0" />
</ItemGroup>
</Project>

@ -1,8 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using Silk.NET.Maths;
using System.Text.RegularExpressions;
namespace gaemstone.Client;
@ -11,8 +12,8 @@ public readonly struct Color
: IEquatable<Color>
{
public static readonly Color Transparent = default;
public static readonly Color Black = FromRGB(0x000000);
public static readonly Color White = FromRGB(0xFFFFFF);
public static readonly Color Black = FromRGB(0.0f, 0.0f, 0.0f);
public static readonly Color White = FromRGB(1.0f, 1.0f, 1.0f);
[FieldOffset(0)]
public readonly uint RGBA;
@ -26,19 +27,62 @@ public readonly struct Color
[FieldOffset(3)]
public readonly byte A;
private Color(uint rgba)
{ Unsafe.SkipInit(out this); RGBA = rgba; }
private Color(uint rgba) => RGBA = rgba;
private Color(byte r, byte g, byte b, byte a)
{ Unsafe.SkipInit(out this); R = r; G = g; B = b; A = a; }
{ R = r; G = g; B = b; A = a; }
public static Color FromRGBA(uint rgba) => new(rgba);
public static Color FromRGBA(byte r, byte g, byte b, byte a) => new(r, g, b, a);
public static Color FromRGBA(uint value) => new((byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value);
public static Color FromRGBA(float r, float g, float b, float a = 1.0f) => new(F2B(r), F2B(g), F2B(b), F2B(a));
public static Color FromRGBA(Vector4 vec) => FromRGBA(vec.X, vec.Y, vec.Z, vec.W);
public static Color FromRGB(uint rgb) => new(rgb | 0xFF000000);
public static Color FromRGB(byte r, byte g, byte b) => new(r, g, b, 0xFF);
public static Color FromRGB(uint value) => new((byte)(value >> 16), (byte)(value >> 8), (byte)value, 0xFF);
public static Color FromRGB(float r, float g, float b) => new(F2B(r), F2B(g), F2B(b), 0xFF);
public static Color FromRGB(Vector3 vec) => FromRGB(vec.X, vec.Y, vec.Z);
public static Color FromGrayscale(byte gray) => new(gray, gray, gray, 0xFF);
public static Color FromGrayscale(byte gray, byte alpha) => new(gray, gray, gray, alpha);
public static Color FromGrayscale(float value, float alpha = 1.0f) => FromRGBA(value, value, value, alpha);
public static Color FromHSV(float hue, float saturation, float value, float alpha = 1.0f)
{
if (saturation <= 0.0f) return FromGrayscale(value, alpha);
var h = (((hue % 360f) + 360f) % 360f) / 60f;
var s = Math.Clamp(saturation, 0.0f, 1.0f);
var v = Math.Clamp(value , 0.0f, 1.0f);
var f = h % 1f;
var p = v * (1.0f - s);
var q = v * (1.0f - s * f);
var t = v * (1.0f - s * (1.0f - f));
return (int)h switch {
0 => FromRGBA(v, t, p, alpha),
1 => FromRGBA(q, v, p, alpha),
2 => FromRGBA(p, v, t, alpha),
3 => FromRGBA(p, q, v, alpha),
4 => FromRGBA(t, p, v, alpha),
5 => FromRGBA(v, p, q, alpha),
_ => throw new InvalidOperationException()
};
}
private static readonly Regex _hexRegex = new("^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$");
public static Color? TryParseHex(string? str)
{
if (str == null) return null;
var match = _hexRegex.Match(str);
if (!match.Success) return null;
var hex = match.Value.AsSpan()[1..];
var value = uint.Parse(hex, NumberStyles.HexNumber);
return (hex.Length == 8) ? FromRGBA(value) : FromRGB(value);
}
public Color WithAlpha(float alpha)
=> new(R, G, B, F2B(alpha));
public static Color Mix(Color a, Color b, float ratio)
=> FromRGBA((B2F(a.R) * (1 - ratio) + B2F(b.R) * ratio) / 2,
(B2F(a.G) * (1 - ratio) + B2F(b.G) * ratio) / 2,
(B2F(a.B) * (1 - ratio) + B2F(b.B) * ratio) / 2,
(B2F(a.A) * (1 - ratio) + B2F(b.A) * ratio) / 2);
public bool Equals(Color other)
=> RGBA == other.RGBA;
@ -46,13 +90,23 @@ public readonly struct Color
=> (obj is Color color) && Equals(color);
public override int GetHashCode()
=> RGBA.GetHashCode();
public override string? ToString()
public override string ToString()
=> $"Color(0x{RGBA:X8})";
public Vector4 ToVector4() => new(B2F(R), B2F(G), B2F(B), B2F(A));
public Vector3 ToVector3() => new(B2F(R), B2F(G), B2F(B));
public string ToHexString() => (A < byte.MaxValue) ? $"#{RGBA:X8}" : $"#{R:X2}{G:X2}{B:X2}";
public static bool operator ==(Color left, Color right) => left.Equals(right);
public static bool operator !=(Color left, Color right) => !left.Equals(right);
public static implicit operator System.Drawing.Color(Color color) => System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B);
public static implicit operator Vector4D<float>(Color color) => new(color.R / 255F, color.G / 255F, color.B / 255F, color.A / 255F);
public static implicit operator Vector4D<byte>(Color color) => new(color.R, color.G, color.B, color.A);
public static implicit operator System.Drawing.Color(Color color)
=> System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B);
/// <summary> Converts a float clamped to range [0.0, 1.0] into a byte in range [0, 255]. </summary>
private static byte F2B(float f) => (byte)(Math.Clamp(f, 0.0f, 1.0f) * 255);
/// <summary> Converts a byte in range [0, 255] into a float in range [0.0, 1.0]. </summary>
private static float B2F(byte b) => b / 255f;
}

@ -1,5 +1,5 @@
using System.Drawing;
using gaemstone.ECS;
using Silk.NET.Maths;
namespace gaemstone.Client.Components;
@ -27,7 +27,7 @@ public class CameraComponents
[Component]
public struct CameraViewport
{
public Vector4D<byte> ClearColor { get; set; }
public Rectangle<int> Viewport { get; set; }
public Color ClearColor { get; set; }
public Rectangle Viewport { get; set; }
}
}

@ -7,30 +7,30 @@ namespace gaemstone.Client.Components;
[Module]
public class InputComponents
{
[Entity(Global = true)]
[Symbol, Path("/Input")]
[Add<Input>]
public struct Input { }
[Entity("Input", "Mouse", Global = true)]
[Symbol, Path("/Input/Mouse")]
[Add<Mouse>]
public struct Mouse { }
[Entity("Input", "Keyboard", Global = true)]
[Symbol, Path("/Input/Keyboard")]
[Add<Keyboard>]
public struct Keyboard { }
[Tag]
[Symbol, Tag]
public struct Gamepad { }
/// <summary> Present on inputs / actions that are currently active. </summary>
[Component] public struct Active { public TimeSpan Duration; }
[Symbol, Component] public struct Active { public TimeSpan Duration; }
/// <summary> Present on inputs / actions were activated this frame. </summary>
[Tag] public struct Activated { }
[Symbol, Tag] public struct Activated { }
/// <summary> Present on inputs / actions were deactivated this frame. </summary>
[Tag] public struct Deactivated { }
[Symbol, Tag] public struct Deactivated { }
/// <summary>
@ -41,7 +41,7 @@ public class InputComponents
/// This is set if a UI element is focused that captures
/// navigational or text input.
/// </remarks>
[Tag, Relation, Exclusive]
[Symbol, Relation, Tag, Exclusive]
public struct InputCapturedBy { }
/// <summary>
@ -52,7 +52,7 @@ public class InputComponents
/// This could for example include the mouse currently being over
/// a UI element, preventing the game from handling mouse input.
/// </remarks>
[Tag, Relation, Exclusive]
[Symbol, Relation, Tag, Exclusive]
public struct MouseInputCapturedBy { }
/// <summary>
@ -62,7 +62,7 @@ public class InputComponents
/// <remarks>
/// This is set when a camera controller assumes control of the mouse.
/// </remarks>
[Tag, Relation, Exclusive]
[Symbol, Relation, Tag, Exclusive]
[With<InputCapturedBy>]
[With<MouseInputCapturedBy>]
public struct CursorCapturedBy { }

@ -1,6 +1,6 @@
using System.Drawing;
using System.Numerics;
using gaemstone.ECS;
using Silk.NET.Maths;
using Silk.NET.OpenGL;
namespace gaemstone.Client.Components;
@ -8,7 +8,7 @@ namespace gaemstone.Client.Components;
[Module]
public class RenderingComponents
{
[Component]
[Symbol, Component]
public readonly struct MeshHandle
{
public uint Handle { get; }
@ -19,7 +19,7 @@ public class RenderingComponents
{ Handle = handle; Count = count; IsIndexed = indexed; }
}
[Component]
[Symbol, Component]
public readonly struct TextureHandle
{
public TextureTarget Target { get; }
@ -29,13 +29,13 @@ public class RenderingComponents
=> (Target, Handle) = (target, handle);
}
[Component]
[Symbol, Component]
public readonly struct TextureCoords4
{
public Vector2D<float> TopLeft { get; }
public Vector2D<float> TopRight { get; }
public Vector2D<float> BottomLeft { get; }
public Vector2D<float> BottomRight { get; }
public Vector2 TopLeft { get; }
public Vector2 TopRight { get; }
public Vector2 BottomLeft { get; }
public Vector2 BottomRight { get; }
public TextureCoords4(float x1, float y1, float x2, float y2)
{

@ -5,7 +5,7 @@ namespace gaemstone.Client.Components;
[Module]
public class ResourceComponents
{
[Tag]
[Symbol, Tag]
public struct Resource { }
// Entities can have for example Texture as a tag, in which case
@ -14,9 +14,9 @@ public class ResourceComponents
// Entities can also have a (Texture, $T) pair where $T is a resource,
// meaning the entity has that resource assigned as their texture.
[Relation, Tag, IsA<Resource>]
[Symbol, Relation, Tag, IsA<Resource>]
public struct Texture { }
[Relation, Tag, IsA<Resource>]
[Symbol, Relation, Tag, IsA<Resource>]
public struct Mesh { }
}

@ -0,0 +1,95 @@
Copyright (c) 2018, Fork Awesome (https://forkawesome.github.io),
with Reserved Font Name Fork Awesome.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,93 @@
Copyright 2020 The Open Sans Project Authors (https://github.com/googlefonts/opensans)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

@ -0,0 +1,763 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using gaemstone.Client.Utility;
using gaemstone.ECS;
using gaemstone.Flecs;
using gaemstone.Utility;
using ImGuiNET;
using static gaemstone.Client.Systems.ImGuiManager;
using Icon = gaemstone.Client.Utility.ForkAwesome;
using ImGuiInternal = ImGuiNET.Internal.ImGui;
namespace gaemstone.Client.Systems;
[Module]
[DependsOn<gaemstone.Client.Systems.ImGuiManager>]
public class EntityInspector
: IModuleInitializer
{
[Tag]
public struct InspectorWindow { }
[Relation, Exclusive]
[Add<DeletionEvent.OnDeleteTarget, DeletionBehavior.Delete>]
public struct Selected { }
[Tag]
public struct ScrollToSelected { }
[Relation]
public struct Expanded { }
[Component]
public class History
{
public Entry? Current { get; set; } = null;
public class Entry
{
public Entity Entity { get; }
public EntityPath? Path { get; }
public Entry? Prev { get; set; }
public Entry? Next { get; set; }
public Entry(EntityRef entity, Entry? prev, Entry? next)
{
Entity = entity;
Path = entity.GetFullPath();
if ((Prev = prev) != null) Prev.Next = this;
if ((Next = next) != null) Next.Prev = this;
}
}
}
[Component]
public struct DocPriority { public float Value; }
[Component]
public struct DocIcon { public char Value; }
public void Initialize(EntityRef module)
{
void SetDocInfo(string path, float priority, string icon, float r, float g, float b)
=> module.World.LookupByPathOrThrow(path)
.Add<Doc.DisplayType>()
.Set(new DocPriority { Value = priority })
.Set(new DocIcon { Value = icon[0] })
.SetDocColor(Color.FromRGB(r, g, b).ToHexString());
SetDocInfo("/flecs/core/Module" , 0 , Icon.Archive , 1.0f, 0.9f, 0.7f);
SetDocInfo("/flecs/system/System" , 1 , Icon.Cog , 1.0f, 0.7f, 0.7f);
SetDocInfo("/flecs/core/Observer" , 2 , Icon.Eye , 1.0f, 0.8f, 0.8f);
SetDocInfo("/gaemstone/Doc/Relation" , 3 , Icon.ShareAlt , 0.7f, 1.0f, 0.8f);
SetDocInfo("/flecs/core/Tag" , 4 , Icon.Tag , 0.7f, 0.8f, 1.0f);
SetDocInfo("/flecs/core/Component" , 5 , Icon.PencilSquare , 0.6f, 0.6f, 1.0f);
SetDocInfo("/flecs/core/Prefab" , 6 , Icon.Cube , 0.9f, 0.8f, 1.0f);
}
[System]
public void ShowUIButton(World world, ImGuiData _)
{
var hasAnyInspector = false;
var inspectorWindow = world.LookupByTypeOrThrow<InspectorWindow>();
foreach (var entity in Iterator.FromTerm(world, new(inspectorWindow)))
{ hasAnyInspector = true; break; }
if (ImGuiUtility.UIButton(0, Icon.Search, "Entity Inspector", hasAnyInspector))
NewEntityInspectorWindow(world);
}
[System]
public void ShowExplorerWindow(EntityRef window, InspectorWindow _, History? history)
{
var isOpen = true;
var fontSize = ImGui.GetFontSize();
var viewCenter = ImGui.GetMainViewport().GetCenter();
ImGui.SetNextWindowPos(viewCenter, ImGuiCond.Appearing, new(0.5f, 0.5f));
ImGui.SetNextWindowSize(new(fontSize * 40, fontSize * 25), ImGuiCond.Appearing);
ImGui.PushFont(ImGui.GetIO().Fonts.Fonts[1]);
if (ImGui.Begin($"{Icon.Search} Entity Inspector##{window.Id}",
ref isOpen, ImGuiWindowFlags.NoScrollbar)) {
ImGui.PushFont(ImGui.GetIO().Fonts.Fonts[0]);
var selected = window.GetTargets<Selected>().FirstOrDefault();
ActionBarAndPath(window, history, selected);
ImGui.BeginTable("Views", 2, ImGuiTableFlags.Resizable);
ImGui.TableSetupColumn("Explorer", ImGuiTableColumnFlags.WidthFixed, fontSize * 12);
ImGui.TableSetupColumn("Entity", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableNextColumn();
ImGui.BeginChild("ExplorerView", new(-float.Epsilon, -float.Epsilon));
ExplorerView(window, history, selected);
ImGui.EndChild();
void Tab(string name, Action<EntityRef, History?, EntityRef?> contentMethod)
{
if (!ImGui.BeginTabItem(name)) return;
ImGui.BeginChild($"{name}Tab", new(-float.Epsilon, -float.Epsilon));
contentMethod(window, history, selected);
ImGui.EndChild();
ImGui.EndTabItem();
}
ImGui.TableNextColumn();
ImGui.BeginChild("EntityView", new(-float.Epsilon, -float.Epsilon));
if (!ImGui.BeginTabBar("Tabs")) return;
Tab($"{Icon.PencilSquare} Components", ComponentsTab);
Tab($"{Icon.ShareAlt} References", ReferencesTab);
Tab($"{Icon.InfoCircle} Documentation", DocumentationTab);
ImGui.EndTabBar();
ImGui.EndChild();
ImGui.EndTable();
ImGui.PopFont();
}
ImGui.PopFont();
ImGui.End();
// If window is closed, delete the entity.
if (!isOpen) window.Delete();
}
[Observer<ObserverEvent.OnRemove>]
public void ClearStorageOnRemove(EntityRef _1, InspectorWindow _2)
{
// TODO: Clear out settings store for the window.
}
private void ActionBarAndPath(EntityRef window, History? history, EntityRef? selected)
{
var world = window.World;
static bool IconButtonWithToolTip(string icon, string tooltip, bool enabled = true) {
if (!enabled) ImGui.BeginDisabled();
var clicked = ImGui.Button(icon);
if (!enabled) ImGui.EndDisabled();
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip(tooltip);
return clicked;
}
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(2, ImGui.GetStyle().ItemSpacing.Y));
ImGui.BeginTable("ActionBar", 3);
ImGui.TableSetupColumn("Explorer", ImGuiTableColumnFlags.WidthFixed);
ImGui.TableSetupColumn("Path", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupColumn("Entity", ImGuiTableColumnFlags.WidthFixed);
ImGui.TableNextColumn();
var hasExpanded = window.Has<Expanded, Core.Wildcard>();
if (IconButtonWithToolTip(Icon.Outdent, "Collapse all items in the Explorer View", hasExpanded))
window.Remove<Expanded, Core.Wildcard>();
if (history != null) {
var hasPrev = ((selected != null) ? history.Current?.Prev : history.Current) != null;
var hasNext = history.Current?.Next != null;
ImGui.SameLine();
if (IconButtonWithToolTip(Icon.ArrowLeft, "Go to the previously viewed entity", hasPrev))
GoToPrevious(window, history, selected);
ImGui.SameLine();
if (IconButtonWithToolTip(Icon.ArrowRight, "Go to the next viewed entity", hasNext))
GoToNext(window, history);
}
ImGui.SameLine();
if (IconButtonWithToolTip(Icon.Crosshairs, "Scroll to the current entity in the Explorer View", (selected != null)))
window.Add<ScrollToSelected>();
ImGui.TableNextColumn();
var availableWidth = ImGui.GetColumnWidth() - ImGui.GetStyle().CellPadding.X * 2;
PathInput(window, history, selected, availableWidth);
ImGui.TableNextColumn();
if (IconButtonWithToolTip(Icon.PlusCircle, "Create a new child entity", (selected != null)))
// FIXME: Replace this once Flecs has been fixed.
SetSelected(window, history, world.New().Build().ChildOf(selected));
// SelectAndScrollTo(windowEntity, windowData, selected!.NewChild().Build(), selected);
ImGui.SameLine();
if (IconButtonWithToolTip(Icon.Pencil, "Rename the current entity", false && (selected != null)))
{ } // TODO: Implement this!
ImGui.SameLine();
var isDisabled = (selected?.IsDisabled == true);
var icon = !isDisabled ? Icon.BellSlash : Icon.Bell;
var tooltip = $"{(!isDisabled ? "Disable" : "Enable")} the current entity";
if (IconButtonWithToolTip(icon, tooltip, (selected != null)))
{ if (isDisabled) selected!.Enable(); else selected!.Disable(); }
ImGui.SameLine();
if (IconButtonWithToolTip(Icon.Trash, "Delete the current entity", (selected != null))) {
SetSelected(window, history, selected!.Parent);
selected.Delete(); // TODO: Confirmation dialog?