diff --git a/src/Alex.ResourcePackLib/MCBedrockResourcePack.cs b/src/Alex.ResourcePackLib/MCBedrockResourcePack.cs index 1f88b76cb..dad9e9469 100644 --- a/src/Alex.ResourcePackLib/MCBedrockResourcePack.cs +++ b/src/Alex.ResourcePackLib/MCBedrockResourcePack.cs @@ -47,15 +47,9 @@ public class MCBedrockResourcePack : ResourcePack, ITextureProvider, IAnimationP public IDictionary RenderControllers { get; private set; } = new ConcurrentDictionary(); public IDictionary AnimationControllers { get; private set; } = new ConcurrentDictionary(); public IDictionary Animations { get; private set; } = new ConcurrentDictionary(); - - public IReadOnlyDictionary Particles { get; private set; } = - new ConcurrentDictionary(); - - public IReadOnlyDictionary GlobalUiVariables { get; private set; } = - new Dictionary(); - + public IReadOnlyDictionary Particles { get; private set; } = new ConcurrentDictionary(); + public IReadOnlyDictionary GlobalUiVariables { get; private set; } = new Dictionary(); public SoundDefinitionFormat SoundDefinitions { get; private set; } = null; - public SoundBindingsCollection SoundBindings { get; private set; } = null; private readonly IFilesystem _archive; @@ -87,7 +81,6 @@ private string NormalisePath(string path) private static readonly Regex IsFontFile = new Regex(@"^font[\\\/](?'filename'.*)\.png$", RegexOpts); private static readonly Regex IsParticleFile = new Regex(@"^particles[\\\/](?'filename'.*)\.json$", RegexOpts); private static readonly Regex IsAttachableFile = new Regex(@"^attachables[\\\/](?'filename'.*)\.json$", RegexOpts); - private static readonly Regex IsUiTexture = new Regex(@"^textures[\\\/]ui[\\\/](?'filename'.*)\.png", RegexOpts); private static readonly Regex IsUiDefinition = new Regex(@"^ui[\\\/](?'filename'.*)\.json", RegexOpts); @@ -96,16 +89,10 @@ private void Load(ResourcePack.LoadProgress progressReporter) Dictionary entityDefinitions = new Dictionary(); Dictionary entityModels = new Dictionary(StringComparer.Ordinal); Dictionary renderControllers = new Dictionary(StringComparer.Ordinal); - - Dictionary - animationControllers = new Dictionary(StringComparer.Ordinal); - + Dictionary animationControllers = new Dictionary(StringComparer.Ordinal); Dictionary particleDefinitions = new Dictionary(StringComparer.Ordinal); - - Dictionary attachableDefinitions = - new Dictionary(StringComparer.Ordinal); - Dictionary - animations = new Dictionary(StringComparer.Ordinal); + Dictionary attachableDefinitions = new Dictionary(StringComparer.Ordinal); + Dictionary animations = new Dictionary(StringComparer.Ordinal); TryAddBitmap("textures/ui/title"); diff --git a/src/Alex.sln b/src/Alex.sln index 2a6c8f117..82aaa2ae0 100644 --- a/src/Alex.sln +++ b/src/Alex.sln @@ -47,6 +47,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiNET.Console", "..\submodu EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Alex.ECS", "Alex.ECS\Alex.ECS.csproj", "{6FB2730B-78B0-4AE5-9E48-E901D2E6DD54}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResourcePackLib", "ResourcePackLib", "{7993E92A-6DD1-4F64-A654-D3ED0ACF0C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourcePackLib.Core", "ResourcePackLib\ResourcePackLib.Core\ResourcePackLib.Core.csproj", "{27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourcePackLib.Loader.Bedrock", "ResourcePackLib\ResourcePackLib.Loader.Bedrock\ResourcePackLib.Loader.Bedrock.csproj", "{801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourcePackLib.Loader.Java", "ResourcePackLib\ResourcePackLib.Loader.Java\ResourcePackLib.Loader.Java.csproj", "{C8511536-2B73-4C40-9F04-64389D50E6E8}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shaders", "Shaders", "{A1B11EA5-36F6-45A3-B9CF-0DDB43DEACA9}" ProjectSection(SolutionItems) = preProject Shaders\Blockshader.fx = Shaders\Blockshader.fx @@ -55,6 +63,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shaders", "Shaders", "{A1B1 Shaders\ShawowMap.fx = Shaders\ShawowMap.fx EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourcePackLib.ModelExplorer", "ResourcePackLib\ResourcePackLib.ModelExplorer\ResourcePackLib.ModelExplorer.csproj", "{75B8EC0E-D606-4886-BFF5-C5B70D2DD066}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Appveyor|Any CPU = Appveyor|Any CPU @@ -242,6 +252,62 @@ Global {6FB2730B-78B0-4AE5-9E48-E901D2E6DD54}.Release|x64.Build.0 = Release|Any CPU {6FB2730B-78B0-4AE5-9E48-E901D2E6DD54}.DirectX|x64.ActiveCfg = Debug|Any CPU {6FB2730B-78B0-4AE5-9E48-E901D2E6DD54}.DirectX|x64.Build.0 = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Appveyor|Any CPU.Build.0 = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Appveyor|x64.ActiveCfg = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Appveyor|x64.Build.0 = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Debug|x64.ActiveCfg = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Debug|x64.Build.0 = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Release|Any CPU.Build.0 = Release|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Release|x64.ActiveCfg = Release|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.Release|x64.Build.0 = Release|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.DirectX|x64.ActiveCfg = Debug|Any CPU + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E}.DirectX|x64.Build.0 = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Appveyor|Any CPU.Build.0 = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Appveyor|x64.ActiveCfg = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Appveyor|x64.Build.0 = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Debug|x64.Build.0 = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Release|Any CPU.Build.0 = Release|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Release|x64.ActiveCfg = Release|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.Release|x64.Build.0 = Release|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.DirectX|x64.ActiveCfg = Debug|Any CPU + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2}.DirectX|x64.Build.0 = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Appveyor|Any CPU.Build.0 = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Appveyor|x64.ActiveCfg = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Appveyor|x64.Build.0 = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Debug|x64.Build.0 = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Release|Any CPU.Build.0 = Release|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Release|x64.ActiveCfg = Release|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.Release|x64.Build.0 = Release|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.DirectX|x64.ActiveCfg = Debug|Any CPU + {C8511536-2B73-4C40-9F04-64389D50E6E8}.DirectX|x64.Build.0 = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Appveyor|Any CPU.ActiveCfg = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Appveyor|Any CPU.Build.0 = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Appveyor|x64.ActiveCfg = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Appveyor|x64.Build.0 = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Debug|x64.ActiveCfg = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Debug|x64.Build.0 = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Release|Any CPU.Build.0 = Release|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Release|x64.ActiveCfg = Release|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.Release|x64.Build.0 = Release|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.DirectX|x64.ActiveCfg = Debug|Any CPU + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066}.DirectX|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -255,6 +321,10 @@ Global {18E484B8-B625-45CD-92DD-52B9CA6A8838} = {56040BCF-D80D-4435-B420-BF8B29A5B4B1} {09378720-C7F2-4AE4-9111-B0D64F0D9496} = {66F9179F-DEB1-45B9-B067-D613A2857D5C} {A1B11EA5-36F6-45A3-B9CF-0DDB43DEACA9} = {5339DA92-BA8E-441B-AF71-27B75FEEC760} + {27DB8A4A-DE08-449E-AF1C-9DD64EEE485E} = {7993E92A-6DD1-4F64-A654-D3ED0ACF0C6E} + {801C2903-C3E5-4B8D-9D04-AF2B0719FBE2} = {7993E92A-6DD1-4F64-A654-D3ED0ACF0C6E} + {C8511536-2B73-4C40-9F04-64389D50E6E8} = {7993E92A-6DD1-4F64-A654-D3ED0ACF0C6E} + {75B8EC0E-D606-4886-BFF5-C5B70D2DD066} = {7993E92A-6DD1-4F64-A654-D3ED0ACF0C6E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {32B4D997-4F97-43FC-9636-1E35A79AA844} diff --git a/src/Alex/Entities/PlayerController.cs b/src/Alex/Entities/PlayerController.cs index c29eef7d3..93fa79c92 100644 --- a/src/Alex/Entities/PlayerController.cs +++ b/src/Alex/Entities/PlayerController.cs @@ -761,7 +761,9 @@ private void UpdateMouseInput(GameTime gt) { var e = MouseInputListener.GetCursorPosition(); - if (e.X < 10 || e.X > Graphics.Viewport.Width - 10 || e.Y < 10 + if (e.X < 10 + || e.X > Graphics.Viewport.Width - 10 + || e.Y < 10 || e.Y > Graphics.Viewport.Height - 10) { CenterCursor(); diff --git a/src/Alex/Gamestates/InGame/PlayingState.cs b/src/Alex/Gamestates/InGame/PlayingState.cs index 7794e77c4..9f0c47bf5 100644 --- a/src/Alex/Gamestates/InGame/PlayingState.cs +++ b/src/Alex/Gamestates/InGame/PlayingState.cs @@ -31,7 +31,7 @@ public class PlayingState : GuiGameStateBase public World World { get; private set; } - private WorldProvider WorldProvider { get; set; } + private WorldProvider WorldProvider { get; set; } private NetworkProvider NetworkProvider { get; set; } private readonly PlayingHud _playingHud; diff --git a/src/Alex/Utils/Assets/MCJavaAssetsUtils.cs b/src/Alex/Utils/Assets/MCJavaAssetsUtils.cs index 1d9bf2b02..a9a4f83ce 100644 --- a/src/Alex/Utils/Assets/MCJavaAssetsUtils.cs +++ b/src/Alex/Utils/Assets/MCJavaAssetsUtils.cs @@ -259,7 +259,8 @@ public async Task EnsureTargetReleaseAsync(string targetRelease, IProgre { foreach (var file in dir.EnumerateFiles("*", SearchOption.AllDirectories)) { - zip.CreateEntryFromFile(file.FullName, Path.GetRelativePath(dir.FullName, file.FullName)); + // Zip file entries MUST use '/' as directory separator + zip.CreateEntryFromFile(file.FullName, Path.GetRelativePath(dir.FullName, file.FullName).Replace(Path.DirectorySeparatorChar, '/')); } } diff --git a/src/Alex/Utils/KeysExtension.cs b/src/Alex/Utils/KeysExtension.cs deleted file mode 100644 index bc4a2e55d..000000000 --- a/src/Alex/Utils/KeysExtension.cs +++ /dev/null @@ -1,309 +0,0 @@ -using Microsoft.Xna.Framework.Input; - -namespace Alex.Utils -{ - public static class KeysExtension - { - public static bool IsSpecialKey(this Keys keyInput) - { - return false; - } - public static bool TryConvertKeyboardInput(this Keys keyInput, out char key) - { - switch (keyInput) - { - //Alphabet keys - case Keys.A: - key = 'a'; - - - return true; - - case Keys.B: - - key = 'b'; - - return true; - - case Keys.C: - - key = 'c'; - - - return true; - - case Keys.D: - - key = 'd'; - - - return true; - - case Keys.E: - - key = 'e'; - - - return true; - - case Keys.F: - - key = 'f'; - - - return true; - - case Keys.G: - - key = 'g'; - - - return true; - - case Keys.H: - - key = 'h'; - - - return true; - - case Keys.I: - - key = 'i'; - - - return true; - - case Keys.J: - - key = 'j'; - - - return true; - - case Keys.K: - - key = 'k'; - - - return true; - - case Keys.L: - - key = 'l'; - - - return true; - - case Keys.M: - - key = 'm'; - - - return true; - - case Keys.N: - - key = 'n'; - - - return true; - - case Keys.O: - - key = 'o'; - - - return true; - - case Keys.P: - key = 'p'; - - - return true; - - case Keys.Q: - key = 'q'; - - - return true; - - case Keys.R: - - key = 'r'; - - - return true; - - case Keys.S: - - key = 's'; - - - return true; - - case Keys.T: - - key = 't'; - - - return true; - - case Keys.U: - - key = 'u'; - - - return true; - - case Keys.V: - - key = 'v'; - - - return true; - - case Keys.W: - - key = 'w'; - - - return true; - - case Keys.X: - - key = 'x'; - - return true; - - case Keys.Y: - - key = 'y'; - - return true; - - case Keys.Z: - - key = 'z'; - - return true; - - //Decimal keys - case Keys.D0: - - key = '0'; - - return true; - - case Keys.D1: - key = '1'; - - - return true; - - case Keys.D2: - - key = '2'; - - return true; - - case Keys.D3: - - key = '3'; - - return true; - - case Keys.D4: - - key = '4'; - - return true; - - case Keys.D5: - - key = '5'; - - - return true; - - case Keys.D6: - - key = '6'; - - return true; - - case Keys.D7: - - key = '7'; - - - return true; - - case Keys.D8: - key = '8'; - - return true; - - case Keys.D9: - key = '9'; - - return true; - - //Decimal numpad keys - case Keys.NumPad0: - key = '0'; - - return true; - - case Keys.NumPad1: - key = '1'; - - return true; - - case Keys.NumPad2: - key = '2'; - - return true; - - case Keys.NumPad3: - key = '3'; - - return true; - - case Keys.NumPad4: - key = '4'; - - return true; - - case Keys.NumPad5: - key = '5'; - - return true; - - case Keys.NumPad6: - key = '6'; - - return true; - - case Keys.NumPad7: - key = '7'; - - return true; - - case Keys.NumPad8: - key = '8'; - - return true; - - case Keys.NumPad9: - key = '9'; - - return true; - } - - key = (char) 0; - - return false; - } - } -} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/IResourcePackReader.cs b/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/IResourcePackReader.cs new file mode 100644 index 000000000..87c281de1 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/IResourcePackReader.cs @@ -0,0 +1,9 @@ +using ResourcePackLib.Core.IO; + +namespace ResourcePackLib.Core; + +public interface IResourcePackReader +{ + Task CanLoadAsync(IDirectory dir); + Task LoadAsync(IDirectory dir); +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDef.cs b/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDef.cs new file mode 100644 index 000000000..d4411d81a --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDef.cs @@ -0,0 +1,10 @@ +namespace ResourcePackLib.Core; + +public interface ISoundDef +{ + List Sounds { get; } + + string Category { get; } + + string Subtitle { get; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDefSound.cs b/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDefSound.cs new file mode 100644 index 000000000..cf461ba40 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDefSound.cs @@ -0,0 +1,14 @@ +namespace ResourcePackLib.Core; + +public interface ISoundDefSound +{ + string Name { get; } + + bool Stream { get; } + + bool LoadOnLowMemory { get; } + + float Volume { get; } + + int Weight { get; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs b/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs new file mode 100644 index 000000000..76835a6f0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs @@ -0,0 +1,11 @@ +namespace ResourcePackLib.Core.Data; + +public enum CubeFace +{ + Up = 1, + Down = 4, + North = 5, + South = 2, + East = 0, + West = 3 +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Data/Cuboid.cs b/src/ResourcePackLib/ResourcePackLib.Core/Data/Cuboid.cs new file mode 100644 index 000000000..046083e8a --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Data/Cuboid.cs @@ -0,0 +1,100 @@ +using System.Numerics; + +namespace ResourcePackLib.Core.Data; + +public class CuboidFace +{ + public CubeFace Face { get; } + public short[] Indices { get; } + public short VertexOffset { get; } + public short IndexOffset { get; } + + public CuboidFace(CubeFace face, short[] indices, short vertexOffset, short indexOffset) + { + Face = face; + Indices = indices; + VertexOffset = vertexOffset; + IndexOffset = indexOffset; + } +} + +public class Cuboid +{ + public Vector3[] Vertices { get; } + public short[] Indices { get; } + public CuboidFace[] Faces { get; } + + public Cuboid(Vector3[] vertices, short[] indices) + { + Vertices = vertices; + Indices = indices; + } + + public Cuboid(Vector3 min, Vector3 max) + { + + var baseVertices = new Vector3[8]; // 8 distinct coordinates + + // Binary notation represents 0bXYZ ;) + baseVertices[0b000] = new Vector3(min.X, min.Y, min.Z); // 0 0 0 + baseVertices[0b001] = new Vector3(min.X, min.Y, max.Z); // 0 0 1 + baseVertices[0b010] = new Vector3(min.X, max.Y, min.Z); // 0 1 0 + baseVertices[0b011] = new Vector3(min.X, max.Y, max.Z); // 0 1 1 + baseVertices[0b100] = new Vector3(max.X, min.Y, min.Z); // 1 0 0 + baseVertices[0b101] = new Vector3(max.X, min.Y, max.Z); // 1 0 1 + baseVertices[0b110] = new Vector3(max.X, max.Y, min.Z); // 1 1 0 + baseVertices[0b111] = new Vector3(max.X, max.Y, max.Z); // 1 1 1 + + var baseFaceIndices = new short[][] + { + new short[] { 0b111, 0b110, 0b100, 0b101 }, + new short[] { 0b010, 0b110, 0b111, 0b011 }, + new short[] { 0b011, 0b111, 0b101, 0b001 }, + new short[] { 0b010, 0b011, 0b001, 0b000 }, + new short[] { 0b100, 0b000, 0b001, 0b101 }, + new short[] { 0b110, 0b010, 0b000, 0b100 }, + }; + + var vertices = new Vector3[24]; + short vertices_i = 0; + var indices = new short[36]; + short indices_i = 0; + + var faces = new CuboidFace[6]; + for(int i = 0; i < baseFaceIndices.Length; i++) + { + var b = vertices_i; + vertices[b + 0] = baseVertices[baseFaceIndices[i][0]]; + vertices[b + 1] = baseVertices[baseFaceIndices[i][1]]; + vertices[b + 2] = baseVertices[baseFaceIndices[i][2]]; + vertices[b + 3] = baseVertices[baseFaceIndices[i][3]]; + + var c = indices_i; + indices[c + 0] = (short)(b + 0); + indices[c + 1] = (short)(b + 1); + indices[c + 2] = (short)(b + 2); + indices[c + 3] = (short)(b + 0); + indices[c + 4] = (short)(b + 2); + indices[c + 5] = (short)(b + 3); + + var faceIndices = new short[6] + { + (short)(b + 0), + (short)(b + 1), + (short)(b + 2), + (short)(b + 0), + (short)(b + 2), + (short)(b + 3) + }; + + faces[i] = new CuboidFace((CubeFace)(i), faceIndices, b, c); + vertices_i += 4; + indices_i += 6; + } + + Vertices = vertices; + Indices = indices; + Faces = faces; + } + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Data/Models/BlockModel.cs b/src/ResourcePackLib/ResourcePackLib.Core/Data/Models/BlockModel.cs new file mode 100644 index 000000000..86470a3c0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Data/Models/BlockModel.cs @@ -0,0 +1,24 @@ +using System.Numerics; + +namespace ResourcePackLib.Core.Data.Models; + +public class BlockModel +{ + public IReadOnlyCollection Parts { get; } + + public BlockModel() + { + + } +} + +public class BlockModelPart : Cuboid +{ + public BlockModelPart(Vector3[] vertices, short[] indices) : base(vertices, indices) + { + } + + public BlockModelPart(Vector3 min, Vector3 max) : base(min, max) + { + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskDirectory.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskDirectory.cs new file mode 100644 index 000000000..0b875eb71 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskDirectory.cs @@ -0,0 +1,44 @@ +namespace ResourcePackLib.Core.IO; + +public class DiskDirectory : DiskFileSystemEntry, IDirectory +{ + private readonly DirectoryInfo _dirInfo; + + internal DiskDirectory(DirectoryInfo dirInfo) : base(dirInfo) + { + _dirInfo = dirInfo; + } + + public IFile GetFile(string name) + { + var fileInfo = new FileInfo(System.IO.Path.Join(_dirInfo.FullName, name)); + return new DiskFile(fileInfo); + } + + public IFile[] GetFiles(Predicate predicate) + { + throw new NotImplementedException(); + } + + public IDirectory[] GetDirectories(Predicate predicate) + { + throw new NotImplementedException(); + } + + public IFileSystemEntry GetEntry(string name) + { + throw new NotImplementedException(); + } + + public IFileSystemEntry[] GetEntries(Predicate predicate) + { + return _dirInfo.GetFileSystemInfos() + .Select(x => x switch + { + DirectoryInfo d => new DiskDirectory(d), + FileInfo f when string.Equals(f.Extension, "zip", StringComparison.OrdinalIgnoreCase) => new ZipDirectoryRoot(f), + FileInfo f => new DiskFile(f), + _ => throw new InvalidOperationException() + }).ToArray(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFile.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFile.cs new file mode 100644 index 000000000..23fda317b --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFile.cs @@ -0,0 +1,16 @@ +namespace ResourcePackLib.Core.IO; + +public class DiskFile : DiskFileSystemEntry, IFile +{ + internal DiskFile(FileInfo fileInfo) : base(fileInfo) + { + } + + public Stream OpenRead() + { + return File.OpenRead(this.FullName); + } + + public long Length { get; } + public string Extension { get; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFileSystemEntry.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFileSystemEntry.cs new file mode 100644 index 000000000..31b750cd2 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFileSystemEntry.cs @@ -0,0 +1,19 @@ +using System.IO.Enumeration; + +namespace ResourcePackLib.Core.IO; + +public class DiskFileSystemEntry : IFileSystemEntry +{ + private readonly FileSystemInfo _fsInfo; + + public string Path => _fsInfo.FullName[.._fsInfo.FullName.IndexOf(Name, StringComparison.Ordinal)]; + public string Name => _fsInfo.Name; + + public string FullName => System.IO.Path.Join(Path, Name); + protected DiskFileSystemEntry(FileSystemInfo fsInfo) + { + _fsInfo = fsInfo; + } + + public bool Exists() => _fsInfo.Exists; +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/IDirectory.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/IDirectory.cs new file mode 100644 index 000000000..5028b477a --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/IDirectory.cs @@ -0,0 +1,33 @@ +namespace ResourcePackLib.Core.IO; + +public interface IDirectory : IFileSystemEntry +{ + internal static readonly Predicate NoFilter = f => true; + + #region Files + + IFile GetFile(string name); + + IFile[] GetFiles() => GetFiles(IFile.NoFilter); + IFile[] GetFiles(Predicate predicate) => GetEntries(f => f is IFile ff && predicate(ff)).Cast().ToArray(); + + + #endregion Files + + #region Directories + + IDirectory[] GetDirectories() => GetDirectories(NoFilter); + IDirectory[] GetDirectories(Predicate predicate) => GetEntries(f => f is IDirectory d && predicate(d)).Cast().ToArray(); + + #endregion Directories + + #region Entries + + IFileSystemEntry this[string name] => GetEntry(name); + IFileSystemEntry GetEntry(string name); + + IFileSystemEntry[] GetEntries() => GetEntries(IFileSystemEntry.NoFilter); + IFileSystemEntry[] GetEntries(Predicate predicate); + + #endregion +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/IFile.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/IFile.cs new file mode 100644 index 000000000..00a004895 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/IFile.cs @@ -0,0 +1,14 @@ +namespace ResourcePackLib.Core.IO; + +public interface IFile : IFileSystemEntry +{ + internal static readonly Predicate NoFilter = f => true; + + Stream OpenRead(); + + long Length { get; } + + string Extension { get; } + + string FullName => System.IO.Path.Join(Path, string.IsNullOrEmpty(Extension) ? Name : $"{Name}.{Extension}"); +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/IFileSystemEntry.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/IFileSystemEntry.cs new file mode 100644 index 000000000..e2a56bf3e --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/IFileSystemEntry.cs @@ -0,0 +1,17 @@ +namespace ResourcePackLib.Core.IO; + +public interface IFileSystemEntry +{ + internal static readonly Predicate NoFilter = f => true; + + string Path { get; } + + string Name { get; } + + bool Exists(); + + public string FullName + { + get => System.IO.Path.Join(Path, Name); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectory.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectory.cs new file mode 100644 index 000000000..550fc238f --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectory.cs @@ -0,0 +1,31 @@ +namespace ResourcePackLib.Core.IO; + +public class ZipDirectory : ZipFileSystemEntry, IDirectory +{ + internal ZipDirectory(ZipDirectoryRoot root, string relativePath) : base(root, relativePath) + { + + } + + public IFile GetFile(string name) => new ZipFile(Root, name); + public IFileSystemEntry GetEntry(string name) => GetEntries(f => string.Equals(f.Name, name, StringComparison.OrdinalIgnoreCase)).SingleOrDefault() ?? new ZipFileSystemEntry(Root, name); + + public IFileSystemEntry[] GetEntries(Predicate predicate) + { + predicate += GetDirectoryPredicate(FullName); + + return Root.GetEntries(predicate); + } + + private static Predicate GetRecursiveDirectoryPredicate(string path) + { + path = path.EndsWith('/') ? path : $"{path}/"; + return (f => f.FullName.StartsWith(path, StringComparison.OrdinalIgnoreCase)); + } + + private static Predicate GetDirectoryPredicate(string path) + { + path = path.TrimEnd('/'); + return (f => f.Path.Equals(path, StringComparison.OrdinalIgnoreCase)); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectoryRoot.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectoryRoot.cs new file mode 100644 index 000000000..9c77efab4 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectoryRoot.cs @@ -0,0 +1,59 @@ +using System.IO.Compression; + +namespace ResourcePackLib.Core.IO; + +public class ZipDirectoryRoot : DiskFile +{ + private readonly FileInfo _zipFileInfo; + private List? _allEntries; + + public ZipDirectoryRoot(FileInfo fileInfo) : base(fileInfo) + { + } + + private bool EnsureEntriesLoaded() + { + if (!Exists()) return false; + LoadEntries(); + + return true; + } + + private void LoadEntries() + { + if(_allEntries != null) return; + + using var fs = OpenRead(); + using var zip = new ZipArchive(fs, ZipArchiveMode.Read, true); + _allEntries = zip.Entries.Select(e => + e.FullName.EndsWith('/') + ? new ZipDirectory(this, e.FullName) + : new ZipFile(this, e)) + .ToList(); + } + + internal ZipFileSystemEntry[] GetEntries(Predicate predicate) + { + if (!EnsureEntriesLoaded()) + throw new InvalidOperationException(); + + return _allEntries.Where(f => predicate(f)).ToArray(); + } + + internal bool EntryExists(string path) + { + if(!EnsureEntriesLoaded()) return false; + + if (path.EndsWith('/')) + { + return _allEntries.Any(f => f.FullName.StartsWith(path, StringComparison.OrdinalIgnoreCase)); + } + + return _allEntries.Any(f => string.Equals(f.FullName, path, StringComparison.OrdinalIgnoreCase)); + } + + public static explicit operator ZipDirectory(ZipDirectoryRoot root) + { + return new ZipDirectory(root, string.Empty); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFile.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFile.cs new file mode 100644 index 000000000..315537315 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFile.cs @@ -0,0 +1,24 @@ +using System.IO.Compression; + +namespace ResourcePackLib.Core.IO; + +public class ZipFile : ZipFileSystemEntry, IFile +{ + + public long Length { get; } + public string Extension { get; } + + internal ZipFile(ZipDirectoryRoot zipDirectoryRoot, string fullPath) : base(zipDirectoryRoot, fullPath) + { + + } + + internal ZipFile(ZipDirectoryRoot zipDirectoryRoot, ZipArchiveEntry entry) : this(zipDirectoryRoot, entry.FullName) + { + } + + public Stream OpenRead() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFileSystemEntry.cs b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFileSystemEntry.cs new file mode 100644 index 000000000..cebfe320d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFileSystemEntry.cs @@ -0,0 +1,36 @@ +using System.IO.Compression; + +namespace ResourcePackLib.Core.IO; + +public class ZipFileSystemEntry : IFileSystemEntry +{ + protected ZipDirectoryRoot Root { get; } + + public string Path { get; } + public string Name { get; } + + public string FullName + { + get => System.IO.Path.Join(Path, Name); + } + + protected internal ZipFileSystemEntry(ZipDirectoryRoot zipDirectoryRoot, string relativePath) + { + Root = zipDirectoryRoot; + + var i = relativePath.LastIndexOfAny(new[] { System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar }); + if (i <= 0) + { + Path = string.Empty; + Name = relativePath; + } + else + { + Path = relativePath[..i]; + Name = relativePath[i..]; + } + } + + + public bool Exists() => Root.EntryExists(FullName); +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector2ArrayJsonConverter.cs b/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector2ArrayJsonConverter.cs new file mode 100644 index 000000000..493264c68 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector2ArrayJsonConverter.cs @@ -0,0 +1,64 @@ +using System.Numerics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ResourcePackLib.Core.Json.Converters; + +public class Vector2ArrayJsonConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, Vector2? value, JsonSerializer serializer) + { + if (value.HasValue) + { + writer.WriteRawValue(JsonConvert.SerializeObject(new float[] + { + value.Value.X, value.Value.Y + }, Formatting.None)); + } + else + { + writer.WriteNull(); + } + /*serializer.Serialize(writer, new float[] + { + v.X, + v.Y, + v.Z + });*/ + } + public override Vector2? ReadJson(JsonReader reader, Type objectType, Vector2? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var obj = JToken.Load(reader); + + if (obj.Type == JTokenType.Array) + { + var arr = (JArray)obj; + if (arr.Count == 2) + { + var v3 = new Vector2(); + + if (arr[0].Type == JTokenType.Integer) + { + v3.X = arr[0].Value(); + } + else if (arr[0].Type == JTokenType.Float) + { + v3.X = arr[0].Value(); + } + + if (arr[1].Type == JTokenType.Integer) + { + v3.Y = arr[1].Value(); + } + else if (arr[1].Type == JTokenType.Float) + { + v3.Y = arr[1].Value(); + } + + return v3; + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector3ArrayJsonConverter.cs b/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector3ArrayJsonConverter.cs new file mode 100644 index 000000000..8b2bf4760 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector3ArrayJsonConverter.cs @@ -0,0 +1,73 @@ +using System.Numerics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ResourcePackLib.Core.Json.Converters; + +public class Vector3ArrayJsonConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, Vector3? value, JsonSerializer serializer) + { + if (value.HasValue) + { + writer.WriteRawValue(JsonConvert.SerializeObject(new float[] + { + value.Value.X, value.Value.Y, value.Value.Z + }, Formatting.None)); + } + else + { + writer.WriteNull(); + } + /*serializer.Serialize(writer, new float[] + { + v.X, + v.Y, + v.Z + });*/ + } + public override Vector3? ReadJson(JsonReader reader, Type objectType, Vector3? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var obj = JToken.Load(reader); + + if (obj.Type == JTokenType.Array) + { + var arr = (JArray)obj; + if (arr.Count == 3) + { + var v3 = new Vector3(); + + if (arr[0].Type == JTokenType.Integer) + { + v3.X = arr[0].Value(); + } + else if (arr[0].Type == JTokenType.Float) + { + v3.X = arr[0].Value(); + } + + if (arr[1].Type == JTokenType.Integer) + { + v3.Y = arr[1].Value(); + } + else if (arr[1].Type == JTokenType.Float) + { + v3.Y = arr[1].Value(); + } + + if (arr[2].Type == JTokenType.Integer) + { + v3.Z = arr[2].Value(); + } + else if (arr[2].Type == JTokenType.Float) + { + v3.Z = arr[2].Value(); + } + + return v3; + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector4ArrayJsonConverter.cs b/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector4ArrayJsonConverter.cs new file mode 100644 index 000000000..61e059a35 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector4ArrayJsonConverter.cs @@ -0,0 +1,82 @@ +using System.Numerics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ResourcePackLib.Core.Json.Converters; + +public class Vector4ArrayJsonConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, Vector4? value, JsonSerializer serializer) + { + if (value.HasValue) + { + writer.WriteRawValue(JsonConvert.SerializeObject(new float[] + { + value.Value.X, value.Value.Y, value.Value.Z, value.Value.W + }, Formatting.None)); + } + else + { + writer.WriteNull(); + } + /*serializer.Serialize(writer, new float[] + { + v.X, + v.Y, + v.Z + });*/ + } + public override Vector4? ReadJson(JsonReader reader, Type objectType, Vector4? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var obj = JToken.Load(reader); + + if (obj.Type == JTokenType.Array) + { + var arr = (JArray)obj; + if (arr.Count == 4) + { + var v3 = new Vector4(); + + if (arr[0].Type == JTokenType.Integer) + { + v3.X = arr[0].Value(); + } + else if (arr[0].Type == JTokenType.Float) + { + v3.X = arr[0].Value(); + } + + if (arr[1].Type == JTokenType.Integer) + { + v3.Y = arr[1].Value(); + } + else if (arr[1].Type == JTokenType.Float) + { + v3.Y = arr[1].Value(); + } + + if (arr[2].Type == JTokenType.Integer) + { + v3.Z = arr[2].Value(); + } + else if (arr[2].Type == JTokenType.Float) + { + v3.Z = arr[2].Value(); + } + + if (arr[3].Type == JTokenType.Integer) + { + v3.W = arr[3].Value(); + } + else if (arr[3].Type == JTokenType.Float) + { + v3.W = arr[3].Value(); + } + + return v3; + } + } + + return null; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs new file mode 100644 index 000000000..921cc0b0e --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs @@ -0,0 +1,47 @@ +using ResourcePackLib.Core.IO; +using ResourcePackLib.Core.Utils; + +namespace ResourcePackLib.Core; + +public class ResourcePackDescription +{ + public string Name { get; set; } + public string Description { get; set; } +} + +public class ResourcePackIndex +{ + public ResourceLocationCollection Music { get; set; } + + public ResourceLocationCollection Sounds { get; set; } + + public ResourceLocationCollection Textures { get; set; } + + public ResourceLocationCollection Models { get; set; } + + public ResourceLocationCollection Locales { get; set; } + + public ResourceLocationCollection Particles { get; set; } +} + +public class ResourcePack +{ + public ResourcePackIndex Index { get; } = new ResourcePackIndex(); + + public IDirectory Directory { get; } + + public ResourcePack(IDirectory directory) + { + Directory = directory; + } + + // public bool Contains(ResourceLocation resourceLocation) + // { + // + // } + // + // public bool TryGet(ResourceLocation resourceLocation, out T resource) + // { + // + // } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs new file mode 100644 index 000000000..217558578 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs @@ -0,0 +1,22 @@ +namespace ResourcePackLib.Core; + +public class ResourcePackFactory +{ + private List _readers = new List(); + + public ResourcePackFactory() + { + + } + + public void Register() + where TResourcePackReader : IResourcePackReader, new() + { + Register(new TResourcePackReader()); + } + + public void Register(IResourcePackReader reader) + { + _readers.Add(reader); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj new file mode 100644 index 000000000..49b54fa72 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj.DotSettings b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj.DotSettings new file mode 100644 index 000000000..98a937b6b --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Utils/NamedResourceLocationCollection.cs b/src/ResourcePackLib/ResourcePackLib.Core/Utils/NamedResourceLocationCollection.cs new file mode 100644 index 000000000..1a06e4f44 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Utils/NamedResourceLocationCollection.cs @@ -0,0 +1,6 @@ +namespace ResourcePackLib.Core.Utils; + +public class NamedResourceLocationCollection : Dictionary +{ + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocation.cs b/src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocation.cs new file mode 100644 index 000000000..7f9bd215e --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocation.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; + +namespace ResourcePackLib.Core.Utils; + +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class ResourceLocation : IEquatable +{ + private string DebuggerDisplay => ToString(); + + public const string DefaultNamespace = "minecraft"; + public string Namespace { get; } + public string Path { get; } + + public int Length => Namespace.Length + Path.Length; + + public bool IsReference { get; } + + public ResourceLocation(string key) + { + if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key)); + var s = key.Split(':', 1); + if (s.Length == 2) + { + Namespace = s[0]; + Path = s[1]; + } + else + { + Namespace = DefaultNamespace; + Path = s[0]; + } + + IsReference = Path.StartsWith("#"); + } + + public ResourceLocation(string @namespace, string path) + { + Namespace = @namespace; + Path = path; + } + + + public static implicit operator ResourceLocation(string input) + { + return new ResourceLocation(input); + } + + /// Returns a string that represents the current object. + /// A string that represents the current object. + public override string ToString() + { + return $"{Namespace}:{Path}"; + } + + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public bool Equals(ResourceLocation other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return GetHashCode() == other.GetHashCode(); + } + + + /// Determines whether the specified object is equal to the current object. + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + return obj.GetType() == this.GetType() && Equals((ResourceLocation)obj); + } + + /// Serves as the default hash function. + /// A hash code for the current object. + public override int GetHashCode() + { + return HashCode.Combine(Namespace, Path); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocationCollection.cs b/src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocationCollection.cs new file mode 100644 index 000000000..a47984acf --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocationCollection.cs @@ -0,0 +1,6 @@ +namespace ResourcePackLib.Core.Utils; + +public class ResourceLocationCollection : List +{ + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackImpl.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackImpl.cs new file mode 100644 index 000000000..0a7bd1829 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackImpl.cs @@ -0,0 +1,12 @@ +using ResourcePackLib.Core; +using ResourcePackLib.Core.IO; + +namespace ResourcePackLib.Loader.Bedrock; + +public class BedrockResourcePackImpl : ResourcePack +{ + public BedrockResourcePackImpl(IDirectory directory) : base(directory) + { + + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackReader.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackReader.cs new file mode 100644 index 000000000..8cddc3572 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackReader.cs @@ -0,0 +1,26 @@ +using ResourcePackLib.Core; +using ResourcePackLib.Core.IO; + +namespace ResourcePackLib.Loader.Bedrock; + +public class BedrockResourcePackReader : IResourcePackReader +{ + public BedrockResourcePackReader() + { + + } + + public async Task CanLoadAsync(IDirectory dir) + { + return dir["manifest.json"].Exists(); + } + + public async Task LoadAsync(IDirectory dir) + { + var pack = new BedrockResourcePackImpl(dir); + + + + return pack; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDef.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDef.cs new file mode 100644 index 000000000..85e133956 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDef.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockBlockDef +{ + [JsonProperty("sound", NullValueHandling = NullValueHandling.Ignore)] + public string Sound { get; set; } + + [JsonProperty("carried_textures", NullValueHandling = NullValueHandling.Ignore)] + public BedrockBlockDefTextures CarriedTextures { get; set; } + + [JsonProperty("textures", NullValueHandling = NullValueHandling.Ignore)] + public BedrockBlockDefTextures Textures { get; set; } + + [JsonProperty("isotropic", NullValueHandling = NullValueHandling.Ignore)] + public BedrockBlockDefIsotropic Isotropic { get; set; } + + [JsonProperty("brightness_gamma", NullValueHandling = NullValueHandling.Ignore)] + public double? BrightnessGamma { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefIsotropic.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefIsotropic.cs new file mode 100644 index 000000000..4fd782800 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefIsotropic.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +[JsonConverter(typeof(BedrockBlockDefIsotropicJsonConverter))] +public class BedrockBlockDefIsotropic +{ + [JsonProperty("up", NullValueHandling = NullValueHandling.Ignore)] + public bool Up { get; set; } + + [JsonProperty("down", NullValueHandling = NullValueHandling.Ignore)] + public bool Down { get; set; } + + public BedrockBlockDefIsotropic() + { + } + + public BedrockBlockDefIsotropic(bool both) : this(both, both) + { + } + + public BedrockBlockDefIsotropic(bool up, bool down) : this() + { + Up = up; + Down = down; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefTextures.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefTextures.cs new file mode 100644 index 000000000..70a7793e0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefTextures.cs @@ -0,0 +1,38 @@ +using Newtonsoft.Json; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +[JsonConverter(typeof(BedrockBlockDefTexturesJsonConverter))] +public class BedrockBlockDefTextures +{ + [JsonProperty("up", NullValueHandling = NullValueHandling.Ignore)] + public string Up { get; set; } + + [JsonProperty("down", NullValueHandling = NullValueHandling.Ignore)] + public string Down { get; set; } + + [JsonProperty("north", NullValueHandling = NullValueHandling.Ignore)] + public string North { get; set; } + + [JsonProperty("east", NullValueHandling = NullValueHandling.Ignore)] + public string East { get; set; } + + [JsonProperty("south", NullValueHandling = NullValueHandling.Ignore)] + public string South { get; set; } + + [JsonProperty("west", NullValueHandling = NullValueHandling.Ignore)] + public string West { get; set; } + + public BedrockBlockDefTextures() { } + public BedrockBlockDefTextures(string all) : this(all, all, all, all, all, all) { } + public BedrockBlockDefTextures(string up, string down, string side) : this(up, down, side, side, side, side) { } + public BedrockBlockDefTextures(string up, string down, string north, string east, string south, string west) : this() + { + Up = up; + Down = down; + North = north; + East = east; + South = south; + West = west; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifest.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifest.cs new file mode 100644 index 000000000..ac7254fb9 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifest.cs @@ -0,0 +1,12 @@ +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockPackManifest +{ + public int FormatVersion { get; set; } + + public BedrockPackManifestHeader Header { get; set; } + public BedrockPackManifestHeader Metadata { get; set; } + public BedrockPackManifestModule[] Modules { get; set; } + public BedrockPackManifestDependency[] Dependencies { get; set; } + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestDependency.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestDependency.cs new file mode 100644 index 000000000..6c3b631e7 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestDependency.cs @@ -0,0 +1,9 @@ +using System.Numerics; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockPackManifestDependency +{ + public Guid Uuid { get; set; } + public Vector3 Version { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestHeader.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestHeader.cs new file mode 100644 index 000000000..4a547bf56 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestHeader.cs @@ -0,0 +1,14 @@ +using System.Numerics; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockPackManifestHeader +{ + public string Name { get; set; } + public string Description { get; set; } + public Guid Uuid { get; set; } + public Vector3 Version { get; set; } + public Vector3 MinEngineVersion { get; set; } + public Vector3 BaseGameVersion { get; set; } + public bool LockTemplateOptions { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestMetadata.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestMetadata.cs new file mode 100644 index 000000000..0d243b153 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestMetadata.cs @@ -0,0 +1,8 @@ +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockPackManifestMetadata +{ + public string[] Authors { get; set; } + public string License { get; set; } + public string Url { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModule.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModule.cs new file mode 100644 index 000000000..e5926b049 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModule.cs @@ -0,0 +1,11 @@ +using System.Numerics; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockPackManifestModule +{ + public string Description { get; set; } + public BedrockPackManifestModuleType Type { get; set; } + public Guid Uuid { get; set; } + public Vector3 Version { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModuleType.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModuleType.cs new file mode 100644 index 000000000..8367d0d49 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModuleType.cs @@ -0,0 +1,11 @@ +namespace ResourcePackLib.Loader.Bedrock.Data; + +public enum BedrockPackManifestModuleType +{ + Resources, + Data, + ClientData, + Interface, + WorldTemplate, + Javascript +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockSoundDef.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockSoundDef.cs new file mode 100644 index 000000000..432db811d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockSoundDef.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; +using ResourcePackLib.Core; +using ResourcePackLib.Core.Utils; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockSoundDefSound : ISoundDefSound +{ + public string Name { get; } + public bool Stream { get; } + public bool LoadOnLowMemory { get; } + public float Volume { get; } + public int Weight { get; } +} + +public class BedrockSoundDef : ISoundDef +{ + public string Category { get; set; } + + [JsonProperty("__use_legacy_max_distance")] + public bool UseLegacyMaxDistance { get; set; } + + public List Sounds { get; set; } + + public string Subtitle { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefIsotropicJsonConverter.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefIsotropicJsonConverter.cs new file mode 100644 index 000000000..1bd29a7b6 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefIsotropicJsonConverter.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockBlockDefIsotropicJsonConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, BedrockBlockDefIsotropic? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override BedrockBlockDefIsotropic? ReadJson(JsonReader reader, Type objectType, BedrockBlockDefIsotropic? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.StartObject) + { + var strMap = serializer.Deserialize>(reader); + if (strMap == null) + throw new InvalidOperationException(); + + return new BedrockBlockDefIsotropic(strMap["up"], strMap["down"]); + } + + if (reader.TokenType == JsonToken.Boolean) + { + return new BedrockBlockDefIsotropic(serializer.Deserialize(reader)); + } + + throw new InvalidOperationException(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefTexturesJsonConverter.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefTexturesJsonConverter.cs new file mode 100644 index 000000000..87a6f330a --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefTexturesJsonConverter.cs @@ -0,0 +1,54 @@ +using Newtonsoft.Json; + +namespace ResourcePackLib.Loader.Bedrock.Data; + +public class BedrockBlockDefTexturesJsonConverter : JsonConverter +{ + public override void WriteJson(JsonWriter writer, BedrockBlockDefTextures? value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override BedrockBlockDefTextures? ReadJson(JsonReader reader, Type objectType, BedrockBlockDefTextures? existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.StartObject) + { + var strMap = serializer.Deserialize>(reader); + if (strMap == null) + throw new InvalidOperationException(); + + var t = new BedrockBlockDefTextures(); + if (strMap.ContainsKey("up")) + t.Up = strMap["up"]; + + if (strMap.ContainsKey("down")) + t.Down = strMap["down"]; + + if (strMap.ContainsKey("side")) + t.North = t.East = t.South = t.West = strMap["side"]; + + if (strMap.ContainsKey("north")) + t.Down = strMap["north"]; + if (strMap.ContainsKey("east")) + t.East = strMap["east"]; + if (strMap.ContainsKey("south")) + t.South = strMap["south"]; + if (strMap.ContainsKey("west")) + t.West = strMap["west"]; + + return t; + } + else if (reader.TokenType == JsonToken.String) + { + var strValue = serializer.Deserialize(reader); + if (strValue == null) + throw new InvalidOperationException(); + + return new BedrockBlockDefTextures(strValue); + } + else + { + throw new InvalidOperationException(); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/ResourcePackLib.Loader.Bedrock.csproj b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/ResourcePackLib.Loader.Bedrock.csproj new file mode 100644 index 000000000..e2ceb7b29 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/ResourcePackLib.Loader.Bedrock.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaBlockModelDef.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaBlockModelDef.cs new file mode 100644 index 000000000..871a179c7 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaBlockModelDef.cs @@ -0,0 +1,117 @@ +using System.Numerics; +using Newtonsoft.Json; +using ResourcePackLib.Core.Data; +using ResourcePackLib.Core.Utils; + +namespace ResourcePackLib.Loader.Java.Data; + +public class JavaUVDef +{ + public int X1 { get; set; } = 0; + public int Y1 { get; set; } = 0; + public int X2 { get; set; } = 16; + public int Y2 { get; set; } = 16; + + public JavaUVDef() + { + } + + public JavaUVDef(int x1, int y1, int x2, int y2) + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + } +} + +public class JavaBlockModelDef +{ + public ResourceLocation Parent { get; set; } + + public List Elements { get; set; } + + public NamedResourceLocationCollection Textures { get; set; } + + /// + /// Whether to use ambient occlusion (true - default), or not (false). + /// + public bool AmbientOcclusion { get; set; } = true; +} + +public class JavaBlockModelDefElementFaceCollection : Dictionary +{ + public JavaBlockModelDefElementFace Down + { + get => this[CubeFace.Down]; + set => this[CubeFace.Down] = value; + } + + public JavaBlockModelDefElementFace Up + { + get => this[CubeFace.Up]; + set => this[CubeFace.Up] = value; + } + + public JavaBlockModelDefElementFace North + { + get => this[CubeFace.North]; + set => this[CubeFace.North] = value; + } + + public JavaBlockModelDefElementFace South + { + get => this[CubeFace.South]; + set => this[CubeFace.South] = value; + } + + public JavaBlockModelDefElementFace East + { + get => this[CubeFace.East]; + set => this[CubeFace.East] = value; + } + + public JavaBlockModelDefElementFace West + { + get => this[CubeFace.West]; + set => this[CubeFace.West] = value; + } +} + +public class JavaBlockModelDefElementFace +{ + /// + /// Defines the area of the texture to use according to the scheme [x1, y1, x2, y2]. If unset, it defaults to values equal to xyz position of the element. The texture behavior will be inconsistent if UV extends below 0 or above 16. If the numbers of x1 and x2 are swapped (e.g. from 0, 0, 16, 16 to 16, 0, 0, 16), the texture will be flipped. UV is optional, and if not supplied it will automatically generate based on the element's position. + /// + public JavaUVDef Uv { get; set; } + + /// + /// Specifies the texture in form of the texture variable prepended with a #. + /// + public ResourceLocation Texture { get; set; } + + /// + /// Specifies whether a face does not need to be rendered when there is a block touching it in the specified position. The position can be: down, up, north, south, west, or east. It will also determine which side of the block to use the light level from for lighting the face, and if unset, defaults to the side. + /// + public CubeFace CullFace { get; set; } + + /// + /// Rotates the texture by the specified number of degrees. Can be 0, 90, 180, or 270. Defaults to 0. Rotation does not affect which part of the texture is used. Instead, it amounts to permutation of the selected texture vertexes (selected implicitly, or explicitly though uv). + /// + public int Rotation { get; set; } = 0; + + /// + /// Determines whether to tint the texture using a hardcoded tint index. The default is not using the tint, and any number causes it to use tint. Note that only certain blocks have a tint index, all others will be unaffected. + /// + public int? TintIndex { get; set; } = null; +} + +public class JavaBlockModelDefElement +{ + public Vector3 From { get; set; } + public Vector3 To { get; set; } + + public JavaBlockModelDefElementFaceCollection Faces { get; set; } + + [JsonProperty("__comment")] public string Comment { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaSoundDef.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaSoundDef.cs new file mode 100644 index 000000000..0a9dfe70a --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaSoundDef.cs @@ -0,0 +1,14 @@ +using ResourcePackLib.Core; +using ResourcePackLib.Core.Utils; + +namespace ResourcePackLib.Loader.Java.Data; + +public class JavaSoundDef : ISoundDef +{ + public List Sounds { get; set; } + + public string Subtitle { get; set; } + + // TODO: Not defined in json, must be extracted from key + public string Category { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/Shaders/ShaderDef.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/Shaders/ShaderDef.cs new file mode 100644 index 000000000..3035b1cd2 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/Shaders/ShaderDef.cs @@ -0,0 +1,33 @@ +namespace ResourcePackLib.Loader.Java.Data.Shaders; + +public class ShaderDefBlend +{ + public string Func { get; set; } + + public string SrcRgb { get; set; } + + public string DstRgb { get; set; } +} + +public class ShaderDefSampler +{ + public string Name { get; set; } +} + +public class ShaderDefUniform +{ + public string Name { get; set; } + public string Type { get; set; } + public int Count { get; set; } + public float[] Values { get; set; } +} + +public class ShaderDef +{ + public ShaderDefBlend Blend { get; set; } + public string Vertex { get; set; } + public string Fragment { get; set; } + public string[] Attributes { get; set; } + public ShaderDefSampler[] Samplers { get; set; } + public ShaderDefUniform[] Uniforms { get; set; } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackImpl.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackImpl.cs new file mode 100644 index 000000000..f3312f3e0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackImpl.cs @@ -0,0 +1,11 @@ +using ResourcePackLib.Core; +using ResourcePackLib.Core.IO; + +namespace ResourcePackLib.Loader.Java; + +public class JavaResourcePackImpl : ResourcePack +{ + public JavaResourcePackImpl(IDirectory directory) : base(directory) + { + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackReader.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackReader.cs new file mode 100644 index 000000000..5587c9e4e --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackReader.cs @@ -0,0 +1,17 @@ +using ResourcePackLib.Core; +using ResourcePackLib.Core.IO; + +namespace ResourcePackLib.Loader.Java; + +public class JavaResourcePackReader : IResourcePackReader +{ + public JavaResourcePackReader() + { + + } + + public async Task CanLoadAsync(IDirectory dir) + { + return dir["pack.mcmeta"].Exists(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj b/src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj new file mode 100644 index 000000000..83f58f26d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs new file mode 100644 index 000000000..905230f68 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs @@ -0,0 +1,27 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace ResourcePackLib.ModelExplorer.Abstractions +{ + public interface ICamera : IUpdateable + { + public Vector3 Up { get; } + public Vector3 Right { get; } + public Vector3 Forward { get; } + public Viewport Viewport { get; } + + public Vector3 Position { get; set; } + public Vector3 Rotation { get; set; } + + public Matrix View { get; } + public Matrix Projection { get; } + + public float NearDistance { get; } + public float FarDistance { get; } + + public void Draw(Action doDraw); + public void MoveRelative(Vector3 move); + + void LookAt(Vector3 target); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs new file mode 100644 index 000000000..1898241d2 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs @@ -0,0 +1,27 @@ +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Scenes; +using RocketUI; +using RocketUI.Input; + +namespace ResourcePackLib.ModelExplorer.Abstractions; + +public interface IGame : IDisposable +{ + Game Game { get; } + + GraphicsDeviceManager DeviceManager { get; } + + // include all the Properties/Methods that you'd want to use on your Game class below. + GameWindow Window { get; } + ICamera Camera { get; } + SceneManager SceneManager { get; } + InputManager InputManager { get; } + + IServiceProvider ServiceProvider { get; } + GuiManager GuiManager { get; } + + event EventHandler Exiting; + + void Run(); + void Exit(); +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/Worker.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/Worker.cs new file mode 100644 index 000000000..318b73862 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/Worker.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.Hosting; + +namespace ResourcePackLib.ModelExplorer.Abstractions +{ + public class Worker : IHostedService + { + + private readonly IGame _game; + private readonly IHostApplicationLifetime _appLifetime; + + public Worker(IGame game, IHostApplicationLifetime appLifetime) + { + _game = game; + _appLifetime = appLifetime; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _appLifetime.ApplicationStarted.Register(OnStarted); + _appLifetime.ApplicationStopping.Register(OnStopping); + _appLifetime.ApplicationStopped.Register(OnStopped); + + _game.Exiting += OnGameExiting; + + return Task.CompletedTask; + } + + private void OnGameExiting(object sender, System.EventArgs e) + { + StopAsync(new CancellationToken()); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _appLifetime.StopApplication(); + + return Task.CompletedTask; + } + + private void OnStarted() + { + _game.Run(); + } + + private void OnStopping() + { + } + + private void OnStopped() + { + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Attributes/ServiceAttribute.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Attributes/ServiceAttribute.cs new file mode 100644 index 000000000..6b85217d3 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Attributes/ServiceAttribute.cs @@ -0,0 +1,7 @@ +namespace ResourcePackLib.ModelExplorer.Attributes; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)] +public class ServiceAttribute : Attribute +{ + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraKeyboardController.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraKeyboardController.cs new file mode 100644 index 000000000..863ffa6da --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraKeyboardController.cs @@ -0,0 +1,74 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Attributes; +using RocketUI.Input; +using RocketUI.Input.Listeners; + +namespace ResourcePackLib.ModelExplorer.Components; + +public class CameraKeyboardController : GameComponent +{ + private const Keys RotateModifier = Keys.LeftAlt; + private const Keys FastModifier = Keys.LeftControl; + private const Keys SuperFastModifier = Keys.LeftShift; + + public float RotateSpeed { get; set; } = 45f; + public float MoveSpeed { get; set; } = 2f; + + [Service] protected InputManager InputManager { get; private set; } + [Service] protected ICamera Camera { get; private set; } + + private KeyboardInputListener _keyboardListener; + public CameraKeyboardController(IGame game) : base(game.Game) + { + + } + public override void Initialize() + { + base.Initialize(); + InputManager.GetOrAddPlayerManager(PlayerIndex.One).TryGetListener(out _keyboardListener); + } + + public override void Update(GameTime gameTime) + { + var rotateSpeed = RotateSpeed; + var moveSpeed = MoveSpeed; + + var keyboard = Keyboard.GetState(); + if (keyboard.IsKeyDown(FastModifier)) + { + moveSpeed *= 5; + rotateSpeed *= 5; + } + if (keyboard.IsKeyDown(SuperFastModifier)) + { + moveSpeed *= 10; + rotateSpeed *= 10; + } + + if (keyboard.IsKeyDown(RotateModifier)) + { + var rotateDiff = (float)((gameTime.ElapsedGameTime.TotalSeconds / 1f) * rotateSpeed); + if (_keyboardListener.IsAnyDown(Keys.NumPad8)) Camera.Rotation += (Vector3.UnitX * MathHelper.ToRadians(-rotateDiff)); + if (_keyboardListener.IsAnyDown(Keys.NumPad2)) Camera.Rotation += (Vector3.UnitX * MathHelper.ToRadians(rotateDiff)); + if (_keyboardListener.IsAnyDown(Keys.NumPad4)) Camera.Rotation += (Vector3.UnitY * MathHelper.ToRadians(-rotateDiff)); + if (_keyboardListener.IsAnyDown(Keys.NumPad6)) Camera.Rotation += (Vector3.UnitY * MathHelper.ToRadians(rotateDiff)); + } + else if (keyboard.IsKeyDown(Keys.Home)) + { + Camera.Position = Vector3.Zero; + Camera.Rotation = Vector3.Zero; + } + else + { + var moveDiff = Vector3.One * (float)((gameTime.ElapsedGameTime.TotalSeconds / 1f) * moveSpeed); + if (keyboard.IsKeyDown(Keys.W)) Camera.MoveRelative(Vector3.Forward * moveDiff); + if (keyboard.IsKeyDown(Keys.A)) Camera.MoveRelative(Vector3.Left * moveDiff); + if (keyboard.IsKeyDown(Keys.S)) Camera.MoveRelative(Vector3.Backward * moveDiff); + if (keyboard.IsKeyDown(Keys.D)) Camera.MoveRelative(Vector3.Right * moveDiff); + if (keyboard.IsKeyDown(Keys.Q)) Camera.MoveRelative(Vector3.Up * moveDiff); + if (keyboard.IsKeyDown(Keys.E)) Camera.MoveRelative(Vector3.Down * moveDiff); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs new file mode 100644 index 000000000..5c8d5ad86 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs @@ -0,0 +1,62 @@ +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Attributes; +using RocketUI.Input; +using RocketUI.Input.Listeners; + +namespace ResourcePackLib.ModelExplorer.Components; + +public class CameraMouseController : GameComponent +{ + + [Service] protected InputManager InputManager { get; private set; } + [Service] protected ICamera Camera { get; private set; } + + public bool InvertX { get; set; } + public bool InvertY { get; set; } + public double Sensitivity { get; set; } = 60d; + + private Vector3 _previous; + private MouseInputListener _cursorInputListener; + + public CameraMouseController(IGame game) : base(game.Game) + { + + } + + public override void Initialize() + { + base.Initialize(); + InputManager.GetOrAddPlayerManager(PlayerIndex.One).TryGetListener(out _cursorInputListener); + } + + public override void Update(GameTime gameTime) + { + if (!_cursorInputListener.IsAnyDown(MouseButton.Right)) + { + return; + } + var cursor = _cursorInputListener.GetCursorRay(); + var p = cursor.Position; + + if (_cursorInputListener.IsAnyBeginPress(MouseButton.Right)) + { + _previous = p; + } + + var c = new Vector3(Camera.Viewport.Bounds.Center.ToVector2(), 0f); + + var delta = p - _previous; + if(delta.LengthSquared() < 10f) return; + + delta *= (float)gameTime.ElapsedGameTime.TotalSeconds; + + var lookDelta = delta * (float)Sensitivity; + + //Camera.LookAt(Vector3.Zero); + Camera.Rotation -= new Vector3(lookDelta.X, lookDelta.Y, 0f); + //_rotation.Normalize(); + + _previous = p; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Configuration/GameOptions.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Configuration/GameOptions.cs new file mode 100644 index 000000000..96144affe --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Configuration/GameOptions.cs @@ -0,0 +1,6 @@ +namespace ResourcePackLib.ModelExplorer.Configuration; + +public class GameOptions +{ + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb new file mode 100644 index 000000000..9ff60c413 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb @@ -0,0 +1,121 @@ + +#----------------------------- Global Properties ----------------------------# + +/outputDir:bin +/intermediateDir:obj +/platform:Windows +/config: +/profile:Reach +/compress:False + +#-------------------------------- References --------------------------------# + + +#---------------------------------- Content ---------------------------------# + +#begin blocks/face_down.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:blocks/face_down.png + +#begin blocks/face_east.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:blocks/face_east.png + +#begin blocks/face_north.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:blocks/face_north.png + +#begin blocks/face_south.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:blocks/face_south.png + +#begin blocks/face_up.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:blocks/face_up.png + +#begin blocks/face_west.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:blocks/face_west.png + +#begin Fonts/Default.spritefont +/importer:FontDescriptionImporter +/processor:FontDescriptionProcessor +/processorParam:PremultiplyAlpha=True +/processorParam:TextureFormat=Compressed +/build:Fonts/Default.spritefont + +#begin Fonts/Kenney Future Square.ttf +/copy:Fonts/Kenney Future Square.ttf + +#begin Gui/Gui.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Gui/Gui.png + +#begin Gui/ScrollBar.png +/importer:TextureImporter +/processor:TextureProcessor +/processorParam:ColorKeyColor=255,0,255,255 +/processorParam:ColorKeyEnabled=True +/processorParam:GenerateMipmaps=False +/processorParam:PremultiplyAlpha=True +/processorParam:ResizeToPowerOfTwo=False +/processorParam:MakeSquare=False +/processorParam:TextureFormat=Color +/build:Gui/ScrollBar.png + diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Default.spritefont b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Default.spritefont new file mode 100644 index 000000000..162d2ee95 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Default.spritefont @@ -0,0 +1,60 @@ + + + + + + + Kenney Future Square.ttf + + + 10 + + + 0 + + + true + + + + + + + + + + + + ~ + + + + diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future Square.ttf b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future Square.ttf new file mode 100644 index 000000000..488f29ed0 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future Square.ttf differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future.ttf b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future.ttf new file mode 100644 index 000000000..1dbb2dd5f Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future.ttf differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/Gui.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/Gui.png new file mode 100644 index 000000000..fcdb8b787 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/Gui.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/ScrollBar.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/ScrollBar.png new file mode 100644 index 000000000..602dc722d Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/ScrollBar.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_down.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_down.png new file mode 100644 index 000000000..95518af49 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_down.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_east.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_east.png new file mode 100644 index 000000000..2c31c1040 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_east.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_north.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_north.png new file mode 100644 index 000000000..d06d8dfaa Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_north.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_south.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_south.png new file mode 100644 index 000000000..62f1a27fb Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_south.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_up.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_up.png new file mode 100644 index 000000000..909662e24 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_up.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_west.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_west.png new file mode 100644 index 000000000..52784cb4d Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_west.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs new file mode 100644 index 000000000..f2df54e91 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs @@ -0,0 +1,49 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Controls; + +public class AutoUpdatingTextInput : TextInput +{ + private readonly Func _getFn; + private readonly Action _setFn; + + public bool UpdateOnlyWhenNotFocused { get; set; } = true; + public TimeSpan UpdateFrequency { get; set; } = TimeSpan.FromSeconds(0.5); + + private TimeSpan _lastUpdate = TimeSpan.Zero; + + public AutoUpdatingTextInput(Func getFn, Action setFn = null) + { + _getFn = getFn; + _setFn = setFn; + } + + protected override void OnUpdate(GameTime gameTime) + { + base.OnUpdate(gameTime); + + if (!Focused || !UpdateOnlyWhenNotFocused) + { + if (gameTime.TotalGameTime - _lastUpdate >= UpdateFrequency) + { + Value = _getFn(); + _lastUpdate = gameTime.TotalGameTime; + + } + } + } + + protected override bool OnKeyInput(char character, Keys key) + { + if (key == Keys.Enter || key == Keys.Tab) + { + _setFn?.Invoke(TextBuilder.Text); + GuiManager.FocusManager.FocusedElement = null; + return true; + } + + return base.OnKeyInput(character, key); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs new file mode 100644 index 000000000..568d7b248 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs @@ -0,0 +1,86 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Components; +using ResourcePackLib.ModelExplorer.Utilities; +using RocketUI; +using Vector3 = System.Numerics.Vector3; + +namespace ResourcePackLib.ModelExplorer.Controls; + +public class CameraLocationControl : RocketControl +{ + private AutoUpdatingTextInput _posX; + private AutoUpdatingTextInput _posY; + private AutoUpdatingTextInput _posZ; + private AutoUpdatingTextInput _movementSpeed; + private AutoUpdatingTextInput _rotationSpeed; + + private ICamera _camera; + private CameraKeyboardController _cameraController; + + public CameraLocationControl() + { + var stack = new MultiStackContainer() + { + Anchor = Alignment.Fill, + ChildAnchor = Alignment.Fill, + Orientation = Orientation.Vertical + }; + + + stack.AddRow( + _posX = new AutoUpdatingTextInput(() => _camera?.Position.X.ToString("F2"), v => + { + if (_camera != null && float.TryParse(v, out var f)) + _camera.Position = new Vector3(f, _camera.Position.Y, _camera.Position.Z); + }), + _posY = new AutoUpdatingTextInput(() => _camera?.Position.Y.ToString("F2"), v => + { + if (_camera != null && float.TryParse(v, out var f)) + _camera.Position = new Vector3(_camera.Position.X, f, _camera.Position.Z); + }), + _posZ = new AutoUpdatingTextInput(() => _camera?.Position.Z.ToString("F2"), v => + { + if (_camera != null && float.TryParse(v, out var f)) + _camera.Position = new Vector3(_camera.Position.X, _camera.Position.Y, f); + }) + ); + stack.AddRow( + _movementSpeed = new AutoUpdatingTextInput( + () => _cameraController?.MoveSpeed.ToString("F2"), + (v) => + { + if (_cameraController != null && float.TryParse(v, out var f)) + { + _cameraController.MoveSpeed = f; + } + }) + ); + stack.AddRow( + _rotationSpeed = new AutoUpdatingTextInput( + () => _cameraController?.RotateSpeed.ToString("F2"), + (v) => + { + if (_cameraController != null && float.TryParse(v, out var f)) + { + _cameraController.RotateSpeed = f; + } + }) + ); + AddChild(stack); + } + + protected override void OnInit(IGuiRenderer renderer) + { + base.OnInit(renderer); + var services = ((IGame)GuiManager.Game).ServiceProvider; + _camera = services.GetRequiredService(); + _cameraController = services.GetOrCreateInstance(); + } + + protected override void OnUpdate(GameTime gameTime) + { + base.OnUpdate(gameTime); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/AxisEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/AxisEntity.cs new file mode 100644 index 000000000..63bf2e95f --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/AxisEntity.cs @@ -0,0 +1,42 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Entities; + +public class AxisEntity : DrawableGameComponent +{ + public AxisEntity(IGame game) : base(game.Game) + { + DrawOrder = 1; + } + + private const float Distance = 10000; + private VertexPositionColor[] Verticies; + + protected override void LoadContent() + { + base.LoadContent(); + Verticies = new VertexPositionColor[6]; + + var i = 0; + Verticies[i++] = new VertexPositionColor(-Vector3.UnitX * Distance, Color.Red); + Verticies[i++] = new VertexPositionColor(Vector3.UnitX * Distance, Color.Red); + + Verticies[i++] = new VertexPositionColor(-Vector3.UnitY * Distance, Color.Green); + Verticies[i++] = new VertexPositionColor(Vector3.UnitY * Distance, Color.Green); + + Verticies[i++] = new VertexPositionColor(-Vector3.UnitZ * Distance, Color.Blue); + Verticies[i++] = new VertexPositionColor(Vector3.UnitZ * Distance, Color.Blue); + } + + public override void Draw(GameTime gameTime) + { + using (GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default, RasterizerState.CullNone)) + { + GraphicsDevice.VertexSamplerStates[0] = SamplerState.PointWrap; + Game.GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineList, Verticies, 0, 3); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/CubeEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/CubeEntity.cs new file mode 100644 index 000000000..f64841760 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/CubeEntity.cs @@ -0,0 +1,114 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ResourcePackLib.Core.Data; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; +using Vector3 = System.Numerics.Vector3; + +namespace ResourcePackLib.ModelExplorer.Entities; + +public class CubeEntity : DrawableEntity +{ + public CubeEntity(IGame game) : base(game) + { + } + + private readonly List _disposables = new List(); + protected override void LoadContent() + { + base.LoadContent(); + + var north = Game.Content.Load("blocks/face_north"); _disposables.Add(north); + var east = Game.Content.Load("blocks/face_east");_disposables.Add(east); + var south = Game.Content.Load("blocks/face_south");_disposables.Add(south); + var west = Game.Content.Load("blocks/face_west");_disposables.Add(west); + var up = Game.Content.Load("blocks/face_up");_disposables.Add(up); + var down = Game.Content.Load("blocks/face_down");_disposables.Add(down); + var textures = new[] + { + east, + up, + south, + west, + down, + north + }; + + var cuboid = new Cuboid(Vector3.Zero, Vector3.One); + + var vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionTexture.VertexDeclaration, cuboid.Vertices.Length, BufferUsage.None);_disposables.Add(vertexBuffer); + var indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, cuboid.Indices.Length, BufferUsage.None);_disposables.Add(indexBuffer); + + var vertexPositionTextures = cuboid.Faces.SelectMany(f => + { + return new[] + { + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 0], new Vector2(0, 0)), + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 1],new Vector2(1, 0)), + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 2], new Vector2(1, 1)), + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 3], new Vector2(0, 1)), + }; + }).ToArray(); + vertexBuffer.SetData(vertexPositionTextures); + indexBuffer.SetData(cuboid.Indices); + + var meshes = new List(); + var bones = new List(); + var parts = new List(); + + foreach(var face in cuboid.Faces) + { + var meshpart = new ModelMeshPart() + { + VertexBuffer = vertexBuffer, + NumVertices = 4, + IndexBuffer = indexBuffer, + VertexOffset = 0, + + PrimitiveCount = 2, + StartIndex = face.IndexOffset, + }; + parts.Add(meshpart); + } + + var bone = new ModelBone(); + bone.Transform = Matrix.Identity; + bone.ModelTransform = Matrix.Identity; + var modelmesh = new ModelMesh(GraphicsDevice, parts); + + modelmesh.ParentBone = bone; + meshes.Add(modelmesh); + + bone.AddMesh(modelmesh); + bones.Add(bone); + var model = new Model(GraphicsDevice, bones, meshes) + { + Root = bone + }; + + for (int i = 0; i < 6; i++) + { + var effect = new BasicEffect(GraphicsDevice) + { + Texture = textures[i], + TextureEnabled = true, + LightingEnabled = true, + VertexColorEnabled = true, + }; + effect.EnableDefaultLighting(); + modelmesh.MeshParts[i].Effect = effect; + } + + Model = model; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + foreach (var d in _disposables.ToArray()) + { + d.Dispose(); + _disposables.Remove(d); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/DrawableEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/DrawableEntity.cs new file mode 100644 index 000000000..c30c36b3d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/DrawableEntity.cs @@ -0,0 +1,119 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Utilities; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Entities +{ + public class DrawableEntity : Entity, IDrawable + { + private bool _disposed; + private int _drawOrder; + private bool _visible = true; + + /// + /// Get the that this uses for drawing. + /// + public GraphicsDevice GraphicsDevice => this.Game.GraphicsDevice; + + public int DrawOrder + { + get => this._drawOrder; + set + { + if (this._drawOrder == value) + return; + this._drawOrder = value; + this.OnDrawOrderChanged((object) this, EventArgs.Empty); + } + } + + public bool Visible + { + get => this._visible; + set + { + if (this._visible == value) + return; + this._visible = value; + this.OnVisibleChanged((object) this, EventArgs.Empty); + } + } + + /// + public event EventHandler DrawOrderChanged; + + /// + public event EventHandler VisibleChanged; + + protected Model Model { get; set; } + + public DrawableEntity(IGame game) : base(game) + { + } + + public override void Initialize() + { + if (Initialized) + return; + base.Initialize(); + this.LoadContent(); + } + + protected override void Dispose(bool disposing) + { + if (this._disposed) + return; + this._disposed = true; + this.UnloadContent(); + } + + /// Load graphical resources needed by this component. + protected virtual void LoadContent() + { + } + + /// Unload graphical resources needed by this component. + protected virtual void UnloadContent() + { + } + + /// Draw this component. + /// The time elapsed since the last call to . + public virtual void Draw(GameTime gameTime) + { + if (!Visible) return; + + DrawModel(Model); + } + + protected void DrawModel(Model model) + { + if (model != null) + { + using (GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default, RasterizerState.CullNone)) + { + var camera = ((IGame) Game).Camera; + model.Draw(Transform.World, camera.View, camera.Projection); + } + } + } + + /// + /// Called when changed. + /// + /// This . + /// Arguments to the event. + protected virtual void OnVisibleChanged(object sender, EventArgs args) => + EventHelpers.Raise(sender, this.VisibleChanged, args); + + /// + /// Called when changed. + /// + /// This . + /// Arguments to the event. + protected virtual void OnDrawOrderChanged(object sender, EventArgs args) => + EventHelpers.Raise(sender, this.DrawOrderChanged, args); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs new file mode 100644 index 000000000..3596cd996 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs @@ -0,0 +1,79 @@ +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Entities +{ + public class Entity : GameComponent, ITransformable + { + public BoundingBox BoundingBox { get; protected set; } + public Vector3 BoundingBoxSize { get; protected set; } = Vector3.One; + public Vector3 BoundingBoxOrigin { get; protected set; } = Vector3.One / 2f; + + private Vector3 _velocity = Vector3.Zero; + public virtual Vector3 Velocity + { + get => _velocity; + set => _velocity = value; + } + public Transform3D Transform { get; } = new Transform3D(); + public Vector3 Scale + { + get => Transform.Scale; + set => Transform.Scale = value; + } + + public Vector3 Position + { + get => Transform.Position; + set => Transform.Position = value; + } + + public Vector3 Rotation + { + get => Transform.Rotation; + set => Transform.Rotation = value; + } + + public Matrix World + { + get => Transform.World; + } + + public bool Initialized => _initialized; + + + public Entity(IGame game) : base(game.Game) + { + Position = Vector3.Zero; + // Scale = Vector3.One / 2f; + Transform.Changed += (sender, args) => OnPositionChanged(); + } + + public override void Initialize() + { + base.Initialize(); + _initialized = true; + } + + protected virtual void OnPositionChanged() + { + var wrld = Matrix.Identity + * Matrix.CreateTranslation(-BoundingBoxOrigin) + * Matrix.CreateScale(BoundingBoxSize) + * World; + var min = Vector3.Transform(Vector3.Zero, wrld); + var max = Vector3.Transform(Vector3.One, wrld); + BoundingBox = new BoundingBox(min, max); + } + + private bool _initialized; + + public override void Update(GameTime gameTime) + { + if(!Enabled) return; + + base.Update(gameTime); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs new file mode 100644 index 000000000..e3a4abe25 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs @@ -0,0 +1,199 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Entities; + +public class GridEntity : DrawableEntity +{ + public int Steps + { + get => _steps; + set + { + _steps = value; + _dirty = true; + } + } + public int SubDivisions + { + get => _subDivisions; + set + { + _subDivisions = value; + _dirty = true; + } + } + public float Spacing + { + get => _spacing; + set + { + _spacing = value; + _dirty = true; + } + } + public Color LineColor + { + get => _lineColor; + set + { + _lineColor = value; + _dirty = true; + } + } + public Color SubLineColor + { + get => _subLineColor; + set + { + _subLineColor = value; + _dirty = true; + } + } + public Vector3 XAxis + { + get => _xAxis; + set + { + _xAxis = value; + _dirty = true; + } + } + public Vector3 YAxis + { + get => _yAxis; + set + { + _yAxis = value; + _dirty = true; + } + } + + private VertexPositionColor[] _verticies; + private VertexBuffer _vertexBuffer; + private IndexBuffer _indexBuffer; + private short[] _indicies; + private bool _dirty = true; + private Vector3 _yAxis = Vector3.UnitZ; + private Vector3 _xAxis = Vector3.UnitX; + private Color _lineColor = Color.LightGray * 0.85f; + private Color _subLineColor = Color.LightGray * 0.15f; + private float _spacing = 1.0f; + private int _subDivisions = 1; + private int _steps = 25; + + private BasicEffect _effect; + + public GridEntity(IGame game) : base(game) + { + } + + protected override void LoadContent() + { + base.LoadContent(); + + if (_effect == null) + _effect = new BasicEffect(GraphicsDevice) + { + VertexColorEnabled = true, + World = Matrix.Identity + }; + + CalculateVertices(); + } + + private void InitBuffers(short arraySize) + { + _verticies = new VertexPositionColor[arraySize]; + _vertexBuffer?.Dispose(); + _indexBuffer?.Dispose(); + _vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColor.VertexDeclaration, arraySize, BufferUsage.WriteOnly); + _indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, arraySize, BufferUsage.WriteOnly); + _indicies = new short[arraySize]; + } + + private void CalculateVertices() + { + var subSteps = SubDivisions; + var steps = Steps * subSteps; + var spacing = Spacing / (float)subSteps; + var halfSteps = (int)Math.Ceiling(steps / 2f); + var vSteps = ((halfSteps * 2f) + 1f) * 2f; + var arraySize = (short)(vSteps * 2); + + if (_vertexBuffer == null || arraySize != _vertexBuffer.VertexCount) + InitBuffers(arraySize); + + var minX = -halfSteps * XAxis * spacing; + var maxX = halfSteps * XAxis * spacing; + + var minY = -halfSteps * YAxis * spacing; + var maxY = halfSteps * YAxis * spacing; + + int i = 0; + for (int x = -halfSteps; x <= halfSteps; x++) + { + var lineColor = ((x % subSteps) == 0) ? LineColor : SubLineColor; + var minPos = (x * XAxis * spacing) + minY; + var maxPos = (x * XAxis * spacing) + maxY; + _verticies[i] = new VertexPositionColor(minPos, lineColor); + _indicies[i] = (short)i; + i++; + _verticies[i] = new VertexPositionColor(maxPos, lineColor); + _indicies[i] = (short)i; + i++; + } + + for (int y = -halfSteps; y <= halfSteps; y++) + { + var lineColor = ((y % subSteps) == 0) ? LineColor : SubLineColor; + var minPos = (y * YAxis * spacing) + minX; + var maxPos = (y * YAxis * spacing) + maxX; + _verticies[i] = new VertexPositionColor(minPos, lineColor); + _indicies[i] = (short)i; + i++; + _verticies[i] = new VertexPositionColor(maxPos, lineColor); + _indicies[i] = (short)i; + i++; + } + + _vertexBuffer.SetData(_verticies); + _indexBuffer.SetData(_indicies); + } + public override void Update(GameTime gameTime) + { + if (_dirty) + { + _dirty = false; + CalculateVertices(); + } + } + + public override void Draw(GameTime gameTime) + { + using (GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default, RasterizerState.CullNone)) + { + var camera = ((IGame)Game).Camera; + _effect.View = camera.View; + _effect.Projection = camera.Projection; + + GraphicsDevice.SetVertexBuffer(_vertexBuffer); + GraphicsDevice.Indices = _indexBuffer; + + foreach (EffectPass pass in _effect.CurrentTechnique.Passes) + { + pass.Apply(); + GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, _indicies.Length / 2); + } + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + _vertexBuffer?.Dispose(); + _indexBuffer?.Dispose(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCBlockModelEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCBlockModelEntity.cs new file mode 100644 index 000000000..f0cc51e1c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCBlockModelEntity.cs @@ -0,0 +1,114 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ResourcePackLib.Core.Data; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; +using Vector3 = System.Numerics.Vector3; + +namespace ResourcePackLib.ModelExplorer.Entities; + +public class MCBlockModelEntity : DrawableEntity +{ + public MCBlockModelEntity(IGame game) : base(game) + { + } + + private readonly List _disposables = new List(); + protected override void LoadContent() + { + base.LoadContent(); + + var north = Game.Content.Load("blocks/face_north"); _disposables.Add(north); + var east = Game.Content.Load("blocks/face_east");_disposables.Add(east); + var south = Game.Content.Load("blocks/face_south");_disposables.Add(south); + var west = Game.Content.Load("blocks/face_west");_disposables.Add(west); + var up = Game.Content.Load("blocks/face_up");_disposables.Add(up); + var down = Game.Content.Load("blocks/face_down");_disposables.Add(down); + var textures = new[] + { + east, + up, + south, + west, + down, + north + }; + + var cuboid = new Cuboid(Vector3.Zero, Vector3.One); + + var vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionTexture.VertexDeclaration, cuboid.Vertices.Length, BufferUsage.None);_disposables.Add(vertexBuffer); + var indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, cuboid.Indices.Length, BufferUsage.None);_disposables.Add(indexBuffer); + + var vertexPositionTextures = cuboid.Faces.SelectMany(f => + { + return new[] + { + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 0], new Vector2(0, 0)), + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 1],new Vector2(1, 0)), + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 2], new Vector2(1, 1)), + new VertexPositionTexture(cuboid.Vertices[f.VertexOffset + 3], new Vector2(0, 1)), + }; + }).ToArray(); + vertexBuffer.SetData(vertexPositionTextures); + indexBuffer.SetData(cuboid.Indices); + + var meshes = new List(); + var bones = new List(); + var parts = new List(); + + foreach(var face in cuboid.Faces) + { + var meshpart = new ModelMeshPart() + { + VertexBuffer = vertexBuffer, + NumVertices = 4, + IndexBuffer = indexBuffer, + VertexOffset = 0, + + PrimitiveCount = 2, + StartIndex = face.IndexOffset, + }; + parts.Add(meshpart); + } + + var bone = new ModelBone(); + bone.Transform = Matrix.Identity; + bone.ModelTransform = Matrix.Identity; + var modelmesh = new ModelMesh(GraphicsDevice, parts); + + modelmesh.ParentBone = bone; + meshes.Add(modelmesh); + + bone.AddMesh(modelmesh); + bones.Add(bone); + var model = new Model(GraphicsDevice, bones, meshes) + { + Root = bone + }; + + for (int i = 0; i < 6; i++) + { + var effect = new BasicEffect(GraphicsDevice) + { + Texture = textures[i], + TextureEnabled = true, + LightingEnabled = true, + VertexColorEnabled = true, + }; + effect.EnableDefaultLighting(); + modelmesh.MeshParts[i].Effect = effect; + } + + Model = model; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + foreach (var d in _disposables.ToArray()) + { + d.Dispose(); + _disposables.Remove(d); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs new file mode 100644 index 000000000..56472a72d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs @@ -0,0 +1,280 @@ +using System.Text; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NLog; +using ResourcePackLib.Core.Data; +using ResourcePackLib.Core.Json.Converters; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Geometry; +using ResourcePackLib.ModelExplorer.Utilities.Extensions; +using RocketUI; +using Vector2 = System.Numerics.Vector2; +using Vector3 = System.Numerics.Vector3; + +namespace ResourcePackLib.ModelExplorer.Entities; + +public class MCEntityGeometryBoneCube +{ + [JsonConverter(typeof(Vector3ArrayJsonConverter))] + public Vector3 Origin { get; set; } + + [JsonConverter(typeof(Vector3ArrayJsonConverter))] + public Vector3 Size { get; set; } + + [JsonConverter(typeof(Vector2ArrayJsonConverter))] + public Vector2 UV { get; set; } +} + +public class MCEntityGeometryBone +{ + public string Name { get; set; } + public string Parent { get; set; } + + [JsonConverter(typeof(Vector3ArrayJsonConverter))] + public Vector3? Pivot { get; set; } + + [JsonConverter(typeof(Vector3ArrayJsonConverter))] + public Vector3? BindPoseRotation { get; set; } + + public MCEntityGeometryBoneCube[] Cubes { get; set; } +} + +public class MCEntityGeometry +{ + public int TextureWidth { get; set; } + public int TextureHeight { get; set; } + public MCEntityGeometryBone[] Bones { get; set; } +} + +public class MCEntityGeometryCollection +{ + [JsonProperty("format_version")] public string FormatVersion { get; set; } + + [JsonExtensionData] public Dictionary Geometries { get; set; } = new Dictionary(); + + public MCEntityGeometry GetGeometry(string name) + { + if (Geometries.TryGetValue(name, out var gToken)) + { + var jsonSerializer = JsonSerializer.CreateDefault(); + + using (var reader = gToken.CreateReader()) + { + return jsonSerializer.Deserialize(reader); + } + } + + return null; + } +} + +public static class MCEntityModelBuilder +{ + public static TruModel BuildEntityModel(MCEntityGeometry geometry) + { + var modelBuilder = new ModelBuilder(); + modelBuilder.TextureSize(new Vector2(geometry.TextureWidth, geometry.TextureHeight)); + + foreach (var bone in geometry.Bones) + { + var bb = modelBuilder + .AddBone(bone.Name); + + if (bone.Pivot.HasValue) + { + bb.Pivot(bone.Pivot.Value); + } + + if (bone.BindPoseRotation.HasValue) + bb.BindPoseRotation(bone.BindPoseRotation.Value); + + if (!string.IsNullOrEmpty(bone.Parent)) + bb.Parent(bone.Parent); + + if (bone.Cubes.Length > 0) + { + foreach (var boneCube in bone.Cubes) + { + bb.AddCube(boneCube.Origin, boneCube.Size) + .Uv(boneCube.UV); + } + } + } + + return modelBuilder.Build(); + } + + private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); + + public static void ResetBonePositions(TruModel model) + { + foreach (var bone in model.Bones) + { + bone.Transform.Position = Microsoft.Xna.Framework.Vector3.Zero; + } + } + + public static void LogBoneLocations(TruModel model) + { + var sb = new StringBuilder(); + sb.AppendLine($"Bone Name,Parent,MinX,MinY,MinZ,MaxX,MaxY,MaxZ"); + + + foreach (var bone in model.Bones) + { + var wlrd = bone.Transform.World; + + if (!bone.Meshes.Any()) continue; + + var boneMinMesh = bone.Meshes.SelectMany(m => + Enumerable.Range(m.VertexOffset, m.NumVertices) + .Select(i => model.Vertices[i]) + ) + .Select(v => + Microsoft.Xna.Framework.Vector3.Transform(v.Position, wlrd)) + .OrderBy(x => x.LengthSquared()) + .ToArray(); + var boneMin = boneMinMesh.FirstOrDefault(); + var boneMax = boneMinMesh.LastOrDefault(); + sb.AppendLine($"{bone.Name},{bone.Parent?.Name},{boneMin.X},{boneMin.Y},{boneMin.Z},{boneMax.X},{boneMax.Y},{boneMax.Z}"); + } + + Log.Info($"\n\n{sb.ToString()}\n\n"); + File.WriteAllText("bonedebug.csv", sb.ToString()); + } +} + +public class MCEntity : DrawableEntity +{ + private MCEntityGeometry Geometry { get; set; } + private TruModel TruModel { get; set; } + + private VertexBuffer _vertexBuffer; + private IndexBuffer _indexBuffer; + + public MCEntity(IGame game) : base(game) + { + } + + private readonly List _disposables = new List(); + + protected override void LoadContent() + { + base.LoadContent(); + + var entityDef = "creeper.geo.json"; + var entityTex = "creeper\\creeper.png"; + var defPath = Path.Join("S:\\Temp\\resource_packs\\bedrock-1.18.1\\models\\entity\\", entityDef); + var texPath = Path.Join("S:\\Temp\\resource_packs\\bedrock-1.18.1\\textures\\entity\\", entityTex); + var json = File.ReadAllText(defPath); + var geometryCollection = JsonConvert.DeserializeObject(json); + + Geometry = geometryCollection.GetGeometry(geometryCollection.Geometries.Keys.FirstOrDefault()); + + var model = MCEntityModelBuilder.BuildEntityModel(Geometry); + + _vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColorTexture.VertexDeclaration, model.Vertices.Length, BufferUsage.None); + _indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, model.Indices.Length, BufferUsage.None); + + _vertexBuffer.SetData(model.Vertices); + _indexBuffer.SetData(model.Indices); + + _effect = new BasicEffect(GraphicsDevice); + + var texture = Texture2D.FromFile(GraphicsDevice, texPath); + ; + //var texture = new ColorTexture2D(GraphicsDevice, Color.HotPink); + _effect.Texture = texture; + _effect.TextureEnabled = true; + _effect.LightingEnabled = false; + _effect.VertexColorEnabled = true; + //_effect.EnableDefaultLighting(); + + //_effect.DiffuseColor = Color.OrangeRed.ToVector3(); + //_effect.Alpha = 0.75f; + + _disposables.Add(_effect); + _disposables.Add(texture); + _disposables.Add(_vertexBuffer); + _disposables.Add(_indexBuffer); + + TruModel = model; + TruModel.Transform.ParentTransform = Transform; + } + + public override void Draw(GameTime gameTime) + { + if (!Visible) return; + + DrawTruModel(TruModel); + } + + private BasicEffect _effect; + + private void DrawTruModel(TruModel model) + { + if (model != null) + { + using (var cxt = GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default)) + { + var r = cxt.RasterizerState.Copy(); + + r.FillMode = ((ModelExplorerGame)Game).IsWireFrame ? FillMode.WireFrame : FillMode.Solid; + //r.CullMode = CullMode.None; + //r.DepthClipEnable = false; + cxt.RasterizerState = r; + + cxt.SamplerState = SamplerState.PointWrap; + var camera = ((IGame)Game).Camera; + + _effect.View = camera.View; + _effect.Projection = camera.Projection; + //_effect.DiffuseColor = Vector3.One; + //_effect.LightingEnabled = false; + //_effect.Alpha = 1f; + GraphicsDevice.SetVertexBuffer(_vertexBuffer); + GraphicsDevice.Indices = _indexBuffer; + + foreach (var bone in model.Bones) + { + //_effect.World = Transform.World; + _effect.World = bone.Transform.World; + + // if (bone.Name.Contains("body", StringComparison.OrdinalIgnoreCase)) + // { + // _effect.DiffuseColor = Color.Blue.ToVector3(); + // } + // else if (bone.Name.Contains("head", StringComparison.OrdinalIgnoreCase)) + // { + // _effect.DiffuseColor = Color.Red.ToVector3(); + // } + // else + // { + // _effect.DiffuseColor = Microsoft.Xna.Framework.Vector3.One; + // } + + foreach (var mesh in bone.Meshes) + { + for (var i = 0; i < _effect.CurrentTechnique.Passes.Count; i++) + { + _effect.CurrentTechnique.Passes[i].Apply(); + GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, mesh.IndexOffset, mesh.PrimitiveCount); + } + } + } + } + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + foreach (var d in _disposables.ToArray()) + { + d.Dispose(); + _disposables.Remove(d); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBoneBuilder.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBoneBuilder.cs new file mode 100644 index 000000000..bbcd6fd65 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBoneBuilder.cs @@ -0,0 +1,82 @@ +using System.Numerics; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public class ModelBoneBuilder +{ + internal readonly ModelBuilder _modelBuilder; + private readonly string _name; + internal string _parentName; + private Vector3? _pivot; + private Vector3? _bindPoseRotation; + private List _cubeBuilders = new List(); + internal TruModelBone _built; + public int _nVertices; + public int _nIndices; + internal (ModelVertexIndexPositionTexture[] vertices, short[] indices)[] _meshes; + + public ModelBoneBuilder(ModelBuilder modelBuilder, string name) + { + _modelBuilder = modelBuilder; + _name = name; + } + + public ModelBoneBuilder Parent(string parentName) + { + _parentName = parentName; + return this; + } + + public ModelBoneBuilder Pivot(Vector3? pivot) + { + _pivot = pivot; + return this; + } + + public ModelBoneBuilder BindPoseRotation(Vector3? bindPoseRotation) + { + _bindPoseRotation = bindPoseRotation; + return this; + } + + public ModelCubeMeshBuilder AddCube(Vector3 origin, Vector3 size) + { + var builder = new ModelCubeMeshBuilder(this); + _cubeBuilders.Add(builder); + + return builder + .Origin(origin) + .Size(size); + } + + + internal TruModelBone Build() + { + var bone = new TruModelBone() + { + Name = _name, + }; + _nVertices = 0; + _nIndices = 0; + + _meshes = new (ModelVertexIndexPositionTexture[] vertices, short[] indices)[_cubeBuilders.Count]; + for (var i = 0; i < _cubeBuilders.Count; i++) + { + var cube = _cubeBuilders[i]; + var mesh = cube.Build(); + bone.AddMesh(mesh); + _meshes[i] = cube._mesh; + _nVertices += cube._mesh.vertices.Length; + _nIndices += cube._mesh.indices.Length; + } + + if (_pivot.HasValue) + bone.Transform.RotationOrigin = _pivot.Value; + + if (_bindPoseRotation.HasValue) + bone.Transform.Rotation = _bindPoseRotation.Value; + + _built = bone; + return bone; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBuilder.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBuilder.cs new file mode 100644 index 000000000..1e0f02fee --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBuilder.cs @@ -0,0 +1,98 @@ +using System.Numerics; +using ResourcePackLib.Core.Data; +using ResourcePackLib.ModelExplorer.Utilities.Extensions; +using Quaternion = Microsoft.Xna.Framework.Quaternion; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public class ModelBuilder +{ + internal Vector2 _textureSize; + private readonly List _boneBuilders = new List(); + + public ModelBuilder() + { + } + + public ModelBuilder TextureSize(Vector2 size) + { + _textureSize = size; + return this; + } + + public ModelBoneBuilder AddBone(string name) + { + var builder = new ModelBoneBuilder(this, name); + _boneBuilders.Add(builder); + return builder; + } + + public TruModel Build() + { + var bones = new List(); + + var nVertices = 0; + var nIndices = 0; + + foreach (var boneBuilder in _boneBuilders) + { + var bone = boneBuilder.Build(); + nVertices += boneBuilder._nVertices; + nIndices += boneBuilder._nIndices; + bones.Add(bone); + } + + var vertices = new ModelVertexIndexPositionTexture[nVertices]; + var indices = new short[nIndices]; + + var verticesIndex = 0; + var indicesIndex = 0; + + for (int i = 0; i < _boneBuilders.Count; i++) + { + var bb = _boneBuilders[i]; + for (int j = 0; j < bb._meshes.Length; j++) + { + var bbm = bb._meshes[j]; + + for (var k = 0; k < bbm.vertices.Length; k++) + { + vertices[verticesIndex + k] = bbm.vertices[k]; + } + + for(var k = 0; k < bbm.indices.Length; k++) + { + indices[indicesIndex + k] = (short)(verticesIndex + bbm.indices[k]); + } + + var m = bb._built.Meshes[j]; + m.VertexOffset = verticesIndex; + m.IndexOffset = indicesIndex; + + verticesIndex += bbm.vertices.Length; + indicesIndex += bbm.indices.Length; + } + } + + foreach (var boneBuilder in _boneBuilders) + { + if (!string.IsNullOrEmpty(boneBuilder._parentName)) + { + // try resolve parent + foreach (var bone in bones) + { + if (string.Equals(bone.Name, boneBuilder._parentName, StringComparison.OrdinalIgnoreCase)) + { + bone.AddChild(boneBuilder._built); + break; + } + } + } + } + + // var textureUvScale = + + var model = new TruModel(vertices, indices, bones); + return model; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelCubeMeshBuilder.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelCubeMeshBuilder.cs new file mode 100644 index 000000000..7a7902be2 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelCubeMeshBuilder.cs @@ -0,0 +1,124 @@ +using System.Numerics; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public class ModelCubeMeshBuilder +{ + internal readonly ModelBoneBuilder _parentBone; + private Vector3 _origin; + private Vector3 _size; + internal Vector2 _uv; + internal Vector2 _uvSize; + + internal (ModelVertexIndexPositionTexture[] vertices, short[] indices) _mesh; + + public ModelCubeMeshBuilder(ModelBoneBuilder parentBone) + { + _parentBone = parentBone; + } + + public ModelCubeMeshBuilder Origin(Vector3 origin) + { + _origin = origin; + return this; + } + + public ModelCubeMeshBuilder Size(Vector3 size) + { + _size = size; + return this; + } + + public ModelCubeMeshBuilder Uv(Vector2 uv) + { + _uv = uv; + return this; + } + + private (ModelVertexIndexPositionTexture[] vertices, short[] indices) CreateCuboid(Vector3 min, Vector3 max) + { + var baseVertices = new Vector3[8]; // 8 distinct coordinates + + // Binary notation represents 0bXYZ ;) + baseVertices[0b000] = new Vector3(min.X, min.Y, min.Z); // 0 0 0 + baseVertices[0b001] = new Vector3(min.X, min.Y, max.Z); // 0 0 1 + baseVertices[0b010] = new Vector3(min.X, max.Y, min.Z); // 0 1 0 + baseVertices[0b011] = new Vector3(min.X, max.Y, max.Z); // 0 1 1 + baseVertices[0b100] = new Vector3(max.X, min.Y, min.Z); // 1 0 0 + baseVertices[0b101] = new Vector3(max.X, min.Y, max.Z); // 1 0 1 + baseVertices[0b110] = new Vector3(max.X, max.Y, min.Z); // 1 1 0 + baseVertices[0b111] = new Vector3(max.X, max.Y, max.Z); // 1 1 1 + + short[][] baseFaceIndices = + { + new short[] { 0b111, 0b110, 0b100, 0b101 }, // East | Right? + new short[] { 0b010, 0b110, 0b111, 0b011 }, // Up | Top? + new short[] { 0b011, 0b111, 0b101, 0b001 }, // South | Back? + new short[] { 0b010, 0b011, 0b001, 0b000 }, // West | Left? + new short[] { 0b100, 0b000, 0b001, 0b101 }, // Down | Bottom? + new short[] { 0b110, 0b010, 0b000, 0b100 }, // North | Front? + }; + + var vertices = new ModelVertexIndexPositionTexture[24]; + var indices = new short[36]; + short b = 0, c = 0; + + var size = max - min; + _uvSize = new Vector2((2 * size.Z) + (2 * size.X), (size.Y + size.Z)); + var uvOffset = _uv; + var globUvSize = _parentBone._modelBuilder._textureSize; + + var defineFace = (short[] vertexIndices, Vector2 uvMin, Vector2 uvSize) => + { + var uvMax = uvMin + uvSize; + //for (int i = 0; i < baseFaceIndices.Length; i++) + //{ + var d = b; + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[0]], (uvOffset + new Vector2(uvMin.X, uvMin.Y)) / globUvSize); + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[1]], (uvOffset + new Vector2(uvMax.X, uvMin.Y)) / globUvSize); + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[2]], (uvOffset + new Vector2(uvMax.X, uvMax.Y)) / globUvSize); + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[3]], (uvOffset + new Vector2(uvMin.X, uvMax.Y)) / globUvSize); + + indices[c++] = (short)(d + 0); + indices[c++] = (short)(d + 1); + indices[c++] = (short)(d + 2); + indices[c++] = (short)(d + 0); + indices[c++] = (short)(d + 2); + indices[c++] = (short)(d + 3); + //} + }; + + + /* East | Right? */ + defineFace(new short[] { 0b111, 0b110, 0b100, 0b101 }, new Vector2(0f, size.Z), new Vector2(size.Z, size.Y)); + + /* Up | Top? */ + defineFace(new short[] { 0b010, 0b110, 0b111, 0b011 }, new Vector2(size.Z, 0f), new Vector2(size.X, size.Z)); + + /* South | Back? */ + defineFace(new short[] { 0b011, 0b111, 0b101, 0b001 }, new Vector2((2*size.Z)+size.X, size.Z), new Vector2(size.X, size.Y)); + + /* West | Left? */ + defineFace(new short[] { 0b010, 0b011, 0b001, 0b000 }, new Vector2(size.Z+size.X, size.Z), new Vector2(size.Z, size.Y)); + + /* Down | Bottom? */ + defineFace(new short[] { 0b100, 0b000, 0b001, 0b101 }, new Vector2(size.Z+size.X, 0f), new Vector2(size.X, size.Z)); + + /* North | Front? */ + defineFace(new short[] { 0b110, 0b010, 0b000, 0b100 }, new Vector2(size.Z, size.Z), new Vector2(size.X, size.Y)); + + return (vertices, indices); + } + + public TruModelMesh Build() + { + _mesh = CreateCuboid(_origin, _origin+_size); + return new TruModelMesh() + { + NumVertices = _mesh.vertices.Length, + PrimitiveCount = _mesh.indices.Length/3, + IndexOffset = 0, + VertexOffset = 0 + }; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelVertexIndexPositionTexture.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelVertexIndexPositionTexture.cs new file mode 100644 index 000000000..aee518233 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelVertexIndexPositionTexture.cs @@ -0,0 +1,15 @@ +using System.Numerics; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public struct ModelVertexIndexPositionTexture +{ + public Vector3 Position; + public Vector2 Uv; + + public ModelVertexIndexPositionTexture(Vector3 position, Vector2 uv) + { + Position = position; + Uv = uv; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModel.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModel.cs new file mode 100644 index 000000000..3e838ddbd --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModel.cs @@ -0,0 +1,36 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Vector2 = System.Numerics.Vector2; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public class TruModel +{ + public TruModelTransform Transform { get; } + + public TruModelBoneCollection Bones { get; private set; } + + public TruModelBone RootBone { get; set; } + + public object Tag { get; set; } + + public VertexPositionColorTexture[] Vertices { get; } + public short[] Indices { get; } + + public TruModel(ModelVertexIndexPositionTexture[] vertices, short[] indices, List bones) + { + Transform = new TruModelTransform(); + Vertices = vertices.Select(v => new VertexPositionColorTexture( + new Microsoft.Xna.Framework.Vector3(v.Position.X, v.Position.Y, v.Position.Z), + Color.WhiteSmoke, + new Microsoft.Xna.Framework.Vector2(v.Uv.X, v.Uv.Y))) + .ToArray(); + Indices = indices; + Bones = new TruModelBoneCollection(bones); + foreach (var bone in Bones) + { + if (bone.Parent == null) + bone.Transform.ParentTransform = Transform; + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBone.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBone.cs new file mode 100644 index 000000000..808e71b18 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBone.cs @@ -0,0 +1,55 @@ +using System.Diagnostics; +using System.Numerics; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public class TruModelBone +{ + private List _children = new List(); + private List _meshes = new List(); + private TruModelBone _parent; + + public TruModelMeshCollection Meshes { get; } + + public string Name { get; set; } + + public TruModelBone Parent + { + get => _parent; + private set + { + _parent = value; + Transform.ParentTransform = value?.Transform; + } + } + + public TruModelBoneCollection Children { get; } + public TruModelTransform Transform { get; } + + public TruModelBone() + { + Transform = new TruModelTransform(); + Meshes = new TruModelMeshCollection(_meshes); + Children = new TruModelBoneCollection(_children); + } + + public void AddMesh(TruModelMesh mesh) + { + _meshes.Add(mesh); + } + + public void AddChild(TruModelBone modelBone) + { + _children.Add(modelBone); + modelBone.Parent = this; + } + + private string DebuggerDisplay + { + get + { + return $"TruModelBone{{{Name}, LocalRotationOrigin={{{Transform.RotationOrigin}}}, LocalScaleOrigin={{{Transform.ScaleOrigin}}}}}"; + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBoneCollection.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBoneCollection.cs new file mode 100644 index 000000000..84e3bd9e4 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBoneCollection.cs @@ -0,0 +1,56 @@ +using System.Collections.ObjectModel; +using System.Diagnostics; +using JetBrains.Annotations; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +[DebuggerDisplay("{_value}", Name = "{_key}")] +internal class KeyValuePairs +{ + private TruModelBoneCollection _boneCollection; + private object _key; + private object _value; + + public KeyValuePairs(TruModelBoneCollection boneCollection, object key, object value) + { + _boneCollection = boneCollection; + _key = key; + _value = value; + } +} +[DebuggerDisplay("Count = {Count}")] +[DebuggerTypeProxy(typeof(TruModelBoneCollectionDebugView))] +public class TruModelBoneCollection : ReadOnlyCollection +{ + public TruModelBoneCollection([NotNull] IList list) : base(list) + { + + } + + + internal class TruModelBoneCollectionDebugView + { + private readonly TruModelBoneCollection _boneCollection; + + public TruModelBoneCollectionDebugView(TruModelBoneCollection boneCollection) + { + _boneCollection = boneCollection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePairs[] Bones + { + get + { + KeyValuePairs[] keys = new KeyValuePairs[_boneCollection.Count]; + for (var i = 0; i < _boneCollection.Count; i++) + { + keys[i] = new KeyValuePairs(_boneCollection, _boneCollection[i].Name, _boneCollection[i]); + } + + return keys; + } + } + + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMesh.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMesh.cs new file mode 100644 index 000000000..6eccdaac4 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMesh.cs @@ -0,0 +1,14 @@ +namespace ResourcePackLib.ModelExplorer.Geometry; + +public class TruModelMesh +{ + public int NumVertices { get; internal set; } + public int VertexOffset { get; internal set; } + public int PrimitiveCount { get; internal set; } + public int IndexOffset { get; internal set; } + + public TruModelMesh() + { + + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMeshCollection.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMeshCollection.cs new file mode 100644 index 000000000..1cea0c2dc --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMeshCollection.cs @@ -0,0 +1,35 @@ +using System.Collections.ObjectModel; +using System.Diagnostics; +using JetBrains.Annotations; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +[DebuggerDisplay("Count = {Count}")] +[DebuggerTypeProxy(typeof(TruModelMeshCollectionDebugView))] +public class TruModelMeshCollection : ReadOnlyCollection +{ + public TruModelMeshCollection([NotNull] IList list) : base(list) + { + + } + + internal class TruModelMeshCollectionDebugView + { + private readonly TruModelMeshCollection _meshCollection; + + public TruModelMeshCollectionDebugView(TruModelMeshCollection meshCollection) + { + _meshCollection = meshCollection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public TruModelMesh[] Meshes + { + get + { + return _meshCollection.ToArray(); + } + } + + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelTransform.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelTransform.cs new file mode 100644 index 000000000..3e4a1f4a1 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelTransform.cs @@ -0,0 +1,10 @@ +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public class TruModelTransform : Transform3D +{ + public TruModelTransform() + { + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs new file mode 100644 index 000000000..830053849 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs @@ -0,0 +1,190 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Utilities.Extensions; +using RocketUI; +using RocketUI.Utilities.Helpers; + +namespace ResourcePackLib.ModelExplorer.Graphics +{ + public enum ProjectionType + { + Perspective, + Orthographic + } + + public class Camera : GameComponent, ICamera, ITransformable + { + private readonly IGame _game; + private Viewport _viewport; + private Rectangle _bounds = Rectangle.Empty; + private float _nearDistance = 0.1f; + private float _farDistance = 10000.0f; + public Transform3D Transform { get; } = new Transform3D(); + private Vector3 _up; + private Vector3 _forward; + private Vector3 _right; + + public Vector3 Up => _up; + public Vector3 Forward => _forward; + public Vector3 Right => _right; + + public Vector3 Scale + { + get => Transform.Scale; + set => Transform.Scale = value; + } + + public Vector3 Position + { + get => Transform.Position; + set => Transform.Position = value; + } + + public Vector3 Rotation + { + get => Transform.Rotation; + set => Transform.Rotation = value; + } + + public Matrix World + { + get => Transform.World; + } + + public Matrix View { get; private set; } + public Matrix Projection { get; private set; } + + public float NearDistance + { + get => _nearDistance; + set + { + _nearDistance = value; + UpdateProjection(); + } + } + + public float FarDistance + { + get => _farDistance; + set + { + _farDistance = value; + UpdateProjection(); + } + } + + public Viewport Viewport + { + get => _viewport; + } + + public Rectangle Bounds + { + get => _bounds; + set + { + _bounds = value; + UpdateProjection(); + } + } + + public ProjectionType ProjectionType { get; set; } = ProjectionType.Perspective; + + public RenderTarget2D RenderTarget { get; set; } + + public Camera(IGame game) : base(game.Game) + { + _game = game; + UpdateView(); + UpdateProjection(); + + _game.DeviceManager.DeviceCreated += (sender, args) => UpdateProjection(); + _game.DeviceManager.DeviceReset += (sender, args) => UpdateProjection(); + _game.Window.ClientSizeChanged += (sender, args) => UpdateProjection(); + Transform.Changed += (sender, args) => UpdateView(); + } + + private void UpdateView() + { + Quaternion rot = default; + Transform.World.ExtractRotation(ref rot); + + _forward = Vector3.Normalize(Vector3.Transform(Vector3.Forward, rot)); + _up = Vector3.Normalize(Vector3.Transform(Vector3.Up, rot)); + _right = Vector3.Normalize(Vector3.Transform(Vector3.Right, rot)); + + View = Matrix.CreateLookAt(Position, Position + _forward, _up); + } + + private void UpdateProjection() + { + if (Bounds == Rectangle.Empty) + { + Bounds = _game.Window.ClientBounds; + } + + var w = Bounds.Width; + var h = Bounds.Height; + + if (!_viewport.Bounds.Equals(Bounds)) + { + _viewport = new Viewport(Bounds) + { + MinDepth = _nearDistance, + MaxDepth = _farDistance + }; + } + + if (ProjectionType == ProjectionType.Perspective) + { + var aspect = (float)w / (float)h; + Projection = Matrix.CreatePerspectiveFieldOfView(80.0f.ToRadians(), aspect, NearDistance, FarDistance); + } + else + { + Projection = Matrix.CreateOrthographic(w, h, NearDistance, FarDistance); + } + } + + + public void LookAt(Vector3 target) + { + var d = Vector3.Normalize(target - Position); + var t = 90f - MathHelper.ToDegrees((float)Math.Atan(d.X / d.Z)); + var yaw = (d.Z < 0 ? 90f : 270f) + t; + + var pitch = MathHelper.ToDegrees((float)Math.Atan(d.Y / (Math.Sqrt((d.X * d.X) + (d.Z * d.Z))))); + + Rotation = new Vector3(pitch, yaw, 0f); + } + + public void MoveRelative(Vector3 move) + { + Quaternion rot = default; + Transform.World.ExtractRotation(ref rot); + var forward = Vector3.Transform(move, rot); + Position += forward; + } + + public void Update(GameTime gameTime) + { + var keyboard = Keyboard.GetState(); + if (keyboard.IsKeyDown(Keys.Right)) + { + } + } + + public void Draw(Action doDraw) + { + var g = _game.Game.GraphicsDevice; + + //g.SetRenderTarget(RenderTarget); + g.Clear(Color.Black); + + doDraw(); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/DebugGrid.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/DebugGrid.cs new file mode 100644 index 000000000..01613b00c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/DebugGrid.cs @@ -0,0 +1,157 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace ResourcePackLib.ModelExplorer.Graphics +{ + public class DebugGrid + { + public int Steps + { + get => _steps; + set + { + _steps = value; + _dirty = true; + } + } + + public float Spacing + { + get => _spacing; + set + { + _spacing = value; + _dirty = true; + } + } + + public Color LineColor + { + get => _lineColor; + set + { + _lineColor = value; + _dirty = true; + } + } + + public Vector3 XAxis + { + get => _xAxis; + set + { + _xAxis = value; + _dirty = true; + } + } + + public Vector3 YAxis + { + get => _yAxis; + set + { + _yAxis = value; + _dirty = true; + } + } + + private VertexPositionColor[] _verticies; + private VertexBuffer _vertexBuffer; + private short[] _indicies; + private bool _dirty; + private Vector3 _yAxis; + private Vector3 _xAxis; + private Color _lineColor = Color.LightGray; + private float _spacing = 1.0f; + private int _steps = 10; + + private BasicEffect _effect; + public void Init(GraphicsDevice graphics) + { + if (_effect == null) + _effect = new BasicEffect(graphics) + { + VertexColorEnabled = true, + World = Matrix.Identity + }; + + var lineCount = (Steps * 2); + + var halfSteps = (int)Math.Ceiling(Steps / 2f); + + var arraySize = (short)((((halfSteps * 2f) + 1f) * 2f) * 2); + _verticies = new VertexPositionColor[arraySize]; + _vertexBuffer = new VertexBuffer(graphics, VertexPositionColor.VertexDeclaration, arraySize, BufferUsage.WriteOnly); + _indicies = new short[arraySize]; + } + + public void CalculateVerticies() + { + var halfSteps = (int)Math.Ceiling(Steps / 2f); + + var arraySize = (short)((((halfSteps * 2f) + 1f) * 2f) * 2); + if (arraySize != _vertexBuffer.VertexCount) + Init(ModelExplorerGame.Instance.Game.GraphicsDevice); + + var verticies = new VertexPositionColor[arraySize]; + var indicies = new short[arraySize]; + + var minX = -halfSteps * XAxis * Spacing; + var maxX = halfSteps * XAxis * Spacing; + + var minY = -halfSteps * YAxis * Spacing; + var maxY = halfSteps * YAxis * Spacing; + + int i = 0; + for (int x = -halfSteps; x <= halfSteps; x++) + { + var minPos = (x * XAxis * Spacing) + minY; + var maxPos = (x * XAxis * Spacing) + maxY; + verticies[i] = new VertexPositionColor(minPos, LineColor); + indicies[i] = (short)i; + i++; + verticies[i] = new VertexPositionColor(maxPos, LineColor); + indicies[i] = (short)i; + i++; + } + + for (int y = -halfSteps; y <= halfSteps; y++) + { + var minPos = (y * YAxis * Spacing) + minX; + var maxPos = (y * YAxis * Spacing) + maxX; + verticies[i] = new VertexPositionColor(minPos, LineColor); + indicies[i] = (short)i; + i++; + verticies[i] = new VertexPositionColor(maxPos, LineColor); + indicies[i] = (short)i; + i++; + } + + _vertexBuffer.SetData(verticies); + _indicies = indicies; + } + + public void Update() + { + if (_dirty) + { + _dirty = false; + CalculateVerticies(); + } + } + + public void Draw(GraphicsDevice graphics, Matrix view, Matrix projection) + { + _effect.View = view; + _effect.Projection = projection; + + foreach (EffectPass pass in _effect.CurrentTechnique.Passes) + { + pass.Apply(); + graphics.SetVertexBuffer(_vertexBuffer); + graphics.DrawUserPrimitives(PrimitiveType.LineList, _verticies, 0, _verticies.Length / 2); + } + } + + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs new file mode 100644 index 000000000..23c464722 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs @@ -0,0 +1,112 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Content; +using Microsoft.Xna.Framework.Graphics; +using RocketUI; +using RocketUI.Audio; +using RocketUI.Serialization.Xaml; + +namespace ResourcePackLib.ModelExplorer.Graphics; + +public class GuiRenderer : IGuiRenderer +{ + public GuiScaledResolution ScaledResolution { get; set; } + + private GraphicsDevice _graphicsDevice; + private ContentManager _content; + private StyleSheet _styleSheet = new StyleSheet(); + + private Dictionary _textureCache = new Dictionary(); + private Dictionary _pathedTextureCache = new Dictionary(); + + public void Init(GraphicsDevice graphics, IServiceProvider serviceProvider) + { + _graphicsDevice = graphics; + _content = ((Game)serviceProvider.GetService(typeof(Game))).Content; + Font = (WrappedSpriteFont)_content.Load("Fonts/Default"); + + LoadTexturesFromContent(); + LoadStyleSheets(); + } + + private void LoadTexturesFromContent() + { + var gui = _content.Load("Gui/Gui"); + _pathedTextureCache["Gui/Gui"] = gui; + + LoadTextureFromSpriteSheet(GuiTextures.ControlDefault, gui, new Rectangle(0, 0, 100, 20), new Thickness(2)); + LoadTextureFromSpriteSheet(GuiTextures.ControlHover, gui, new Rectangle(0, 20, 100, 20), new Thickness(2)); + LoadTextureFromSpriteSheet(GuiTextures.ControlFocused, gui, new Rectangle(0, 40, 100, 20), new Thickness(2)); + + LoadTextureFromSpriteSheet(GuiTextures.ButtonDefault, gui, new Rectangle(100, 0, 60, 20), new Thickness(2)); + LoadTextureFromSpriteSheet(GuiTextures.ButtonHover, gui, new Rectangle(100, 20, 60, 20), new Thickness(2)); + LoadTextureFromSpriteSheet(GuiTextures.ButtonFocused, gui, new Rectangle(100, 40, 60, 20), new Thickness(2)); + } + + private void LoadStyleSheets() + { + RocketXamlLoader.Load(_styleSheet, "ResourcePackLib.ModelExplorer.Scenes.Styles.xaml"); + } + + private TextureSlice2D LoadTextureFromEmbeddedResource(GuiTextures guiTexture, byte[] resource) + { + //_textureCache[guiTexture] = TextureUtils.ImageToTexture2D(_graphicsDevice, resource); + return _textureCache[guiTexture]; + } + + private void LoadTextureFromSpriteSheet(GuiTextures guiTexture, Texture2D spriteSheet, Rectangle sliceRectangle, Thickness ninePatchThickness) + { + _textureCache[guiTexture] = new NinePatchTexture2D(spriteSheet.Slice(sliceRectangle), ninePatchThickness); + } + + private void LoadTextureFromSpriteSheet(GuiTextures guiTexture, Texture2D spriteSheet, Rectangle sliceRectangle) + { + _textureCache[guiTexture] = spriteSheet.Slice(sliceRectangle); + } + + + public IFont Font { get; set; } + + public ISoundEffect GetSoundEffect(GuiSoundEffects soundEffects) + { + return default; + } + + public TextureSlice2D GetTexture(GuiTextures guiTexture) + { + if (_textureCache.TryGetValue(guiTexture, out var texture)) + { + return texture; + } + + return (TextureSlice2D)RocketUI.GpuResourceManager.CreateTexture2D(1, 1); + } + + public TextureSlice2D GetTexture(string texturePath) + { + texturePath = texturePath.ToLowerInvariant(); + + if (!_pathedTextureCache.TryGetValue(texturePath, out TextureSlice2D texture)) + { + texture = Texture2D.FromFile(_graphicsDevice, texturePath); + _pathedTextureCache.Add(texturePath, texture); + } + + return texture; + } + + public Texture2D GetTexture2D(GuiTextures guiTexture) + { + return GetTexture(guiTexture).Texture; + } + + public string GetTranslation(string key) => key; + + public Vector2 Project(Vector2 point) => Vector2.Transform(point, ScaledResolution.TransformMatrix); + + public Vector2 Unproject(Vector2 screen) => Vector2.Transform(screen, ScaledResolution.InverseTransformMatrix); + + public IStyle[] ResolveStyles(Type elementType, string[] classNames) + { + return _styleSheet.ResolveStyles(elementType, classNames); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs new file mode 100644 index 000000000..ea98a0f79 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs @@ -0,0 +1,182 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Configuration; +using ResourcePackLib.ModelExplorer.Graphics; +using ResourcePackLib.ModelExplorer.Scenes; +using ResourcePackLib.ModelExplorer.Scenes.Screens; +using ResourcePackLib.ModelExplorer.Utilities.Extensions; +using RocketUI; +using RocketUI.Input; +using RocketUI.Input.Listeners; +using RocketUI.Utilities.Helpers; +using SpriteBatchExtensions = RocketUI.Utilities.Extensions.SpriteBatchExtensions; + +namespace ResourcePackLib.ModelExplorer; + +public class ModelExplorerGame : Game, IGame +{ + public IServiceProvider ServiceProvider { get; } + public Game Game => this; + public static IGame Instance { get; private set; } + public GraphicsDeviceManager DeviceManager => _graphics; + + private readonly GraphicsDeviceManager _graphics; + private SpriteBatch _spriteBatch; + + public SceneManager SceneManager { get; private set; } + public InputManager InputManager { get; private set; } + + public GuiManager GuiManager { get; private set; } + + public bool IsWireFrame { get; set; } + + public ModelExplorerGame(IServiceProvider services) + { + Instance = this; + ServiceProvider = services; + _graphics = new GraphicsDeviceManager(this) + { + SynchronizeWithVerticalRetrace = true, + GraphicsProfile = GraphicsProfile.HiDef, + PreferHalfPixelOffset = false, + PreferredDepthStencilFormat = DepthFormat.Depth24Stencil8, + PreferredBackBufferWidth = Window.ClientBounds.Width, + PreferredBackBufferHeight = Window.ClientBounds.Height + }; + _graphics.PreparingDeviceSettings += OnGraphicsManagerOnPreparingDeviceSettings; + IsFixedTimeStep = true; + Window.AllowUserResizing = true; + Window.ClientSizeChanged += OnWindowOnClientSizeChanged; + + Content.RootDirectory = "Content"; + IsMouseVisible = true; + + var hostApplicationLifetime = services.GetRequiredService(); + hostApplicationLifetime.ApplicationStopping.Register(OnHostApplicationStopping); + } + + private void OnWindowOnClientSizeChanged(object? sender, EventArgs e) + { + if (DeviceManager.PreferredBackBufferWidth != Window.ClientBounds.Width || + DeviceManager.PreferredBackBufferHeight != Window.ClientBounds.Height) + { + if (DeviceManager.IsFullScreen) + { + DeviceManager.PreferredBackBufferWidth = DeviceManager.GraphicsDevice.Adapter.CurrentDisplayMode.Width; + DeviceManager.PreferredBackBufferHeight = DeviceManager.GraphicsDevice.Adapter.CurrentDisplayMode.Height; + } + else + { + DeviceManager.PreferredBackBufferWidth = Window.ClientBounds.Width; + DeviceManager.PreferredBackBufferHeight = Window.ClientBounds.Height; + } + DeviceManager.ApplyChanges(); + } + + GuiManager.ScaledResolution.TargetWidth = Window.ClientBounds.Width; + GuiManager.ScaledResolution.TargetHeight = Window.ClientBounds.Height; + GuiManager.ScaledResolution.GuiScale = 1; + GuiManager.ScaledResolution.ViewportSize = new Size(Window.ClientBounds.Width, Window.ClientBounds.Height); + GuiManager.ScaledResolution.Update(); + + } + + private void OnGraphicsManagerOnPreparingDeviceSettings(object? sender, PreparingDeviceSettingsEventArgs e) + { + e.GraphicsDeviceInformation.PresentationParameters.DepthStencilFormat = DepthFormat.Depth24Stencil8; + + DeviceManager.PreferredBackBufferFormat = SurfaceFormat.Color; + // DeviceManager.PreferMultiSampling = true; + + DeviceManager.PreferredBackBufferWidth = Window.ClientBounds.Width; + DeviceManager.PreferredBackBufferHeight = Window.ClientBounds.Height; + } + + private void OnHostApplicationStopping() + { + GpuResourceManager.Dispose(); + } + protected override void Initialize() + { + DeviceManager.GraphicsProfile = GraphicsProfile.HiDef; + GraphicsDevice.PresentationParameters.MultiSampleCount = 8; + DeviceManager.SynchronizeWithVerticalRetrace = true; +// GraphicsDeviceManager.PreferMultiSampling = true; + IsFixedTimeStep = true; + Window.AllowUserResizing = true; + + DeviceManager.ApplyChanges(); + + Components.Add(SceneManager = ServiceProvider.GetRequiredService()); + Components.Add(GuiManager = ServiceProvider.GetRequiredService()); + Components.Add(InputManager = ServiceProvider.GetRequiredService()); + + base.Initialize(); + } + + protected override void LoadContent() + { + GpuResourceManager.Init(GraphicsDevice); + SpriteBatchExtensions.Init(GraphicsDevice); + _spriteBatch = new SpriteBatch(GraphicsDevice); + + base.LoadContent(); + _camera = new Camera(this) + { + + }; + Components.Add(_camera); + Camera = _camera; + Camera.Position = new Vector3(1.7f, 1.7f, 1.7f); + // cam.Rotation = Quaternion.CreateFromYawPitchRoll(270f, (float)Math.PI / 2f, 0f); + + + GuiManager.ScaledResolution.TargetWidth = Window.ClientBounds.Width; + GuiManager.ScaledResolution.TargetHeight = Window.ClientBounds.Height; + GuiManager.ScaledResolution.GuiScale = 1; + GuiManager.DrawOrder = 100; + GuiManager.Init(); + GuiManager.AddScreen(_debugGui = new DebugGui()); + + Options = ServiceProvider.GetRequiredService>().Value; + + SceneManager.SetScene(); + _graphics.GraphicsDevice.Viewport = new Viewport(Window.ClientBounds); + + DeviceManager.ApplyChanges(); + + GuiManager.Reinitialize(); + + InputManager.GetOrAddPlayerManager(PlayerIndex.One).TryGetListener(out _keyboardListener); + + } + public GameOptions Options { get; set; } + private DebugGui _debugGui; + private Camera _camera; + private KeyboardInputListener _keyboardListener; + + protected override void Update(GameTime gameTime) + { + // if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || + // Keyboard.GetState().IsKeyDown(Keys.Escape)) + // Exit(); + + if (_keyboardListener.IsAnyPressed(Keys.F8)) + { + IsWireFrame = !IsWireFrame; + } + + base.Update(gameTime); + } + public ICamera Camera { get; private set; } + + protected override void Draw(GameTime gameTime) + { + Camera?.Draw(() => base.Draw(gameTime)); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/NLog.config b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/NLog.config new file mode 100644 index 000000000..7e954445f --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/NLog.config @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Program.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Program.cs new file mode 100644 index 000000000..54582ad53 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Program.cs @@ -0,0 +1,95 @@ +// See https://aka.ms/new-console-template for more information + +using System.Reflection; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Xna.Framework; +using NLog; +using NLog.Extensions.Hosting; +using NLog.Extensions.Logging; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Configuration; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace ResourcePackLib.ModelExplorer; + +public static class Program +{ + [STAThread] + private static void Main(string[] args) + { + ConfigureNLog(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))); + var logger = LogManager.GetCurrentClassLogger(); + try + { + logger.Info("Starting TruSaber"); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + // NLog: catch any exception and log it. + logger.Error(ex, "Stopped program because of exception"); + throw; + } + finally + { + // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) + LogManager.Shutdown(); + } + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .UseNLog() + .ConfigureHostConfiguration(builder => + { + builder.AddEnvironmentVariables("TruSaber_"); + builder.AddJsonFile("TruSaber.hostconfig.json", optional: true, reloadOnChange: false); + }) + .ConfigureAppConfiguration(builder => + { + builder.AddCommandLine(args); + builder.AddEnvironmentVariables("TruSaber_"); + builder.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false); + }) + .ConfigureLogging((context, builder) => + { + builder + .ClearProviders() + .SetMinimumLevel(LogLevel.Trace) + .AddNLog(); + }) + .ConfigureServices((hostContext, services) => + { + services.AddLogging(); + + var opts = new GameOptions(); + hostContext.Configuration.Bind(opts); + + services.AddOptions().Bind(hostContext.Configuration); + services.AddSingleton(opts); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService().Game); + services.AddHostedService(); + Startup.RegisterServices(services); + }) + .UseConsoleLifetime(); + + 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; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj new file mode 100644 index 000000000..6d218c5b2 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj @@ -0,0 +1,49 @@ + + + + Exe + net6.0 + enable + latest + + + + + + + + + + + + + + + + + + + + + + false + + + + + + %(Filename).cs + + + PreserveNewest + + + + + MainMenuScreen.xaml + + + diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/GuiSceneBase.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/GuiSceneBase.cs new file mode 100644 index 000000000..36082e73c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/GuiSceneBase.cs @@ -0,0 +1,64 @@ +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Attributes; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Scenes +{ + public abstract class GuiSceneBase : GuiSceneBase where TScreen : Screen, new() + { + protected GuiSceneBase() : this(new TScreen()) { } + + protected GuiSceneBase(TScreen screen) : base() + { + screen.Anchor = Alignment.Fill; + screen.AutoSizeMode = AutoSizeMode.GrowAndShrink; + Screen = screen; + } + } + + public abstract class GuiSceneBase : Scene + { + private Screen _screen; + + public Screen Screen + { + get => _screen; + set => SetupScreen(value); + } + + [Service] protected GuiManager GuiManager { get; private set; } + + private void SetupScreen(Screen screen) + { + if (_screen != null && _screen != screen) + { + GuiManager.RemoveScreen(_screen); + } + _screen = screen; + if (screen == null) return; + + screen.Tag = this; + screen.IsSelfManaged = false; + //screen.Background = (Color.Black * 0.2f); + screen.ClipToBounds = true; + //screen.UpdateSize(screen.Width, screen.Height); + + if(Visible) + GuiManager.AddScreen(_screen); + } + + protected override void OnShow() + { + GuiManager.AddScreen(_screen); + } + + protected override void OnHide() + { + if (_screen != null && GuiManager != null) + { + GuiManager.RemoveScreen(_screen); + } + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/IScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/IScene.cs new file mode 100644 index 000000000..f277a4bc7 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/IScene.cs @@ -0,0 +1,17 @@ +using Microsoft.Xna.Framework; + +namespace ResourcePackLib.ModelExplorer.Scenes +{ + public interface IScene + { + GameComponentCollection Components { get; } + + bool Initialized { get; } + + void Initialize(); + void Show(); + void Hide(); + void Update(GameTime gameTime); + void Draw(GameTime gameTime); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.Components.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.Components.cs new file mode 100644 index 000000000..b545aa62c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.Components.cs @@ -0,0 +1,288 @@ +using System.Collections; +using Microsoft.Xna.Framework; + +namespace ResourcePackLib.ModelExplorer.Scenes; + +public abstract partial class Scene +{ + + private readonly GameComponentCollection _components = new GameComponentCollection(); + public GameComponentCollection Components => _components; + + private void InitializeComponents() + { + InitializeExistingComponents(); + + CategorizeComponents(); + _components.ComponentAdded += Components_ComponentAdded; + _components.ComponentRemoved += Components_ComponentRemoved; + } + + private void Components_ComponentAdded(object sender, GameComponentCollectionEventArgs e) + { + e.GameComponent.Initialize(); + CategorizeComponent(e.GameComponent); + } + + private void Components_ComponentRemoved(object sender, GameComponentCollectionEventArgs e) + { + DecategorizeComponent(e.GameComponent); + } + + private void InitializeExistingComponents() + { + for (int index = 0; index < Components.Count; ++index) + Components[index].Initialize(); + } + + private void CategorizeComponents() + { + DecategorizeComponents(); + for (int index = 0; index < Components.Count; ++index) + CategorizeComponent(Components[index]); + } + + private void DecategorizeComponents() + { + _updateables.Clear(); + _drawables.Clear(); + } + + private void CategorizeComponent(IGameComponent component) + { + if (component is IUpdateable) + _updateables.Add((IUpdateable)component); + if (!(component is IDrawable)) + return; + _drawables.Add((IDrawable)component); + } + + private void DecategorizeComponent(IGameComponent component) + { + if (component is IUpdateable) + _updateables.Remove((IUpdateable)component); + if (!(component is IDrawable)) + return; + _drawables.Remove((IDrawable)component); + } + + + private SortingFilteringCollection _drawables = new( + d => d.Visible, + (d, handler) => d.VisibleChanged += handler, + (d, handler) => d.VisibleChanged -= handler, + (d1, d2) => Comparer.Default.Compare(d1.DrawOrder, d2.DrawOrder), + (d, handler) => d.DrawOrderChanged += handler, + (d, handler) => d.DrawOrderChanged -= handler); + + private SortingFilteringCollection _updateables = new( + u => u.Enabled, + (u, handler) => u.EnabledChanged += handler, + (u, handler) => u.EnabledChanged -= handler, + (u1, u2) => Comparer.Default.Compare(u1.UpdateOrder, u2.UpdateOrder), + (u, handler) => u.UpdateOrderChanged += handler, + (u, handler) => u.UpdateOrderChanged -= handler); + + /// + /// The SortingFilteringCollection class provides efficient, reusable + /// sorting and filtering based on a configurable sort comparer, filter + /// predicate, and associate change events. + /// + private class SortingFilteringCollection : ICollection, IEnumerable, IEnumerable + { + private readonly List _items; + private readonly List> _addJournal; + private readonly Comparison> _addJournalSortComparison; + private readonly List _removeJournal; + private readonly List _cachedFilteredItems; + private bool _shouldRebuildCache; + private readonly Predicate _filter; + private readonly Comparison _sort; + private readonly Action> _filterChangedSubscriber; + private readonly Action> _filterChangedUnsubscriber; + private readonly Action> _sortChangedSubscriber; + private readonly Action> _sortChangedUnsubscriber; + + private static readonly Comparison RemoveJournalSortComparison = + (x, y) => Comparer.Default.Compare(y, x); + + public SortingFilteringCollection( + Predicate filter, + Action> filterChangedSubscriber, + Action> filterChangedUnsubscriber, + Comparison sort, + Action> sortChangedSubscriber, + Action> sortChangedUnsubscriber) + { + _items = new List(); + _addJournal = new List>(); + _removeJournal = new List(); + _cachedFilteredItems = new List(); + _shouldRebuildCache = true; + _filter = filter; + _filterChangedSubscriber = filterChangedSubscriber; + _filterChangedUnsubscriber = filterChangedUnsubscriber; + _sort = sort; + _sortChangedSubscriber = sortChangedSubscriber; + _sortChangedUnsubscriber = sortChangedUnsubscriber; + _addJournalSortComparison = CompareAddJournalEntry; + } + + private int CompareAddJournalEntry(AddJournalEntry x, AddJournalEntry y) + { + int num = _sort(x.Item, y.Item); + return num != 0 ? num : x.Order - y.Order; + } + + public void ForEachFilteredItem(Action action, TUserData userData) + { + if (_shouldRebuildCache) + { + ProcessRemoveJournal(); + ProcessAddJournal(); + _cachedFilteredItems.Clear(); + for (int index = 0; index < _items.Count; ++index) + { + if (_filter(_items[index])) + _cachedFilteredItems.Add(_items[index]); + } + + _shouldRebuildCache = false; + } + + for (int index = 0; index < _cachedFilteredItems.Count; ++index) + action(_cachedFilteredItems[index], userData); + if (!_shouldRebuildCache) + return; + _cachedFilteredItems.Clear(); + } + + public void Add(T item) + { + _addJournal.Add(new AddJournalEntry(_addJournal.Count, item)); + InvalidateCache(); + } + + public bool Remove(T item) + { + if (_addJournal.Remove(AddJournalEntry.CreateKey(item))) + return true; + int num = _items.IndexOf(item); + if (num < 0) + return false; + UnsubscribeFromItemEvents(item); + _removeJournal.Add(num); + InvalidateCache(); + return true; + } + + public void Clear() + { + for (int index = 0; index < _items.Count; ++index) + { + _filterChangedUnsubscriber(_items[index], Item_FilterPropertyChanged); + _sortChangedUnsubscriber(_items[index], Item_SortPropertyChanged); + } + + _addJournal.Clear(); + _removeJournal.Clear(); + _items.Clear(); + InvalidateCache(); + } + + public bool Contains(T item) => _items.Contains(item); + + public void CopyTo(T[] array, int arrayIndex) => _items.CopyTo(array, arrayIndex); + + public int Count => _items.Count; + + public bool IsReadOnly => false; + + public IEnumerator GetEnumerator() => _items.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_items).GetEnumerator(); + + private void ProcessRemoveJournal() + { + if (_removeJournal.Count == 0) + return; + _removeJournal.Sort(RemoveJournalSortComparison); + for (int index = 0; index < _removeJournal.Count; ++index) + _items.RemoveAt(_removeJournal[index]); + _removeJournal.Clear(); + } + + private void ProcessAddJournal() + { + if (_addJournal.Count == 0) + return; + _addJournal.Sort(_addJournalSortComparison); + int index1 = 0; + for (int index2 = 0; index2 < _items.Count && index1 < _addJournal.Count; ++index2) + { + T x = _addJournal[index1].Item; + if (_sort(x, _items[index2]) < 0) + { + SubscribeToItemEvents(x); + _items.Insert(index2, x); + ++index1; + } + } + + for (; index1 < _addJournal.Count; ++index1) + { + T obj = _addJournal[index1].Item; + SubscribeToItemEvents(obj); + _items.Add(obj); + } + + _addJournal.Clear(); + } + + private void SubscribeToItemEvents(T item) + { + _filterChangedSubscriber(item, Item_FilterPropertyChanged); + _sortChangedSubscriber(item, Item_SortPropertyChanged); + } + + private void UnsubscribeFromItemEvents(T item) + { + _filterChangedUnsubscriber(item, Item_FilterPropertyChanged); + _sortChangedUnsubscriber(item, Item_SortPropertyChanged); + } + + private void InvalidateCache() => _shouldRebuildCache = true; + + private void Item_FilterPropertyChanged(object sender, EventArgs e) => InvalidateCache(); + + private void Item_SortPropertyChanged(object sender, EventArgs e) + { + T obj = (T)sender; + int num = _items.IndexOf(obj); + _addJournal.Add(new AddJournalEntry(_addJournal.Count, obj)); + _removeJournal.Add(num); + UnsubscribeFromItemEvents(obj); + InvalidateCache(); + } + } + + private struct AddJournalEntry + { + public readonly int Order; + public readonly T Item; + + public AddJournalEntry(int order, T item) + { + Order = order; + Item = item; + } + + public static AddJournalEntry CreateKey(T item) => new AddJournalEntry(-1, item); + + public override int GetHashCode() => Item.GetHashCode(); + + public override bool Equals(object obj) => + obj is AddJournalEntry addJournalEntry && Equals(Item, addJournalEntry.Item); + } + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.cs new file mode 100644 index 000000000..8e93d96ca --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.cs @@ -0,0 +1,86 @@ +using System.Collections; +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Attributes; + +namespace ResourcePackLib.ModelExplorer.Scenes +{ + public abstract partial class Scene : IScene, IDisposable + { + private static readonly Action DrawAction = (drawable, gameTime) => drawable.Draw(gameTime); + private static readonly Action UpdateAction = (updateable, gameTime) => updateable.Update(gameTime); + + [Service] protected IGame Game { get; private set; } + [Service] protected IServiceProvider Services { get; private set; } + + public bool Initialized { get; private set; } + public bool Visible { get; private set; } + + public void Initialize() + { + if (Initialized) + return; + + OnInitialize(); + + InitializeComponents(); + + Initialized = true; + } + + public void Show() + { + if(Visible) return; + + Visible = true; + OnShow(); + } + + public void Hide() + { + if(!Visible) return; + Visible = false; + OnHide(); + } + + public void Update(GameTime gameTime) + { + _updateables.ForEachFilteredItem(UpdateAction, gameTime); + OnUpdate(gameTime); + } + + public void Draw(GameTime gameTime) + { + _drawables.ForEachFilteredItem(DrawAction, gameTime); + OnDraw(gameTime); + } + + protected virtual void OnInitialize() + { + } + + protected virtual void OnShow() + { + } + + protected virtual void OnHide() + { + } + + protected virtual void OnUpdate(GameTime gameTime) + { + } + + protected virtual void OnDraw(GameTime gameTime) + { + } + + public virtual void OnDispose() { } + + public void Dispose() + { + Hide(); + OnDispose(); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/SceneManager.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/SceneManager.cs new file mode 100644 index 000000000..ec1b850c0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/SceneManager.cs @@ -0,0 +1,117 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Utilities; + +namespace ResourcePackLib.ModelExplorer.Scenes +{ + public class SceneManager : DrawableGameComponent + { + public IScene ActiveScene => _activeScene?.Scene; + + private WrappedScene _activeScene; + private readonly Stack _sceneStack; + + private bool _initialized = false; + private IServiceProvider _serviceProvider; + + public SceneManager(IGame game) : base(game.Game) + { + _serviceProvider = game.ServiceProvider; + DrawOrder = 0; + _sceneStack = new Stack(); + } + + public override void Initialize() + { + _initialized = true; + base.Initialize(); + + ActiveScene?.Initialize(); + } + + public void SetScene() where TScene : class, IScene + { + ResetStack(); + SetSceneInternal(); + } + + private void SetSceneInternal() where TScene : class, IScene + { + var scope = _serviceProvider.CreateScope(); + var scene = scope.ServiceProvider.GetOrCreateInstance(); + var wrappedScene = new WrappedScene(scope, scene); + SetSceneInternal(wrappedScene); + } + + private void SetSceneInternal([NotNull] WrappedScene wrappedScene) + { + if (_initialized) + wrappedScene.Scene.Initialize(); + + _activeScene?.Scene.Hide(); + _activeScene?.Dispose(); + _activeScene = null; + + _activeScene = wrappedScene; + _activeScene?.Scene.Show(); + } + + public void PushScene() where TScene : class, IScene + { + if (ActiveScene != null) + _sceneStack.Push(_activeScene); + SetSceneInternal(); + } + + public void Pop() + { + if (_sceneStack.TryPop(out var previousScene)) + { + SetSceneInternal(previousScene); + } + } + + public void Back() => Pop(); + + public void ResetStack() + { + if (_sceneStack.Count > 0) + { + while (_sceneStack.TryPop(out var s)) + { + s?.Scene.Hide(); + s?.Dispose(); + } + } + } + + public override void Update(GameTime gameTime) + { + ActiveScene?.Update(gameTime); + } + + public override void Draw(GameTime gameTime) + { + ActiveScene?.Draw(gameTime); + } + + class WrappedScene : IDisposable + { + public WrappedScene(IServiceScope scope, IScene scene) + { + ServiceScope = scope; + Scene = scene; + } + + public IServiceScope ServiceScope { get; } + public IScene Scene { get; } + + public void Dispose() + { + ServiceScope?.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs new file mode 100644 index 000000000..d330dd5a8 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs @@ -0,0 +1,64 @@ +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Components; +using ResourcePackLib.ModelExplorer.Entities; +using ResourcePackLib.ModelExplorer.Scenes.Screens; +using ResourcePackLib.ModelExplorer.Utilities; + +namespace ResourcePackLib.ModelExplorer.Scenes; + +public class MainMenuScene : GuiSceneBase +{ + protected override void OnInitialize() + { + base.OnInitialize(); + + Components.Add(new GridEntity(ModelExplorerGame.Instance) + { + Visible = true, + Scale = Vector3.One, + Position = Vector3.Zero, + Steps = 25, + SubDivisions = 16, + Spacing = 1.0f, + LineColor = Color.LightGray * 0.85f, + SubLineColor = Color.LightGray * 0.15f + }); + + var cube = new CubeEntity(ModelExplorerGame.Instance); + cube.Visible = true; + cube.Position = Vector3.Zero; + cube.Scale = Vector3.One; + + var fox = Services.CreateInstance(); + fox.Visible = true; + fox.Position = Vector3.Zero; + fox.Transform.Scale = Vector3.One / 16f; + + Components.Add(Services.GetOrCreateInstance()); + //Components.Add(cube); + Components.Add(fox); + + Components.Add(Services.GetOrCreateInstance()); + Components.Add(Services.GetOrCreateInstance()); + + Game.Camera.Position = Vector3.Backward; + } + + protected override void OnUpdate(GameTime gameTime) + { + var a = "b"; + foreach (var c in Components) + { + if (c is MCEntity mcEntity) + { + // mcEntity.Scale = Vector3.One / 16f; + } + } + base.OnUpdate(gameTime); + } + + protected override void OnShow() + { + base.OnShow(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs new file mode 100644 index 000000000..2bbad1ad7 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using Microsoft.Xna.Framework; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Scenes.Screens; + +public class DebugGui : Screen +{ + private StackContainer _topleft; + + public DebugGui() + { + AddChild(_topleft = new StackContainer() + { + Anchor = Alignment.TopLeft, + ChildAnchor = Alignment.TopLeft + }); + + AddDebugLine(Assembly.GetExecutingAssembly().GetName().FullName); + AddDebugLine($"Version {Assembly.GetExecutingAssembly().GetName().Version.ToString()}"); + + AddDebugLine(() => + { + var p = ModelExplorerGame.Instance.Camera.Position; + return $"Camera Position: {p.X:F2}, {p.Y:F2}, {p.Z:F2}"; + }); + AddDebugLine(() => + { + var p = ModelExplorerGame.Instance.Camera.Rotation; + return $"Camera Rotation: {p.X:F2}, {p.Y:F2}, {p.Z:F2}"; + }); + + AddDebugLine(() => + { + var pos = ModelExplorerGame.Instance.GuiManager.FocusManager.CursorPosition; + return $"Cursor: {pos.X.ToString("00000")}, {pos.Y.ToString("00000")}"; + }); + } + + private void AddDebugLine(string text) + { + _topleft.AddChild(new TextElement(text) + { + TextColor = Color.White + }); + } + + private void AddDebugLine(Func updateFunc) + { + _topleft.AddChild(new AutoUpdatingTextElement(updateFunc) + { + TextColor = Color.White + }); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.cs new file mode 100644 index 000000000..5d9367560 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.cs @@ -0,0 +1,13 @@ +using RocketUI; +using RocketUI.Serialization.Xaml; + +namespace ResourcePackLib.ModelExplorer.Scenes.Screens; + +public partial class MainMenuScreen : Screen +{ + + public MainMenuScreen() : base() + { + RocketXamlLoader.Load(this); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml new file mode 100644 index 000000000..1350e0e35 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml new file mode 100644 index 000000000..e4af6dcfd --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Startup.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Startup.cs new file mode 100644 index 000000000..87cd1d3a2 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Startup.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using ResourcePackLib.ModelExplorer.Abstractions; +using ResourcePackLib.ModelExplorer.Graphics; +using ResourcePackLib.ModelExplorer.Scenes; +using RocketUI; +using RocketUI.Debugger; +using RocketUI.Input; +using RocketUI.Input.Listeners; +using RocketUI.Utilities.Extensions; + +namespace ResourcePackLib.ModelExplorer; + +public static class Startup +{ + + public static void RegisterServices(IServiceCollection services) + { + services.AddInputListenerFactory(p => new MouseInputListener(p)); + services.AddInputListenerFactory(p => new KeyboardInputListener(p)); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService().Camera); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddHostedService(sp => sp.GetRequiredService()); + + } + +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/EventHelpers.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/EventHelpers.cs new file mode 100644 index 000000000..106e0c9be --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/EventHelpers.cs @@ -0,0 +1,28 @@ +namespace ResourcePackLib.ModelExplorer.Utilities +{ + /// + /// Provides helper methods to make it easier + /// to safely raise events. + /// + internal static class EventHelpers + { + /// + /// Safely raises an event by storing a copy of the event's delegate + /// in the parameter and checking it for + /// null before invoking it. + /// + /// + /// The object raising the event. + /// to be invoked + /// The passed to + internal static void Raise( + object sender, + EventHandler handler, + TEventArgs e) + { + if (handler == null) + return; + handler(sender, e); + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/GraphcisExtensions.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/GraphcisExtensions.cs new file mode 100644 index 000000000..d924a413b --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/GraphcisExtensions.cs @@ -0,0 +1,22 @@ +using Microsoft.Xna.Framework.Graphics; + +namespace ResourcePackLib.ModelExplorer.Utilities.Extensions; + +public static class GraphcisExtensions +{ + + public static RasterizerState Copy(this RasterizerState state) + { + return new RasterizerState + { + CullMode = state.CullMode, + DepthBias = state.DepthBias, + FillMode = state.FillMode, + MultiSampleAntiAlias = state.MultiSampleAntiAlias, + Name = state.Name, + ScissorTestEnable = state.ScissorTestEnable, + SlopeScaleDepthBias = state.SlopeScaleDepthBias, + Tag = state.Tag, + }; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/MathExtensions.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/MathExtensions.cs new file mode 100644 index 000000000..ab7fd028c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/MathExtensions.cs @@ -0,0 +1,16 @@ +using Microsoft.Xna.Framework; + +namespace ResourcePackLib.ModelExplorer.Utilities.Extensions; + +public static class MathExtensions +{ + public static float ToRadians(this float degrees) + { + return MathHelper.ToRadians(degrees); + } + + public static float ToDegrees(this float radians) + { + return MathHelper.ToDegrees(radians); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/ServiceInjectingActivator.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/ServiceInjectingActivator.cs new file mode 100644 index 000000000..94468f5e7 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/ServiceInjectingActivator.cs @@ -0,0 +1,120 @@ +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using NLog; +using ResourcePackLib.ModelExplorer.Attributes; + +namespace ResourcePackLib.ModelExplorer.Utilities; + +public static class ServiceInjectingActivator +{ + private static ILogger Log = LogManager.GetCurrentClassLogger(); + + public static T GetOrCreateInstance([NotNull] this IServiceProvider serviceProvider) where T : class + { + try + { + var svc = serviceProvider.GetService(); + if (svc != default) + return svc; + } + catch + { + } + + return CreateInstance(serviceProvider); + } + + private static T InjectAttributedMembers(IServiceProvider serviceProvider, T obj) where T : class + { + var type = typeof(T); + var membersWithServiceAttribute = type + .GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Where(m => m.GetCustomAttribute() != null).ToArray(); + + if (membersWithServiceAttribute.Length == 0) return obj; + + foreach (var memberInfo in membersWithServiceAttribute) + { + if (memberInfo is PropertyInfo propertyInfo) + { + try + { + var svc = serviceProvider.GetService(propertyInfo.PropertyType); + propertyInfo.DeclaringType.GetProperty(propertyInfo.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .SetValue(obj, svc, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, null, null); + } + catch (Exception ex) + { + Log.Error(ex, $"Unable to inject service of type ({propertyInfo.PropertyType}) to property '{propertyInfo.Name}' on type '{propertyInfo.DeclaringType?.FullName}'"); + throw; + } + } + else if (memberInfo is FieldInfo fieldInfo) + { + try + { + var svc = serviceProvider.GetService(fieldInfo.FieldType); + fieldInfo.DeclaringType.GetField(fieldInfo.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .SetValue(obj, svc, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetField, null, null); + } + catch (Exception ex) + { + Log.Error(ex, $"Unable to inject service of type ({fieldInfo.FieldType}) to field '{fieldInfo.Name}' on type '{fieldInfo.DeclaringType?.FullName}'"); + throw; + } + } + else + { + Log.Error($"Unable to inject service to member '{memberInfo.Name}' on type '{memberInfo.DeclaringType?.FullName}' because it is not a Property or a Field!"); + } + } + + return obj; + } + + public static T CreateInstance([NotNull] this IServiceProvider serviceProvider) where T : class + { + if (serviceProvider == null) + throw new ArgumentNullException(nameof(serviceProvider)); + + var type = typeof(T); + var constructors = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length).ToArray(); + + if (constructors.Length == 0) + return Activator.CreateInstance(); + + var exceptions = new List(); + + // Find a constructor we can populate (start with most args) + foreach (var constructor in constructors) + { + T constructedObj = default; + try + { + var parameters = constructor.GetParameters(); + var parameterValues = new object[parameters.Length]; + + for (var i = 0; i < parameters.Length; i++) + { + parameterValues[i] = serviceProvider.GetService(parameters[i].ParameterType); + } + + constructedObj = Activator.CreateInstance(type, parameterValues) as T; + } + catch (Exception ex) + { + exceptions.Add(ex); + constructedObj = default; + } + + if (constructedObj != default) + { + InjectAttributedMembers(serviceProvider, constructedObj); + return constructedObj; + } + } + + throw new InvalidOperationException($"No suitable constructor for type {type} found.", new AggregateException(exceptions)); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/appsettings.json b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/appsettings.json new file mode 100644 index 000000000..b4b1b915c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/BlockTextures.psd b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/BlockTextures.psd new file mode 100644 index 000000000..3efb637d7 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/BlockTextures.psd differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.png new file mode 100644 index 000000000..700d0e7ff Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.png differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.psd b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.psd new file mode 100644 index 000000000..6bbd8a585 Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.psd differ diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/Gui.psd b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/Gui.psd new file mode 100644 index 000000000..b312ca7ad Binary files /dev/null and b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/Gui.psd differ diff --git a/submodules/MiNET b/submodules/MiNET index c45365576..907ebc51a 160000 --- a/submodules/MiNET +++ b/submodules/MiNET @@ -1 +1 @@ -Subproject commit c45365576a3fe3d9c1214c3edde2b09e7ed49620 +Subproject commit 907ebc51a887a10dcb317a38f84e219a2e124a74 diff --git a/submodules/RocketUI b/submodules/RocketUI index 1bffeb07c..7bbfa73f1 160000 --- a/submodules/RocketUI +++ b/submodules/RocketUI @@ -1 +1 @@ -Subproject commit 1bffeb07c429eff0c8495a36725752928332d97f +Subproject commit 7bbfa73f141cd109aedc44a92859cbf9bb015276