From b8cd3c83398ca3fa158263ec84fdfd0b2641e474 Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Tue, 11 Jan 2022 01:19:31 +0100 Subject: [PATCH 1/9] resourcepacklib 2.0 initial commit --- src/Alex.sln | 53 ++++++++++++++++++ .../ResourcePackLib.Core/ResourcePack.cs | 11 ++++ .../ResourcePackFactory.cs | 27 ++++++++++ .../ResourcePackLib.Core.csproj | 13 +++++ .../ResourcePackLib.Loader.Bedrock/Class1.cs | 5 ++ .../Data/BedrockBlockDef.cs | 22 ++++++++ .../Data/BedrockBlockDefIsotropic.cs | 27 ++++++++++ .../Data/BedrockBlockDefTextures.cs | 38 +++++++++++++ .../BedrockBlockDefIsotropicJsonConverter.cs | 30 +++++++++++ .../BedrockBlockDefTexturesJsonConverter.cs | 54 +++++++++++++++++++ .../ResourcePackLib.Loader.Bedrock.csproj | 13 +++++ .../ResourcePackLib.Loader.Java/Class1.cs | 5 ++ .../ResourcePackLib.Loader.Java.csproj | 13 +++++ 13 files changed, 311 insertions(+) create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Class1.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDef.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefIsotropic.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockBlockDefTextures.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefIsotropicJsonConverter.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/JsonConverters/BedrockBlockDefTexturesJsonConverter.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/ResourcePackLib.Loader.Bedrock.csproj create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/Class1.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj diff --git a/src/Alex.sln b/src/Alex.sln index 31398a1f2..fa1f84221 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Appveyor|Any CPU = Appveyor|Any CPU @@ -234,6 +242,48 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -246,6 +296,9 @@ Global {3825C1C5-02EE-41B9-B679-1A724D256F3E} = {FB362EE7-987B-4316-BEC6-7F27C7117E92} {18E484B8-B625-45CD-92DD-52B9CA6A8838} = {56040BCF-D80D-4435-B420-BF8B29A5B4B1} {09378720-C7F2-4AE4-9111-B0D64F0D9496} = {66F9179F-DEB1-45B9-B067-D613A2857D5C} + {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {32B4D997-4F97-43FC-9636-1E35A79AA844} diff --git a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs new file mode 100644 index 000000000..66c53223e --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs @@ -0,0 +1,11 @@ +namespace ResourcePackLib.Core; + +public class ResourcePack +{ + + public ResourcePack() + { + + } + +} \ 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..b4a7bbef4 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs @@ -0,0 +1,27 @@ +namespace ResourcePackLib.Core; + +public interface IResourcePackReader +{ + Task CanLoadAsync(); +} + +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.Loader.Bedrock/Class1.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Class1.cs new file mode 100644 index 000000000..787896128 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Class1.cs @@ -0,0 +1,5 @@ +namespace ResourcePackLib.Loader.Bedrock; + +public class Class1 +{ +} \ 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/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/Class1.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Class1.cs new file mode 100644 index 000000000..40e9b3062 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Class1.cs @@ -0,0 +1,5 @@ +namespace ResourcePackLib.Loader.Java; + +public class Class1 +{ +} \ 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..e2ceb7b29 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + From 62bf223d92c52d807c9ade5260e8182d67b7587b Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Sat, 15 Jan 2022 00:49:06 +0100 Subject: [PATCH 2/9] new resourcepack lib, implement new IO stuff for dir/zip handling and more work on resourcepack structuring - bedrock focused --- .../MCBedrockResourcePack.cs | 23 +--- src/Alex/Utils/Assets/MCJavaAssetsUtils.cs | 3 +- .../Abstractions/IResourcePackReader.cs | 9 ++ .../Abstractions/ISoundDef.cs | 10 ++ .../Abstractions/ISoundDefSound.cs | 14 +++ .../ResourcePackLib.Core/Data/CubeFace.cs | 11 ++ .../IO/Disk/DiskDirectory.cs | 44 +++++++ .../ResourcePackLib.Core/IO/Disk/DiskFile.cs | 16 +++ .../IO/Disk/DiskFileSystemEntry.cs | 19 +++ .../ResourcePackLib.Core/IO/IDirectory.cs | 33 +++++ .../ResourcePackLib.Core/IO/IFile.cs | 14 +++ .../IO/IFileSystemEntry.cs | 17 +++ .../IO/Zip/ZipDirectory.cs | 31 +++++ .../IO/Zip/ZipDirectoryRoot.cs | 59 +++++++++ .../ResourcePackLib.Core/IO/Zip/ZipFile.cs | 24 ++++ .../IO/Zip/ZipFileSystemEntry.cs | 36 ++++++ .../ResourcePackLib.Core/ResourcePack.cs | 42 ++++++- .../ResourcePackFactory.cs | 5 - .../ResourcePackLib.Core.csproj.DotSettings | 4 + .../Utils/NamedResourceLocationCollection.cs | 6 + .../Utils/ResourceLocation.cs | 83 +++++++++++++ .../Utils/ResourceLocationCollection.cs | 6 + .../BedrockResourcePackImpl.cs | 12 ++ .../BedrockResourcePackReader.cs | 26 ++++ .../ResourcePackLib.Loader.Bedrock/Class1.cs | 5 - .../Data/BedrockPackManifest.cs | 12 ++ .../Data/BedrockPackManifestDependency.cs | 9 ++ .../Data/BedrockPackManifestHeader.cs | 14 +++ .../Data/BedrockPackManifestMetadata.cs | 8 ++ .../Data/BedrockPackManifestModule.cs | 11 ++ .../Data/BedrockPackManifestModuleType.cs | 11 ++ .../Data/BedrockSoundDef.cs | 26 ++++ .../ResourcePackLib.Loader.Java/Class1.cs | 5 - .../Data/JavaBlockModelDef.cs | 117 ++++++++++++++++++ .../Data/JavaSoundDef.cs | 14 +++ .../JavaResourcePackImpl.cs | 11 ++ .../JavaResourcePackReader.cs | 17 +++ .../ResourcePackLib.Loader.Java.csproj | 4 + 38 files changed, 774 insertions(+), 37 deletions(-) create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Abstractions/IResourcePackReader.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDef.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Abstractions/ISoundDefSound.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskDirectory.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFile.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Disk/DiskFileSystemEntry.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/IDirectory.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/IFile.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/IFileSystemEntry.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectory.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipDirectoryRoot.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFile.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/IO/Zip/ZipFileSystemEntry.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/ResourcePackLib.Core.csproj.DotSettings create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Utils/NamedResourceLocationCollection.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocation.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Utils/ResourceLocationCollection.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackImpl.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/BedrockResourcePackReader.cs delete mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Class1.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifest.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestDependency.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestHeader.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestMetadata.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModule.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockPackManifestModuleType.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Data/BedrockSoundDef.cs delete mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/Class1.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaBlockModelDef.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/JavaSoundDef.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackImpl.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/JavaResourcePackReader.cs 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/Utils/Assets/MCJavaAssetsUtils.cs b/src/Alex/Utils/Assets/MCJavaAssetsUtils.cs index 7b634ea6e..a38229805 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/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..deb51386d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs @@ -0,0 +1,11 @@ +namespace ResourcePackLib.Core.Data; + +public enum CubeFace +{ + Up, + Down, + North, + South, + East, + West +} \ 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/ResourcePack.cs b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs index 66c53223e..be026d50e 100644 --- a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs @@ -1,11 +1,47 @@ -namespace ResourcePackLib.Core; +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 ResourcePack() + 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 index b4a7bbef4..217558578 100644 --- a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePackFactory.cs @@ -1,10 +1,5 @@ namespace ResourcePackLib.Core; -public interface IResourcePackReader -{ - Task CanLoadAsync(); -} - public class ResourcePackFactory { private List _readers = new List(); 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/Class1.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Class1.cs deleted file mode 100644 index 787896128..000000000 --- a/src/ResourcePackLib/ResourcePackLib.Loader.Bedrock/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace ResourcePackLib.Loader.Bedrock; - -public class Class1 -{ -} \ 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.Java/Class1.cs b/src/ResourcePackLib/ResourcePackLib.Loader.Java/Class1.cs deleted file mode 100644 index 40e9b3062..000000000 --- a/src/ResourcePackLib/ResourcePackLib.Loader.Java/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace ResourcePackLib.Loader.Java; - -public class Class1 -{ -} \ No newline at end of file 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/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 index e2ceb7b29..83f58f26d 100644 --- a/src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj +++ b/src/ResourcePackLib/ResourcePackLib.Loader.Java/ResourcePackLib.Loader.Java.csproj @@ -10,4 +10,8 @@ + + + + From 94162effa73897bd8642883861664de3c7281292 Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Mon, 17 Jan 2022 00:19:41 +0100 Subject: [PATCH 3/9] Start on ModelExplorer app to debug model rendering --- src/Alex.sln | 17 ++ src/Alex/Entities/PlayerController.cs | 4 +- .../ResourcePackLib.Core/Data/CubeFace.cs | 12 +- .../ResourcePackLib.Core/Data/Cuboid.cs | 100 ++++++ .../Data/Models/BlockModel.cs | 24 ++ .../Converters/Vector2ArrayJsonConverter.cs | 64 ++++ .../Converters/Vector3ArrayJsonConverter.cs | 73 +++++ .../Converters/Vector4ArrayJsonConverter.cs | 82 +++++ .../ResourcePackLib.Core/ResourcePack.cs | 18 +- .../Data/Shaders/ShaderDef.cs | 33 ++ .../Abstractions/ICamera.cs | 26 ++ .../Abstractions/IGame.cs | 27 ++ .../Abstractions/Worker.cs | 53 ++++ .../Attributes/ServiceAttribute.cs | 7 + .../Components/CameraMouseController.cs | 66 ++++ .../Configuration/GameOptions.cs | 6 + .../Content/Content.mgcb | 97 ++++++ .../Content/Fonts/Default.spritefont | 60 ++++ .../Content/Fonts/Kenney Future Square.ttf | Bin 0 -> 22884 bytes .../Content/Fonts/Kenney Future.ttf | Bin 0 -> 34116 bytes .../Content/blocks/face_down.png | Bin 0 -> 2135 bytes .../Content/blocks/face_east.png | Bin 0 -> 2202 bytes .../Content/blocks/face_north.png | Bin 0 -> 2161 bytes .../Content/blocks/face_south.png | Bin 0 -> 2126 bytes .../Content/blocks/face_up.png | Bin 0 -> 2051 bytes .../Content/blocks/face_west.png | Bin 0 -> 2215 bytes .../Entities/AxisEntity.cs | 42 +++ .../Entities/CubeEntity.cs | 114 +++++++ .../Entities/DrawableEntity.cs | 119 ++++++++ .../Entities/Entity.cs | 79 +++++ .../Entities/MCBlockModelEntity.cs | 114 +++++++ .../Entities/MCEntity.cs | 186 +++++++++++ .../Graphics/Camera.cs | 170 +++++++++++ .../Graphics/DebugGrid.cs | 157 ++++++++++ .../Graphics/GuiRenderer.cs | 102 +++++++ .../ModelExplorerGame.cs | 151 +++++++++ .../ResourcePackLib.ModelExplorer/NLog.config | 36 +++ .../ResourcePackLib.ModelExplorer/Program.cs | 95 ++++++ .../ResourcePackLib.ModelExplorer.csproj | 55 ++++ .../Scenes/GuiSceneBase.cs | 65 ++++ .../Scenes/IScene.cs | 17 ++ .../Scenes/ModelViewScene.cs | 25 ++ .../Scenes/Scene.Components.cs | 288 ++++++++++++++++++ .../Scenes/Scene.cs | 86 ++++++ .../Scenes/SceneManager.cs | 117 +++++++ .../Scenes/Screens/DebugGui.cs | 55 ++++ .../Scenes/Screens/MainMenuScreen.cs | 13 + .../Scenes/Screens/MainMenuScreen.xaml | 18 ++ .../Scenes/Styles.xaml | 5 + .../ResourcePackLib.ModelExplorer/Startup.cs | 30 ++ .../Utilities/EventHelpers.cs | 28 ++ .../Extensions/GraphcisExtensions.cs | 22 ++ .../Utilities/Extensions/MathExtensions.cs | 16 + .../Utilities/ServiceInjectingActivator.cs | 120 ++++++++ .../appsettings.json | 9 + .../assets/BlockTextures.psd | Bin 0 -> 357817 bytes 56 files changed, 3087 insertions(+), 16 deletions(-) create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Data/Cuboid.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Data/Models/BlockModel.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector2ArrayJsonConverter.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector3ArrayJsonConverter.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Core/Json/Converters/Vector4ArrayJsonConverter.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.Loader.Java/Data/Shaders/ShaderDef.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/Worker.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Attributes/ServiceAttribute.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Configuration/GameOptions.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Default.spritefont create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future Square.ttf create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Fonts/Kenney Future.ttf create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_down.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_east.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_north.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_south.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_up.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/blocks/face_west.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/AxisEntity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/CubeEntity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/DrawableEntity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCBlockModelEntity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/DebugGrid.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/NLog.config create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Program.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/IScene.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.Components.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/SceneManager.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Startup.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/EventHelpers.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/GraphcisExtensions.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/Extensions/MathExtensions.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Utilities/ServiceInjectingActivator.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/appsettings.json create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/BlockTextures.psd diff --git a/src/Alex.sln b/src/Alex.sln index 3fbe20ee1..82aaa2ae0 100644 --- a/src/Alex.sln +++ b/src/Alex.sln @@ -63,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 @@ -292,6 +294,20 @@ Global {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 @@ -308,6 +324,7 @@ Global {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/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs b/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs index deb51386d..76835a6f0 100644 --- a/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs +++ b/src/ResourcePackLib/ResourcePackLib.Core/Data/CubeFace.cs @@ -2,10 +2,10 @@ public enum CubeFace { - Up, - Down, - North, - South, - East, - West + 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/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 index be026d50e..921cc0b0e 100644 --- a/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs +++ b/src/ResourcePackLib/ResourcePackLib.Core/ResourcePack.cs @@ -35,13 +35,13 @@ public ResourcePack(IDirectory directory) Directory = directory; } - public bool Contains(ResourceLocation resourceLocation) - { - - } - - public bool TryGet(ResourceLocation resourceLocation, out T resource) - { - - } + // 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.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.ModelExplorer/Abstractions/ICamera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs new file mode 100644 index 000000000..595868999 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs @@ -0,0 +1,26 @@ +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 Quaternion 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); + + } +} \ 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..025c11ad7 --- /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 GraphicsDeviceManager { 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/CameraMouseController.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs new file mode 100644 index 000000000..8e7462f59 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs @@ -0,0 +1,66 @@ +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 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; } = 180d; + + private Vector3 _rotation = Vector3.Zero; + + 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; + + _rotation -= new Vector3(MathHelper.ToRadians(lookDelta.X), MathHelper.ToRadians(lookDelta.Y), MathHelper.ToRadians(lookDelta.Z)); + //_rotation.Normalize(); + + Camera.Rotation = Quaternion.CreateFromYawPitchRoll(_rotation.X, _rotation.Y, _rotation.Z); + + _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..b10bf864d --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb @@ -0,0 +1,97 @@ + +#----------------------------- 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 + 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 0000000000000000000000000000000000000000..488f29ed0bb65e6922d1f9a971c6a79dfd567e9e GIT binary patch literal 22884 zcmeHPYiu0Xbw11GL!utGqlfK8cE+wOQ;9_CLEDm|Iy9s*a_yvoq_{z2G+t6GaxId} zESI5_y6F_K3M23j|1b=rHHsAQ4+BvO{RrCp@DCbD(X7R= zL)-5=_nw(Mv$G;4OKsqCs5AF<&UYU7+&gz?DN0F1`X!dKM89=>XixWZJ)afXd<4CZ zjZar*|MD+>mmKFsx?h->I(zD`{^sxh6XV|!`S|c;wQ}-vH@2P;=^_8Ulj!J*TfdKX zjP};a>E@YFcdvgQ?H5JLC#UM;6)7G4vB;<2L3?t#a%Q&mk1{H3%>rL9uEbmsOyjW$NN zM^SWVv@JRioxkJGx4Yl&ZMDD~$)Cxemu5WAEuLq;@(6ia|I&J;wb(k=`puu*b@9T* zuU$NU@v9eKy7=71yZ-4y&ucy3q2`tAeH4L}9C{y3=Kql^+!H`={NC)pf9{5Z|AqZh z4f)>Im;R6ymaVuY<+vQ9xe9xg+eL6MqaXY|tys=M%JrkiUq39JL#@XRaIl1W7pUyX z>g*>kDeUWFpC8jY$Cn`GhYWF)*O%0}9N9$foU?N&8II?%&6gs7E{A9FF`hiG_2
88B)u>>VD;5D93WhY+K;jmjV?d^mS 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�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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..95518af491ec0eda9584aab24aa494797525f2c0 GIT binary patch literal 2135 zcmbVNYfuwc6u#l15T!7nXe%w-XjKN1-AzJ5Vjw)iBMJx+1r^0SHV`4%bhDTcP>}v` ztd6#297kJ4r=!Ry5VZsK3DhF>F(V^dt*^v!B(#btgohwPH@x(RV{P}(-gEbS-#Pbu z-<{o_mYV28|AYQbde0lxkd)0 zQAIK`!&8tHeH@mjNiq}I3Ug|P(wwgps2JiXNMsUH1X_%gLndv3&LA{JGRAm?)Y`QT zGoY~)GCz_r-c(jf8We{U7{p^COeKP_A-;fx^0-_c5(cpmHUcBzFv@14JR!mrqI_uL zVNlfwm0Bp1BuvzzqDV#_N$Q0#Y&05KMh*)na$!^;5WolK;GLk`cI(`JLJ|$&}SZA0R6g4u~B-g_z3xTy-*SW^B4WtZv z)5bfq4H-px43=RAypT{*dZ_15f~ncPzmO}D>PEPn&`_et3naL*P>bnEsU(s?ePO9I zDj^$FVjKm^XDYA=9+Ru)V$2AY5@n)lMFfT*C>D;WCwbn5=f&~`v4V&QK93uNqVaJt z2@*lPB%F&Nt_==4iIwUMq+F-OCTnY`+7np*d$Gbe0+W+Ck%8j{lP*Zh!%5tbhwC9W z%3?!{Gc`IDZZw3rG#Wp$1S2$?FjWG9YoW0r3pMX(fQ#`tJf$3C3KSeQlgH*NnQ}Co z!{jI<*vbf%K!9KvV*;=Gzr%y6reK#--g3_57G+1*a_SyX!PHH{bky}BsC$xC+kG1V z53^JflVQ5|{2`hjkPe!H zis(zNwgGG6aM$Xy4~k9-Z2kP4ga`SybskMShx#;m?$7SeYsuein=xeHR#)gy%))2)<(BUoil?;$~d9e5Z zMPi9v{hu9U`Z=qIZ#GtEHUf+1{wB+XEk%`)1-|PZwJs+P`k9Blfcj~&Z5m%( zAN>>CJrr;@_S%K7da4HM9uW2`EzJ2*vyC;1&3?e=T>tFs^S|#%z3MZ|ni>bEk;}V|z1V7{H;IB8Yru86dkb7mu#rdiqgH{i zg{#25w&*<-TTdEC=g=e?nS*}oTEmd_g}5aA@5HN>&NJlT^V{ts4aLWAOdB1kXg3ek zAP1K%xAZ#WDouapmaiXKl5=I_jnb5wizS1r&x-!Six;(2Rt1QfN1cwOON;>ERu;eV z)dJI9`^)2V`v+f1U#vCyuEB;YM2RN=1pI&(=;&N9_vlfl!qBsO?;2X($+lqnDK|@- zPZmw@KXXF2@ysb1-wK-wb~x7u_WJk4)CD)*>Um~O)F?*2KTsM9>Z9LEbBX_fVOhxb zAGzLU@BURe;;7!2&OO(M`J&NXw7YIwKb&OTZmx>S=~cxge!aAw^?~&^F6uIDLHW+o zZ(D=sJ5n-j~KZ_18;JI~_q_QS%3O`lg5%W%2e|i5mZ2`YlR|O`YBL z(0zlrY2;IPi>KR|_5U+xa^epIrw;AifcN!1iQ6q+8sM}(A6`rQ|9sM*Bht2ImWH9B z?!&E>b*hfW$AiY1O@UtT()Hg2+wisU;ghcp`F=BS%HmnIy?fs1-4SmDTw59&63(&K QyMFc3_*6-4?1u7x0KHJzZ~y=R literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2c31c104098aaffdb98c9f39df36700a45b6c200 GIT binary patch literal 2202 zcmd5;Yfuwc6u#jhiVv6;aO!i3GrnN6yLpjBLV^T@)*u5YK@mtcH;_uQFtHkhz7~DDidHG-N=VAUQG0VZHiL;l0}w5PFl~8qI=OraDb})OF-Wvc!VnlBtwwBy0;5G@ld=bRCCuLY z8e<~^DfAL4`(;xqWeO5UnIR(N;3$dXTqH`&A%uLs5RXK-I2Xro0Y-39LMXxc5+Vv2 zeArAivsNcbRU{17Vj?L!pQcR`46|CT94n7Qne#A0EEZ!p7vpkKCIht;8ELf*HCm<& zF({ygH0w>Yo-!g{Mzw}2q@`@8)0amun3T#_#74`YP>f`lO>M#m4vrZN-g6CPTj*5y zR~v84wxk!CAeIU(RH2z<=AoN11ZK2*dm(Qk(~V@FSqI?}6q9QFxpoC5%f;dh<0j?Y3c@sXCh!Wu3*x6A+ek?)6$BE*_ z{DcHC!4ru1f_P!<5H`tZq18qb4%OB(wFj}WZ^cUD%ur2J=5&fG7;-^MK1EZOe9DAy z2@V&To}o8tDXV3McSbLdtbk_yGN?^3QwC%}WQqO_4e((UPe`gED%S9HsF2GiQ8gjp zp*&K=B}H1X7>AHOh}XWWco@?Z=5@;7oHO)_v7>kU>K-t`tD6Lk%=Ixd_oVQl2L-^d zDoGKWZfkhdbTGM54(zvgJ-c{r;=1Nv?Q6JCVp@Oa9z3+8Epc%sZ+_Flz3*WsL*_qp zhE&Usa>6#Q3CUWsy0u4BQM@gE@6HibS)JYI%a$%{o9dW7H#2c(si)|-jjo>YAB}bB zrrjqdI~(@|<%YQv+yU#v?z*kd+n2dre%CZUa)5wfFdX=SMnvFGx3si1t(WyL>vdnR z+}3t;-?6JTt%+b%?N1f53#I)w`hacc7HI`$ES0%xn@u%U1uNwscjPJLXu<~coOzMs z#)~bjpX>w2g+Uj(?xZ>P=Q+Ru|L^slhjXfCO(-et>%Y?dRd@f>J0;yG`w!C+o1M|K`S_u`tX}um zqDd2@`*uG?Pc0Ap6|XsIe)4R`c3>*}UWv)onX6&~Y#SD+mY_FD0^3h&=SQhc)F$Jx27{sh@F2;u+$ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d06d8dfaa3b75e9a7806be5cc1aa269b51be88d2 GIT binary patch literal 2161 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 JI=&>M=r7Mq*~S0> literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..909662e24a8441d0e966b24f42c6b27c0e9c1f90 GIT binary patch literal 2051 zcmbtVYfuwc6uyus5~R?<7mk{3EEGB zI+j}6&a}4b7%Ee>4}@`A>)2@n9bdE+bgYkRt#+_Nl^Ust2m!j`q1B-}WB1R!clUhX z`ObIGncb3=u_%-?mjeI@)h*U!0}#M$1HiOEW;qkC!k8tJ*5=VGC?oA4Y!IXvC_O}U zW}*mYL&8wLwiPAGN?h$B+D0F#Kt5@8&I3vnSTj7Kq?k4Y3lu>zAK z;}4I?W-}NS*_yQRT+B(yE2e3y0!1AThrl5cP_`lzlgs6(5Jz#G&qVO;<-oY)wypKy0y(8^u_LIteR^352NG?5}Gy+D>P~ zhcYIOw&#>vA({>CRGE!r?!&ln0?c^#f5;SGYp{UY{F32jTXv$tpSrHr) z;7Cla$zq@!_E`TNjg_o{Hq$z2NV8F9WYlDZX%YcqC>2Ra0`ld0k&!RK#U!7=;zfKB zDZ@#bK`s|U$Q#ESp4B{*5rz6Y<=Hw)@&YV4aitKY8#DXZA6T~cd| zV%p4A2_f2`pjd;vC4Z}?dP#nT&b_{Ms4^?qOzuj$UUe_=1!Y2G`)>`KW_+SMTZD39aeUA zdh-0tz}#P^$MqekRRRgC4Sex~vGDh^BW|!;9J_AHjayrLPIe4#wtI%$KNej%{GsnY ztaL>e3@3QurcO^rN0@SCo5^u5H%RqHReoF#Yj{`HqPZ8BF?C=3>~^&Cqx0UqL(gF{ z)|iX?{)nElwIt$B!C$@vR$(yvzcG(dJaUZD&0~6DX_Itdpy%V`?tA#1f*xPO)Mw<&+MO}t zd$EgkZDU+%i%IW0=4raXj;z{ooExU<%Nz{g*0qkjmfF?BZSZa%S<`uC0oYOaw2B{* zJ09%9k1pTN{x-0z9(B*J@Rg&Yk(uot5`2*SbO$HpvmLpQgj59gr4^`>di(3e*vQ#7 zkLcO?-aF#g$6jCG$mRZjeNi`h+cT?^l9Nt+KWk^Se`p7znt`Y*GV`bQ7yu< literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..52784cb4d3b97a74c2afc5cf2f8a456f284691d9 GIT binary patch literal 2215 zcmbVOe^8TU9DfEoK%q1&P89KVomuL;_kFjQ{o?1?ASnTlC@4?2ciS@MQ_w+?EGI0=T7RY9hfckxWW1^9pTb9Fz?LrGeH#B+X3a z!7NA_N>+Ri;{XV>o3eA6-1Nl?J#7|JejB0FY~|1Z#3eedl)ex$hz{nNEb)TAW2^u% z8R7*wG3j`^RR!}+i%MS26#9L(kJza9AvIW>w*n~5t5Rivi%#b#(>($w(+?vK!5 zGAKk)55+n{it3&oNmHw8Q>_zee{%RzsUn|ocV#jh{gRpu>eHnY7niY6H6 zT_<)loT<*#M)=geDeI1UTg08NJ`Wpmpow1rk^lmxf)L8(n+7C}FD{6NllTGPWhAJMoL@<9fL`5S zntC4*c#AE$+!9 z;~XyN%LC+rn_qnOb@1u>%r(=%teT_2Io@W>Lf1tU92vM&2f9Zyc6P{4-nbhJdp9oa z=}4KkU`CfA8V|((xVB_N(8slJM)B7~R@9;q{Z&!PWGTU{s8k;-(SF1RKi@u z?x>juLTk3BzB_e+DF61mS|iZKXeaT{ZI|4tOYh1IpXtbOI%Po6GJjoZdMxQRR- zFqGea>FV9KUsi3Q$CfEH?D6I0w&<~;>-Dbs?x^i)rd9hE&)@M!e(zfr-E!Xua`kKz zyRE8uE4HKPc>gDCU_i-j9`(<=sQYL=muQGrPtB6nUXbiev#3vDaQ zxKlTCzf$Un2K1K2&d=_lRtqdemA8Mw-Q;w0nd5x^A z-w{@^M*BGGE6gt)+N3@aFqD4vOkSoyb!^9dNv>opab#O)L*4e@jo5XQZm%F$=bL721Kb3eE _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..3f49e9e1e --- /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 Quaternion 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/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..d8eab36b0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs @@ -0,0 +1,186 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Newtonsoft.Json; +using ResourcePackLib.Core.Data; +using ResourcePackLib.Core.Json.Converters; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; +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 TruMesh +{ + +} + +public class TruBone +{ + public string Name { get; } + public TruBone Parent { get; set; } + + public Transform3D Transform { get; } = new Transform3D(); + + public TruBone(string name) + { + Name = name; + } +} + +public class TruModel +{ + public string Name { get; } + public List Meshes { get; } + public List Bones { get; } + public Transform3D Transform { get; } = new Transform3D(); + + public TruModel(string name) + { + Name = name; + } +} + +public class MCEntity : DrawableEntity +{ + private MCEntityGeometry Geometry { get; set; } + + + public MCEntity(IGame game) : base(game) + { + } + + private readonly List _disposables = new List(); + protected override void LoadContent() + { + base.LoadContent(); + + var entityDef = "fox.geo.json"; + var defPath = Path.Join("S:\\Temp\\resource_packs\\bedrock-1.18.1\\models\\entity\\", entityDef); + Geometry = JsonConvert.DeserializeObject(File.ReadAllText(defPath)); + + 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/Graphics/Camera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs new file mode 100644 index 000000000..c56e644aa --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs @@ -0,0 +1,170 @@ +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; + +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 = 1000.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 Quaternion 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.GraphicsDeviceManager.DeviceCreated += (sender, args) => UpdateProjection(); + _game.GraphicsDeviceManager.DeviceReset += (sender, args) => UpdateProjection(); + _game.Window.ClientSizeChanged += (sender, args) => UpdateProjection(); + Transform.Changed += (sender, args) => UpdateView(); + } + + private void UpdateView() + { + _forward = Vector3.Normalize(Vector3.Transform(Vector3.Forward, Rotation)); + _up = Vector3.Normalize(Vector3.Transform(Vector3.Up, Rotation)); + _right = Vector3.Normalize(Vector3.Transform(Vector3.Right, Rotation)); + + 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(60.0f.ToRadians(), aspect, NearDistance, FarDistance); + } + else + { + Projection = Matrix.CreateOrthographic(w, h, NearDistance, FarDistance); + } + } + + + + public void MoveRelative(Vector3 move) + { + var forward = Vector3.Transform(move, Rotation); + 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..4c5525610 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs @@ -0,0 +1,102 @@ +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() + { + } + + 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..d5d22c61c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs @@ -0,0 +1,151 @@ +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; + +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 GraphicsDeviceManager => _graphics; + + private GraphicsDeviceManager _graphics; + private SpriteBatch _spriteBatch; + + public SceneManager SceneManager { get; private set; } + public InputManager InputManager { get; private set; } + + public GuiManager GuiManager { get; private set; } + + public ModelExplorerGame(IServiceProvider services) + { + Instance = this; + ServiceProvider = services; + _graphics = new GraphicsDeviceManager(this); + Content.RootDirectory = "Content"; + IsMouseVisible = true; + + var hostApplicationLifetime = services.GetRequiredService(); + hostApplicationLifetime.ApplicationStopping.Register(OnHostApplicationStopping); + } + + private void OnHostApplicationStopping() + { + GpuResourceManager.Dispose(); + } + protected override void Initialize() + { + GraphicsDeviceManager.GraphicsProfile = GraphicsProfile.HiDef; + GraphicsDeviceManager.SynchronizeWithVerticalRetrace = false; +// GraphicsDeviceManager.PreferMultiSampling = true; + IsFixedTimeStep = false; + Window.AllowUserResizing = true; + + GraphicsDeviceManager.ApplyChanges(); + + InputManager = ServiceProvider.GetRequiredService(); + Components.Add(InputManager); + + Components.Add(SceneManager = ServiceProvider.GetRequiredService()); + + base.Initialize(); + } + + protected override void LoadContent() + { + GpuResourceManager.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 = ServiceProvider.GetRequiredService(); + GuiManager.ScaledResolution.TargetWidth = 720; + GuiManager.ScaledResolution.TargetHeight = 360; + GuiManager.AddScreen(_debugGui = new DebugGui()); + Components.Add(GuiManager); + GuiManager.DrawOrder = 10; + GuiManager.Init(); + + Options = ServiceProvider.GetRequiredService>().Value; + + SceneManager.SetScene(); + _graphics.GraphicsDevice.Viewport = new Viewport(Window.ClientBounds); + + 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)) + { + var r = GraphicsDevice.RasterizerState.Copy(); + r.FillMode = (r.FillMode == FillMode.Solid) + ? FillMode.WireFrame + : FillMode.Solid; + GraphicsDevice.RasterizerState = r; + } + + var rotateSpeed = 45f; + var moveSpeed = 2f; + + var keyboard = Keyboard.GetState(); + if (keyboard.IsKeyDown(Keys.LeftControl)) + { + var rotateDiff = (float)((gameTime.ElapsedGameTime.TotalSeconds / 1f) * rotateSpeed); + if (_keyboardListener.IsAnyDown(Keys.NumPad8)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Up, MathHelper.ToRadians(-rotateDiff)); + if (_keyboardListener.IsAnyDown(Keys.NumPad2)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Up, MathHelper.ToRadians(rotateDiff)); + if (_keyboardListener.IsAnyDown(Keys.NumPad4)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Right, MathHelper.ToRadians(-rotateDiff)); + if (_keyboardListener.IsAnyDown(Keys.NumPad6)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Right, MathHelper.ToRadians(rotateDiff)); + } + 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); + } + + 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..24905d2f8 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj @@ -0,0 +1,55 @@ + + + + Exe + net6.0 + enable + latest + + + + + + + + + + + + + + + + + + + + + + false + + + + + + %(Filename).cs + + + PreserveNewest + + + + + MainMenuScreen.xaml + + + Scene.cs + + + + + + diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs new file mode 100644 index 000000000..188702f3e --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs @@ -0,0 +1,65 @@ +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 = screen; + 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/IScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/IScene.cs new file mode 100644 index 000000000..f277a4bc7 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/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/ModelViewScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs new file mode 100644 index 000000000..21cff2a7f --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs @@ -0,0 +1,25 @@ +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(); + + var cube = new CubeEntity(ModelExplorerGame.Instance); + cube.Visible = true; + cube.Position = Vector3.Zero; + cube.Scale = Vector3.One; + + Components.Add(Services.GetOrCreateInstance()); + Components.Add(cube); + + Components.Add(Services.GetOrCreateInstance()); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.Components.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.Components.cs new file mode 100644 index 000000000..b545aa62c --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/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/Scene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.cs new file mode 100644 index 000000000..8e93d96ca --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/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/SceneManager.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/SceneManager.cs new file mode 100644 index 000000000..ec1b850c0 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/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/Screens/DebugGui.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs new file mode 100644 index 000000000..c96132758 --- /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}, {p.W: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..26f9f6c88 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml @@ -0,0 +1,18 @@ + + + + + + + + \ 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..973d1ebf6 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml @@ -0,0 +1,5 @@ + + + \ 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 0000000000000000000000000000000000000000..3efb637d786c5189a93414cc062843680b7e8862 GIT binary patch literal 357817 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 literal 0 HcmV?d00001 From c83bda9e56b149267cf2dac4524c23aff1003b5d Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Tue, 18 Jan 2022 01:13:14 +0100 Subject: [PATCH 4/9] start modelbuilder+cube texture mapping --- .../Geometry/ModelBuilder.cs | 241 ++++++++++++++++++ .../Geometry/TruModel.cs | 98 +++++++ .../ResourcePackLib.ModelExplorer.csproj | 3 - .../assets/CubeTextureMap.png | Bin 0 -> 1988 bytes .../assets/CubeTextureMap.psd | Bin 0 -> 103992 bytes 5 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.psd diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs new file mode 100644 index 000000000..e3efdfd73 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs @@ -0,0 +1,241 @@ +using System.Numerics; +using ResourcePackLib.Core.Data; + +namespace ResourcePackLib.ModelExplorer.Geometry; + +public struct ModelVertexIndexPositionTexture +{ + public Vector3 Position; + public Vector2 Uv; + + public ModelVertexIndexPositionTexture(Vector3 position, Vector2 uv) + { + Position = position; + Uv = uv; + } +} +public class ModelCubeMeshBuilder +{ + private readonly ModelBoneBuilder _parentBone; + private Vector3 _origin; + private Vector3 _size; + private Vector2 _uv; + + internal Cuboid _cuboid; + + 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, Vector2 uvSize) + { + 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; + var o1 = new Vector2((2 * size.Y) + (2 * size.X), (size.Y + size.Z)); + + 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]], new Vector2(uvMin.X, uvMin.Y) / o1); + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[1]], new Vector2(uvMax.X, uvMin.Y) / o1); + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[2]], new Vector2(uvMax.X, uvMax.Y) / o1); + vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[3]], new Vector2(uvMin.X, uvMax.Y) / o1); + + 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.Y), new Vector2(size.Y, size.Z)); + + /* Up | Top? */ + defineFace(new short[] { 0b010, 0b110, 0b111, 0b011 }, new Vector2((2*size.Y)+size.X, size.Y), new Vector2(size.X, size.Z)); + + /* South | Back? */ + defineFace(new short[] { 0b011, 0b111, 0b101, 0b001 }, new Vector2(size.Y+size.X, 0f), new Vector2(size.X, size.Y)); + + /* West | Left? */ + defineFace(new short[] { 0b010, 0b011, 0b001, 0b000 }, new Vector2(size.Y+size.X, size.Y), new Vector2(size.Y, size.Z)); + + /* Down | Bottom? */ + defineFace(new short[] { 0b100, 0b000, 0b001, 0b101 }, new Vector2(size.Y, size.Y), new Vector2(size.X, size.Z)); + + /* North | Front? */ + defineFace(new short[] { 0b110, 0b010, 0b000, 0b100 }, new Vector2(size.Y, 0f), new Vector2(size.X, size.Y)); + + return (vertices, indices); + } + + public TruModelMesh Build() + { + var cuboid = new Cuboid(_origin, _origin + _size); + _cuboid = cuboid; + + return new TruModelMesh() + { + }; + } +} + +public class ModelBoneBuilder +{ + private readonly ModelBuilder _modelBuilder; + private readonly string _name; + internal string _parentName; + private Vector3 _pivot; + private List _cubeBuilders = new List(); + internal TruModelBone _built; + + 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 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, + }; + + foreach (var cube in _cubeBuilders) + { + bone.AddMesh(cube.Build()); + } + + bone.Transform.LocalRotationOrigin = _pivot; + + _built = bone; + return bone; + } +} + +public class ModelBuilder +{ + private 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(); + + foreach (var boneBuilder in _boneBuilders) + { + var bone = boneBuilder.Build(); + bones.Add(bone); + } + + 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)) + { + boneBuilder._built.Parent = bone; + break; + } + } + } + } + + var model = new TruModel(); + return model; + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs new file mode 100644 index 000000000..63bb6ee02 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs @@ -0,0 +1,98 @@ +using System.Collections.ObjectModel; +using System.Numerics; +using JetBrains.Annotations; +using Microsoft.Xna.Framework.Graphics; +using RocketUI; + +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() + { + + } +} + +public class TruModelTransform : Transform3D +{ + public TruModelTransform() + { + } +} + +public class TruModelMeshCollection : ReadOnlyCollection +{ + public TruModelMeshCollection([NotNull] IList list) : base(list) + { + + } +} +public class TruModelBoneCollection : ReadOnlyCollection +{ + public TruModelBoneCollection([NotNull] IList list) : base(list) + { + + } +} +public class TruModelBone +{ + private List _children = new List(); + private List _meshes = new List(); + + public List Meshes + { + get => _meshes; + private set => _meshes = value; + } + + public string Name { get; set; } + + public TruModelBone Parent { get; set; } + public TruModelBoneCollection Children { get; private set; } + public TruModelTransform Transform { get; } + + public TruModelBone() + { + Transform = new TruModelTransform(); + Children = new TruModelBoneCollection(_children); + } + + public void AddMesh(TruModelMesh mesh) + { + _meshes.Add(mesh); + } + + public void AddChild(TruModelBone modelBone) + { + _children.Add(modelBone); + modelBone.Parent = this; + } +} + +public class TruModel +{ + public TruModelTransform Transform { get; } + + public TruModelBoneCollection Bones { get; private set; } + + public TruModelMeshCollection Meshes { get; private set; } + + public TruModelBone RootBone { get; set; } + + public object Tag { get; set; } + + public Vector2 TextureSize { get; } + + public VertexPositionTexture[] Vertices { get; } + public short[] Indices { get; } + + public TruModel() + { + Transform = new TruModelTransform(); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj index 24905d2f8..ccd853e86 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj @@ -49,7 +49,4 @@ Scene.cs - - - diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/CubeTextureMap.png new file mode 100644 index 0000000000000000000000000000000000000000..700d0e7ff493ff92c3bca6f42eb15fd59529f3c9 GIT binary patch literal 1988 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%0A+{xUN$dr&*Tvow`&jHtu^VHz#Qqu=7dIeo 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 D*ZjF; literal 0 HcmV?d00001 From 52e82ff4469b6eacec6eaf5783d869e664578d03 Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Tue, 18 Jan 2022 23:30:31 +0100 Subject: [PATCH 5/9] progress on modelexplorer stuffs --- src/Alex/Gamestates/InGame/PlayingState.cs | 2 +- .../Abstractions/ICamera.cs | 3 +- .../Components/CameraMouseController.cs | 9 +- .../Entities/Entity.cs | 2 +- .../Entities/MCEntity.cs | 302 +++++++++++------- .../Geometry/ModelBuilder.cs | 101 ++++-- .../Geometry/TruModel.cs | 47 ++- .../Graphics/Camera.cs | 68 ++-- .../ModelExplorerGame.cs | 14 +- .../Scenes/ModelViewScene.cs | 23 +- .../Scenes/Screens/DebugGui.cs | 2 +- submodules/RocketUI | 2 +- 12 files changed, 392 insertions(+), 183 deletions(-) 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/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs index 595868999..905230f68 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/ICamera.cs @@ -11,7 +11,7 @@ public interface ICamera : IUpdateable public Viewport Viewport { get; } public Vector3 Position { get; set; } - public Quaternion Rotation { get; set; } + public Vector3 Rotation { get; set; } public Matrix View { get; } public Matrix Projection { get; } @@ -22,5 +22,6 @@ public interface ICamera : IUpdateable 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/Components/CameraMouseController.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs index 8e7462f59..9e64c0428 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs @@ -15,9 +15,7 @@ public class CameraMouseController : GameComponent public bool InvertX { get; set; } public bool InvertY { get; set; } - public double Sensitivity { get; set; } = 180d; - - private Vector3 _rotation = Vector3.Zero; + public double Sensitivity { get; set; } = 60d; private Vector3 _previous; private MouseInputListener _cursorInputListener; @@ -56,11 +54,10 @@ public override void Update(GameTime gameTime) var lookDelta = delta * (float)Sensitivity; - _rotation -= new Vector3(MathHelper.ToRadians(lookDelta.X), MathHelper.ToRadians(lookDelta.Y), MathHelper.ToRadians(lookDelta.Z)); + //Camera.LookAt(Vector3.Zero); + Camera.Rotation -= new Vector3(lookDelta.X, lookDelta.Y, 0f); //_rotation.Normalize(); - Camera.Rotation = Quaternion.CreateFromYawPitchRoll(_rotation.X, _rotation.Y, _rotation.Z); - _previous = p; } } \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs index 3f49e9e1e..3596cd996 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/Entity.cs @@ -29,7 +29,7 @@ public Vector3 Position set => Transform.Position = value; } - public Quaternion Rotation + public Vector3 Rotation { get => Transform.Rotation; set => Transform.Rotation = value; diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs index d8eab36b0..3c2f0eec3 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs @@ -1,10 +1,16 @@ -using Microsoft.Xna.Framework; +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; @@ -13,26 +19,28 @@ 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; } @@ -40,138 +48,218 @@ public class MCEntityGeometry public MCEntityGeometryBone[] Bones { get; set; } } - -public class TruMesh +public class MCEntityGeometryCollection { - -} + [JsonProperty("format_version")] public string FormatVersion { get; set; } -public class TruBone -{ - public string Name { get; } - public TruBone Parent { get; set; } - - public Transform3D Transform { get; } = new Transform3D(); - - public TruBone(string name) + [JsonExtensionData] public Dictionary Geometries { get; set; } = new Dictionary(); + + public MCEntityGeometry GetGeometry(string name) { - Name = name; + if (Geometries.TryGetValue(name, out var gToken)) + { + var jsonSerializer = JsonSerializer.CreateDefault(); + + using (var reader = gToken.CreateReader()) + { + return jsonSerializer.Deserialize(reader); + } + } + + return null; } } -public class TruModel +public static class MCEntityModelBuilder { - public string Name { get; } - public List Meshes { get; } - public List Bones { get; } - public Transform3D Transform { get; } = new Transform3D(); + 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) + .Pivot(bone.Pivot); + + 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(); + } - public TruModel(string name) + private static readonly ILogger Log = LogManager.GetCurrentClassLogger(); + + public static void ResetBonePositions(TruModel model) { - Name = name; + foreach (var bone in model.Bones) + { + bone.Transform.LocalPosition = 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 = "fox.geo.json"; + 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); - Geometry = JsonConvert.DeserializeObject(File.ReadAllText(defPath)); - - 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 texPath = Path.Join("S:\\Temp\\resource_packs\\bedrock-1.18.1\\textures\\entity\\", entityTex); + var json = File.ReadAllText(defPath); + var geometryCollection = JsonConvert.DeserializeObject(json); - 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); - } + Geometry = geometryCollection.GetGeometry(geometryCollection.Geometries.Keys.FirstOrDefault()); - var bone = new ModelBone(); - bone.Transform = Matrix.Identity; - bone.ModelTransform = Matrix.Identity; - var modelmesh = new ModelMesh(GraphicsDevice, parts); - - modelmesh.ParentBone = bone; - meshes.Add(modelmesh); + var model = MCEntityModelBuilder.BuildEntityModel(Geometry); - bone.AddMesh(modelmesh); - bones.Add(bone); - var model = new Model(GraphicsDevice, bones, meshes) - { - Root = bone - }; - - for (int i = 0; i < 6; i++) + _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) { - var effect = new BasicEffect(GraphicsDevice) + using (var cxt = GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default)) { - Texture = textures[i], - TextureEnabled = true, - LightingEnabled = true, - VertexColorEnabled = true, - }; - effect.EnableDefaultLighting(); - modelmesh.MeshParts[i].Effect = effect; - } + var r = cxt.RasterizerState.Copy(); + r.FillMode = FillMode.Solid; + //r.CullMode = CullMode.None; + //r.DepthClipEnable = false; + cxt.RasterizerState = r; + + cxt.SamplerState = SamplerState.PointWrap; + var camera = ((IGame)Game).Camera; - Model = model; + _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) diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs index e3efdfd73..61d2323da 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs @@ -1,5 +1,7 @@ using System.Numerics; using ResourcePackLib.Core.Data; +using ResourcePackLib.ModelExplorer.Utilities.Extensions; +using Quaternion = Microsoft.Xna.Framework.Quaternion; namespace ResourcePackLib.ModelExplorer.Geometry; @@ -16,12 +18,13 @@ public ModelVertexIndexPositionTexture(Vector3 position, Vector2 uv) } public class ModelCubeMeshBuilder { - private readonly ModelBoneBuilder _parentBone; + internal readonly ModelBoneBuilder _parentBone; private Vector3 _origin; private Vector3 _size; - private Vector2 _uv; + internal Vector2 _uv; + internal Vector2 _uvSize; - internal Cuboid _cuboid; + internal (ModelVertexIndexPositionTexture[] vertices, short[] indices) _mesh; public ModelCubeMeshBuilder(ModelBoneBuilder parentBone) { @@ -46,7 +49,7 @@ public ModelCubeMeshBuilder Uv(Vector2 uv) return this; } - private (ModelVertexIndexPositionTexture[] vertices, short[] indices) CreateCuboid(Vector3 min, Vector3 max, Vector2 uvSize) + private (ModelVertexIndexPositionTexture[] vertices, short[] indices) CreateCuboid(Vector3 min, Vector3 max) { var baseVertices = new Vector3[8]; // 8 distinct coordinates @@ -75,7 +78,9 @@ public ModelCubeMeshBuilder Uv(Vector2 uv) short b = 0, c = 0; var size = max - min; - var o1 = new Vector2((2 * size.Y) + (2 * size.X), (size.Y + size.Z)); + _uvSize = new Vector2((2 * size.Y) + (2 * size.X), (size.Y + size.Z)); + var uvOffset = _uv; + var globUvSize = _parentBone._modelBuilder._textureSize; var defineFace = (short[] vertexIndices, Vector2 uvMin, Vector2 uvSize) => { @@ -83,10 +88,10 @@ public ModelCubeMeshBuilder Uv(Vector2 uv) //for (int i = 0; i < baseFaceIndices.Length; i++) //{ var d = b; - vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[0]], new Vector2(uvMin.X, uvMin.Y) / o1); - vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[1]], new Vector2(uvMax.X, uvMin.Y) / o1); - vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[2]], new Vector2(uvMax.X, uvMax.Y) / o1); - vertices[b++] = new ModelVertexIndexPositionTexture(baseVertices[vertexIndices[3]], new Vector2(uvMin.X, uvMax.Y) / o1); + 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); @@ -121,23 +126,29 @@ public ModelCubeMeshBuilder Uv(Vector2 uv) public TruModelMesh Build() { - var cuboid = new Cuboid(_origin, _origin + _size); - _cuboid = cuboid; - + _mesh = CreateCuboid(_origin, _origin+_size); return new TruModelMesh() { + NumVertices = _mesh.vertices.Length, + PrimitiveCount = _mesh.indices.Length/3, + IndexOffset = 0, + VertexOffset = 0 }; } } public class ModelBoneBuilder { - private readonly ModelBuilder _modelBuilder; + 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) { @@ -156,6 +167,12 @@ 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) { @@ -174,13 +191,22 @@ internal TruModelBone Build() { Name = _name, }; + _nVertices = 0; + _nIndices = 0; - foreach (var cube in _cubeBuilders) + _meshes = new (ModelVertexIndexPositionTexture[] vertices, short[] indices)[_cubeBuilders.Count]; + for (var i = 0; i < _cubeBuilders.Count; i++) { - bone.AddMesh(cube.Build()); + 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; } bone.Transform.LocalRotationOrigin = _pivot; + bone.Transform.LocalRotation = _bindPoseRotation; _built = bone; return bone; @@ -189,7 +215,7 @@ internal TruModelBone Build() public class ModelBuilder { - private Vector2 _textureSize; + internal Vector2 _textureSize; private readonly List _boneBuilders = new List(); public ModelBuilder() @@ -213,12 +239,49 @@ 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)) @@ -228,14 +291,16 @@ public TruModel Build() { if (string.Equals(bone.Name, boneBuilder._parentName, StringComparison.OrdinalIgnoreCase)) { - boneBuilder._built.Parent = bone; + bone.AddChild(boneBuilder._built); break; } } } } - var model = new TruModel(); + // 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/TruModel.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs index 63bb6ee02..ae8c75c5c 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs @@ -1,8 +1,9 @@ using System.Collections.ObjectModel; -using System.Numerics; using JetBrains.Annotations; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using RocketUI; +using Vector2 = System.Numerics.Vector2; namespace ResourcePackLib.ModelExplorer.Geometry; @@ -12,6 +13,7 @@ public class TruModelMesh public int VertexOffset { get; internal set; } public int PrimitiveCount { get; internal set; } public int IndexOffset { get; internal set; } + public TruModelMesh() { @@ -43,22 +45,29 @@ public class TruModelBone { private List _children = new List(); private List _meshes = new List(); + private TruModelBone _parent; - public List Meshes - { - get => _meshes; - private set => _meshes = value; - } + public TruModelMeshCollection Meshes { get; } public string Name { get; set; } - - public TruModelBone Parent { get; set; } - public TruModelBoneCollection Children { get; private 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); } @@ -80,19 +89,27 @@ public class TruModel public TruModelBoneCollection Bones { get; private set; } - public TruModelMeshCollection Meshes { get; private set; } - public TruModelBone RootBone { get; set; } public object Tag { get; set; } - public Vector2 TextureSize { get; } - - public VertexPositionTexture[] Vertices { get; } + public VertexPositionColorTexture[] Vertices { get; } public short[] Indices { get; } - public TruModel() + 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/Graphics/Camera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs index c56e644aa..c8be332d3 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs @@ -4,6 +4,7 @@ using ResourcePackLib.ModelExplorer.Abstractions; using ResourcePackLib.ModelExplorer.Utilities.Extensions; using RocketUI; +using RocketUI.Utilities.Helpers; namespace ResourcePackLib.ModelExplorer.Graphics { @@ -12,19 +13,19 @@ 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 = 1000.0f; - public Transform3D Transform { get; } = new Transform3D(); + 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; @@ -34,22 +35,25 @@ public Vector3 Scale get => Transform.Scale; set => Transform.Scale = value; } + public Vector3 Position { get => Transform.Position; set => Transform.Position = value; } - public Quaternion Rotation + + public Vector3 Rotation { get => Transform.Rotation; set => Transform.Rotation = value; } + public Matrix World { get => Transform.World; } - public Matrix View { get; private set; } + public Matrix View { get; private set; } public Matrix Projection { get; private set; } public float NearDistance @@ -76,6 +80,7 @@ public Viewport Viewport { get => _viewport; } + public Rectangle Bounds { get => _bounds; @@ -87,7 +92,7 @@ public Rectangle Bounds } public ProjectionType ProjectionType { get; set; } = ProjectionType.Perspective; - + public RenderTarget2D RenderTarget { get; set; } public Camera(IGame game) : base(game.Game) @@ -97,17 +102,20 @@ public Camera(IGame game) : base(game.Game) UpdateProjection(); _game.GraphicsDeviceManager.DeviceCreated += (sender, args) => UpdateProjection(); - _game.GraphicsDeviceManager.DeviceReset += (sender, args) => UpdateProjection(); - _game.Window.ClientSizeChanged += (sender, args) => UpdateProjection(); - Transform.Changed += (sender, args) => UpdateView(); + _game.GraphicsDeviceManager.DeviceReset += (sender, args) => UpdateProjection(); + _game.Window.ClientSizeChanged += (sender, args) => UpdateProjection(); + Transform.Changed += (sender, args) => UpdateView(); } - + private void UpdateView() { - _forward = Vector3.Normalize(Vector3.Transform(Vector3.Forward, Rotation)); - _up = Vector3.Normalize(Vector3.Transform(Vector3.Up, Rotation)); - _right = Vector3.Normalize(Vector3.Transform(Vector3.Right, Rotation)); + 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); } @@ -117,7 +125,7 @@ private void UpdateProjection() { Bounds = _game.Window.ClientBounds; } - + var w = Bounds.Width; var h = Bounds.Height; @@ -132,8 +140,8 @@ private void UpdateProjection() if (ProjectionType == ProjectionType.Perspective) { - var aspect = (float) w / (float) h; - Projection = Matrix.CreatePerspectiveFieldOfView(60.0f.ToRadians(), aspect, NearDistance, FarDistance); + var aspect = (float)w / (float)h; + Projection = Matrix.CreatePerspectiveFieldOfView(80.0f.ToRadians(), aspect, NearDistance, FarDistance); } else { @@ -141,11 +149,23 @@ private void UpdateProjection() } } - - + + 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) { - var forward = Vector3.Transform(move, Rotation); + Quaternion rot = default; + Transform.World.ExtractRotation(ref rot); + var forward = Vector3.Transform(move, rot); Position += forward; } diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs index d5d22c61c..51a0b08c7 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs @@ -104,9 +104,9 @@ protected override void LoadContent() protected override void Update(GameTime gameTime) { - if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || - Keyboard.GetState().IsKeyDown(Keys.Escape)) - Exit(); + // if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || + // Keyboard.GetState().IsKeyDown(Keys.Escape)) + // Exit(); if (_keyboardListener.IsAnyPressed(Keys.F8)) { @@ -124,10 +124,10 @@ protected override void Update(GameTime gameTime) if (keyboard.IsKeyDown(Keys.LeftControl)) { var rotateDiff = (float)((gameTime.ElapsedGameTime.TotalSeconds / 1f) * rotateSpeed); - if (_keyboardListener.IsAnyDown(Keys.NumPad8)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Up, MathHelper.ToRadians(-rotateDiff)); - if (_keyboardListener.IsAnyDown(Keys.NumPad2)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Up, MathHelper.ToRadians(rotateDiff)); - if (_keyboardListener.IsAnyDown(Keys.NumPad4)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Right, MathHelper.ToRadians(-rotateDiff)); - if (_keyboardListener.IsAnyDown(Keys.NumPad6)) _camera.Rotation *= Quaternion.CreateFromAxisAngle(_camera.Right, MathHelper.ToRadians(rotateDiff)); + 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 { diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs index 21cff2a7f..df2c63880 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs @@ -16,10 +16,31 @@ protected override void OnInitialize() cube.Visible = true; cube.Position = Vector3.Zero; cube.Scale = Vector3.One; + + var fox = Services.CreateInstance(); + fox.Visible = true; + fox.Position = Vector3.Up; + fox.Transform.LocalScale = Vector3.One / 16f; Components.Add(Services.GetOrCreateInstance()); - Components.Add(cube); + //Components.Add(cube); + Components.Add(fox); 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); } } \ 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 index c96132758..2bbad1ad7 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/DebugGui.cs @@ -27,7 +27,7 @@ public DebugGui() AddDebugLine(() => { var p = ModelExplorerGame.Instance.Camera.Rotation; - return $"Camera Rotation: {p.X:F2}, {p.Y:F2}, {p.Z:F2}, {p.W:F2}"; + return $"Camera Rotation: {p.X:F2}, {p.Y:F2}, {p.Z:F2}"; }); AddDebugLine(() => diff --git a/submodules/RocketUI b/submodules/RocketUI index 1bffeb07c..f12c1631b 160000 --- a/submodules/RocketUI +++ b/submodules/RocketUI @@ -1 +1 @@ -Subproject commit 1bffeb07c429eff0c8495a36725752928332d97f +Subproject commit f12c1631b1f0bc1e81cfa0455a3e69091ef951b2 From 2bc57b6327fcb6f320857747b7242fd5905616bc Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Mon, 24 Jan 2022 19:35:01 +0100 Subject: [PATCH 6/9] xaml fixes --- .../Controls/AutoUpdatingTextInput.cs | 33 +++++++++++++++++++ .../Controls/CameraLocationControl.cs | 30 +++++++++++++++++ .../Scenes/GuiSceneBase.cs | 1 - .../Scenes/Screens/MainMenuScreen.xaml | 24 +++++++------- 4 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs new file mode 100644 index 000000000..9d64cdf88 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs @@ -0,0 +1,33 @@ +using Microsoft.Xna.Framework; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Controls; + +public class AutoUpdatingTextInput : TextInput +{ + private readonly Func _updateFn; + + public bool UpdateOnlyWhenNotFocused { get; set; } = true; + public TimeSpan UpdateFrequency { get; set; } = TimeSpan.FromSeconds(0.5); + + private TimeSpan _lastUpdate = TimeSpan.Zero; + + public AutoUpdatingTextInput(Func updateFn) + { + _updateFn = updateFn; + } + + protected override void OnUpdate(GameTime gameTime) + { + base.OnUpdate(gameTime); + + if (!Focused || !UpdateOnlyWhenNotFocused) + { + if (gameTime.TotalGameTime - _lastUpdate >= UpdateFrequency) + { + Value = _updateFn(); + _lastUpdate = gameTime.TotalGameTime; + } + } + } +} \ 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..a6888d993 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Xna.Framework; +using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; + +namespace ResourcePackLib.ModelExplorer.Controls; + +public class CameraLocationControl : RocketControl +{ + private AutoUpdatingTextInput _posX; + private AutoUpdatingTextInput _posY; + private AutoUpdatingTextInput _posZ; + + public CameraLocationControl() + { + var stack = new MultiStackContainer(); + AddChild(stack); + + stack.AddRow( + _posX = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.X.ToString("F2")), + _posY = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.Y.ToString("F2")), + _posZ = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.Z.ToString("F2")) + ); + } + + protected override void OnUpdate(GameTime gameTime) + { + base.OnUpdate(gameTime); + } +} \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs index 188702f3e..36082e73c 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs @@ -11,7 +11,6 @@ protected GuiSceneBase() : this(new TScreen()) { } protected GuiSceneBase(TScreen screen) : base() { - screen = screen; screen.Anchor = Alignment.Fill; screen.AutoSizeMode = AutoSizeMode.GrowAndShrink; Screen = screen; diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml index 26f9f6c88..65c196059 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml @@ -1,18 +1,18 @@ - + - - - - - + + + \ No newline at end of file From 0db12d848b0d623c5b3751f855ee3bbfe246e449 Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Tue, 25 Jan 2022 20:52:00 +0100 Subject: [PATCH 7/9] WORKING RENDERING OF ENTITIES WOW --- .../Content/Content.mgcb | 24 ++ .../Content/Gui/Gui.png | Bin 0 -> 1751 bytes .../Content/Gui/ScrollBar.png | Bin 0 -> 1688 bytes .../Controls/CameraLocationControl.cs | 9 +- .../Entities/GridEntity.cs | 176 ++++++++++ .../Entities/MCEntity.cs | 28 +- .../Geometry/Builder/ModelBoneBuilder.cs | 82 +++++ .../Geometry/Builder/ModelBuilder.cs | 98 ++++++ .../Geometry/Builder/ModelCubeMeshBuilder.cs | 124 +++++++ .../Geometry/ModelBuilder.cs | 306 ------------------ .../ModelVertexIndexPositionTexture.cs | 15 + .../Geometry/Models/TruModel.cs | 36 +++ .../Geometry/Models/TruModelBone.cs | 55 ++++ .../Geometry/Models/TruModelBoneCollection.cs | 56 ++++ .../Geometry/Models/TruModelMesh.cs | 14 + .../Geometry/Models/TruModelMeshCollection.cs | 35 ++ .../Geometry/Models/TruModelTransform.cs | 10 + .../Geometry/TruModel.cs | 115 ------- .../Graphics/GuiRenderer.cs | 12 +- .../ModelExplorerGame.cs | 15 +- .../ResourcePackLib.ModelExplorer.csproj | 3 - .../Scenes/{ => Framework}/GuiSceneBase.cs | 0 .../Scenes/{ => Framework}/IScene.cs | 0 .../{ => Framework}/Scene.Components.cs | 0 .../Scenes/{ => Framework}/Scene.cs | 0 .../Scenes/{ => Framework}/SceneManager.cs | 0 .../Scenes/ModelViewScene.cs | 13 +- .../Scenes/Screens/MainMenuScreen.xaml | 5 +- .../Scenes/Styles.xaml | 22 ++ .../assets/Gui.psd | Bin 0 -> 52054 bytes submodules/MiNET | 2 +- submodules/RocketUI | 2 +- 32 files changed, 807 insertions(+), 450 deletions(-) create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/Gui.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/ScrollBar.png create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBoneBuilder.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelBuilder.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Builder/ModelCubeMeshBuilder.cs delete mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelVertexIndexPositionTexture.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModel.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBone.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelBoneCollection.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMesh.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelMeshCollection.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/Models/TruModelTransform.cs delete mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs rename src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/{ => Framework}/GuiSceneBase.cs (100%) rename src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/{ => Framework}/IScene.cs (100%) rename src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/{ => Framework}/Scene.Components.cs (100%) rename src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/{ => Framework}/Scene.cs (100%) rename src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/{ => Framework}/SceneManager.cs (100%) create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/Gui.psd diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb index b10bf864d..9ff60c413 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Content.mgcb @@ -95,3 +95,27 @@ #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/Gui/Gui.png b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Content/Gui/Gui.png new file mode 100644 index 0000000000000000000000000000000000000000..fcdb8b78783bbed0a146ce317d94fee964a313f0 GIT binary patch literal 1751 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 Wd?*d_+0kSBSIDDcWrrg^S@$0)6oJJ6 literal 0 HcmV?d00001 diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs index a6888d993..fbcd3af26 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs @@ -13,14 +13,19 @@ public class CameraLocationControl : RocketControl public CameraLocationControl() { - var stack = new MultiStackContainer(); - AddChild(stack); + var stack = new MultiStackContainer() + { + Anchor = Alignment.Fill, + ChildAnchor = Alignment.Fill, + Orientation = Orientation.Vertical + }; stack.AddRow( _posX = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.X.ToString("F2")), _posY = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.Y.ToString("F2")), _posZ = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.Z.ToString("F2")) ); + AddChild(stack); } protected override void OnUpdate(GameTime gameTime) diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs new file mode 100644 index 000000000..478f53bd8 --- /dev/null +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs @@ -0,0 +1,176 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using ResourcePackLib.ModelExplorer.Abstractions; + +namespace ResourcePackLib.ModelExplorer.Entities; + +public class GridEntity : DrawableEntity +{ + 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 IndexBuffer _indexBuffer; + private short[] _indicies; + private bool _dirty; + private Vector3 _yAxis = Vector3.UnitZ; + private Vector3 _xAxis = Vector3.UnitX; + private Color _lineColor = Color.LightGray; + private float _spacing = 1.0f; + 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 + }; + + var halfSteps = (int)Math.Ceiling(Steps / 2f); + + var arraySize = (short)((((halfSteps * 2f) + 1f) * 2f) * 2); + InitBuffers(arraySize); + } + + 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 halfSteps = (int)Math.Ceiling(Steps / 2f); + + var arraySize = (short)((((halfSteps * 2f) + 1f) * 2f) * 2); + if (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 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); + _indexBuffer.SetData(_indicies); + } + + public void Update() + { + if (_dirty) + { + _dirty = false; + CalculateVertices(); + } + } + + public void Draw(GraphicsDevice graphics, Matrix view, Matrix projection) + { + _effect.View = view; + _effect.Projection = projection; + + graphics.SetVertexBuffer(_vertexBuffer); + graphics.Indices = _indexBuffer; + + foreach (EffectPass pass in _effect.CurrentTechnique.Passes) + { + pass.Apply(); + graphics.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/MCEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs index 3c2f0eec3..56472a72d 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/MCEntity.cs @@ -33,7 +33,7 @@ public class MCEntityGeometryBone public string Parent { get; set; } [JsonConverter(typeof(Vector3ArrayJsonConverter))] - public Vector3 Pivot { get; set; } + public Vector3? Pivot { get; set; } [JsonConverter(typeof(Vector3ArrayJsonConverter))] public Vector3? BindPoseRotation { get; set; } @@ -80,9 +80,13 @@ public static TruModel BuildEntityModel(MCEntityGeometry geometry) foreach (var bone in geometry.Bones) { var bb = modelBuilder - .AddBone(bone.Name) - .Pivot(bone.Pivot); + .AddBone(bone.Name); + if (bone.Pivot.HasValue) + { + bb.Pivot(bone.Pivot.Value); + } + if (bone.BindPoseRotation.HasValue) bb.BindPoseRotation(bone.BindPoseRotation.Value); @@ -108,10 +112,10 @@ public static void ResetBonePositions(TruModel model) { foreach (var bone in model.Bones) { - bone.Transform.LocalPosition = Microsoft.Xna.Framework.Vector3.Zero; + bone.Transform.Position = Microsoft.Xna.Framework.Vector3.Zero; } } - + public static void LogBoneLocations(TruModel model) { var sb = new StringBuilder(); @@ -121,9 +125,9 @@ public static void LogBoneLocations(TruModel model) foreach (var bone in model.Bones) { var wlrd = bone.Transform.World; - - if(!bone.Meshes.Any()) continue; - + + if (!bone.Meshes.Any()) continue; + var boneMinMesh = bone.Meshes.SelectMany(m => Enumerable.Range(m.VertexOffset, m.NumVertices) .Select(i => model.Vertices[i]) @@ -136,7 +140,7 @@ public static void LogBoneLocations(TruModel model) 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()); } @@ -179,7 +183,8 @@ protected override void LoadContent() _effect = new BasicEffect(GraphicsDevice); - var texture = Texture2D.FromFile(GraphicsDevice, texPath);; + var texture = Texture2D.FromFile(GraphicsDevice, texPath); + ; //var texture = new ColorTexture2D(GraphicsDevice, Color.HotPink); _effect.Texture = texture; _effect.TextureEnabled = true; @@ -215,7 +220,8 @@ private void DrawTruModel(TruModel model) using (var cxt = GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default)) { var r = cxt.RasterizerState.Copy(); - r.FillMode = FillMode.Solid; + + r.FillMode = ((ModelExplorerGame)Game).IsWireFrame ? FillMode.WireFrame : FillMode.Solid; //r.CullMode = CullMode.None; //r.DepthClipEnable = false; cxt.RasterizerState = r; 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/ModelBuilder.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs deleted file mode 100644 index 61d2323da..000000000 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/ModelBuilder.cs +++ /dev/null @@ -1,306 +0,0 @@ -using System.Numerics; -using ResourcePackLib.Core.Data; -using ResourcePackLib.ModelExplorer.Utilities.Extensions; -using Quaternion = Microsoft.Xna.Framework.Quaternion; - -namespace ResourcePackLib.ModelExplorer.Geometry; - -public struct ModelVertexIndexPositionTexture -{ - public Vector3 Position; - public Vector2 Uv; - - public ModelVertexIndexPositionTexture(Vector3 position, Vector2 uv) - { - Position = position; - Uv = uv; - } -} -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.Y) + (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.Y), new Vector2(size.Y, size.Z)); - - /* Up | Top? */ - defineFace(new short[] { 0b010, 0b110, 0b111, 0b011 }, new Vector2((2*size.Y)+size.X, size.Y), new Vector2(size.X, size.Z)); - - /* South | Back? */ - defineFace(new short[] { 0b011, 0b111, 0b101, 0b001 }, new Vector2(size.Y+size.X, 0f), new Vector2(size.X, size.Y)); - - /* West | Left? */ - defineFace(new short[] { 0b010, 0b011, 0b001, 0b000 }, new Vector2(size.Y+size.X, size.Y), new Vector2(size.Y, size.Z)); - - /* Down | Bottom? */ - defineFace(new short[] { 0b100, 0b000, 0b001, 0b101 }, new Vector2(size.Y, size.Y), new Vector2(size.X, size.Z)); - - /* North | Front? */ - defineFace(new short[] { 0b110, 0b010, 0b000, 0b100 }, new Vector2(size.Y, 0f), 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 - }; - } -} - -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; - } - - bone.Transform.LocalRotationOrigin = _pivot; - bone.Transform.LocalRotation = _bindPoseRotation; - - _built = bone; - return bone; - } -} - -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/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/Geometry/TruModel.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs deleted file mode 100644 index ae8c75c5c..000000000 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Geometry/TruModel.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.ObjectModel; -using JetBrains.Annotations; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using RocketUI; -using Vector2 = System.Numerics.Vector2; - -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() - { - - } -} - -public class TruModelTransform : Transform3D -{ - public TruModelTransform() - { - } -} - -public class TruModelMeshCollection : ReadOnlyCollection -{ - public TruModelMeshCollection([NotNull] IList list) : base(list) - { - - } -} -public class TruModelBoneCollection : ReadOnlyCollection -{ - public TruModelBoneCollection([NotNull] IList list) : base(list) - { - - } -} -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; - } -} - -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/Graphics/GuiRenderer.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs index 4c5525610..23c464722 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/GuiRenderer.cs @@ -30,11 +30,21 @@ public void Init(GraphicsDevice graphics, IServiceProvider serviceProvider) 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"); + RocketXamlLoader.Load(_styleSheet, "ResourcePackLib.ModelExplorer.Scenes.Styles.xaml"); } private TextureSlice2D LoadTextureFromEmbeddedResource(GuiTextures guiTexture, byte[] resource) diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs index 51a0b08c7..32304693e 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs @@ -31,6 +31,8 @@ public class ModelExplorerGame : Game, IGame public InputManager InputManager { get; private set; } public GuiManager GuiManager { get; private set; } + + public bool IsWireFrame { get; set; } public ModelExplorerGame(IServiceProvider services) { @@ -51,7 +53,7 @@ private void OnHostApplicationStopping() protected override void Initialize() { GraphicsDeviceManager.GraphicsProfile = GraphicsProfile.HiDef; - GraphicsDeviceManager.SynchronizeWithVerticalRetrace = false; + GraphicsDeviceManager.SynchronizeWithVerticalRetrace = true; // GraphicsDeviceManager.PreferMultiSampling = true; IsFixedTimeStep = false; Window.AllowUserResizing = true; @@ -82,8 +84,8 @@ protected override void LoadContent() // cam.Rotation = Quaternion.CreateFromYawPitchRoll(270f, (float)Math.PI / 2f, 0f); GuiManager = ServiceProvider.GetRequiredService(); - GuiManager.ScaledResolution.TargetWidth = 720; - GuiManager.ScaledResolution.TargetHeight = 360; + GuiManager.ScaledResolution.TargetWidth = 480; + GuiManager.ScaledResolution.TargetHeight = 320; GuiManager.AddScreen(_debugGui = new DebugGui()); Components.Add(GuiManager); GuiManager.DrawOrder = 10; @@ -93,6 +95,7 @@ protected override void LoadContent() SceneManager.SetScene(); _graphics.GraphicsDevice.Viewport = new Viewport(Window.ClientBounds); + GuiManager.Reinitialize(); InputManager.GetOrAddPlayerManager(PlayerIndex.One).TryGetListener(out _keyboardListener); @@ -110,11 +113,7 @@ protected override void Update(GameTime gameTime) if (_keyboardListener.IsAnyPressed(Keys.F8)) { - var r = GraphicsDevice.RasterizerState.Copy(); - r.FillMode = (r.FillMode == FillMode.Solid) - ? FillMode.WireFrame - : FillMode.Solid; - GraphicsDevice.RasterizerState = r; + IsWireFrame = !IsWireFrame; } var rotateSpeed = 45f; diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj index ccd853e86..6d218c5b2 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ResourcePackLib.ModelExplorer.csproj @@ -45,8 +45,5 @@ MainMenuScreen.xaml - - Scene.cs - diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/GuiSceneBase.cs similarity index 100% rename from src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/GuiSceneBase.cs rename to src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/GuiSceneBase.cs diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/IScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/IScene.cs similarity index 100% rename from src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/IScene.cs rename to src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/IScene.cs diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.Components.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.Components.cs similarity index 100% rename from src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.Components.cs rename to src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.Components.cs diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.cs similarity index 100% rename from src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Scene.cs rename to src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/Scene.cs diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/SceneManager.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/SceneManager.cs similarity index 100% rename from src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/SceneManager.cs rename to src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Framework/SceneManager.cs diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs index df2c63880..e183f24e6 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs @@ -11,6 +11,12 @@ public class MainMenuScene : GuiSceneBase protected override void OnInitialize() { base.OnInitialize(); + + var grid = new GridEntity(ModelExplorerGame.Instance); + grid.Visible = true; + grid.Position = Vector3.Zero; + grid.Scale = Vector3.One; + Components.Add(grid); var cube = new CubeEntity(ModelExplorerGame.Instance); cube.Visible = true; @@ -20,7 +26,7 @@ protected override void OnInitialize() var fox = Services.CreateInstance(); fox.Visible = true; fox.Position = Vector3.Up; - fox.Transform.LocalScale = Vector3.One / 16f; + //fox.Transform.Scale = Vector3.One / 16f; Components.Add(Services.GetOrCreateInstance()); //Components.Add(cube); @@ -43,4 +49,9 @@ protected override void OnUpdate(GameTime gameTime) } base.OnUpdate(gameTime); } + + protected override void OnShow() + { + base.OnShow(); + } } \ 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 index 65c196059..1350e0e35 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Screens/MainMenuScreen.xaml @@ -10,9 +10,12 @@ Orientation="Vertical" MinWidth="200" MinHeight="300" - Width="300" + Width="250" Background="#00000099" + Padding="10,10,10,10" + > + \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml index 973d1ebf6..e4af6dcfd 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/Styles.xaml @@ -2,4 +2,26 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:markup="clr-namespace:Portable.Xaml.Markup;assembly=Portable.Xaml"> + + + + \ No newline at end of file diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/Gui.psd b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/assets/Gui.psd new file mode 100644 index 0000000000000000000000000000000000000000..b312ca7ad6975032d534f576880a7aaad375098f GIT binary patch literal 52054 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 Date: Wed, 26 Jan 2022 00:08:54 +0100 Subject: [PATCH 8/9] improve ui + input for modelexplorer --- src/Alex/Utils/KeysExtension.cs | 309 ------------------ .../Abstractions/IGame.cs | 2 +- .../Components/CameraKeyboardController.cs | 74 +++++ .../Components/CameraMouseController.cs | 1 - .../Controls/AutoUpdatingTextInput.cs | 24 +- .../Controls/CameraLocationControl.cs | 57 +++- .../Graphics/Camera.cs | 4 +- .../ModelExplorerGame.cs | 91 ++++-- .../Scenes/ModelViewScene.cs | 1 + submodules/RocketUI | 2 +- 10 files changed, 208 insertions(+), 357 deletions(-) delete mode 100644 src/Alex/Utils/KeysExtension.cs create mode 100644 src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraKeyboardController.cs 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.ModelExplorer/Abstractions/IGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs index 025c11ad7..1898241d2 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Abstractions/IGame.cs @@ -9,7 +9,7 @@ public interface IGame : IDisposable { Game Game { get; } - GraphicsDeviceManager GraphicsDeviceManager { get; } + GraphicsDeviceManager DeviceManager { get; } // include all the Properties/Methods that you'd want to use on your Game class below. GameWindow Window { get; } 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 index 9e64c0428..5c8d5ad86 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Components/CameraMouseController.cs @@ -1,5 +1,4 @@ using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Input; using ResourcePackLib.ModelExplorer.Abstractions; using ResourcePackLib.ModelExplorer.Attributes; using RocketUI.Input; diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs index 9d64cdf88..f2df54e91 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/AutoUpdatingTextInput.cs @@ -1,20 +1,23 @@ using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; using RocketUI; namespace ResourcePackLib.ModelExplorer.Controls; public class AutoUpdatingTextInput : TextInput { - private readonly Func _updateFn; + 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 updateFn) + public AutoUpdatingTextInput(Func getFn, Action setFn = null) { - _updateFn = updateFn; + _getFn = getFn; + _setFn = setFn; } protected override void OnUpdate(GameTime gameTime) @@ -25,9 +28,22 @@ protected override void OnUpdate(GameTime gameTime) { if (gameTime.TotalGameTime - _lastUpdate >= UpdateFrequency) { - Value = _updateFn(); + 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 index fbcd3af26..568d7b248 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Controls/CameraLocationControl.cs @@ -1,7 +1,10 @@ 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; @@ -10,6 +13,11 @@ 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() { @@ -20,14 +28,57 @@ public CameraLocationControl() Orientation = Orientation.Vertical }; + stack.AddRow( - _posX = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.X.ToString("F2")), - _posY = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.Y.ToString("F2")), - _posZ = new AutoUpdatingTextInput(() => (GuiManager.Game as IGame)?.ServiceProvider.GetRequiredService().Position.Z.ToString("F2")) + _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); diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs index c8be332d3..830053849 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Graphics/Camera.cs @@ -101,8 +101,8 @@ public Camera(IGame game) : base(game.Game) UpdateView(); UpdateProjection(); - _game.GraphicsDeviceManager.DeviceCreated += (sender, args) => UpdateProjection(); - _game.GraphicsDeviceManager.DeviceReset += (sender, args) => UpdateProjection(); + _game.DeviceManager.DeviceCreated += (sender, args) => UpdateProjection(); + _game.DeviceManager.DeviceReset += (sender, args) => UpdateProjection(); _game.Window.ClientSizeChanged += (sender, args) => UpdateProjection(); Transform.Changed += (sender, args) => UpdateView(); } diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs index 32304693e..cb2758b08 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs @@ -22,9 +22,9 @@ public class ModelExplorerGame : Game, IGame public IServiceProvider ServiceProvider { get; } public Game Game => this; public static IGame Instance { get; private set; } - public GraphicsDeviceManager GraphicsDeviceManager => _graphics; + public GraphicsDeviceManager DeviceManager => _graphics; - private GraphicsDeviceManager _graphics; + private readonly GraphicsDeviceManager _graphics; private SpriteBatch _spriteBatch; public SceneManager SceneManager { get; private set; } @@ -38,7 +38,20 @@ public ModelExplorerGame(IServiceProvider services) { Instance = this; ServiceProvider = services; - _graphics = new GraphicsDeviceManager(this); + _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; @@ -46,24 +59,54 @@ public ModelExplorerGame(IServiceProvider services) 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(); + } + } + + 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() { - GraphicsDeviceManager.GraphicsProfile = GraphicsProfile.HiDef; - GraphicsDeviceManager.SynchronizeWithVerticalRetrace = true; + DeviceManager.GraphicsProfile = GraphicsProfile.HiDef; + GraphicsDevice.PresentationParameters.MultiSampleCount = 8; + DeviceManager.SynchronizeWithVerticalRetrace = true; // GraphicsDeviceManager.PreferMultiSampling = true; - IsFixedTimeStep = false; + IsFixedTimeStep = true; Window.AllowUserResizing = true; - GraphicsDeviceManager.ApplyChanges(); - - InputManager = ServiceProvider.GetRequiredService(); - Components.Add(InputManager); - + DeviceManager.ApplyChanges(); + + Components.Add(InputManager = ServiceProvider.GetRequiredService()); Components.Add(SceneManager = ServiceProvider.GetRequiredService()); + Components.Add(GuiManager = ServiceProvider.GetRequiredService()); base.Initialize(); } @@ -83,11 +126,10 @@ protected override void LoadContent() Camera.Position = new Vector3(1.7f, 1.7f, 1.7f); // cam.Rotation = Quaternion.CreateFromYawPitchRoll(270f, (float)Math.PI / 2f, 0f); - GuiManager = ServiceProvider.GetRequiredService(); + GuiManager.ScaledResolution.TargetWidth = 480; GuiManager.ScaledResolution.TargetHeight = 320; GuiManager.AddScreen(_debugGui = new DebugGui()); - Components.Add(GuiManager); GuiManager.DrawOrder = 10; GuiManager.Init(); @@ -116,29 +158,6 @@ protected override void Update(GameTime gameTime) IsWireFrame = !IsWireFrame; } - var rotateSpeed = 45f; - var moveSpeed = 2f; - - var keyboard = Keyboard.GetState(); - if (keyboard.IsKeyDown(Keys.LeftControl)) - { - 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 - { - 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); - } - base.Update(gameTime); } public ICamera Camera { get; private set; } diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs index e183f24e6..a8a4d8e4d 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs @@ -33,6 +33,7 @@ protected override void OnInitialize() Components.Add(fox); Components.Add(Services.GetOrCreateInstance()); + Components.Add(Services.GetOrCreateInstance()); Game.Camera.Position = Vector3.Backward; } diff --git a/submodules/RocketUI b/submodules/RocketUI index 5d0e79dfa..7bbfa73f1 160000 --- a/submodules/RocketUI +++ b/submodules/RocketUI @@ -1 +1 @@ -Subproject commit 5d0e79dfabbd04d7912594e7367fa66745c0c789 +Subproject commit 7bbfa73f141cd109aedc44a92859cbf9bb015276 From c41ae03a21a7a8cf712577104d030d0a8b8576c6 Mon Sep 17 00:00:00 2001 From: Dan Spiteri Date: Wed, 26 Jan 2022 18:31:29 +0100 Subject: [PATCH 9/9] fix grid + rendering without first resizing window --- .../Entities/GridEntity.cs | 99 ++++++++++++------- .../ModelExplorerGame.cs | 27 +++-- .../Scenes/ModelViewScene.cs | 20 ++-- 3 files changed, 94 insertions(+), 52 deletions(-) diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs index 478f53bd8..e3a4abe25 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Entities/GridEntity.cs @@ -1,6 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using ResourcePackLib.ModelExplorer.Abstractions; +using RocketUI; namespace ResourcePackLib.ModelExplorer.Entities; @@ -15,7 +16,15 @@ public int Steps _dirty = true; } } - + public int SubDivisions + { + get => _subDivisions; + set + { + _subDivisions = value; + _dirty = true; + } + } public float Spacing { get => _spacing; @@ -25,7 +34,6 @@ public float Spacing _dirty = true; } } - public Color LineColor { get => _lineColor; @@ -35,7 +43,15 @@ public Color LineColor _dirty = true; } } - + public Color SubLineColor + { + get => _subLineColor; + set + { + _subLineColor = value; + _dirty = true; + } + } public Vector3 XAxis { get => _xAxis; @@ -45,7 +61,6 @@ public Vector3 XAxis _dirty = true; } } - public Vector3 YAxis { get => _yAxis; @@ -60,11 +75,13 @@ public Vector3 YAxis private VertexBuffer _vertexBuffer; private IndexBuffer _indexBuffer; private short[] _indicies; - private bool _dirty; + private bool _dirty = true; private Vector3 _yAxis = Vector3.UnitZ; private Vector3 _xAxis = Vector3.UnitX; - private Color _lineColor = Color.LightGray; + 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; @@ -84,10 +101,7 @@ protected override void LoadContent() World = Matrix.Identity }; - var halfSteps = (int)Math.Ceiling(Steps / 2f); - - var arraySize = (short)((((halfSteps * 2f) + 1f) * 2f) * 2); - InitBuffers(arraySize); + CalculateVertices(); } private void InitBuffers(short arraySize) @@ -102,39 +116,45 @@ private void InitBuffers(short arraySize) private void CalculateVertices() { - var halfSteps = (int)Math.Ceiling(Steps / 2f); - - var arraySize = (short)((((halfSteps * 2f) + 1f) * 2f) * 2); - if (arraySize != _vertexBuffer.VertexCount) + 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 minX = -halfSteps * XAxis * spacing; + var maxX = halfSteps * XAxis * spacing; - var minY = -halfSteps * YAxis * Spacing; - var maxY = halfSteps * YAxis * 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); + 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); + _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); + 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); + _verticies[i] = new VertexPositionColor(maxPos, lineColor); _indicies[i] = (short)i; i++; } @@ -142,8 +162,7 @@ private void CalculateVertices() _vertexBuffer.SetData(_verticies); _indexBuffer.SetData(_indicies); } - - public void Update() + public override void Update(GameTime gameTime) { if (_dirty) { @@ -152,18 +171,22 @@ public void Update() } } - public void Draw(GraphicsDevice graphics, Matrix view, Matrix projection) + public override void Draw(GameTime gameTime) { - _effect.View = view; - _effect.Projection = projection; - - graphics.SetVertexBuffer(_vertexBuffer); - graphics.Indices = _indexBuffer; - - foreach (EffectPass pass in _effect.CurrentTechnique.Passes) + using (GraphicsContext.CreateContext(GraphicsDevice, BlendState.AlphaBlend, DepthStencilState.Default, RasterizerState.CullNone)) { - pass.Apply(); - graphics.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, _indicies.Length / 2); + 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); + } } } diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs index cb2758b08..ea98a0f79 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/ModelExplorerGame.cs @@ -14,6 +14,7 @@ using RocketUI.Input; using RocketUI.Input.Listeners; using RocketUI.Utilities.Helpers; +using SpriteBatchExtensions = RocketUI.Utilities.Extensions.SpriteBatchExtensions; namespace ResourcePackLib.ModelExplorer; @@ -76,6 +77,13 @@ private void OnWindowOnClientSizeChanged(object? sender, EventArgs e) } 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) @@ -103,10 +111,10 @@ protected override void Initialize() Window.AllowUserResizing = true; DeviceManager.ApplyChanges(); - - Components.Add(InputManager = ServiceProvider.GetRequiredService()); + Components.Add(SceneManager = ServiceProvider.GetRequiredService()); Components.Add(GuiManager = ServiceProvider.GetRequiredService()); + Components.Add(InputManager = ServiceProvider.GetRequiredService()); base.Initialize(); } @@ -114,6 +122,7 @@ protected override void Initialize() protected override void LoadContent() { GpuResourceManager.Init(GraphicsDevice); + SpriteBatchExtensions.Init(GraphicsDevice); _spriteBatch = new SpriteBatch(GraphicsDevice); base.LoadContent(); @@ -126,17 +135,21 @@ protected override void LoadContent() Camera.Position = new Vector3(1.7f, 1.7f, 1.7f); // cam.Rotation = Quaternion.CreateFromYawPitchRoll(270f, (float)Math.PI / 2f, 0f); - - GuiManager.ScaledResolution.TargetWidth = 480; - GuiManager.ScaledResolution.TargetHeight = 320; - GuiManager.AddScreen(_debugGui = new DebugGui()); - GuiManager.DrawOrder = 10; + + 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); diff --git a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs index a8a4d8e4d..d330dd5a8 100644 --- a/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs +++ b/src/ResourcePackLib/ResourcePackLib.ModelExplorer/Scenes/ModelViewScene.cs @@ -12,11 +12,17 @@ protected override void OnInitialize() { base.OnInitialize(); - var grid = new GridEntity(ModelExplorerGame.Instance); - grid.Visible = true; - grid.Position = Vector3.Zero; - grid.Scale = Vector3.One; - Components.Add(grid); + 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; @@ -25,8 +31,8 @@ protected override void OnInitialize() var fox = Services.CreateInstance(); fox.Visible = true; - fox.Position = Vector3.Up; - //fox.Transform.Scale = Vector3.One / 16f; + fox.Position = Vector3.Zero; + fox.Transform.Scale = Vector3.One / 16f; Components.Add(Services.GetOrCreateInstance()); //Components.Add(cube);