diff --git a/Assets/Grille.BeamNG.ico b/Assets/Grille.BeamNG.ico new file mode 100644 index 0000000..f20ca12 Binary files /dev/null and b/Assets/Grille.BeamNG.ico differ diff --git a/Assets/Grille.BeamNG.png b/Assets/Grille.BeamNG.png new file mode 100644 index 0000000..164f681 Binary files /dev/null and b/Assets/Grille.BeamNG.png differ diff --git a/Grille.BeamNGLib/Collections/IKeyed.cs b/Grille.BeamNGLib/Collections/IKeyed.cs new file mode 100644 index 0000000..88056fb --- /dev/null +++ b/Grille.BeamNGLib/Collections/IKeyed.cs @@ -0,0 +1,6 @@ +namespace Grille.BeamNgLib.Collections; + +public interface IKeyed +{ + public string? Key { get; } +} diff --git a/LevelTemplateCreator/Collections/KeyedCollection.cs b/Grille.BeamNGLib/Collections/KeyedCollection.cs similarity index 78% rename from LevelTemplateCreator/Collections/KeyedCollection.cs rename to Grille.BeamNGLib/Collections/KeyedCollection.cs index 5313411..bc02dde 100644 --- a/LevelTemplateCreator/Collections/KeyedCollection.cs +++ b/Grille.BeamNGLib/Collections/KeyedCollection.cs @@ -1,15 +1,9 @@ -using LevelTemplateCreator.Assets; -using System; +using Grille.BeamNgLib.SceneTree.Art; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -namespace LevelTemplateCreator.Collections; +namespace Grille.BeamNgLib.Collections; -internal class KeyedCollection : ICollection where T : class, IKeyed +public class KeyedCollection : ICollection where T : class, IKeyed { readonly Dictionary _dict; @@ -20,6 +14,8 @@ public KeyedCollection() public int Count => _dict.Count; + public bool IgnoreFalseDuplicates { get; set; } = false; + public bool IsReadOnly => false; public Dictionary.KeyCollection Keys => _dict.Keys; @@ -40,7 +36,7 @@ public void Add(T item) if (TryGetValue(key, out T old)) { - if (old == item) + if (old == item || IgnoreFalseDuplicates) { return; } @@ -98,6 +94,17 @@ public bool Remove(T item) return true; } + public IEnumerable EnumerateItems() where TItem : T + { + foreach (var item in _dict.Values) + { + if (item is TItem) + { + yield return (TItem)item; + } + } + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/Grille.BeamNGLib/Grille.BeamNgLib.csproj b/Grille.BeamNGLib/Grille.BeamNgLib.csproj new file mode 100644 index 0000000..dc0c366 --- /dev/null +++ b/Grille.BeamNGLib/Grille.BeamNgLib.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + 12 + enable + Grille.BeamNgLib + Grille.BeamNgLib + Grille + https://github.com/Grille/BeamNG_LevelTemplateCreator + True + Copyright (c) 2024 Paul Hirch + README.md + LICENSE + Grille.BeamNG.png + Grille + + + + + + + + + + + + + diff --git a/Grille.BeamNGLib/IO/Binary/TerrainBinary.cs b/Grille.BeamNGLib/IO/Binary/TerrainBinary.cs new file mode 100644 index 0000000..6cc10e2 --- /dev/null +++ b/Grille.BeamNGLib/IO/Binary/TerrainBinary.cs @@ -0,0 +1,39 @@ +namespace Grille.BeamNgLib.IO.Binary; + +public class TerrainBinary +{ + public struct TerrainData + { + public ushort Height; + public byte Material; + + public bool IsHole => Material == byte.MaxValue; + + public void SetHeight(float value, float maxHeight) => Height = TerrainV9Serializer.GetU16Height(value, maxHeight); + public float GetHeight(float maxHeight) => TerrainV9Serializer.GetSingleHeight(Height, maxHeight); + } + + public int Size { get; } + + public TerrainData[] Data { get; } + + public string[] MaterialNames { get; set; } + + public int TotalSize => Size * Size; + + public TerrainBinary(int size) : this(size, Array.Empty()) { } + + public TerrainBinary(int size, string[] materialNames) : this(size, materialNames, new TerrainData[size * size]) { } + + public TerrainBinary(int size, string[] materialNames, TerrainData[] data) + { + Size = size; + Data = data; + MaterialNames = materialNames; + + if (data.Length != TotalSize) + { + throw new ArgumentException($"data.Length must be {TotalSize}", nameof(data)); + } + } +} \ No newline at end of file diff --git a/LevelTemplateCreator/IO/JsonDictSerializer.cs b/Grille.BeamNGLib/IO/JsonDictSerializer.cs similarity index 93% rename from LevelTemplateCreator/IO/JsonDictSerializer.cs rename to Grille.BeamNGLib/IO/JsonDictSerializer.cs index af40d1e..51b1377 100644 --- a/LevelTemplateCreator/IO/JsonDictSerializer.cs +++ b/Grille.BeamNGLib/IO/JsonDictSerializer.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; +using System.Text.Json; -namespace LevelTemplateCreator.IO; +namespace Grille.BeamNgLib.IO; -static class JsonDictSerializer +public class JsonDictSerializer { public const string ArrayClassName = "Template_Array"; public static void Serialize(Stream stream, T value, bool intended = false) where T : IDictionary diff --git a/LevelTemplateCreator/IO/LevelInfoSerializer.cs b/Grille.BeamNGLib/IO/LevelInfoSerializer.cs similarity index 80% rename from LevelTemplateCreator/IO/LevelInfoSerializer.cs rename to Grille.BeamNGLib/IO/LevelInfoSerializer.cs index f6c0ab8..30d915f 100644 --- a/LevelTemplateCreator/IO/LevelInfoSerializer.cs +++ b/Grille.BeamNGLib/IO/LevelInfoSerializer.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.IO; -namespace LevelTemplateCreator.IO; - -static class LevelInfoSerializer +public class LevelInfoSerializer { public static void Serialize(Level level, string path) { diff --git a/Grille.BeamNGLib/IO/Resources/FileResource.cs b/Grille.BeamNGLib/IO/Resources/FileResource.cs new file mode 100644 index 0000000..919f073 --- /dev/null +++ b/Grille.BeamNGLib/IO/Resources/FileResource.cs @@ -0,0 +1,16 @@ +namespace Grille.BeamNgLib.IO.Resources; + +public class FileResource : Resource +{ + public string Path { get; } + + public FileResource(string name, string path, bool isGameResource) : base(name, isGameResource) + { + Path = path; + } + + public override Stream Open() + { + return new FileStream(Path, FileMode.Open); + } +} diff --git a/Grille.BeamNGLib/IO/Resources/GroupResource.cs b/Grille.BeamNGLib/IO/Resources/GroupResource.cs new file mode 100644 index 0000000..7c29c08 --- /dev/null +++ b/Grille.BeamNGLib/IO/Resources/GroupResource.cs @@ -0,0 +1,14 @@ +namespace Grille.BeamNgLib.IO.Resources; + +internal class GroupResource : Resource +{ + public GroupResource(string name, bool isGameResource, Resource[] resources) : base(name, isGameResource) + { + + } + + public override Stream Open() + { + throw new NotImplementedException(); + } +} diff --git a/Grille.BeamNGLib/IO/Resources/PathEvaluator.cs b/Grille.BeamNGLib/IO/Resources/PathEvaluator.cs new file mode 100644 index 0000000..71c857c --- /dev/null +++ b/Grille.BeamNGLib/IO/Resources/PathEvaluator.cs @@ -0,0 +1,38 @@ +namespace Grille.BeamNgLib.IO.Resources; + +public static class PathEvaluator +{ + static public Resource Get(string entry, string gamePath) + { + return Get(entry, gamePath, string.Empty); + } + + static public Resource Get(string entry, string gamePath, string userPath) + { + entry = entry.Replace("\\", "/"); + + string path = entry.StartsWith('/') ? entry.Substring(1) : entry; + return ParseAbsolute(path, gamePath, userPath); + } + + static Resource ParseAbsolute(string entry, string gamePath, string userPath) + { + var split = entry.ToLower().Split([Path.PathSeparator, Path.AltDirectorySeparatorChar]); + + if (split[0] == "levels") + { + var level = split[1]; + var filename = split[split.Length - 1]; + var key = $"beamng.{level}.{filename}"; + var zippath = $"{gamePath}/content/levels/{level}.zip"; + if (File.Exists(zippath)) + { + return new ZipFileResource(key, zippath, entry, true); + } + } + + throw new NotImplementedException(); + } + + +} diff --git a/LevelTemplateCreator/IO/Resources/Resource.cs b/Grille.BeamNGLib/IO/Resources/Resource.cs similarity index 53% rename from LevelTemplateCreator/IO/Resources/Resource.cs rename to Grille.BeamNGLib/IO/Resources/Resource.cs index 396bd6e..b3fe675 100644 --- a/LevelTemplateCreator/IO/Resources/Resource.cs +++ b/Grille.BeamNGLib/IO/Resources/Resource.cs @@ -1,33 +1,29 @@ -using LevelTemplateCreator.Collections; -using LevelTemplateCreator.Properties; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.IO.Resources; - -internal abstract class Resource : IKeyed +using Grille.BeamNgLib.Collections; + +namespace Grille.BeamNgLib.IO.Resources; + +public abstract class Resource : IKeyed { string IKeyed.Key => Name; + public bool IsGameResource { get; } + public string Name { get; } public string DynamicName { get; protected set; } - public Resource(string name) + public Resource(string name, bool isGameResource) { Name = name; DynamicName = name; + IsGameResource = isGameResource; } - public abstract Stream OpenStream(); + public abstract Stream Open(); public void SaveToDirectory(string directory) { - using var stream = OpenStream(); + using var stream = Open(); var dstpath = Path.Combine(directory, DynamicName); using var file = File.OpenWrite(dstpath); stream.CopyTo(file); @@ -35,7 +31,7 @@ public void SaveToDirectory(string directory) public void Save(string path) { - using var src = OpenStream(); + using var src = Open(); using var dst = new FileStream(path, FileMode.Create); src.CopyTo(dst); } diff --git a/Grille.BeamNGLib/IO/Resources/ResourceCollection.cs b/Grille.BeamNGLib/IO/Resources/ResourceCollection.cs new file mode 100644 index 0000000..c0b6640 --- /dev/null +++ b/Grille.BeamNGLib/IO/Resources/ResourceCollection.cs @@ -0,0 +1,21 @@ +using Grille.BeamNgLib.Collections; + +namespace Grille.BeamNgLib.IO.Resources; + +public class ResourceCollection : KeyedCollection +{ + public ResourceCollection() { } + + public ResourceCollection(bool ignoreFalseDuplicates) + { + IgnoreFalseDuplicates = ignoreFalseDuplicates; + } + + public void Save(string path) + { + foreach (var resource in this) + { + resource.SaveToDirectory(path); + } + } +} diff --git a/Grille.BeamNGLib/IO/Resources/StaticPath.cs b/Grille.BeamNGLib/IO/Resources/StaticPath.cs new file mode 100644 index 0000000..3f4c964 --- /dev/null +++ b/Grille.BeamNGLib/IO/Resources/StaticPath.cs @@ -0,0 +1,4 @@ +namespace Grille.BeamNgLib.IO.Resources; +internal class StaticPath +{ +} diff --git a/LevelTemplateCreator/IO/Resources/ZipFileResource.cs b/Grille.BeamNGLib/IO/Resources/ZipFileResource.cs similarity index 73% rename from LevelTemplateCreator/IO/Resources/ZipFileResource.cs rename to Grille.BeamNGLib/IO/Resources/ZipFileResource.cs index 2f6e6a5..12811d5 100644 --- a/LevelTemplateCreator/IO/Resources/ZipFileResource.cs +++ b/Grille.BeamNGLib/IO/Resources/ZipFileResource.cs @@ -1,25 +1,17 @@ -using System; -using System.Collections.Generic; -using System.IO.Compression; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.IO.Resources; -namespace LevelTemplateCreator.IO.Resources; - -internal class ZipFileResource : Resource +public class ZipFileResource : Resource { public string ZipFilePath { get; } public string EntryPath { get; } - public ZipFileResource(string name, string zipFilePath, string path) : base(name) + public ZipFileResource(string name, string zipFilePath, string path, bool isGameResource) : base(name, isGameResource) { ZipFilePath = zipFilePath; EntryPath = path; } - public override Stream OpenStream() + public override Stream Open() { var archive = ZipFileManager.Open(ZipFilePath); var entry = archive.GetEntry(EntryPath); @@ -46,5 +38,5 @@ public override Stream OpenStream() public void Find() { - } + } } diff --git a/Grille.BeamNGLib/IO/TerrainV9Serializer.cs b/Grille.BeamNGLib/IO/TerrainV9Serializer.cs new file mode 100644 index 0000000..7d38cae --- /dev/null +++ b/Grille.BeamNGLib/IO/TerrainV9Serializer.cs @@ -0,0 +1,140 @@ +using System.Text; +using GGL.IO; +using Grille.BeamNgLib.IO.Binary; + +namespace Grille.BeamNgLib.IO; + +public class TerrainV9Serializer +{ + public static TerrainBinary Deserialize(string path, bool ignoreVersion = false) + { + using var stream = new FileStream(path, FileMode.Open); + return Deserialize(stream, ignoreVersion); + } + + public static TerrainBinary Deserialize(Stream stream, bool ignoreVersion = false) + { + using var br = new BinaryViewReader(stream); + + byte version = br.ReadByte(); + if (version != 9 && !ignoreVersion) + { + throw new InvalidDataException($"Unsupported terrain version '{version}'."); + } + + int size = (int)br.ReadUInt32(); + + var terrain = new TerrainBinary(size); + + int sqsize = size * size; + + var data = terrain.Data; + + for (int i = 0; i< sqsize; i++) + { + data[i].Height = br.ReadUInt16(); + } + for (int i = 0; i < sqsize; i++) + { + data[i].Material = br.ReadByte(); + } + + int materialCount = (int)br.ReadUInt32(); + terrain.MaterialNames = new string[materialCount]; + + for (int i = 0; i < materialCount; i++) + { + terrain.MaterialNames[i] = br.ReadString(LengthPrefix.Byte, Encoding.UTF8); + } + + return terrain; + } + + public static void Serialize(TerrainBinary terrain, string path) + { + using var stream = new FileStream(path, FileMode.Create); + Serialize(terrain, stream); + } + + public static void Serialize(TerrainBinary terrain, Stream stream) + { + using var bw = new BinaryViewWriter(stream); + + int size = terrain.Size; + int sqsize = size * size; + + if (terrain.Data.Length != sqsize) + throw new ArgumentException("Data.Length must equal Data.Size^2."); + + bw.WriteByte(9); + bw.WriteUInt32((uint)size); + + var data = terrain.Data; + + for (int i = 0;i < sqsize; i++) + { + bw.WriteUInt16(data[i].Height); + } + for (int i = 0;i < sqsize; i++) + { + bw.WriteByte(data[i].Material); + } + + var names = terrain.MaterialNames; + bw.WriteUInt32((uint)names.Length); + for (int i = 0; i < names.Length; i++) + { + bw.WriteString(names[i], LengthPrefix.Byte, Encoding.UTF8); + } + } + + public static void Serialize(TerrainInfo info, ICollection materials, string path) + { + using var stream = new FileStream(path, FileMode.Create); + Serialize(info, materials, stream); + } + + public static void Serialize(TerrainInfo info, ICollection materials, Stream stream) + { + using var bw = new BinaryViewWriter(stream); + + bw.WriteByte(9); + bw.WriteUInt32((uint)info.Resolution); + + long size = info.Resolution * (long)info.Resolution; + ushort u16height = info.U16Height; + + for (int i = 0; i < size; i++) + { + bw.WriteUInt16(u16height); + } + + bw.Seek(size, SeekOrigin.Current); + + bw.WriteUInt32((uint)materials.Count); + + foreach (var material in materials) + { + bw.WriteString(material, LengthPrefix.Byte, Encoding.UTF8); + } + } + + public static ushort GetU16Height(float height, float maxHeight) + { + float u16max = ushort.MaxValue; + + float u16height = height / maxHeight * u16max; + if (u16height > u16max) + u16height = u16max; + + return (ushort)u16height; + } + + public static float GetSingleHeight(ushort u16height, float maxHeight) + { + float height = u16height; + float u16max = ushort.MaxValue; + + return (height / u16max) * maxHeight; + } +} diff --git a/LevelTemplateCreator/IO/ZipFileManager.cs b/Grille.BeamNGLib/IO/ZipFileManager.cs similarity index 92% rename from LevelTemplateCreator/IO/ZipFileManager.cs rename to Grille.BeamNGLib/IO/ZipFileManager.cs index 2014b0d..d926982 100644 --- a/LevelTemplateCreator/IO/ZipFileManager.cs +++ b/Grille.BeamNGLib/IO/ZipFileManager.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; +using Grille.BeamNgLib.Logging; using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace LevelTemplateCreator.IO; +namespace Grille.BeamNgLib.IO; -internal static class ZipFileManager +public static class ZipFileManager { public class ZipArchiveWrapper : IDisposable { diff --git a/Grille.BeamNGLib/Level.cs b/Grille.BeamNGLib/Level.cs new file mode 100644 index 0000000..4b571d4 --- /dev/null +++ b/Grille.BeamNGLib/Level.cs @@ -0,0 +1,24 @@ +using Grille.BeamNgLib.SceneTree.Art; +using Grille.BeamNgLib.SceneTree.Main; + +namespace Grille.BeamNgLib; + +public class Level +{ + public LevelInfo Info { get; } + + public TerrainInfo Terrain { get; } + + public SimGroupRoot Main { get; } + + public ArtGroupRoot Art { get; } + + public Level() + { + Info = new LevelInfo(); + Terrain = new TerrainInfo(); + + Main = new SimGroupRoot(); + Art = new ArtGroupRoot(); + } +} diff --git a/LevelTemplateCreator/LevelInfo.cs b/Grille.BeamNGLib/LevelInfo.cs similarity index 68% rename from LevelTemplateCreator/LevelInfo.cs rename to Grille.BeamNGLib/LevelInfo.cs index 1e9cc55..507294f 100644 --- a/LevelTemplateCreator/LevelInfo.cs +++ b/Grille.BeamNGLib/LevelInfo.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; +namespace Grille.BeamNgLib; -namespace LevelTemplateCreator; - -internal class LevelInfo +public class LevelInfo { public string Title { get; set; } diff --git a/LevelTemplateCreator/Assets/ErrorLogger.cs b/Grille.BeamNGLib/Logging/ErrorLogger.cs similarity index 81% rename from LevelTemplateCreator/Assets/ErrorLogger.cs rename to Grille.BeamNGLib/Logging/ErrorLogger.cs index f7ab953..b52b21f 100644 --- a/LevelTemplateCreator/Assets/ErrorLogger.cs +++ b/Grille.BeamNGLib/Logging/ErrorLogger.cs @@ -1,11 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.Assets; -internal class ErrorLogger +namespace Grille.BeamNgLib.Logging; +public class ErrorLogger { readonly List _errors = new List(); diff --git a/Grille.BeamNGLib/Logging/Logger.cs b/Grille.BeamNGLib/Logging/Logger.cs new file mode 100644 index 0000000..500aebe --- /dev/null +++ b/Grille.BeamNGLib/Logging/Logger.cs @@ -0,0 +1,85 @@ +namespace Grille.BeamNgLib.Logging; + +public enum LoggerColor +{ + Default = ConsoleColor.Gray, + Red = ConsoleColor.Red, +} + +public static class Logger +{ + public static bool EnableConsoleOutput { get; set; } = true; + + static Stream? _stream; + static StreamWriter? _writer; + + public static Stream? OutputStream { get => _stream; set + { + if (_stream == value) + return; + + _stream = value; + + if (_stream == null) + { + _writer = null; + return; + } + + _writer = new StreamWriter(_stream); + } + } + + public static void WriteLine() + { + WriteToStream(); + WriteToConsole(); + } + + public static void WriteLine(string text) + { + WriteToStream(text); + WriteToConsole(text); + } + + public static void WriteLine(string text, LoggerColor color) + { + WriteToStream(text); + WriteToConsole(text, color); + } + + static void WriteToStream() + { + if (_writer == null) + return; + + _writer.WriteLine(); + } + + static void WriteToStream(string text) + { + if (_writer == null) + return; + + _writer.WriteLine(text); + _writer.Flush(); + } + + static void WriteToConsole() + { + if (!EnableConsoleOutput) + return; + + Console.WriteLine(); + } + + static void WriteToConsole(string text, LoggerColor color = LoggerColor.Default) + { + if (!EnableConsoleOutput) + return; + + Console.ForegroundColor = (ConsoleColor)color; + Console.WriteLine(text); + Console.ForegroundColor = (ConsoleColor)LoggerColor.Default; + } +} diff --git a/Grille.BeamNGLib/SceneTree/Art/ArtGroup.cs b/Grille.BeamNGLib/SceneTree/Art/ArtGroup.cs new file mode 100644 index 0000000..091115c --- /dev/null +++ b/Grille.BeamNGLib/SceneTree/Art/ArtGroup.cs @@ -0,0 +1,56 @@ +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.IO; +using Grille.BeamNgLib.IO.Resources; + +namespace Grille.BeamNgLib.SceneTree.Art; + +public class ArtGroup : IKeyed +{ + string IKeyed.Key => Name; + + public string Name { get; } + + public KeyedCollection Children { get; } + + public ResourceCollection Resources { get; } + + public MaterialLibary MaterialItems { get; } + + public ArtGroup(string name) + { + Name = name; + Children = new(); + Resources = new(true); + MaterialItems = new(Resources); + } + + public IEnumerable EnumerateMaterialItems() where T : ArtItem + { + foreach (var item in MaterialItems) + { + if (item is T) + { + yield return (T)item; + } + } + } + + public void SaveTree(string path) + { + Directory.CreateDirectory(path); + + foreach (var item in Children) + { + var childpath = Path.Combine(path, item.Name); + item.SaveTree(childpath); + } + + var materialsPath = Path.Combine(path, MaterialLibary.FileName); + MaterialItems.SerializeItems(materialsPath); + + foreach (var resource in Resources) + { + resource.SaveToDirectory(path); + } + } +} diff --git a/Grille.BeamNGLib/SceneTree/Art/ArtGroupRoot.cs b/Grille.BeamNGLib/SceneTree/Art/ArtGroupRoot.cs new file mode 100644 index 0000000..3ca2124 --- /dev/null +++ b/Grille.BeamNGLib/SceneTree/Art/ArtGroupRoot.cs @@ -0,0 +1,16 @@ +namespace Grille.BeamNgLib.SceneTree.Art; +public class ArtGroupRoot : ArtGroup +{ + public ArtGroup Terrains { get; } + + public ArtGroup Groundcover { get; } + + public ArtGroupRoot() : base("art") + { + Terrains = new("terrains"); + Groundcover = new("groundcover"); + + Children.Add(Terrains); + Children.Add(Groundcover); + } +} diff --git a/Grille.BeamNGLib/SceneTree/Art/ArtItem.cs b/Grille.BeamNGLib/SceneTree/Art/ArtItem.cs new file mode 100644 index 0000000..11d7b9e --- /dev/null +++ b/Grille.BeamNGLib/SceneTree/Art/ArtItem.cs @@ -0,0 +1,8 @@ + +namespace Grille.BeamNgLib.SceneTree.Art; +public class ArtItem : JsonDictWrapper +{ + public ArtItem(JsonDict dict) : base(dict) + { + } +} diff --git a/LevelTemplateCreator/SceneTree/Art/ForestItemData.cs b/Grille.BeamNGLib/SceneTree/Art/ForestItemData.cs similarity index 51% rename from LevelTemplateCreator/SceneTree/Art/ForestItemData.cs rename to Grille.BeamNGLib/SceneTree/Art/ForestItemData.cs index d250ade..fcb6001 100644 --- a/LevelTemplateCreator/SceneTree/Art/ForestItemData.cs +++ b/Grille.BeamNGLib/SceneTree/Art/ForestItemData.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Art; -namespace LevelTemplateCreator.SceneTree.Art; - -internal class ForestItemData : JsonDictWrapper +public sealed class ForestItemData : JsonDictWrapper { public const string ClassName = "TSForestItemData"; diff --git a/Grille.BeamNGLib/SceneTree/Art/Material.cs b/Grille.BeamNGLib/SceneTree/Art/Material.cs new file mode 100644 index 0000000..b145d5c --- /dev/null +++ b/Grille.BeamNGLib/SceneTree/Art/Material.cs @@ -0,0 +1,8 @@ +namespace Grille.BeamNgLib.SceneTree.Art; + +public abstract class Material : ArtItem +{ + protected Material(JsonDict dict) : base(dict) { } + + public abstract IEnumerable> EnumerateTexturePaths(); +} diff --git a/Grille.BeamNGLib/SceneTree/Art/MaterialLibary.cs b/Grille.BeamNGLib/SceneTree/Art/MaterialLibary.cs new file mode 100644 index 0000000..b64f006 --- /dev/null +++ b/Grille.BeamNGLib/SceneTree/Art/MaterialLibary.cs @@ -0,0 +1,45 @@ +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.IO; +using Grille.BeamNgLib.IO.Resources; + +namespace Grille.BeamNgLib.SceneTree.Art; + +public class MaterialLibary : KeyedCollection +{ + public const string FileName = "main.materials.json"; + + public ResourceCollection Resources { get; } + + public MaterialLibary(ResourceCollection resources) + { + Resources = resources; + } + + public string[] GetMaterialNames() + { + List names = new List(); + foreach (var material in EnumerateItems()) + { + names.Add(material.Name.Value); + } + return names.ToArray(); + } + + public void SerializeItems(string path) + { + using var stream = new FileStream(path, FileMode.Create); + SerializeItems(stream); + } + + public void SerializeItems(Stream stream) + { + var dict = new JsonDict(); + + foreach (var item in this) + { + dict[item.Name.Value] = item.Dict; + } + + JsonDictSerializer.Serialize(stream, dict, true); + } +} diff --git a/LevelTemplateCreator/SceneTree/Art/MaterialNode.cs b/Grille.BeamNGLib/SceneTree/Art/MaterialNode.cs similarity index 54% rename from LevelTemplateCreator/SceneTree/Art/MaterialNode.cs rename to Grille.BeamNGLib/SceneTree/Art/MaterialNode.cs index fdcd61b..6ff0465 100644 --- a/LevelTemplateCreator/SceneTree/Art/MaterialNode.cs +++ b/Grille.BeamNGLib/SceneTree/Art/MaterialNode.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Art; -namespace LevelTemplateCreator.SceneTree.Art; - -internal class MaterialNode +public class MaterialNode { public string Name { get; } diff --git a/LevelTemplateCreator/SceneTree/Art/ObjectMaterial.cs b/Grille.BeamNGLib/SceneTree/Art/ObjectMaterial.cs similarity index 57% rename from LevelTemplateCreator/SceneTree/Art/ObjectMaterial.cs rename to Grille.BeamNGLib/SceneTree/Art/ObjectMaterial.cs index f0d122e..949e857 100644 --- a/LevelTemplateCreator/SceneTree/Art/ObjectMaterial.cs +++ b/Grille.BeamNGLib/SceneTree/Art/ObjectMaterial.cs @@ -1,26 +1,19 @@ -using LevelTemplateCreator.Assets; -using LevelTemplateCreator.IO.Resources; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.SceneTree.Art; - -internal class ObjectMaterial : Material +using System.Collections.ObjectModel; + +namespace Grille.BeamNgLib.SceneTree.Art; + +public class ObjectMaterial : Material { public const string ClassName = "Material"; public JsonDictProperty Version { get; } - public ReadOnlyCollection Stages { get; } + public ReadOnlyCollection Stages { get; } - public ObjectPbrMaterialStage Stage0 { get; } - public ObjectPbrMaterialStage Stage1 { get; } - public ObjectPbrMaterialStage Stage2 { get; } - public ObjectPbrMaterialStage Stage3 { get; } + public ObjectMaterialStage Stage0 { get; } + public ObjectMaterialStage Stage1 { get; } + public ObjectMaterialStage Stage2 { get; } + public ObjectMaterialStage Stage3 { get; } public ObjectMaterial(JsonDict dict) : base(dict) { @@ -36,7 +29,7 @@ public ObjectMaterial(JsonDict dict) : base(dict) Stages = new([Stage0, Stage1, Stage2, Stage3]); } - public override void ResolveTexturePaths(MaterialLibary libary, AssetInfo info) + public override IEnumerable> EnumerateTexturePaths() { foreach (var stage in Stages) { @@ -44,23 +37,14 @@ public override void ResolveTexturePaths(MaterialLibary libary, AssetInfo info) { if (!map.Exists) continue; - var key = libary.Textures.RegisterRelative(map.Value, info); - map.Value = Path.Combine(libary.TexturesPath, key); + + yield return map; } } } - - public override ObjectMaterial Copy() - { - var clone = new JsonDict(Dict); - var stages = new JsonDict[4] { new(Stage0.Dict), new(Stage1.Dict), new(Stage2.Dict), new(Stage3.Dict) }; - clone["Stages"] = stages; - - return new ObjectMaterial(clone); - } } -class ObjectPbrMaterialStage : JsonDictWrapper +public class ObjectMaterialStage : JsonDictWrapper { public JsonDictProperty AmbientOcclusionMap { get; } @@ -78,7 +62,7 @@ class ObjectPbrMaterialStage : JsonDictWrapper public ReadOnlyCollection> Maps { get; } - public ObjectPbrMaterialStage(JsonDict dict) : base(dict) + public ObjectMaterialStage(JsonDict dict) : base(dict) { AmbientOcclusionMap = new(this, "ambientOcclusionMap"); BaseColorMap = new(this, "baseColorMap"); diff --git a/LevelTemplateCreator/SceneTree/Art/TerrainMaterial.cs b/Grille.BeamNGLib/SceneTree/Art/TerrainMaterial.cs similarity index 84% rename from LevelTemplateCreator/SceneTree/Art/TerrainMaterial.cs rename to Grille.BeamNGLib/SceneTree/Art/TerrainMaterial.cs index 11a7499..5cc9119 100644 --- a/LevelTemplateCreator/SceneTree/Art/TerrainMaterial.cs +++ b/Grille.BeamNGLib/SceneTree/Art/TerrainMaterial.cs @@ -1,15 +1,7 @@ -using LevelTemplateCreator.Assets; -using LevelTemplateCreator.IO.Resources; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.SceneTree.Art; -internal class TerrainMaterial : Material +using System.Collections.ObjectModel; + +namespace Grille.BeamNgLib.SceneTree.Art; +public class TerrainMaterial : Material { public const string ClassName = "TerrainMaterial"; @@ -86,25 +78,20 @@ public void CreatePersistentId() Name.Value = $"{InternalName}_{PersistentId}"; } - public override void ResolveTexturePaths(MaterialLibary libary, AssetInfo source) + public override IEnumerable> EnumerateTexturePaths() { - foreach (var level in Levels) + foreach(var level in Levels) { - if (level.IsTextureEmpty) + var texture = level.Texture; + if (!texture.Exists) continue; - var key = libary.Textures.RegisterRelative(level.Texture.Value, source); - level.Texture.Value = Path.Combine(libary.TexturesPath, key); - } - } - public override TerrainMaterial Copy() - { - var copy = new JsonDict(Dict); - return new TerrainMaterial(copy); + yield return level.Texture; + } } } -internal class TerrainMaterialDistances +public class TerrainMaterialDistances { public string Prefix { get; } public TerrainMaterial Owner { get; } @@ -136,7 +123,7 @@ public TerrainMaterialDistances(TerrainMaterial owner, string prefix, float[] di public int EndFadeOut { get => (int)Distances.Value[3]; set => Distances.Value[3] = value; } } -internal class TerrainMaterialTexture +public class TerrainMaterialTexture { public string Prefix { get; } public TerrainMaterial Owner { get; } @@ -156,7 +143,7 @@ public TerrainMaterialTexture(TerrainMaterial owner, string prefix) } } -internal class TerrainMaterialTextureLayer +public class TerrainMaterialTextureLayer { public string Prefix { get; } public TerrainMaterial Owner { get; } diff --git a/LevelTemplateCreator/SceneTree/Art/TerrainMaterialTextureSet.cs b/Grille.BeamNGLib/SceneTree/Art/TerrainMaterialTextureSet.cs similarity index 72% rename from LevelTemplateCreator/SceneTree/Art/TerrainMaterialTextureSet.cs rename to Grille.BeamNGLib/SceneTree/Art/TerrainMaterialTextureSet.cs index 6b0742c..173a5d1 100644 --- a/LevelTemplateCreator/SceneTree/Art/TerrainMaterialTextureSet.cs +++ b/Grille.BeamNGLib/SceneTree/Art/TerrainMaterialTextureSet.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Numerics; +namespace Grille.BeamNgLib.SceneTree.Art; -namespace LevelTemplateCreator.SceneTree.Art; - -internal class TerrainMaterialTextureSet : JsonDictWrapper +public class TerrainMaterialTextureSet : ArtItem { public JsonDictProperty BaseTexSize { get; } public JsonDictProperty DetailTexSize { get; } diff --git a/LevelTemplateCreator/SceneTree/JsonDictProperty.cs b/Grille.BeamNGLib/SceneTree/JsonDictProperty.cs similarity index 91% rename from LevelTemplateCreator/SceneTree/JsonDictProperty.cs rename to Grille.BeamNGLib/SceneTree/JsonDictProperty.cs index c175940..4ca4391 100644 --- a/LevelTemplateCreator/SceneTree/JsonDictProperty.cs +++ b/Grille.BeamNGLib/SceneTree/JsonDictProperty.cs @@ -1,20 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; +using System.Runtime.CompilerServices; -namespace LevelTemplateCreator.SceneTree; +namespace Grille.BeamNgLib.SceneTree; struct JsonDictProperty { public readonly JsonDictWrapper Owner; public readonly string Key; - public JsonDictProperty(JsonDictWrapper owner, string key) { + public JsonDictProperty(JsonDictWrapper owner, string key) + { Owner = owner; Key = key; } @@ -72,7 +66,7 @@ public float Single() } } -class JsonDictProperty where T : notnull +public class JsonDictProperty where T : notnull { enum PropertyType { @@ -90,7 +84,7 @@ enum PropertyType public T? DefaultValue { get; } - public T Value { get => Get();set => Set(value); } + public T Value { get => Get(); set => Set(value); } public JsonDictProperty(JsonDictWrapper owner, string key) { @@ -105,7 +99,7 @@ public JsonDictProperty(JsonDictWrapper owner, string key) _type = PropertyType.Vec2; else if (t == typeof(Vector3)) _type = PropertyType.Vec3; - else + else _type = PropertyType.Object; } diff --git a/LevelTemplateCreator/SceneTree/JsonDictWrapper.cs b/Grille.BeamNGLib/SceneTree/JsonDictWrapper.cs similarity index 69% rename from LevelTemplateCreator/SceneTree/JsonDictWrapper.cs rename to Grille.BeamNGLib/SceneTree/JsonDictWrapper.cs index 248c431..f14987f 100644 --- a/LevelTemplateCreator/SceneTree/JsonDictWrapper.cs +++ b/Grille.BeamNGLib/SceneTree/JsonDictWrapper.cs @@ -1,18 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using LevelTemplateCreator.Collections; -using LevelTemplateCreator.IO; +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.IO; -using LevelTemplateCreator.SceneTree.Main; +namespace Grille.BeamNgLib.SceneTree; -namespace LevelTemplateCreator.SceneTree; - -internal class JsonDictWrapper : IKeyed +public abstract class JsonDictWrapper : IKeyed { public JsonDict Dict { get; } @@ -51,7 +42,8 @@ public void Deserialize(Stream stream) public bool TryGetValue(string key, out T value) { - if (Dict.TryGetValue(key, out var obj)){ + if (Dict.TryGetValue(key, out var obj)) + { value = (T)obj; return true; } @@ -77,17 +69,6 @@ public void TryPopValue(string key, out T value, T defaultValue) } } - public virtual void CopyTo(JsonDictWrapper target) - { - if (target.Class.Exists && target.Class.Value != Class.Value) - throw new ArgumentException($"Canot copy '{Class}' to '{target.Class}'"); - - foreach (var pair in Dict) - { - target[pair.Key] = pair.Value; - } - } - public void ApplyNamespace(string @namespace) { if (!string.IsNullOrEmpty(@namespace)) @@ -108,10 +89,15 @@ public virtual void ApplyPrefix(string prefix) } } - public virtual JsonDictWrapper Copy() + public JsonDict CopyDict() { - var dict = new JsonDict(Dict); - return new JsonDictWrapper(dict); + var dict = new JsonDict(); + CopyDict(Dict, dict); + return dict; } + public static void CopyDict(JsonDict src, JsonDict dst) + { + + } } \ No newline at end of file diff --git a/LevelTemplateCreator/SceneTree/Main/GroundCover.cs b/Grille.BeamNGLib/SceneTree/Main/GroundCover.cs similarity index 56% rename from LevelTemplateCreator/SceneTree/Main/GroundCover.cs rename to Grille.BeamNGLib/SceneTree/Main/GroundCover.cs index 0915bf9..ee1a97e 100644 --- a/LevelTemplateCreator/SceneTree/Main/GroundCover.cs +++ b/Grille.BeamNGLib/SceneTree/Main/GroundCover.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class GroundCover : SimItem +public class GroundCover : SimItem { public const string ClassName = "GroundCover"; @@ -21,17 +15,6 @@ public GroundCover(JsonDict dict) : base(dict) Types = new(this, "Types"); } - public override GroundCover Copy() - { - if (Types.Exists) - { - throw new Exception(); - } - - var dict = new JsonDict(Dict); - return new GroundCover(dict); - } - public void AddInstance(GroundCoverInstance instance) { diff --git a/LevelTemplateCreator/SceneTree/Main/GroundCoverInstance.cs b/Grille.BeamNGLib/SceneTree/Main/GroundCoverInstance.cs similarity index 73% rename from LevelTemplateCreator/SceneTree/Main/GroundCoverInstance.cs rename to Grille.BeamNGLib/SceneTree/Main/GroundCoverInstance.cs index 1c0e98c..1025ee2 100644 --- a/LevelTemplateCreator/SceneTree/Main/GroundCoverInstance.cs +++ b/Grille.BeamNGLib/SceneTree/Main/GroundCoverInstance.cs @@ -1,13 +1,6 @@ -using LevelTemplateCreator.SceneTree.Art; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class GroundCoverInstance : JsonDictWrapper +public class GroundCoverInstance : JsonDictWrapper { public const string ClassName = "GroundCover_Instance"; @@ -38,7 +31,7 @@ static GroundCoverInstance[] PopFromObject(JsonDictWrapper obj) for (int i = 0; i < array.Value.Length; i++) { var dict = array.Value[i]; - if (dict.Count == 0) + if (dict.Count == 0) continue; var instance = new GroundCoverInstance(dict); @@ -51,12 +44,6 @@ static GroundCoverInstance[] PopFromObject(JsonDictWrapper obj) return result.ToArray(); } - public override GroundCoverInstance Copy() - { - var dict = new JsonDict(Dict); - return new GroundCoverInstance(dict); - } - public override void ApplyPrefix(string prefix) { base.ApplyPrefix(prefix); diff --git a/LevelTemplateCreator/SceneTree/Main/SimGroup.cs b/Grille.BeamNGLib/SceneTree/Main/SimGroup.cs similarity index 66% rename from LevelTemplateCreator/SceneTree/Main/SimGroup.cs rename to Grille.BeamNGLib/SceneTree/Main/SimGroup.cs index b01e323..4f0a3be 100644 --- a/LevelTemplateCreator/SceneTree/Main/SimGroup.cs +++ b/Grille.BeamNGLib/SceneTree/Main/SimGroup.cs @@ -1,16 +1,9 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.SceneTree.Art; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -using LevelTemplateCreator.Collections; - -namespace LevelTemplateCreator.SceneTree.Main; - -internal class SimGroup : SimItem +public class SimGroup : SimItem { public bool IsMain { get; set; } = false; @@ -46,6 +39,17 @@ public void SerializeItems(Stream stream) } } + public IEnumerable EnumerateItems() where T : SimItem + { + foreach (var item in Items) + { + if (item is T) + { + yield return (T)item; + } + } + } + public const string FileName = "items.level.json"; public void SaveTree(string path) @@ -57,19 +61,11 @@ public void SaveTree(string path) var filePath = Path.Combine(path, FileName); SerializeItems(filePath); - foreach (var item in Items) + foreach (var item in EnumerateItems()) { - if (item is SimGroup) - { - var childPath = Path.Combine(path, item.Name.Value); - Directory.CreateDirectory(childPath); - ((SimGroup)item).SaveTree(childPath); - } + var childPath = Path.Combine(path, item.Name.Value); + Directory.CreateDirectory(childPath); + item.SaveTree(childPath); } } - - public override void CopyTo(JsonDictWrapper target) - { - throw new NotSupportedException(); - } } diff --git a/LevelTemplateCreator/SceneTree/Main/SimGroupLevelObjects.cs b/Grille.BeamNGLib/SceneTree/Main/SimGroupLevelObjects.cs similarity index 74% rename from LevelTemplateCreator/SceneTree/Main/SimGroupLevelObjects.cs rename to Grille.BeamNGLib/SceneTree/Main/SimGroupLevelObjects.cs index a3a0d25..6ffec1b 100644 --- a/LevelTemplateCreator/SceneTree/Main/SimGroupLevelObjects.cs +++ b/Grille.BeamNGLib/SceneTree/Main/SimGroupLevelObjects.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class SimGroupLevelObjects : SimGroup +public class SimGroupLevelObjects : SimGroup { public SimGroup Cloud { get; } diff --git a/LevelTemplateCreator/SceneTree/Main/SimGroupMissionGroup.cs b/Grille.BeamNGLib/SceneTree/Main/SimGroupMissionGroup.cs similarity index 61% rename from LevelTemplateCreator/SceneTree/Main/SimGroupMissionGroup.cs rename to Grille.BeamNGLib/SceneTree/Main/SimGroupMissionGroup.cs index 4fd4501..7827087 100644 --- a/LevelTemplateCreator/SceneTree/Main/SimGroupMissionGroup.cs +++ b/Grille.BeamNGLib/SceneTree/Main/SimGroupMissionGroup.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class SimGroupMissionGroup : SimGroup +public class SimGroupMissionGroup : SimGroup { public SimGroupLevelObjects LevelObject { get; } diff --git a/LevelTemplateCreator/SceneTree/Main/SimGroupRoot.cs b/Grille.BeamNGLib/SceneTree/Main/SimGroupRoot.cs similarity index 51% rename from LevelTemplateCreator/SceneTree/Main/SimGroupRoot.cs rename to Grille.BeamNGLib/SceneTree/Main/SimGroupRoot.cs index 417ef69..39315e8 100644 --- a/LevelTemplateCreator/SceneTree/Main/SimGroupRoot.cs +++ b/Grille.BeamNGLib/SceneTree/Main/SimGroupRoot.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class SimGroupRoot : SimGroup +public class SimGroupRoot : SimGroup { public SimGroupMissionGroup MissionGroup { get; } diff --git a/LevelTemplateCreator/SceneTree/Main/SimItem.cs b/Grille.BeamNGLib/SceneTree/Main/SimItem.cs similarity index 66% rename from LevelTemplateCreator/SceneTree/Main/SimItem.cs rename to Grille.BeamNGLib/SceneTree/Main/SimItem.cs index 8e1732c..fb6caf7 100644 --- a/LevelTemplateCreator/SceneTree/Main/SimItem.cs +++ b/Grille.BeamNGLib/SceneTree/Main/SimItem.cs @@ -1,13 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class SimItem : JsonDictWrapper +public class SimItem : JsonDictWrapper { public JsonDictProperty Position { get; } diff --git a/LevelTemplateCreator/SceneTree/Main/SpawnSphere.cs b/Grille.BeamNGLib/SceneTree/Main/SpawnSphere.cs similarity index 82% rename from LevelTemplateCreator/SceneTree/Main/SpawnSphere.cs rename to Grille.BeamNGLib/SceneTree/Main/SpawnSphere.cs index 2570213..251814f 100644 --- a/LevelTemplateCreator/SceneTree/Main/SpawnSphere.cs +++ b/Grille.BeamNGLib/SceneTree/Main/SpawnSphere.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class SpawnSphere : SimItem +public class SpawnSphere : SimItem { public JsonDictProperty DataBlock { get; } diff --git a/LevelTemplateCreator/SceneTree/Main/TerrainBlock.cs b/Grille.BeamNGLib/SceneTree/Main/TerrainBlock.cs similarity index 82% rename from LevelTemplateCreator/SceneTree/Main/TerrainBlock.cs rename to Grille.BeamNGLib/SceneTree/Main/TerrainBlock.cs index ba3a925..cac6d60 100644 --- a/LevelTemplateCreator/SceneTree/Main/TerrainBlock.cs +++ b/Grille.BeamNGLib/SceneTree/Main/TerrainBlock.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +namespace Grille.BeamNgLib.SceneTree.Main; -namespace LevelTemplateCreator.SceneTree.Main; - -internal class TerrainBlock : SimItem +public class TerrainBlock : SimItem { public JsonDictProperty MaterialTextureSet { get; } public JsonDictProperty TerrainFile { get; } diff --git a/LevelTemplateCreator/TerrainInfo.cs b/Grille.BeamNGLib/TerrainInfo.cs similarity index 82% rename from LevelTemplateCreator/TerrainInfo.cs rename to Grille.BeamNGLib/TerrainInfo.cs index f79dd03..78a1efb 100644 --- a/LevelTemplateCreator/TerrainInfo.cs +++ b/Grille.BeamNGLib/TerrainInfo.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Grille.BeamNgLib.IO; -namespace LevelTemplateCreator; +namespace Grille.BeamNgLib; -internal class TerrainInfo +public class TerrainInfo { private int resolution; @@ -55,4 +51,6 @@ public float WorldSize public float MaxHeight { get; set; } public float Height { get; set; } + + public ushort U16Height => TerrainV9Serializer.GetU16Height(Height, MaxHeight); } diff --git a/Grille.BeamNGLib/usings.cs b/Grille.BeamNGLib/usings.cs new file mode 100644 index 0000000..2c7b819 --- /dev/null +++ b/Grille.BeamNGLib/usings.cs @@ -0,0 +1,6 @@ +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Numerics; + +global using JsonDict = System.Collections.Generic.Dictionary; \ No newline at end of file diff --git a/Grille.BeamNGLib_Tests/Grille.BeamNgLib_Tests.csproj b/Grille.BeamNGLib_Tests/Grille.BeamNgLib_Tests.csproj new file mode 100644 index 0000000..2755523 --- /dev/null +++ b/Grille.BeamNGLib_Tests/Grille.BeamNgLib_Tests.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + diff --git a/Grille.BeamNGLib_Tests/Program.cs b/Grille.BeamNGLib_Tests/Program.cs new file mode 100644 index 0000000..a544ddc --- /dev/null +++ b/Grille.BeamNGLib_Tests/Program.cs @@ -0,0 +1,13 @@ +namespace Grille.BeamNgLib_Tests; + +internal class Program +{ + static void Main(string[] args) + { + ExecuteImmediately = true; + + Terrain.Run(); + + RunTestsSynchronously(); + } +} diff --git a/Grille.BeamNGLib_Tests/Terrain.cs b/Grille.BeamNGLib_Tests/Terrain.cs new file mode 100644 index 0000000..e6bf0d1 --- /dev/null +++ b/Grille.BeamNGLib_Tests/Terrain.cs @@ -0,0 +1,72 @@ +using Grille.BeamNgLib.IO.Binary; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Grille.BeamNgLib.IO; +using Grille.BeamNgLib; +using System.IO.Enumeration; + +namespace Grille.BeamNgLib_Tests; + +static class Terrain +{ + const string FileName = "terrain.ter"; + + public static void Run() + { + Section("Terrain"); + + Test("Terrain", TestTerrainFromInfo); + Test("Terrain", TestTerrain); + } + + static void TestTerrainFromInfo() + { + var names = new string[] + { + "Material0", + "Material1", + }; + + float maxHeight = 512; + float height = 10; + var info = new TerrainInfo() { Height = height, MaxHeight = maxHeight }; + ushort u16height = info.U16Height; + + TerrainV9Serializer.Serialize(info, names, FileName); + + var result = TerrainV9Serializer.Deserialize(FileName); + + AssertIsEqual((int)height, (int)(result.Data[0].GetHeight(maxHeight) + 0.5f)); + + for (int i = 0;i(int size, T value) + { + var array = new T[size]; + for (int i = 0; i < array.Length; i++) { array[i] = value; } + return array; + } +} diff --git a/Grille.BeamNGLib_Tests/usings.cs b/Grille.BeamNGLib_Tests/usings.cs new file mode 100644 index 0000000..49c10e5 --- /dev/null +++ b/Grille.BeamNGLib_Tests/usings.cs @@ -0,0 +1,5 @@ +global using Grille.ConsoleTestLib; +global using static Grille.ConsoleTestLib.GlobalTestSystem; +global using static Grille.ConsoleTestLib.TestResult; +global using static Grille.ConsoleTestLib.UsingSyntaxAsserts; +global using static Grille.BeamNgLib_Tests.Utils; \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..32cdd27 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Paul Hirch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/LevelTemplateCreator.sln b/LevelTemplateCreator.sln index 5065e92..70c6670 100644 --- a/LevelTemplateCreator.sln +++ b/LevelTemplateCreator.sln @@ -3,13 +3,20 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34525.116 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LevelTemplateCreator", "LevelTemplateCreator/LevelTemplateCreator.csproj", "{C31B145C-37AF-46FA-B194-BB982020A461}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LevelTemplateCreator", "LevelTemplateCreator\LevelTemplateCreator.csproj", "{C31B145C-37AF-46FA-B194-BB982020A461}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C3C03C73-5671-4D98-A299-32AC586BBA17}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + .gitignore = .gitignore + LICENSE = LICENSE + README.md = README.md EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grille.BeamNgLib", "Grille.BeamNGLib\Grille.BeamNgLib.csproj", "{71707660-F679-4412-AC29-0358B3D29738}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grille.BeamNgLib_Tests", "Grille.BeamNGLib_Tests\Grille.BeamNgLib_Tests.csproj", "{5435185E-3DDF-4D35-BF74-E79A8ECD649C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,6 +27,14 @@ Global {C31B145C-37AF-46FA-B194-BB982020A461}.Debug|Any CPU.Build.0 = Debug|Any CPU {C31B145C-37AF-46FA-B194-BB982020A461}.Release|Any CPU.ActiveCfg = Release|Any CPU {C31B145C-37AF-46FA-B194-BB982020A461}.Release|Any CPU.Build.0 = Release|Any CPU + {71707660-F679-4412-AC29-0358B3D29738}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71707660-F679-4412-AC29-0358B3D29738}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71707660-F679-4412-AC29-0358B3D29738}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71707660-F679-4412-AC29-0358B3D29738}.Release|Any CPU.Build.0 = Release|Any CPU + {5435185E-3DDF-4D35-BF74-E79A8ECD649C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5435185E-3DDF-4D35-BF74-E79A8ECD649C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5435185E-3DDF-4D35-BF74-E79A8ECD649C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5435185E-3DDF-4D35-BF74-E79A8ECD649C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/LevelTemplateCreator/Assets/Asset.cs b/LevelTemplateCreator/Assets/Asset.cs index 3ec9cf9..93eef11 100644 --- a/LevelTemplateCreator/Assets/Asset.cs +++ b/LevelTemplateCreator/Assets/Asset.cs @@ -1,6 +1,7 @@ -using LevelTemplateCreator.Collections; +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.SceneTree; using LevelTemplateCreator.IO.Resources; -using LevelTemplateCreator.SceneTree; +using LevelTemplateCreator.Properties; using System; using System.Collections.Generic; using System.Diagnostics; @@ -9,13 +10,12 @@ using System.Text; using System.Threading.Tasks; using System.Xml.Linq; -using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; namespace LevelTemplateCreator.Assets; -abstract class Asset : IKeyed +public abstract class Asset : IKeyed { - string IKeyed.Key => Object.Name.Value; + public string Key { get; } public string DisplayName { get; set; } = string.Empty; @@ -29,24 +29,29 @@ abstract class Asset : IKeyed public string Namespace { get; } - public AssetInfo Info { get; } + public AssetSource Info { get; } - public JsonDictWrapper Object { get; } + public string Class { get; } - public Asset(JsonDictWrapper obj, AssetInfo info) + public Asset(JsonDictWrapper obj, AssetSource info) { + if (!obj.Name.Exists) + { + throw new ArgumentException("Object must have a name.", nameof(obj)); + } Info = info; - Object = obj; SourceFile = info.SourceFile; Namespace = info.Namespace; obj.ApplyNamespace(Namespace); + Key = obj.Name.Value; if (obj.TryPopValue("preview", out string path)) { try { - using var stream = ResourceManager.Parse(path, SourceFile).OpenStream(); + var resource = PathExpressionEvaluator.Get(path, info); + using var stream = resource.Open(); var bitmap = new Bitmap(stream); Preview = bitmap; } @@ -66,30 +71,33 @@ public Asset(JsonDictWrapper obj, AssetInfo info) } else if (obj.Name.Exists) { - DisplayName = Object.Name.Value; + DisplayName = obj.Name.Value; } if (obj.TryPopValue("description", out string desc)) { Description = desc; } - } - public virtual JsonDictWrapper GetCopy() - { - return Object.Copy(); + if (obj.TryGetValue("class", out string @class)){ + Class = @class; + } + else + { + Class = string.Empty; + } + } } -abstract class Asset : Asset where T : JsonDictWrapper +public abstract class Asset : Asset where T : JsonDictWrapper { - public new T Object => (T)base.Object; - - public Asset(T obj, AssetInfo info) : base(obj, info) { } + public T Object { get; } - public override T GetCopy() - { - return (T)Object.Copy(); + public Asset(T obj, AssetSource info) : base(obj, info) { + Object = obj; } + + public abstract T GetCopy(); } diff --git a/LevelTemplateCreator/Assets/AssetCollection.cs b/LevelTemplateCreator/Assets/AssetCollection.cs index b43fe39..6560d39 100644 --- a/LevelTemplateCreator/Assets/AssetCollection.cs +++ b/LevelTemplateCreator/Assets/AssetCollection.cs @@ -1,4 +1,4 @@ -using LevelTemplateCreator.Collections; +using Grille.BeamNgLib.Collections; using System; using System.Collections; using System.Collections.Generic; @@ -8,4 +8,4 @@ namespace LevelTemplateCreator.Assets; -internal class AssetCollection : KeyedCollection where T : Asset { } +public class AssetCollection : KeyedCollection where T : Asset { } diff --git a/LevelTemplateCreator/Assets/AssetLibary.cs b/LevelTemplateCreator/Assets/AssetLibary.cs index 0b543b6..03d345d 100644 --- a/LevelTemplateCreator/Assets/AssetLibary.cs +++ b/LevelTemplateCreator/Assets/AssetLibary.cs @@ -1,13 +1,10 @@ -using LevelTemplateCreator.Collections; -using LevelTemplateCreator.IO; -using LevelTemplateCreator.IO.Resources; -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Art; -using LevelTemplateCreator.SceneTree.Main; +using Grille.BeamNgLib.Logging; +using Grille.BeamNgLib.SceneTree.Main; + namespace LevelTemplateCreator.Assets; -internal class AssetLibary +public class AssetLibary { public AssetCollection LevelPresets { get; } diff --git a/LevelTemplateCreator/Assets/AssetLibaryContent.cs b/LevelTemplateCreator/Assets/AssetLibaryContent.cs index c5f4ac8..0ac37ec 100644 --- a/LevelTemplateCreator/Assets/AssetLibaryContent.cs +++ b/LevelTemplateCreator/Assets/AssetLibaryContent.cs @@ -1,5 +1,5 @@ -using LevelTemplateCreator.Collections; -using LevelTemplateCreator.SceneTree.Main; +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.SceneTree.Main; using System; using System.Collections.Generic; using System.Linq; @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace LevelTemplateCreator.Assets; -internal class AssetLibaryContent : AssetLibary +public class AssetLibaryContent : AssetLibary { public List GroundCoverObjects { get; } @@ -83,7 +83,6 @@ void CreateGroundCoverObjects() builders.Add(builder); } - foreach (var item in GroundCoverInstances) { var parent = item.Parent.Value; diff --git a/LevelTemplateCreator/Assets/AssetLibaryLoader.cs b/LevelTemplateCreator/Assets/AssetLibaryLoader.cs index d71fd24..34de95f 100644 --- a/LevelTemplateCreator/Assets/AssetLibaryLoader.cs +++ b/LevelTemplateCreator/Assets/AssetLibaryLoader.cs @@ -1,15 +1,17 @@ -using LevelTemplateCreator.IO; +using Grille.BeamNgLib.IO; using LevelTemplateCreator.IO.Resources; -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Art; -using LevelTemplateCreator.SceneTree.Main; +using LevelTemplateCreator.Properties; +using Grille.BeamNgLib.SceneTree; +using Grille.BeamNgLib.SceneTree.Art; +using Grille.BeamNgLib.SceneTree.Main; using System.Linq; using System.Text; using System.Xml.Linq; +using Grille.BeamNgLib.Logging; namespace LevelTemplateCreator.Assets; -internal class AssetLibaryLoader +public class AssetLibaryLoader { public record class Error(string File, Exception Exception) { @@ -57,8 +59,6 @@ public AssetLibaryLoader(AssetLibary libary) { Libary = libary; Errors = new ErrorLogger(); - - } void LogException(Exception e) @@ -185,7 +185,7 @@ void ParseObject(JsonDict dict) void ParseObject(JsonDict dict, string className) { - var createInfo = new AssetInfo(_currentFile, _currentNamespace); + var createInfo = new AssetSource(_currentFile, _currentNamespace); switch (className) { @@ -206,7 +206,7 @@ void ParseObject(JsonDict dict, string className) } case LevelObjectsAsset.ClassName: { - var obj = new JsonDictWrapper(dict); + var obj = new SimItem(dict); var asset = new LevelObjectsAsset(obj, createInfo); Libary.LevelPresets.Add(asset); break; @@ -337,8 +337,8 @@ void Include(string path) { var filename = Path.GetFileName(path); - var resource = ResourceManager.Parse(path, _currentFile, _currentNamespace); - using var stream = resource.OpenStream(); + var resource = PathExpressionEvaluator.Get(path, _currentFile, _currentNamespace); + using var stream = resource.Open(); try { diff --git a/LevelTemplateCreator/Assets/AssetInfo.cs b/LevelTemplateCreator/Assets/AssetSource.cs similarity index 69% rename from LevelTemplateCreator/Assets/AssetInfo.cs rename to LevelTemplateCreator/Assets/AssetSource.cs index e15e7ea..1d6ed54 100644 --- a/LevelTemplateCreator/Assets/AssetInfo.cs +++ b/LevelTemplateCreator/Assets/AssetSource.cs @@ -5,4 +5,4 @@ using System.Threading.Tasks; namespace LevelTemplateCreator.Assets; -record AssetInfo(string SourceFile, string Namespace); \ No newline at end of file +public record struct AssetSource(string SourceFile, string Namespace); \ No newline at end of file diff --git a/LevelTemplateCreator/Assets/GroundCoverAsset.cs b/LevelTemplateCreator/Assets/GroundCoverAsset.cs index 326be85..6b18968 100644 --- a/LevelTemplateCreator/Assets/GroundCoverAsset.cs +++ b/LevelTemplateCreator/Assets/GroundCoverAsset.cs @@ -1,13 +1,18 @@ -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Main; +using Grille.BeamNgLib.SceneTree.Main; namespace LevelTemplateCreator.Assets; -internal class GroundCoverAsset : Asset +public class GroundCoverAsset : Asset { public const string ClassName = GroundCover.ClassName; public GroundCover GroundCover => Object; - public GroundCoverAsset(GroundCover item, AssetInfo info) : base(item, info) { } + public GroundCoverAsset(GroundCover item, AssetSource info) : base(item, info) { } + + public override GroundCover GetCopy() + { + var dict = Object.Dict; + return new GroundCover(dict); + } } diff --git a/LevelTemplateCreator/Assets/GroundCoverBuilder.cs b/LevelTemplateCreator/Assets/GroundCoverBuilder.cs index 0b7817f..baf43eb 100644 --- a/LevelTemplateCreator/Assets/GroundCoverBuilder.cs +++ b/LevelTemplateCreator/Assets/GroundCoverBuilder.cs @@ -1,5 +1,5 @@ -using LevelTemplateCreator.Collections; -using LevelTemplateCreator.SceneTree.Main; +using Grille.BeamNgLib.Collections; +using Grille.BeamNgLib.SceneTree.Main; using System; using System.Collections.Generic; using System.Linq; @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace LevelTemplateCreator.Assets; -internal class GroundCoverBuilder : IKeyed +public class GroundCoverBuilder : IKeyed { GroundCoverAsset _groundCover; @@ -23,7 +23,8 @@ public GroundCoverBuilder(GroundCoverAsset groundCover) public void AddInstance(GroundCoverInstance instance) { - var copy = instance.Copy(); + var dict = new JsonDict(instance.Dict); + var copy = new GroundCoverInstance(dict); copy.Parent.Remove(); _instances.Add(copy); } diff --git a/LevelTemplateCreator/Assets/LevelObjectsAsset.cs b/LevelTemplateCreator/Assets/LevelObjectsAsset.cs index 670d564..779e3d9 100644 --- a/LevelTemplateCreator/Assets/LevelObjectsAsset.cs +++ b/LevelTemplateCreator/Assets/LevelObjectsAsset.cs @@ -1,5 +1,5 @@ -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Main; +using Grille.BeamNgLib.SceneTree; +using Grille.BeamNgLib.SceneTree.Main; using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -9,14 +9,14 @@ namespace LevelTemplateCreator.Assets; -internal class LevelObjectsAsset : Asset +public class LevelObjectsAsset : Asset { public const string ClassName = "LevelObjects"; Collection Items; - public LevelObjectsAsset(JsonDictWrapper data, AssetInfo info) : base(data, info) + public LevelObjectsAsset(JsonDictWrapper data, AssetSource info) : base(data, info) { Items = new Collection(); @@ -62,6 +62,11 @@ public void Apply(SimGroupLevelObjects group) } } + public override JsonDictWrapper GetCopy() + { + throw new NotImplementedException(); + } + public override string ToString() { return DisplayName; diff --git a/LevelTemplateCreator/Assets/MaterialAsset.cs b/LevelTemplateCreator/Assets/MaterialAsset.cs index 29f7c3c..8669aef 100644 --- a/LevelTemplateCreator/Assets/MaterialAsset.cs +++ b/LevelTemplateCreator/Assets/MaterialAsset.cs @@ -1,17 +1,12 @@ -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Art; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Grille.BeamNgLib.SceneTree.Art; + namespace LevelTemplateCreator.Assets; -abstract class MaterialAsset : Asset where T : Material +public abstract class MaterialAsset : Asset where T : Material { public T Material => Object; - protected MaterialAsset(T obj, AssetInfo info) : base(obj, info) + protected MaterialAsset(T obj, AssetSource info) : base(obj, info) { } } diff --git a/LevelTemplateCreator/Assets/ObjectMaterialAsset.cs b/LevelTemplateCreator/Assets/ObjectMaterialAsset.cs index 035ea54..4423ba5 100644 --- a/LevelTemplateCreator/Assets/ObjectMaterialAsset.cs +++ b/LevelTemplateCreator/Assets/ObjectMaterialAsset.cs @@ -1,19 +1,24 @@ -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Art; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Grille.BeamNgLib.SceneTree.Art; +using static System.Windows.Forms.AxHost; + namespace LevelTemplateCreator.Assets; -internal class ObjectMaterialAsset : MaterialAsset +public class ObjectMaterialAsset : MaterialAsset { public const string ClassName = ObjectMaterial.ClassName; - public ObjectMaterialAsset(ObjectMaterial item, AssetInfo info) : base(item, info) + public ObjectMaterialAsset(ObjectMaterial item, AssetSource info) : base(item, info) { //Material = new ObjectMaterial(item.Dict); } + + public override ObjectMaterial GetCopy() + { + var clone = new JsonDict(Object.Dict); + var stages = new JsonDict[4] { new(Object.Stage0.Dict), new(Object.Stage1.Dict), new(Object.Stage2.Dict), new(Object.Stage3.Dict) }; + clone["Stages"] = stages; + + return new ObjectMaterial(clone); + } } diff --git a/LevelTemplateCreator/Assets/TerrainMaterialAsset.cs b/LevelTemplateCreator/Assets/TerrainMaterialAsset.cs index 5c624ee..fa5ed73 100644 --- a/LevelTemplateCreator/Assets/TerrainMaterialAsset.cs +++ b/LevelTemplateCreator/Assets/TerrainMaterialAsset.cs @@ -1,21 +1,15 @@ -using LevelTemplateCreator.IO.Resources; -using LevelTemplateCreator.SceneTree; -using LevelTemplateCreator.SceneTree.Art; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Grille.BeamNgLib.SceneTree.Art; +using LevelTemplateCreator.IO.Resources; namespace LevelTemplateCreator.Assets; -internal class TerrainMaterialAsset : MaterialAsset +public class TerrainMaterialAsset : MaterialAsset { public const string ClassName = TerrainMaterial.ClassName; public float SquareSize { get; } - public TerrainMaterialAsset(TerrainMaterial item, AssetInfo info) : base(item, info) + public TerrainMaterialAsset(TerrainMaterial item, AssetSource info) : base(item, info) { Material.TryPopValue("squareSize", out float squareSize, 1); SquareSize = squareSize; @@ -39,4 +33,10 @@ void SetLayerIfEmpty(TerrainMaterialTextureLayer layer, string value) layer.Texture.Value = value; } } + + public override TerrainMaterial GetCopy() + { + var dict = new JsonDict(Object.Dict); + return new TerrainMaterial(dict); + } } diff --git a/LevelTemplateCreator/Collections/IKeyed.cs b/LevelTemplateCreator/Collections/IKeyed.cs deleted file mode 100644 index 666368f..0000000 --- a/LevelTemplateCreator/Collections/IKeyed.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.Collections; - -internal interface IKeyed -{ - public string? Key { get; } -} diff --git a/LevelTemplateCreator/EnvironmentInfo.cs b/LevelTemplateCreator/EnvironmentInfo.cs index 9c7feba..71b0514 100644 --- a/LevelTemplateCreator/EnvironmentInfo.cs +++ b/LevelTemplateCreator/EnvironmentInfo.cs @@ -1,11 +1,12 @@ -using LevelTemplateCreator.IO; +using Grille.BeamNgLib.IO; +using Grille.BeamNgLib.IO.Resources; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace LevelTemplateCreator; +namespace Grille.BeamNgLib; public static class EnvironmentInfo { @@ -127,7 +128,7 @@ static public void TryFindUserDir() { UserData.TryFindValidPath( [ - $"C:\\Users\\{System.Environment.UserName}\\AppData\\Local\\BeamNG.drive", + $"C:\\Users\\{Environment.UserName}\\AppData\\Local\\BeamNG.drive", ]); } @@ -155,7 +156,7 @@ static public void Load() static public void Save() { - var dict = new Dictionary(){ + var dict = new JsonDict(){ { "gamepath", GameData.Path }, { "userpath", UserData.Path }, { "packages", Packages.Path }, diff --git a/LevelTemplateCreator/GUI/AssetViewBox.cs b/LevelTemplateCreator/GUI/AssetViewBox.cs index ab5c4db..d5d83d2 100644 --- a/LevelTemplateCreator/GUI/AssetViewBox.cs +++ b/LevelTemplateCreator/GUI/AssetViewBox.cs @@ -218,7 +218,7 @@ void DrawItem(Graphics g, Asset item, int index, int position) { sb.AppendLine(item.Description); } - sb.AppendLine(item.Object.Class.Value); + sb.AppendLine(item.Class); sb.AppendLine(item.SourceFile); g.DrawString(sb.ToString(), Font, brush, boundsText); diff --git a/LevelTemplateCreator/GUI/LevelSettings.cs b/LevelTemplateCreator/GUI/LevelSettings.cs index 6917fb5..3f18706 100644 --- a/LevelTemplateCreator/GUI/LevelSettings.cs +++ b/LevelTemplateCreator/GUI/LevelSettings.cs @@ -14,9 +14,9 @@ namespace LevelTemplateCreator.GUI; internal partial class LevelSettings : UserControl { - Level? _level; + LevelExporter? _level; - public void SetLevel(Level level) + public void SetLevel(LevelExporter level) { _level = level; diff --git a/LevelTemplateCreator/GUI/MainForm.cs b/LevelTemplateCreator/GUI/MainForm.cs index 83429e6..8f9ada9 100644 --- a/LevelTemplateCreator/GUI/MainForm.cs +++ b/LevelTemplateCreator/GUI/MainForm.cs @@ -1,22 +1,24 @@ using System.Diagnostics; using System.Text; +using Grille.BeamNgLib; +using Grille.BeamNgLib.IO; +using Grille.BeamNgLib.Logging; using LevelTemplateCreator.Assets; using LevelTemplateCreator.GUI; -using LevelTemplateCreator.IO; namespace LevelTemplateCreator { public partial class MainForm : Form { AssetLibary AssetLibary { get; set; } - Level Level { get; set; } + LevelExporter Level { get; set; } public MainForm() { InitializeComponent(); AssetLibary = new AssetLibary(); - Level = new Level(AssetLibary); + Level = new LevelExporter(AssetLibary); LevelSettings.ButtonSave.Click += (object? sender, EventArgs e) => { @@ -110,7 +112,6 @@ void Export(string path) catch (Exception e) { ExceptionBox.Show(this, e); - if (Program.Debug) throw; } @@ -144,7 +145,7 @@ void LoadContent() AssetLibary.Clear(); - var loader = new AssetLibaryLoader(AssetLibary) { Debug = false }; + var loader = new AssetLibaryLoader(AssetLibary) { Debug = true }; loader.LoadDirectory(EnvironmentInfo.Packages.Path); if (ZipFileManager.Count > 0) diff --git a/LevelTemplateCreator/GUI/SettingsForm.cs b/LevelTemplateCreator/GUI/SettingsForm.cs index 539b870..bef9bd9 100644 --- a/LevelTemplateCreator/GUI/SettingsForm.cs +++ b/LevelTemplateCreator/GUI/SettingsForm.cs @@ -1,4 +1,5 @@ -using System; +using Grille.BeamNgLib; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; diff --git a/LevelTemplateCreator/GUI/TerrainSettings.Designer.cs b/LevelTemplateCreator/GUI/TerrainSettings.Designer.cs index 09a7a6d..1a7f932 100644 --- a/LevelTemplateCreator/GUI/TerrainSettings.Designer.cs +++ b/LevelTemplateCreator/GUI/TerrainSettings.Designer.cs @@ -50,10 +50,10 @@ private void InitializeComponent() // numericWorldSize.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; numericWorldSize.DecimalPlaces = 2; - numericWorldSize.Location = new Point(101, 32); + numericWorldSize.Location = new Point(103, 32); numericWorldSize.Maximum = new decimal(new int[] { 100000, 0, 0, 0 }); numericWorldSize.Name = "numericWorldSize"; - numericWorldSize.Size = new Size(477, 23); + numericWorldSize.Size = new Size(475, 23); numericWorldSize.TabIndex = 3; numericWorldSize.ValueChanged += numericUpDownWorldSize_ValueChanged; // @@ -62,9 +62,9 @@ private void InitializeComponent() comboBoxRes.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; comboBoxRes.FormattingEnabled = true; comboBoxRes.Items.AddRange(new object[] { "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768", "65536" }); - comboBoxRes.Location = new Point(101, 3); + comboBoxRes.Location = new Point(103, 3); comboBoxRes.Name = "comboBoxRes"; - comboBoxRes.Size = new Size(477, 23); + comboBoxRes.Size = new Size(475, 23); comboBoxRes.TabIndex = 4; comboBoxRes.SelectedIndexChanged += comboBoxRes_SelectedIndexChanged; // @@ -72,10 +72,10 @@ private void InitializeComponent() // numericSquareSize.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; numericSquareSize.DecimalPlaces = 2; - numericSquareSize.Location = new Point(101, 61); + numericSquareSize.Location = new Point(103, 61); numericSquareSize.Maximum = new decimal(new int[] { 100000, 0, 0, 0 }); numericSquareSize.Name = "numericSquareSize"; - numericSquareSize.Size = new Size(477, 23); + numericSquareSize.Size = new Size(475, 23); numericSquareSize.TabIndex = 5; numericSquareSize.ValueChanged += numericUpDownSquareSize_ValueChanged; // @@ -83,10 +83,10 @@ private void InitializeComponent() // numericMaxHeight.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; numericMaxHeight.DecimalPlaces = 2; - numericMaxHeight.Location = new Point(101, 90); + numericMaxHeight.Location = new Point(103, 90); numericMaxHeight.Maximum = new decimal(new int[] { 100000, 0, 0, 0 }); numericMaxHeight.Name = "numericMaxHeight"; - numericMaxHeight.Size = new Size(477, 23); + numericMaxHeight.Size = new Size(475, 23); numericMaxHeight.TabIndex = 6; numericMaxHeight.ValueChanged += numericUpDownMaxHeight_ValueChanged; // @@ -135,7 +135,7 @@ private void InitializeComponent() tableLayoutPanel1.AutoSize = true; tableLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink; tableLayoutPanel1.ColumnCount = 2; - tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle()); + tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100F)); tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); tableLayoutPanel1.Controls.Add(numericBaseHeight, 1, 4); tableLayoutPanel1.Controls.Add(numericMaxHeight, 1, 3); @@ -163,10 +163,10 @@ private void InitializeComponent() // numericBaseHeight.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; numericBaseHeight.DecimalPlaces = 2; - numericBaseHeight.Location = new Point(101, 119); + numericBaseHeight.Location = new Point(103, 119); numericBaseHeight.Maximum = new decimal(new int[] { 100000, 0, 0, 0 }); numericBaseHeight.Name = "numericBaseHeight"; - numericBaseHeight.Size = new Size(477, 23); + numericBaseHeight.Size = new Size(475, 23); numericBaseHeight.TabIndex = 12; numericBaseHeight.ValueChanged += numericBaseHeight_ValueChanged; // diff --git a/LevelTemplateCreator/GUI/TerrainSettings.cs b/LevelTemplateCreator/GUI/TerrainSettings.cs index c54fd7b..1852253 100644 --- a/LevelTemplateCreator/GUI/TerrainSettings.cs +++ b/LevelTemplateCreator/GUI/TerrainSettings.cs @@ -1,4 +1,5 @@ -using System; +using Grille.BeamNgLib; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; diff --git a/LevelTemplateCreator/IO/Resources/FileResource.cs b/LevelTemplateCreator/IO/Resources/FileResource.cs deleted file mode 100644 index cdad6a4..0000000 --- a/LevelTemplateCreator/IO/Resources/FileResource.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.IO.Resources; - -internal class FileResource : Resource -{ - public string Path { get; } - - public FileResource(string name, string path) : base(name) - { - Path = path; - } - - public override Stream OpenStream() - { - return new FileStream(Path, FileMode.Open); - } -} diff --git a/LevelTemplateCreator/IO/Resources/ResourceCollection.cs b/LevelTemplateCreator/IO/Resources/ResourceCollection.cs deleted file mode 100644 index 484fa7e..0000000 --- a/LevelTemplateCreator/IO/Resources/ResourceCollection.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections; -using System.Globalization; -using System.Collections.Generic; -using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LevelTemplateCreator.SceneTree.Art; -using LevelTemplateCreator.Collections; -using LevelTemplateCreator.Assets; - -namespace LevelTemplateCreator.IO.Resources; - -internal class ResourceCollection : KeyedCollection -{ - public string Register(string entry) - { - var resource = ResourceManager.Parse(entry); - return TryAdd(resource); - } - - public string RegisterRelative(string entry, AssetInfo info) - { - var resource = ResourceManager.Parse(entry, info.SourceFile, info.Namespace); - return TryAdd(resource); - } - - string TryAdd(Resource resource) - { - var key = resource.Name; - if (ContainsKey(key)) - return key; - Add(resource); - return key; - } - - public void Save(string path) - { - foreach (var resource in this) - { - resource.SaveToDirectory(path); - } - } -} diff --git a/LevelTemplateCreator/IO/Resources/ResourceManager.cs b/LevelTemplateCreator/IO/Resources/ResourceManager.cs deleted file mode 100644 index f12c537..0000000 --- a/LevelTemplateCreator/IO/Resources/ResourceManager.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static System.Windows.Forms.VisualStyles.VisualStyleElement; - -namespace LevelTemplateCreator.IO.Resources; - -internal static class ResourceManager -{ - public static Resource Parse(string entry, string rootPath, string @namespace) - { - if (entry.StartsWith("$")) - { - var name = entry.Substring(1); - var key = '$' + @namespace + name; - return ParseVariable(key); - } - return Parse(entry, rootPath); - } - - public static Resource Parse(string entry, string rootPath) - { - if (entry.StartsWith('.')) - { - return ParseRelative(entry, rootPath); - } - return Parse(entry); - } - - public static Resource Parse(string entry) - { - if (entry.StartsWith("$")) - { - return ParseVariable(entry); - } - - entry = entry.Replace("\\", "/"); - - if (entry.StartsWith('#')) - { - return ParseColor(entry); - } - else if (entry.StartsWith('/')) - { - return ParseAbsolute(entry); - } - else - { - return ParseAbsolute('/' + entry); - } - } - - static Resource ParseVariable(string key) - { - if (Constants.TryGet(key, out var value)) - { - return Parse(value); - } - else - { - throw new Exception($"Constant '{key}' not defined."); - } - } - - static Resource ParseRelative(string rpath, string rootPath) - { - var abspackpath = Path.GetFullPath(EnvironmentInfo.Packages.Path); - var dirpath = Path.GetDirectoryName(rootPath); - if (dirpath == null) - throw new Exception(); - var path = Path.GetFullPath(Path.Combine(dirpath, rpath)); - var subpath = path.Substring(abspackpath.Length); - - return Parse(subpath); - } - - static Resource ParseColor(string hex) - { - var name = hex.Substring(1); - - var key = $"#{name}.png"; - - int color; - - if (!int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out color)) - { - SolidColorNames.TryGet(hex, out color); - } - - var resource = new SolidColorResource(key, color); - return resource; - } - - static Resource ParseAbsolute(string path) - { - var split = path.ToLower().Split([Path.PathSeparator, Path.AltDirectorySeparatorChar]); - - if (split[1] == "levels") - { - var level = split[2]; - var filename = split[split.Length - 1]; - var key = $"beamng.{level}.{filename}"; - var zippath = $"{EnvironmentInfo.GameData.Path}/content/levels/{level}.zip"; - if (File.Exists(zippath)) - { - var subpath = path.Substring(1); - var resource = new ZipFileResource(key, zippath, subpath); - return resource; - } - } - - { - var filename = path.Substring(1).Replace("/", "."); - var key = $"local.{filename}"; - var fpath = Path.Join(EnvironmentInfo.Packages.Path, path); - var resource = new FileResource(key, fpath); - return resource; - } - } -} diff --git a/LevelTemplateCreator/IO/TerrainV9Serializer.cs b/LevelTemplateCreator/IO/TerrainV9Serializer.cs deleted file mode 100644 index 18c7efb..0000000 --- a/LevelTemplateCreator/IO/TerrainV9Serializer.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using GGL.IO; -using LevelTemplateCreator.SceneTree.Art; - -namespace LevelTemplateCreator.IO; - -static class TerrainV9Serializer -{ - public static void Serialize(TerrainInfo info, ICollection materials, string path) - { - using var stream = new FileStream(path, FileMode.Create); - Serialize(info, materials, stream); - } - - public static void Serialize(TerrainInfo info, ICollection materials, Stream stream) - { - using var bw = new BinaryViewWriter(stream); - - bw.WriteByte(9); - bw.WriteUInt32((uint)info.Resolution); - - long size = info.Resolution * (long)info.Resolution; - - float u16max = ushort.MaxValue; - - float height = info.Height / info.MaxHeight * u16max; - if (height > u16max) - height = u16max; - - ushort u16height = (ushort)height; - - for (int i = 0; i < size; i++) - { - bw.WriteUInt16(u16height); - } - - bw.Seek(size, SeekOrigin.Current); - - bw.WriteUInt32((uint)materials.Count); - - foreach (var material in materials) - { - bw.WriteString(material, LengthPrefix.Byte, Encoding.UTF8); - } - } -} diff --git a/LevelTemplateCreator/Level.cs b/LevelTemplateCreator/LevelExporter.cs similarity index 50% rename from LevelTemplateCreator/Level.cs rename to LevelTemplateCreator/LevelExporter.cs index 760fec6..92e7ab7 100644 --- a/LevelTemplateCreator/Level.cs +++ b/LevelTemplateCreator/LevelExporter.cs @@ -1,5 +1,4 @@ -using LevelTemplateCreator.IO; -using LevelTemplateCreator.Assets; +using LevelTemplateCreator.Assets; using System; using System.Collections.Generic; using System.IO; @@ -7,15 +6,18 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; -using LevelTemplateCreator.SceneTree.Art; -using LevelTemplateCreator.SceneTree.Main; using LevelTemplateCreator.IO.Resources; -using LevelTemplateCreator.Properties; -using LevelTemplateCreator.GUI; +using Grille.BeamNgLib; +using Grille.BeamNgLib.SceneTree.Main; +using Grille.BeamNgLib.SceneTree.Art; +using Grille.BeamNgLib.IO; +using Grille.BeamNgLib.Logging; +using System.Numerics; +using Grille.BeamNgLib.IO.Resources; namespace LevelTemplateCreator; -internal class Level +public class LevelExporter { public AssetLibary Libary { get; } @@ -23,20 +25,19 @@ internal class Level public string Namespace { get; set; } - public LevelInfo Info { get; set; } + public Level Level { get; set; } - public TerrainInfo Terrain { get; set; } + public LevelInfo Info => Level.Info; + + public TerrainInfo Terrain => Level.Terrain; public ErrorLogger Errors { get; } - public Level(AssetLibary libary) + public LevelExporter(AssetLibary libary) { Namespace = "new_pbr_template"; - Info = new LevelInfo(); - Terrain = new TerrainInfo() - { - Resolution = 1024, - }; + Level = new(); + Level.Terrain.Resolution = 1024; Content = new AssetLibaryContent(); Libary = libary; Errors = new ErrorLogger(); @@ -50,18 +51,59 @@ public TerrainBlock BuildTerrainBlock() return terrain; } - public TerrainMaterialLibary BuildTerrainMaterialLibary() + public ArtGroupRoot BuildArtGroup() + { + var root = new ArtGroupRoot(); + + BuildArtGroupTerrain(root); + BuildArtGroupGroundcover(root); + + return root; + } + + void BuildArtGroupTerrain(ArtGroupRoot root) { var path = $"/levels/{Namespace}/art/terrains"; - var lib = new TerrainMaterialLibary(path, Terrain.SquareSize); - lib.AddAssets(Content.TerrainMaterials, Errors); - lib.CreateTerrainMaterialTextureSet($"{Namespace}_TerrainMaterialTextureSet"); + var group = root.Terrains; + var materials = group.MaterialItems; - return lib; + foreach (var asset in Content.TerrainMaterials) + { + float squareSize = asset switch + { + TerrainMaterialAsset tma => tma.SquareSize, + _ => 1 + }; + + var copy = asset.GetCopy(); + copy.CreatePersistentId(); + copy.MultiplyMappingScale(squareSize / Terrain.SquareSize); + copy.EvalPathExpressions(asset, path, group.Resources); + materials.Add(copy); + } + + var textureSetName = $"{Namespace}_TerrainMaterialTextureSet"; + materials.Add(new TerrainMaterialTextureSet(textureSetName)); } - public SimGroup BuildMissionGroup() + void BuildArtGroupGroundcover(ArtGroupRoot root) + { + var path = $"/levels/{Namespace}/art/groundcover"; + + var group = root.Groundcover; + var materials = group.MaterialItems; + + foreach (var asset in Content.ObjectMaterials) + { + var copy = asset.GetCopy(); + copy.EvalPathExpressions(asset, path, group.Resources); + materials.Add(copy); + } + + } + + public SimGroupRoot BuildSimGroup() { var root = new SimGroupRoot(); var group = root.MissionGroup; @@ -99,34 +141,16 @@ public void SetContent(Asset[] assets) public void Export(string path) { - Directory.CreateDirectory(Path.Combine(path, "art/terrains")); - - Directory.CreateDirectory(Path.Combine(path, "art/objects")); - - var objpath = $"/levels/{Namespace}/art/objects"; - var lib = new ObjectMaterialLibary(objpath); - lib.AddAssets(Content.ObjectMaterials, Errors); - - lib.SerializeItems(Path.Combine(path, "art/objects/" + MaterialLibary.FileName)); - lib.Textures.Save(Path.Combine(path, "art/objects")); - - var terrainMaterials = BuildTerrainMaterialLibary(); - - terrainMaterials.SerializeItems(Path.Combine(path, "art/terrains/" + MaterialLibary.FileName)); - - var texturespath = Path.Combine(path, "art/terrains"); - - terrainMaterials.Textures.Save(texturespath); - + LevelInfoSerializer.Serialize(Level, Path.Combine(path, "info.json")); + TerrainV9Serializer.Serialize(Terrain, Content.TerrainMaterials.Keys, Path.Combine(path, "terrain.ter")); Content.Preview?.Save(Path.Combine(path, "preview.png")); + var simPath = Path.Combine(path, "main"); + var artPath = Path.Combine(path, "art"); - BuildMissionGroup().SaveTree(Path.Combine(path, "main")); - - TerrainV9Serializer.Serialize(Terrain, Content.TerrainMaterials.Keys, Path.Combine(path, "terrain.ter")); - - LevelInfoSerializer.Serialize(this, Path.Combine(path, "info.json")); + BuildSimGroup().SaveTree(simPath); + BuildArtGroup().SaveTree(artPath); if (ZipFileManager.Count > 0) { diff --git a/LevelTemplateCreator/LevelTemplateCreator.csproj b/LevelTemplateCreator/LevelTemplateCreator.csproj index f016061..0ea22b1 100644 --- a/LevelTemplateCreator/LevelTemplateCreator.csproj +++ b/LevelTemplateCreator/LevelTemplateCreator.csproj @@ -6,7 +6,7 @@ 12 enable true - enable + ../Assets/Grille.BeamNG.ico @@ -17,6 +17,10 @@ + + + + True diff --git a/LevelTemplateCreator/Logger.cs b/LevelTemplateCreator/Logger.cs deleted file mode 100644 index 9c644df..0000000 --- a/LevelTemplateCreator/Logger.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator; - -enum LoggerColor -{ - Default = ConsoleColor.Gray, - Red = ConsoleColor.Red, -} - -internal static class Logger -{ - public static void WriteLine() { - Console.WriteLine(); - } - - public static void WriteLine(string text) - { - Console.WriteLine(text); - } - - public static void WriteLine(string text, LoggerColor color) - { - Console.ForegroundColor = (ConsoleColor)color; - Console.WriteLine(text); - Console.ForegroundColor = (ConsoleColor)LoggerColor.Default; - } - - public static void Error() - { - - } -} diff --git a/LevelTemplateCreator/MaterialExtension.cs b/LevelTemplateCreator/MaterialExtension.cs new file mode 100644 index 0000000..1f23673 --- /dev/null +++ b/LevelTemplateCreator/MaterialExtension.cs @@ -0,0 +1,23 @@ +using Grille.BeamNgLib.IO.Resources; +using Grille.BeamNgLib.SceneTree.Art; +using LevelTemplateCreator.Assets; +using LevelTemplateCreator.IO.Resources; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LevelTemplateCreator; +internal static class MaterialExtension +{ + public static void EvalPathExpressions(this Material material, Asset asset, string path, ResourceCollection textures) + { + foreach (var texture in material.EnumerateTexturePaths()) + { + var resource = PathExpressionEvaluator.Get(texture.Value, asset.Info); + textures.Add(resource); + texture.Value = Path.Combine(path, resource.DynamicName); + } + } +} diff --git a/LevelTemplateCreator/Program.cs b/LevelTemplateCreator/Program.cs index 8ce91c2..54623af 100644 --- a/LevelTemplateCreator/Program.cs +++ b/LevelTemplateCreator/Program.cs @@ -1,4 +1,6 @@ +using Grille.BeamNgLib.Logging; using System.Globalization; +using System.Threading; namespace LevelTemplateCreator; @@ -17,6 +19,9 @@ internal static class Program [STAThread] static void Main() { + using var stream = new FileStream("console.log", FileMode.Create); + Logger.OutputStream = stream; + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; ApplicationConfiguration.Initialize(); Application.Run(new MainForm()); diff --git a/LevelTemplateCreator/IO/Resources/Constants.cs b/LevelTemplateCreator/Resouces/Constants.cs similarity index 94% rename from LevelTemplateCreator/IO/Resources/Constants.cs rename to LevelTemplateCreator/Resouces/Constants.cs index d353881..777c9fc 100644 --- a/LevelTemplateCreator/IO/Resources/Constants.cs +++ b/LevelTemplateCreator/Resouces/Constants.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace LevelTemplateCreator.IO.Resources; -internal static class Constants +public static class Constants { readonly static Dictionary _dict; diff --git a/LevelTemplateCreator/Resouces/PathExpressionEvaluator.cs b/LevelTemplateCreator/Resouces/PathExpressionEvaluator.cs new file mode 100644 index 0000000..de587ed --- /dev/null +++ b/LevelTemplateCreator/Resouces/PathExpressionEvaluator.cs @@ -0,0 +1,108 @@ +using Grille.BeamNgLib; +using Grille.BeamNgLib.IO.Resources; +using LevelTemplateCreator.Assets; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LevelTemplateCreator.IO.Resources; + +public static class PathExpressionEvaluator +{ + public static Resource Get(string entry, AssetSource source) + { + return Get(entry, source.SourceFile, source.Namespace); + } + + public static Resource Get(string entry) + { + return Get(entry, string.Empty, string.Empty); + } + + public static Resource Get(string entry, string filePath) + { + return Get(entry, filePath, string.Empty); + } + + public static Resource Get(string entry, string filePath, string @namespace) + { + if (entry.StartsWith("$")) + { + return ParseVariable(entry, @namespace); + } + else if (entry.StartsWith('#')) + { + return ParseColor(entry); + } + else if (entry.StartsWith('.')) + { + return ParseRelative(entry, EnvironmentInfo.Packages.Path, filePath); + } + return PathEvaluator.Get(entry, EnvironmentInfo.GameData.Path, EnvironmentInfo.UserData.Path); + } + + static Resource ParseRelative(string entry, string rootPath, string filePath) + { + entry = entry.Replace("\\", "/"); + + if (string.IsNullOrEmpty(rootPath)) + { + throw new ArgumentException("Relative path not supported, RootPath is empty."); + } + if (string.IsNullOrEmpty(filePath)) + { + throw new ArgumentException("Relative path not supported, FilePath is empty."); + } + + var abspackpath = Path.GetFullPath(rootPath); + var dirpath = Path.GetDirectoryName(filePath); + if (dirpath == null) + { + throw new InvalidOperationException($"Relative path directory was null '{entry}'"); + } + + var path = Path.GetFullPath(Path.Combine(dirpath, entry)); + var subpath = path.Substring(abspackpath.Length); + + var filename = subpath.Substring(1).Replace("/", "_").Replace("\\", "_"); + var key = $"local.{filename}"; + var fpath = Path.Join(rootPath, subpath); + var resource = new FileResource(key, fpath, false); + return resource; + } + + static Resource ParseVariable(string entry, string @namespace) + { + var name = entry.Substring(1); + var key = '$' + @namespace + name; + + if (Constants.TryGet(key, out var value)) + { + return Get(value); + } + else + { + throw new Exception($"Constant '{key}' not defined."); + } + } + + static Resource ParseColor(string hex) + { + var name = hex.Substring(1); + + var key = $"#{name}.png"; + + int color; + + if (!int.TryParse(name, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out color)) + { + SolidColorNames.TryGet(hex, out color); + } + + var resource = new SolidColorResource(key, color); + return resource; + } +} diff --git a/LevelTemplateCreator/IO/Resources/SolidColorNames.cs b/LevelTemplateCreator/Resouces/SolidColorNames.cs similarity index 98% rename from LevelTemplateCreator/IO/Resources/SolidColorNames.cs rename to LevelTemplateCreator/Resouces/SolidColorNames.cs index 2074328..fe5f26b 100644 --- a/LevelTemplateCreator/IO/Resources/SolidColorNames.cs +++ b/LevelTemplateCreator/Resouces/SolidColorNames.cs @@ -7,7 +7,7 @@ namespace LevelTemplateCreator.IO.Resources; -internal static class SolidColorNames +public static class SolidColorNames { static readonly Dictionary _colors; diff --git a/LevelTemplateCreator/IO/Resources/SolidColorResource.cs b/LevelTemplateCreator/Resouces/SolidColorResource.cs similarity index 85% rename from LevelTemplateCreator/IO/Resources/SolidColorResource.cs rename to LevelTemplateCreator/Resouces/SolidColorResource.cs index 35fc737..e372ca0 100644 --- a/LevelTemplateCreator/IO/Resources/SolidColorResource.cs +++ b/LevelTemplateCreator/Resouces/SolidColorResource.cs @@ -5,14 +5,15 @@ using System.Threading.Tasks; using System.Drawing.Imaging; using System.Runtime.CompilerServices; +using Grille.BeamNgLib.IO.Resources; namespace LevelTemplateCreator.IO.Resources; -internal class SolidColorResource : Resource +public class SolidColorResource : Resource { public byte[] Bytes { get; } - public SolidColorResource(string name, int color, int size = 1024) : base(name) + public SolidColorResource(string name, int color, int size = 1024) : base(name, false) { const int mask = -16777216; @@ -27,7 +28,7 @@ public SolidColorResource(string name, int color, int size = 1024) : base(name) Bytes = stream.ToArray(); } - public override Stream OpenStream() + public override Stream Open() { return new MemoryStream(Bytes); } diff --git a/LevelTemplateCreator/SceneTree/Art/Material.cs b/LevelTemplateCreator/SceneTree/Art/Material.cs deleted file mode 100644 index ac42321..0000000 --- a/LevelTemplateCreator/SceneTree/Art/Material.cs +++ /dev/null @@ -1,22 +0,0 @@ -using LevelTemplateCreator.Assets; -using LevelTemplateCreator.IO.Resources; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.SceneTree.Art; - -internal abstract class Material : JsonDictWrapper -{ - protected Material(JsonDict dict) : base(dict) - { - - } - - public abstract void ResolveTexturePaths(MaterialLibary libary, AssetInfo info); - - public override abstract Material Copy(); -} diff --git a/LevelTemplateCreator/SceneTree/Art/MaterialLibary.cs b/LevelTemplateCreator/SceneTree/Art/MaterialLibary.cs deleted file mode 100644 index dc0892c..0000000 --- a/LevelTemplateCreator/SceneTree/Art/MaterialLibary.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.DirectoryServices.ActiveDirectory; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using LevelTemplateCreator.IO; -using LevelTemplateCreator.IO.Resources; -using LevelTemplateCreator.Properties; -using LevelTemplateCreator.Assets; - -namespace LevelTemplateCreator.SceneTree.Art; - -internal abstract class MaterialLibary -{ - public const string FileName = "main.materials.json"; - - public string TexturesPath { get; } - - public ResourceCollection Textures { get; } - - public List Misc { get; } - - public abstract IEnumerable Materials { get; } - - public MaterialLibary(string path) - { - TexturesPath = path; - Misc = new(); - Textures = new(); - } -} - -internal abstract class MaterialLibary : MaterialLibary where T : Material -{ - public override List Materials { get; } - - public MaterialLibary(string path) : base(path) - { - Materials = new(); - } - - public abstract void AddAsset(TAsset asset) where TAsset : MaterialAsset; - - public void AddAssets(IEnumerable assets, ErrorLogger logger) where TAsset : MaterialAsset - { - foreach (var asset in assets) - { - try - { - AddAsset(asset); - } - catch (Exception e) { - logger.Add($"At {asset.DisplayName} From {asset.SourceFile}\n{e.Message}"); - } - } - } - - - public string[] GetMaterialNames() - { - var result = new string[Materials.Count]; - for (int i = 0; i < result.Length; i++) - { - result[i] = Materials[i].Name.Value; - } - return result; - } - - public void SerializeItems(string path) - { - using var stream = new FileStream(path, FileMode.Create); - SerializeItems(stream); - } - - public void SerializeItems(Stream stream) - { - var dict = new JsonDict(); - - foreach (var item in Materials) - { - dict[item.Name.Value] = item.Dict; - } - - foreach (var item in Misc) - { - dict[item.Name.Value] = item.Dict; - } - - JsonDictSerializer.Serialize(stream, dict, true); - } -} diff --git a/LevelTemplateCreator/SceneTree/Art/ObjectMaterialLibary.cs b/LevelTemplateCreator/SceneTree/Art/ObjectMaterialLibary.cs deleted file mode 100644 index 4910819..0000000 --- a/LevelTemplateCreator/SceneTree/Art/ObjectMaterialLibary.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.SceneTree.Art; - -internal class ObjectMaterialLibary : MaterialLibary -{ - public ObjectMaterialLibary(string path) : base(path) - { - } - - public override void AddAsset(TAsset asset) - { - var copy = asset.GetCopy(); - copy.ResolveTexturePaths(this, asset.Info); - Materials.Add(copy); - } -} diff --git a/LevelTemplateCreator/SceneTree/Art/TerrainMaterialLibary.cs b/LevelTemplateCreator/SceneTree/Art/TerrainMaterialLibary.cs deleted file mode 100644 index a5a95ab..0000000 --- a/LevelTemplateCreator/SceneTree/Art/TerrainMaterialLibary.cs +++ /dev/null @@ -1,39 +0,0 @@ -using LevelTemplateCreator.Assets; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LevelTemplateCreator.SceneTree.Art; - -internal class TerrainMaterialLibary : MaterialLibary -{ - readonly float _squareSize; - - public TerrainMaterialLibary(string path, float squareSize) : base(path) - { - _squareSize = squareSize; - } - - public override void AddAsset(TAsset asset) - { - float squareSize = asset switch - { - TerrainMaterialAsset tma => tma.SquareSize, - _ => 1 - }; - - var copy = asset.GetCopy(); - copy.CreatePersistentId(); - copy.MultiplyMappingScale(squareSize / _squareSize); - copy.ResolveTexturePaths(this, asset.Info); - Materials.Add(copy); - } - - public void CreateTerrainMaterialTextureSet(string name) - { - Misc.Add(new TerrainMaterialTextureSet(name)); - } -} diff --git a/LevelTemplateCreator/usings.cs b/LevelTemplateCreator/usings.cs index 62b04e0..2eea4e4 100644 --- a/LevelTemplateCreator/usings.cs +++ b/LevelTemplateCreator/usings.cs @@ -1 +1,7 @@ -global using JsonDict = System.Collections.Generic.Dictionary; \ No newline at end of file +global using System; +global using System.IO; +global using System.Collections.Generic; +global using System.Windows.Forms; +global using System.Drawing; + +global using JsonDict = System.Collections.Generic.Dictionary; \ No newline at end of file