From da3dc2f8f33479c21d3ac92c2186ce9efe457e82 Mon Sep 17 00:00:00 2001 From: Niek Schoemaker <32094562+niekschoemaker@users.noreply.github.com> Date: Mon, 8 Jan 2024 05:00:55 +0100 Subject: [PATCH] Optimizations and refactoring Added pooling to most of the Space Init functions (BVH Builders, etc) Added multi threading to search world and Space Init functions --- CodeWalker.Benchmarks/Benchmarks.cs | 298 +++++++- .../GameFiles/FileTypes/AwcFile.cs | 21 +- .../GameFiles/FileTypes/CacheDatFile.cs | 60 +- .../GameFiles/FileTypes/WatermapFile.cs | 5 +- .../GameFiles/FileTypes/YddFile.cs | 6 +- .../GameFiles/FileTypes/YmapFile.cs | 60 +- .../GameFiles/FileTypes/YndFile.cs | 296 ++++---- CodeWalker.Core/GameFiles/GameFile.cs | 77 +-- CodeWalker.Core/GameFiles/GameFileCache.cs | 10 +- .../GameFiles/MetaTypes/MetaTypes.cs | 105 +-- .../GameFiles/MetaTypes/MetaXml.cs | 11 +- CodeWalker.Core/GameFiles/MetaTypes/Rbf.cs | 9 +- CodeWalker.Core/GameFiles/Resources/Bounds.cs | 130 +--- CodeWalker.Core/GameFiles/Resources/Frag.cs | 17 +- .../GameFiles/Resources/ResourceBaseTypes.cs | 10 +- CodeWalker.Core/GameFiles/RpfFile.cs | 28 +- CodeWalker.Core/GameFiles/RpfManager.cs | 44 +- CodeWalker.Core/GameFiles/Utils/GTACrypto.cs | 9 +- CodeWalker.Core/GameFiles/Utils/Jenk.cs | 5 +- CodeWalker.Core/Tests/GameFileCache.cs | 30 +- CodeWalker.Core/Utils/Matrices.cs | 16 +- CodeWalker.Core/Utils/SharedObjectPool.cs | 42 +- CodeWalker.Core/Utils/Timer.cs | 79 ++- .../TypeConverters/LinkedListConverter.cs | 186 +++++ CodeWalker.Core/Utils/Utils.cs | 26 +- CodeWalker.Core/Utils/Vectors.cs | 12 + CodeWalker.Core/World/Scenarios.cs | 402 ++++++----- CodeWalker.Core/World/Space.cs | 321 +++++---- CodeWalker.Core/World/TimecycleMods.cs | 7 +- CodeWalker.WinForms/FormUtils.cs | 2 +- CodeWalker.WinForms/Utils/TypeConverters.cs | 186 +++++ CodeWalker/ExploreForm.cs | 23 +- CodeWalker/Forms/ModelForm.cs | 8 +- CodeWalker/PedsForm.cs | 2 +- .../Project/Panels/EditAudioZonePanel.cs | 4 +- CodeWalker/Project/Panels/EditYndNodePanel.cs | 2 +- .../Project/Panels/GenerateNavMeshPanel.cs | 6 +- .../Project/Panels/ProjectExplorerPanel.cs | 18 +- CodeWalker/Project/ProjectForm.cs | 49 +- CodeWalker/Project/UndoStep.cs | 3 +- .../PublishProfiles/ClickOnceProfile.pubxml | 2 +- CodeWalker/Rendering/Renderable.cs | 128 ++-- CodeWalker/Rendering/Renderer.cs | 641 +++++++++--------- CodeWalker/Rendering/Shaders/CableShader.cs | 3 - CodeWalker/Rendering/Shaders/MarkerShader.cs | 1 - CodeWalker/Rendering/Shaders/ShadowShader.cs | 7 +- .../Rendering/Utils/ShaderExtensions.cs | 1 - CodeWalker/Tools/BinarySearchForm.cs | 20 +- CodeWalker/Tools/BrowseForm.cs | 2 +- CodeWalker/Tools/ExtractTexForm.cs | 103 ++- CodeWalker/Tools/JenkIndForm.cs | 2 +- CodeWalker/Utils/MapUtils.cs | 67 +- CodeWalker/VehicleForm.cs | 6 +- CodeWalker/World/CutsceneForm.cs | 2 +- CodeWalker/World/WorldSearchForm.cs | 12 +- CodeWalker/WorldForm.cs | 558 ++++++--------- 56 files changed, 2412 insertions(+), 1768 deletions(-) create mode 100644 CodeWalker.Core/Utils/TypeConverters/LinkedListConverter.cs create mode 100644 CodeWalker.WinForms/Utils/TypeConverters.cs diff --git a/CodeWalker.Benchmarks/Benchmarks.cs b/CodeWalker.Benchmarks/Benchmarks.cs index 6aa2e6c..8b21a8b 100644 --- a/CodeWalker.Benchmarks/Benchmarks.cs +++ b/CodeWalker.Benchmarks/Benchmarks.cs @@ -8,11 +8,14 @@ using CodeWalker.Core.Utils; using CodeWalker.GameFiles; using Collections.Pooled; using CommunityToolkit.HighPerformance; +using Iced.Intel; using SharpDX; using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; +using System.Drawing; +using System.Globalization; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -127,7 +130,7 @@ namespace CodeWalker.Benchmarks } } - [Params(10, 100, 1000)] + [Params(1000)] public int Length { get; set; } = 10000; [Params(100)] @@ -147,10 +150,14 @@ namespace CodeWalker.Benchmarks private int randomValue; - private uint[] ushorts = new uint[0]; + private byte[] bytes = new byte[16]; private string Str = "iakslgbhfibnrihbderpiugaehigoI BIHGVUIVDSOUFVBOUADGBOIUYfgiuywetrg872q13rh9872`134tgyihsbaopuJGUIYODGBFIOUFgvbouailksdbhnfp"; + private char[] chars1 = new char[0]; + + private Vector3 WorldPos; + [GlobalSetup] public void Setup() { @@ -159,19 +166,39 @@ namespace CodeWalker.Benchmarks valueByte = 0; valueUint = 0; - ushorts = new uint[Length]; - + Str = ""; for (int i = 0; i < Length; i++) { - ushorts[i] = (uint)random.Next(0, int.MaxValue); + if (random.Next(0, 10) <= 1) + { + Str += random.Next('A', 'Z'); + } else + { + Str += random.Next('a', 'z'); + } } - var hashes = MemoryMarshal.Cast(ushorts); + chars1 = Str.ToCharArray(); - for (int i = 0; i < Length; i++) - { - Console.WriteLine($"{ushorts[i]} -> {hashes[i]}"); - } + + //float f0 = Flags0.Value / 255.0f; + //float f1 = Flags1.Value / 255.0f; + //float f2 = Flags2.Value / 255.0f; + //var c = new Color4(f0, f1, f2, 1.0f); + + var c = new Color4(0.0f, 0.0f, 0.0f, 0.5f); + + // Shortcut + Console.WriteLine(GetBytesReadable(1234)); + Console.WriteLine(GetBytesReadableNew(1234)); + + Console.WriteLine(GetBytesReadable(100234)); + Console.WriteLine(GetBytesReadableNew(100234)); + + Console.WriteLine(GetBytesReadable(long.MaxValue)); + Console.WriteLine(GetBytesReadableNew(long.MaxValue)); + + WorldPos = new Vector3(random.NextFloat(float.MinValue, float.MaxValue), random.NextFloat(float.MinValue, float.MaxValue), random.NextFloat(float.MinValue, float.MaxValue)); //Console.WriteLine("Setup done"); @@ -245,6 +272,234 @@ namespace CodeWalker.Benchmarks // return Str.AsSpan().IndexOf('\0'); //} + public static string GetBytesReadable(long i) + { + //shamelessly stolen from stackoverflow, and a bit mangled + + // Returns the human-readable file size for an arbitrary, 64-bit file size + // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" + // Get absolute value + long absolute_i = Math.Abs(i); + // Determine the suffix and readable value + string suffix; + double readable; + if (absolute_i >= 0x1000000000000000) // Exabyte + { + suffix = "EB"; + readable = (i >> 50); + } + else if (absolute_i >= 0x4000000000000) // Petabyte + { + suffix = "PB"; + readable = (i >> 40); + } + else if (absolute_i >= 0x10000000000) // Terabyte + { + suffix = "TB"; + readable = (i >> 30); + } + else if (absolute_i >= 0x40000000) // Gigabyte + { + suffix = "GB"; + readable = (i >> 20); + } + else if (absolute_i >= 0x100000) // Megabyte + { + suffix = "MB"; + readable = (i >> 10); + } + else if (absolute_i >= 0x400) // Kilobyte + { + suffix = "KB"; + readable = i; + } + else + { + return i.ToString("0 bytes"); // Byte + } + // Divide by 1024 to get fractional value + readable = (readable / 1024); + + string fmt = "0.### "; + if (readable > 1000) + { + fmt = "0"; + } + else if (readable > 100) + { + fmt = "0.#"; + } + else if (readable > 10) + { + fmt = "0.##"; + } + + // Return formatted number with suffix + return $"{readable.ToString(fmt)}{suffix}"; + } + + static string[] sizeSuffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; + + public static string GetBytesReadableNew(long size) + { + //shamelessly stolen from stackoverflow, and a bit mangled + + // Returns the human-readable file size for an arbitrary, 64-bit file size + // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" + // Get absolute value + Debug.Assert(sizeSuffixes.Length > 0); + + if (size == 0) + { + return $"{0:0.#} {sizeSuffixes[0]}"; + } + + var absSize = Math.Abs(size); + var fpPower = Math.Log(absSize, 1024); + var intPower = (int)fpPower; + var normSize = absSize / Math.Pow(1024, intPower); + + return $"{normSize:G4} {sizeSuffixes[intPower]}"; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static byte ToLower(char c) + { + return ToLower((byte)c); + //return (c >= 'A' && c <= 'Z') ? (byte)(c - 'A' + 'a') : (byte)c; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static byte ToLower(byte c) + { + return ('A' <= c && c <= 'Z') ? (byte)(c | 0x20) : c; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static byte ToLowerDirect(char c) + { + return (byte)(('A' <= c && c <= 'Z') ? (byte)(c | 0x20) : (byte)c); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static uint GenHashLower(ReadOnlySpan data) + { + uint h = 0; + foreach (var b in data) + { + h += ToLower(b); + h += h << 10; + h ^= h >> 6; + } + h += h << 3; + h ^= h >> 11; + h += h << 15; + + return h; + } + + [SkipLocalsInit] + public static void WriteDataOneGo(Span data) + { + Unsafe.WriteUnaligned(ref data[0], new Int128(ulong.MaxValue, ulong.MaxValue)); + } + + [SkipLocalsInit] + public static void WriteData(Span data) + { + Unsafe.WriteUnaligned(ref data[0], ulong.MaxValue); + Unsafe.WriteUnaligned(ref data[8], ulong.MaxValue); + } + + [SkipLocalsInit] + public static void WriteDataOld(Span data) + { + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..8]), ulong.MaxValue); + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 8)), ulong.MaxValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static uint GenHashLowerDirect(ReadOnlySpan data) + { + uint h = 0; + foreach (var b in data) + { + h += ToLowerDirect(b); + h += h << 10; + h ^= h >> 6; + } + h += h << 3; + h ^= h >> 11; + h += h << 15; + + return h; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static uint GenHashLowerVectorized(Span data) + { + Ascii.ToLowerInPlace(data, out _); + + uint h = 0; + for (int i = 0; i < data.Length; i++) + { + h += (byte)data[i]; + h += h << 10; + h ^= h >> 6; + } + h += h << 3; + h ^= h >> 11; + h += h << 15; + + return h; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static uint GenHashLowerVectorized(ReadOnlySpan data) + { + Span chars = stackalloc char[data.Length]; + + Ascii.ToLower(data, chars, out _); + + uint h = 0; + foreach (var b in chars) + { + h += (byte)b; + h += h << 10; + h ^= h >> 6; + } + h += h << 3; + h ^= h >> 11; + h += h << 15; + + return h; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SkipLocalsInit] + public static uint GenHashLower(string data) + { + uint h = 0; + foreach (var b in data) + { + h += ToLower(b); + h += h << 10; + h ^= h >> 6; + } + h += h << 3; + h ^= h >> 11; + h += h << 15; + + return h; + } + private PooledList getPooledListClass() { var list = new PooledList(); @@ -343,22 +598,25 @@ namespace CodeWalker.Benchmarks } } - - [Benchmark] - public void ReverseEndianness() + [SkipLocalsInit] + public void WriteData() { - //BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast(ushorts), MemoryMarshal.Cast(ushorts)); + WriteData(bytes); } [Benchmark] - public void SwapBytes() + [SkipLocalsInit] + public void WriteDataOld() { - var _ushorts = ushorts; - for (int i = 0; i < _ushorts.Length; i++) - { - _ushorts[i] = MetaTypes.SwapBytes(_ushorts[i]); - } + WriteDataOld(bytes); + } + + [Benchmark] + [SkipLocalsInit] + public void WriteDataOneGo() + { + WriteDataOneGo(bytes); } } } diff --git a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs index 58db58a..ca3db1e 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs @@ -859,8 +859,10 @@ namespace CodeWalker.GameFiles for (uint i = 0; i < 8; i++) { var th = h + (i << 29); - if (!string.IsNullOrEmpty(JenkIndex.TryGetString(th))) return th; - if (MetaNames.TryGetString(th, out string str)) return th; + if (JenkIndex.TryGetString(th, out _)) + return th; + if (MetaNames.TryGetString(th, out _)) + return th; } return h; } @@ -869,13 +871,16 @@ namespace CodeWalker.GameFiles { get { - if (CachedName != null) return CachedName; + if (CachedName is not null) + return CachedName; + var ha = HashAdjusted; - var str = JenkIndex.TryGetString(ha); - if (!string.IsNullOrEmpty(str)) CachedName = str; - else if (MetaNames.TryGetString(ha, out str)) CachedName = str; - else CachedName = "0x" + Hash.Hex; - return CachedName; + if (JenkIndex.TryGetString(ha, out CachedName)) + return CachedName; + else if (MetaNames.TryGetString(ha, out CachedName)) + return CachedName; + else + return CachedName = $"0x{Hash.Hex}"; } } private string CachedName; diff --git a/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs b/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs index 7b78075..953b18f 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/CacheDatFile.cs @@ -1,4 +1,6 @@ -using Collections.Pooled; +using CodeWalker.Core.Utils; +using Collections.Pooled; +using Microsoft.Extensions.ObjectPool; using SharpDX; using System; using System.Collections.Generic; @@ -60,8 +62,8 @@ namespace CodeWalker.GameFiles uint modlen; bool indates = false; using var dates = new PooledList(); - using var allMapNodes = new PooledList(); - using var allCInteriorProxies = new PooledList(); + var allMapNodes = PooledListPool.Shared.Get(); + using var allCInteriorProxies = new PooledList();; using var allBoundsStoreItems = new PooledList(); for (int i = 100; i < data.Length; i++) { @@ -105,8 +107,6 @@ namespace CodeWalker.GameFiles { allCInteriorProxies.Add(new CInteriorProxy(br)); } - if (allCInteriorProxies.Count != structcount) - { }//all pass here i += (int)(modlen + 4); break; case "BoundsStore": @@ -118,14 +118,10 @@ namespace CodeWalker.GameFiles { allBoundsStoreItems.Add(new BoundsStoreItem(br)); } - if (allBoundsStoreItems.Count != structcount) - { }//all pass here i += (int)(modlen + 4); break; default: - if (!indates) - { } //just testing - else + if (indates) { string line = new string(charArr.Slice(0, length)); dates.Add(new CacheFileDate(line));//eg: 2740459947 (hash of: platform:/data/cdimages/scaleform_frontend.rpf) 130680580712018938 8944 @@ -146,8 +142,10 @@ namespace CodeWalker.GameFiles AllCInteriorProxies = allCInteriorProxies.ToArray(); AllBoundsStoreItems = allBoundsStoreItems.ToArray(); + PooledListPool.Shared.Return(allMapNodes); + MapNodeDict = new Dictionary(); - var rootMapNodes = new List(); + var rootMapNodes = PooledListPool.Shared.Get(); foreach (var mapnode in AllMapNodes) { MapNodeDict[mapnode.Name] = mapnode; @@ -155,33 +153,26 @@ namespace CodeWalker.GameFiles { rootMapNodes.Add(mapnode); } - if (mapnode.UnkExtra != null) - { }//notsure what to do with this } foreach (var mapnode in AllMapNodes) { - MapDataStoreNode pnode; - if (MapNodeDict.TryGetValue(mapnode.ParentName, out pnode)) + if (MapNodeDict.TryGetValue(mapnode.ParentName, out MapDataStoreNode pnode)) { pnode.AddChildToList(mapnode); } - else if ((mapnode.ParentName != 0)) - { } } foreach (var mapnode in AllMapNodes) { mapnode.ChildrenListToArray(); } RootMapNodes = rootMapNodes.ToArray(); + PooledListPool.Shared.Return(rootMapNodes); BoundsStoreDict = new Dictionary(); foreach (BoundsStoreItem item in AllBoundsStoreItems) { - BoundsStoreItem mbsi = null; - if (BoundsStoreDict.TryGetValue(item.Name, out mbsi)) - { } BoundsStoreDict[item.Name] = item; } @@ -199,8 +190,6 @@ namespace CodeWalker.GameFiles { mnode.AddInteriorToList(prx); } - else - { } } foreach (var mapnode in AllMapNodes) { @@ -434,8 +423,11 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public class BoundsStoreItem : IMetaXmlItem { public MetaHash Name { get; set; } - public Vector3 Min { get; set; } - public Vector3 Max { get; set; } + public Vector3 _Min; + public ref Vector3 Min => ref _Min; + + public Vector3 _Max; + public ref Vector3 Max => ref _Max; public uint Layer { get; set; } public BoundsStoreItem() @@ -795,10 +787,10 @@ namespace CodeWalker.GameFiles public MapDataStoreNodeExtra UnkExtra { get; set; } public MapDataStoreNode[] Children { get; set; } - private List ChildrenList; //used when building the array + private PooledList? ChildrenList; //used when building the array public CInteriorProxy[] InteriorProxies { get; set; } - private List InteriorProxyList; + private PooledList? InteriorProxyList; public MapDataStoreNode() { } @@ -826,15 +818,6 @@ namespace CodeWalker.GameFiles Unk3 = br.ReadByte(); //slod flag? Unk4 = br.ReadByte(); - if (Unk1 != 0) - { } - if (Unk2 != 0) - { } - if (Unk3 != 0) - { } - if (Unk4 != 0) - { } //no hits here now.. - //if (Unk4 == 0xFE) //{ // //this seems to never be hit anymore... @@ -896,10 +879,9 @@ namespace CodeWalker.GameFiles Unk4 = (byte)Xml.GetChildUIntAttribute(node, "unk4"); } - public void AddChildToList(MapDataStoreNode child) { - ChildrenList ??= new List(); + ChildrenList ??= PooledListPool.Shared.Get(); ChildrenList.Add(child); } public void ChildrenListToArray() @@ -907,12 +889,13 @@ namespace CodeWalker.GameFiles if (ChildrenList is not null) { Children = ChildrenList.ToArray(); + PooledListPool.Shared.Return(ChildrenList); ChildrenList = null; //plz get this GC } } public void AddInteriorToList(CInteriorProxy iprx) { - InteriorProxyList ??= new List(); + InteriorProxyList ??= PooledListPool.Shared.Get(); InteriorProxyList.Add(iprx); } public void InteriorProxyListToArray() @@ -920,6 +903,7 @@ namespace CodeWalker.GameFiles if (InteriorProxyList is not null) { InteriorProxies = InteriorProxyList.ToArray(); + PooledListPool.Shared.Return(InteriorProxyList); InteriorProxyList = null; //plz get this GC } } diff --git a/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs b/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs index e2d568c..948b9e8 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs @@ -297,8 +297,7 @@ namespace CodeWalker.GameFiles public override string ToString() { - return string.Format("{0}, {1}, {2}", - Start, Count, Offset); + return $"{Start}, {Count}, {Offset}"; } } @@ -399,7 +398,7 @@ namespace CodeWalker.GameFiles public override string ToString() { - return string.Format("{0} - Size: {1}, Pos: {2}", Type, Size, Position); + return $"{Type} - Size: {Size}, Pos: {Position}"; } } public class WaterFlow : WaterItem diff --git a/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs index 8609ebd..4c2246f 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs @@ -11,10 +11,10 @@ namespace CodeWalker.GameFiles { [TypeConverter(typeof(ExpandableObjectConverter))] public class YddFile : GameFile, PackedFile { - public DrawableDictionary DrawableDict { get; set; } + public DrawableDictionary? DrawableDict { get; set; } - public Dictionary Dict { get; set; } - public Drawable[] Drawables { get; set; } + public Dictionary? Dict { get; set; } + public Drawable[]? Drawables { get; set; } public YddFile() : base(null, GameFileType.Ydd) { diff --git a/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs index c7ad1bf..4859a23 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs @@ -13,6 +13,9 @@ using System.Diagnostics.CodeAnalysis; using CommunityToolkit.HighPerformance; using Collections.Pooled; using System.Threading; +using System.Collections; +using System.Globalization; +using CodeWalker.Core.Utils.TypeConverters; namespace CodeWalker.GameFiles; @@ -41,7 +44,7 @@ public class YmapFile : GameFile, PackedFile public YmapEntityDef[] RootEntities { get; set; } = []; public YmapEntityDef[] MloEntities { get; set; } = []; - private WeakReference parent = new WeakReference(null); + private WeakReference parent = new WeakReference(null); public YmapFile? Parent { @@ -52,6 +55,12 @@ public class YmapFile : GameFile, PackedFile return null; } + if (target is not null && target.IsDisposed) + { + parent.SetTarget(null); + return null; + } + return target; } set @@ -296,9 +305,9 @@ public class YmapFile : GameFile, PackedFile { //build the entity hierarchy. - List roots = new List(instcount); - List alldefs = new List(instcount); - List mlodefs = null; + var roots = PooledListPool.Shared.Get(); + var alldefs = PooledListPool.Shared.Get(); + YmapEntityDef[]? mlodefs = null; if (CEntityDefs.Length > 0) { @@ -310,7 +319,7 @@ public class YmapFile : GameFile, PackedFile } if (CMloInstanceDefs.Length > 0) { - mlodefs = new List(CMloInstanceDefs.Length); + mlodefs = new YmapEntityDef[CMloInstanceDefs.Length]; for (int i = 0; i < CMloInstanceDefs.Length; i++) { YmapEntityDef d = new YmapEntityDef(this, i, CMloInstanceDefs[i]); @@ -320,29 +329,31 @@ public class YmapFile : GameFile, PackedFile d.MloInstance.defaultEntitySets = defentsets; } alldefs.Add(d); - mlodefs.Add(d); + mlodefs[i] = d; } } + var allEntities = alldefs.ToArray(); + PooledListPool.Shared.Return(alldefs); + MloEntities = mlodefs ?? []; + AllEntities = allEntities; - for (int i = 0; i < alldefs.Count; i++) + foreach(var d in allEntities) { - YmapEntityDef d = alldefs[i]; int pind = d._CEntityDef.parentIndex; bool isroot = false; - if ((pind < 0) || (pind >= alldefs.Count) || d.LodInParentYmap) + if (pind < 0 || pind >= allEntities.Length || d.LodInParentYmap) { isroot = true; } else { - YmapEntityDef p = alldefs[pind]; + YmapEntityDef p = allEntities[pind]; if ((p._CEntityDef.lodLevel <= d._CEntityDef.lodLevel) || ((p._CEntityDef.lodLevel == rage__eLodType.LODTYPES_DEPTH_ORPHANHD) && (d._CEntityDef.lodLevel != rage__eLodType.LODTYPES_DEPTH_ORPHANHD))) { isroot = true; - p = null; } } @@ -352,22 +363,24 @@ public class YmapFile : GameFile, PackedFile } else { - YmapEntityDef p = alldefs[pind]; + YmapEntityDef p = allEntities[pind]; p.AddChild(d); } } - for (int i = 0; i < alldefs.Count; i++) + + foreach(var d in allEntities) { - alldefs[i].ChildListToArray(); + d.ChildListToArray(); } - AllEntities = alldefs.ToArray(); + RootEntities = roots.ToArray(); - MloEntities = mlodefs?.ToArray() ?? Array.Empty(); - foreach(var ent in AllEntities) + PooledListPool.Shared.Return(roots); + + foreach(var ent in allEntities) { - ent.Extensions = MetaTypes.GetExtensions(Meta, in ent._CEntityDef.extensions) ?? Array.Empty(); + ent.Extensions = MetaTypes.GetExtensions(Meta, in ent._CEntityDef.extensions) ?? []; } } @@ -963,7 +976,7 @@ public class YmapFile : GameFile, PackedFile if (pind >= 0) //connect root entities to parents if they have them.. { - if (pymap.AllEntities != null && pymap.AllEntities.Length > 0) + if (pymap.AllEntities is not null && pymap.AllEntities.Length > 0) { if (pind < pymap.AllEntities.Length) { @@ -1740,7 +1753,11 @@ public class YmapEntityDef public uint EntityHash { get; set; } = 0; //used by CW as a unique position+name identifier - public LinkedList LodManagerChildren { get; set; } = null; + [TypeConverter(typeof(LinkedListConverter))] + public LinkedList? LodManagerChildren { get; set; } = null; + + public YmapEntityDef[]? LodManagerChildrenDisp => LodManagerChildren?.ToArray(); + public object LodManagerRenderable = null; @@ -2079,7 +2096,8 @@ public class YmapEntityDef public void ChildListToArray() { - if (ChildList == null) return; + if (ChildList is null || ChildList.Count == 0) + return; //if (Children == null) //{ Children = ChildList.ToArray(); diff --git a/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs index 8c61526..69eb217 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs @@ -1,5 +1,7 @@ -using CodeWalker.World; +using CodeWalker.Core.Utils; +using CodeWalker.World; using Collections.Pooled; +using Microsoft.Extensions.ObjectPool; using SharpDX; using System; using System.Collections.Generic; @@ -226,9 +228,7 @@ namespace CodeWalker.GameFiles Nodes = new YndNode[nodes.Length]; for (int i = 0; i < nodes.Length; i++) { - var n = new YndNode(); - n.Init(this, nodes[i]); - Nodes[i] = n; + Nodes[i] = new YndNode(this, nodes[i]); } } if (NodeDictionary.JunctionRefs is not null && NodeDictionary.Junctions is not null) @@ -254,17 +254,17 @@ namespace CodeWalker.GameFiles public YndNode AddNode() { int cnt = Nodes?.Length ?? 0; - YndNode yn = new YndNode(); Node n = new Node(); n.AreaID = (ushort)AreaID; n.NodeID = (ushort)(Nodes?.Length ?? 0); - yn.Init(this, n); + + YndNode yn = new YndNode(this, n); int ncnt = cnt + 1; YndNode[] nnodes = new YndNode[ncnt]; for (int i = 0; i < cnt; i++) { - nnodes[i] = Nodes[i]; + nnodes[i] = Nodes![i]; } nnodes[cnt] = yn; Nodes = nnodes; @@ -419,7 +419,7 @@ namespace CodeWalker.GameFiles { if (Nodes is null || Nodes.Length == 0) { - NodePositions = Array.Empty(); + NodePositions = []; return; } int cnt = Nodes.Length; @@ -441,72 +441,69 @@ namespace CodeWalker.GameFiles private void UpdateLinkTriangleVertices() { - //build triangles for the path links display - int vc = 0; - if (Links != null) + if (Links is null || Nodes is null) { - vc = Links.Length * 6; + TriangleVerts = []; + return; } - using PooledList verts = new PooledList(vc); - //EditorVertex v0 = new EditorVertex(); - //EditorVertex v1 = new EditorVertex(); - //EditorVertex v2 = new EditorVertex(); - //EditorVertex v3 = new EditorVertex(); - if (Links is not null && Nodes is not null) + //build triangles for the path links display + + PooledList verts = PooledListPool.Shared.Get(); + + var vectorZero = Vector3.Zero; + + foreach (var node in Nodes) { - foreach (var node in Nodes) + if (node.Links is null) { - if (node.Links is null) + continue; + } + + foreach (var link in node.Links) + { + ref var p0 = ref link.Node1.Position; + ref var p1 = ref link.Node2.Position; + var dir = link.GetDirection(); + var ax = Vectors.Cross(in dir, in Vector3.UnitZ); + + float lanestot = link.LaneCountForward + link.LaneCountBackward; + //float backfrac = Math.Min(Math.Max(link.LaneCountBackward / lanestot, 0.1f), 0.9f); + //float lanewidth = 7.0f; + //float inner = totwidth*(backfrac-0.5f); + //float outer = totwidth*0.5f; + + float lanewidth = link.GetLaneWidth(); + + float inner = link.LaneOffset * lanewidth;// 0.0f; + float outer = inner + lanewidth * link.LaneCountForward; + + float totwidth = lanestot * lanewidth; + float halfwidth = totwidth * 0.5f; + if (link.LaneCountBackward == 0) { - continue; + inner -= halfwidth; + outer -= halfwidth; + } + if (link.LaneCountForward == 0) + { + inner += halfwidth; + outer += halfwidth; } - foreach (var link in node.Links) - { - var p0 = link.Node1?.Position ?? Vector3.Zero; - var p1 = link.Node2?.Position ?? Vector3.Zero; - var dir = link.GetDirection(); - var ax = Vector3.Cross(dir, Vector3.UnitZ); + var c = link.GetColourUint(); - float lanestot = link.LaneCountForward + link.LaneCountBackward; - //float backfrac = Math.Min(Math.Max(link.LaneCountBackward / lanestot, 0.1f), 0.9f); - //float lanewidth = 7.0f; - //float inner = totwidth*(backfrac-0.5f); - //float outer = totwidth*0.5f; + var v0 = new EditorVertex(p1 + ax * inner, c); + var v1 = new EditorVertex(p0 + ax * inner, c); + var v2 = new EditorVertex(p1 + ax * outer, c); + var v3 = new EditorVertex(p0 + ax * outer, c); - float lanewidth = link.GetLaneWidth(); - - float inner = link.LaneOffset * lanewidth;// 0.0f; - float outer = inner + lanewidth * link.LaneCountForward; - - float totwidth = lanestot * lanewidth; - float halfwidth = totwidth * 0.5f; - if (link.LaneCountBackward == 0) - { - inner -= halfwidth; - outer -= halfwidth; - } - if (link.LaneCountForward == 0) - { - inner += halfwidth; - outer += halfwidth; - } - - var c = (uint)link.GetColour().ToRgba(); - - var v0 = new EditorVertex(p1 + ax * inner, c); - var v1 = new EditorVertex(p0 + ax * inner, c); - var v2 = new EditorVertex(p1 + ax * outer, c); - var v3 = new EditorVertex(p0 + ax * outer, c); - - verts.Add(v0); - verts.Add(v1); - verts.Add(v2); - verts.Add(v2); - verts.Add(v1); - verts.Add(v3); - } + verts.Add(v0); + verts.Add(v1); + verts.Add(v2); + verts.Add(v2); + verts.Add(v1); + verts.Add(v3); } } @@ -519,17 +516,19 @@ namespace CodeWalker.GameFiles { TriangleVerts = []; } + + PooledListPool.Shared.Return(verts); } private void UpdateJunctionTriangleVertices(YndNode[]? selectedNodes) { - if (selectedNodes is null) + if (selectedNodes is null || selectedNodes.Length == 0) { return; } //build triangles for the junctions bytes display.... - using PooledList verts = new PooledList(); + PooledList verts = PooledListPool.Shared.Get(); EditorVertex v0; EditorVertex v1; EditorVertex v2; @@ -537,10 +536,9 @@ namespace CodeWalker.GameFiles foreach (var node in selectedNodes) { - if (node.Ynd != this) - continue; - if (node.Junction is null) + if (node.Ynd != this || node.Junction is null) continue; + var j = node.Junction; var d = j.Heightmap; if (d is null) @@ -604,6 +602,8 @@ namespace CodeWalker.GameFiles TriangleVerts = vertsarr; } } + + PooledListPool.Shared.Return(verts); } @@ -725,7 +725,9 @@ namespace CodeWalker.GameFiles public ref Vector3 Position => ref _Position; public int LinkCount { get; set; } public int LinkCountUnk { get; set; } - public YndLink[]? Links { get; set; } + + private YndLink[]? _links; + public YndLink[]? Links { get => _links; set => _links = value; } public ushort AreaID { get => _RawData.AreaID; set => _RawData.AreaID = value; } public ushort NodeID { get => _RawData.NodeID; set => _RawData.NodeID = value; } @@ -737,7 +739,9 @@ namespace CodeWalker.GameFiles public FlagsByte Flags4 { get => _RawData.Flags4; set => _RawData.Flags4 = value; } public TextHash StreetName { get => _RawData.StreetName; set => _RawData.StreetName = value; } - public Color4 Colour { get; set; } + public Color4 Colour => new Color4(ColourRgba); + + public uint ColourRgba { get; set; } public YndJunction Junction { get; set; } public bool HasJunction; @@ -875,17 +879,16 @@ namespace CodeWalker.GameFiles public bool IsPedNode => IsSpecialTypeAPedNode(Special);// If Special is 10, 14 or 18 this is a ped node. - public void Init(YndFile ynd, Node node) + public YndNode(YndFile ynd, Node node) { Ynd = ynd; - RawData = node; + _RawData = node; Position = new Vector3(node.PositionX / 4.0f, node.PositionY / 4.0f, node.PositionZ / 32.0f); LinkCount = node.LinkCountFlags.Value >> 3; LinkCountUnk = node.LinkCountFlags.Value & 7; - Colour = GetColour(); - + ColourRgba = (uint)GetColour().ToRgba(); } public Color4 GetColour() @@ -995,13 +998,10 @@ namespace CodeWalker.GameFiles public YndLink AddLink(YndNode? tonode = null, bool bidirectional = true) { - if (Links == null) - { - Links = Array.Empty(); - } + Links ??= []; var existing = Links.FirstOrDefault(el => el.Node2 == tonode); - if (existing != null) + if (existing is not null) { return existing; } @@ -1026,15 +1026,10 @@ namespace CodeWalker.GameFiles } l.UpdateLength(); - int cnt = Links?.Length ?? 0; + int cnt = Links.Length; int ncnt = cnt + 1; - YndLink[] nlinks = new YndLink[ncnt]; - for (int i = 0; i < cnt; i++) - { - nlinks[i] = Links[i]; - } - nlinks[cnt] = l; - Links = nlinks; + Array.Resize(ref _links, ncnt); + _links[cnt] = l; LinkCount = ncnt; if (bidirectional) @@ -1168,15 +1163,15 @@ namespace CodeWalker.GameFiles if (AreaID != expectedArea?.ID) { - var nodeYnd = space.NodeGrid.GetCell(AreaID).Ynd; var newYnd = expectedArea?.Ynd; if (newYnd is null) { SetPosition(oldPosition); - affectedFiles = Array.Empty(); + affectedFiles = []; return; } + var nodeYnd = space.NodeGrid.GetCell(AreaID)?.Ynd; if ((nodeYnd is null) || nodeYnd.RemoveYndNode(space, this, false, out var affectedFilesFromDelete)) { @@ -1207,10 +1202,7 @@ namespace CodeWalker.GameFiles public void GenerateYndNodeJunctionHeightMap(Space space) { - if (Junction == null) - { - Junction = new YndJunction(); - } + Junction ??= new YndJunction(); var junc = Junction; var maxZ = junc.MaxZ / 32f; @@ -1279,7 +1271,8 @@ namespace CodeWalker.GameFiles } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class YndLink + [TypeConverter(typeof(ExpandableObjectConverter))] + public class YndLink { public YndFile Ynd { get; set; } public YndNode Node1 { get; set; } @@ -1296,11 +1289,11 @@ namespace CodeWalker.GameFiles } public NodeLink _RawData; - public NodeLink RawData { get { return _RawData; } set { _RawData = value; } } - public FlagsByte Flags0 { get { return _RawData.Flags0; } set { _RawData.Flags0 = value; } } - public FlagsByte Flags1 { get { return _RawData.Flags1; } set { _RawData.Flags1 = value; } } - public FlagsByte Flags2 { get { return _RawData.Flags2; } set { _RawData.Flags2 = value; } } - public FlagsByte LinkLength { get { return _RawData.LinkLength; } set { _RawData.LinkLength = value; } } + public NodeLink RawData { get => _RawData; set => _RawData = value; } + public FlagsByte Flags0 { get => _RawData.Flags0; set => _RawData.Flags0 = value; } + public FlagsByte Flags1 { get => _RawData.Flags1; set => _RawData.Flags1 = value; } + public FlagsByte Flags2 { get => _RawData.Flags2; set => _RawData.Flags2 = value; } + public FlagsByte LinkLength { get => _RawData.LinkLength; set => _RawData.LinkLength = value; } public int LaneCountForward { @@ -1333,16 +1326,19 @@ namespace CodeWalker.GameFiles set => Flags2 = value ? (byte)(Flags2 | 2) : (byte)(Flags2 &~ 2); } + public YndLink() + { + + } - public void Init(YndFile ynd, YndNode node1, YndNode node2, NodeLink link) + public YndLink(YndFile ynd, YndNode node1, YndNode node2, NodeLink link) { Ynd = ynd; Node1 = node1; Node2 = node2; - RawData = link; + _RawData = link; } - public void UpdateLength() { if (Node1 is null || Node2 is null) @@ -1398,6 +1394,54 @@ namespace CodeWalker.GameFiles Node2?.CheckIfJunction(); } + public uint GetColourUint() + { + if (Shortcut) + { + // new Color4(0.0f, 0.2f, 0.2f, 0.5f); + return 2134061824; + } + + var node1 = Node1; + var node2 = Node2; + + if (node1.IsDisabledUnk0 + || node1.IsDisabledUnk1 + || node2.IsDisabledUnk0 + || node2.IsDisabledUnk1) + { + if (node1.OffRoad || node2.OffRoad) + { + return 2130772741; + } + + return 2130706437; + } + + if (node1.IsPedNode || node2.IsPedNode) + { + return 2130716211; + } + + if (node1.OffRoad || node2.OffRoad) + { + return 2131371825; + } + + if (DontUseForNavigation) + { + return 2134048819; + } + + if (LaneCountForward == 0) + { + return 2130706559; + } + + + return 2130719488; + } + public Color4 GetColour() { //float f0 = Flags0.Value / 255.0f; @@ -1414,29 +1458,33 @@ namespace CodeWalker.GameFiles return c; } - if (Node1.IsDisabledUnk0 - || Node1.IsDisabledUnk1 - || Node2.IsDisabledUnk0 - || Node2.IsDisabledUnk1) + var node1 = Node1; + var node2 = Node2; + + if (node1.IsDisabledUnk0 + || node1.IsDisabledUnk1 + || node2.IsDisabledUnk0 + || node2.IsDisabledUnk1) { - if (Node1.OffRoad || Node2.OffRoad) + c.Red = 0.02f; + + if (node1.OffRoad || node2.OffRoad) { - c.Red = 0.0196f; c.Green = 0.0156f; c.Blue = 0.0043f; } - c.Red = 0.02f; + return c; } - if (Node1.IsPedNode || Node2.IsPedNode) + if (node1.IsPedNode || node2.IsPedNode) { c.Red = 0.2f; c.Green = 0.15f; return c; } - if (Node1.OffRoad || Node2.OffRoad) + if (node1.OffRoad || node2.OffRoad) { c.Red = 0.196f; c.Green = 0.156f; @@ -1706,7 +1754,7 @@ namespace CodeWalker.GameFiles Sphere.Radius = (Box.Maximum - Box.Minimum).Length() * 0.5f; } - + private static ObjectPool> basePathNodeListPool => PooledListPool.Shared; public void Build() { if (Nodes == null || Nodes.Length == 0 || Nodes.Length <= Threshold || Depth >= MaxDepth) @@ -1715,9 +1763,9 @@ namespace CodeWalker.GameFiles Vector3 avgsum = Vector3.Zero; foreach (var node in Nodes) { - avgsum += node.Position; + Vector3.Add(ref node.Position, ref avgsum, out avgsum); } - Vector3 avg = avgsum * Nodes.Length; + Vector3 avg = avgsum / Nodes.Length; int countx = 0, county = 0, countz = 0; foreach (var node in Nodes) @@ -1735,14 +1783,16 @@ namespace CodeWalker.GameFiles int dy = Math.Abs(target - county); int dz = Math.Abs(target - countz); - int axis = -1; + int axis; if ((dx <= dy) && (dx <= dz)) axis = 0; //x seems best else if (dy <= dz) axis = 1; //y seems best else axis = 2; //z seems best - using PooledList l1 = new PooledList(Nodes.Length); - using PooledList l2 = new PooledList(Nodes.Length); + PooledList l1 = PooledListPool.Shared.Get(); + PooledList l2 = PooledListPool.Shared.Get(); + l1.Clear(); + l2.Clear(); foreach (var node in Nodes) { var s = axis switch @@ -1759,13 +1809,19 @@ namespace CodeWalker.GameFiles var cdepth = Depth + 1; + var nodesL1 = l1.ToArray(); + PooledListPool.Shared.Return(l1); + var nodesL2 = l2.ToArray(); + PooledListPool.Shared.Return(l2); + Node1 = new PathBVHNode { Depth = cdepth, MaxDepth = MaxDepth, Threshold = Threshold, - Nodes = l1.ToArray(), + Nodes = nodesL1, }; + Node1.CalcBounds(); Node1.Build(); @@ -1774,7 +1830,7 @@ namespace CodeWalker.GameFiles Depth = cdepth, MaxDepth = MaxDepth, Threshold = Threshold, - Nodes = l2.ToArray(), + Nodes = nodesL2, }; Node2.CalcBounds(); Node2.Build(); diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs index b6b01f8..1f9c042 100644 --- a/CodeWalker.Core/GameFiles/GameFile.cs +++ b/CodeWalker.Core/GameFiles/GameFile.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -14,45 +15,43 @@ namespace CodeWalker.GameFiles public static class LoadState { public const int None = 0; - public const int Loaded = 1; - public const int LoadQueued = 2; + public const int Loaded = 1 << 0; + public const int LoadQueued = 1 << 1; + public const int Disposed = 1 << 2; + + public static bool ToggleFlag(ref int currentValue, int flagToToggle, bool value) + { + if (value) + { + return (Interlocked.Or(ref currentValue, flagToToggle) & flagToToggle) == 0; + } + else + { + return (Interlocked.And(ref currentValue, ~flagToToggle) & flagToToggle) == flagToToggle; + } + } } + public abstract class GameFile : Cacheable, IDisposable { public byte LoadAttempts = 0; public int loadState = (int)LoadState.None; - [NotifyParentProperty(true)] public bool LoadQueued { get => (loadState & LoadState.LoadQueued) == LoadState.LoadQueued; - set { - if (value) - { - Interlocked.Or(ref loadState, LoadState.LoadQueued); - } - else - { - Interlocked.And(ref loadState, ~LoadState.LoadQueued); - } - } + set => SetLoadQueued(value); } - [NotifyParentProperty(true)] public bool Loaded { get => (loadState & LoadState.Loaded) == LoadState.Loaded; - set - { - if (value) - { - Interlocked.Or(ref loadState, LoadState.Loaded); - } - else - { - Interlocked.And(ref loadState, ~LoadState.Loaded); - } - } + set => SetLoaded(value); + } + + public bool IsDisposed { + get => (loadState & LoadState.Disposed) == LoadState.Disposed; + set => SetDisposed(value); } public DateTime LastLoadTime = DateTime.MinValue; @@ -60,7 +59,6 @@ namespace CodeWalker.GameFiles public string Name { get; set; } public string FilePath { get; set; } //used by the project form. public GameFileType Type { get; set; } - public bool IsDisposed { get; set; } = false; @@ -84,29 +82,11 @@ namespace CodeWalker.GameFiles } } - public bool SetLoadQueued(bool value) - { - if (value) - { - return (Interlocked.Or(ref loadState, LoadState.LoadQueued) & LoadState.LoadQueued) == 0; - } - else - { - return (Interlocked.And(ref loadState, ~LoadState.LoadQueued) & LoadState.LoadQueued) == LoadState.LoadQueued; - } - } + public bool SetLoadQueued(bool value) => LoadState.ToggleFlag(ref loadState, LoadState.LoadQueued, value); - public bool SetLoaded(bool value) - { - if (value) - { - return (Interlocked.Or(ref loadState, LoadState.Loaded) & LoadState.Loaded) == 0; - } - else - { - return (Interlocked.And(ref loadState, ~LoadState.Loaded) & LoadState.Loaded) == LoadState.Loaded; - } - } + public bool SetLoaded(bool value) => LoadState.ToggleFlag(ref loadState, LoadState.Loaded, value); + + public bool SetDisposed(bool value) => LoadState.ToggleFlag(ref loadState, LoadState.Disposed, value); public override string ToString() => string.IsNullOrEmpty(Name) ? JenkIndex.GetString(Key.Hash) : Name; @@ -188,6 +168,7 @@ namespace CodeWalker.GameFiles DistantLights = 30, Ypdb = 31, PedShopMeta = 32, + None = 33, } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index b28d248..16a8a73 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -1052,11 +1052,12 @@ namespace CodeWalker.GameFiles YftDict ??= new Dictionary(40000); YcdDict ??= new Dictionary(20000); YedDict ??= new Dictionary(300); - foreach (var rpffile in AllRpfs) + foreach (var rpffile in AllRpfs.AsSpan()) { if (rpffile.AllEntries is null) continue; - foreach (var entry in rpffile.AllEntries) + + foreach (var entry in rpffile.AllEntries.Span) { if (entry is RpfFileEntry fentry) { @@ -1106,7 +1107,7 @@ namespace CodeWalker.GameFiles { if (rpffile.AllEntries is null) continue; - foreach (var entry in rpffile.AllEntries) + foreach (var entry in rpffile.AllEntries.Span) { if (entry is RpfFileEntry fentry) { @@ -3415,9 +3416,6 @@ namespace CodeWalker.GameFiles } var csv = sb.ToString(); - - - } diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs index 2c67286..c939e18 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaTypes.cs @@ -1931,64 +1931,71 @@ namespace CodeWalker.GameFiles return null; //couldn't find the strings data section. } - using PooledList strings = new PooledList(); - var currentblock = startblock; - int currentblockind = startblockind; - while (currentblock != null) + PooledList strings = PooledListPool.Shared.Get(); + try { - //read strings from the block. - int startindex = 0; - int endindex = 0; - var data = currentblock.Data; - foreach(var span in data.AsSpan().EnumerateSplit((byte)0)) + var currentblock = startblock; + int currentblockind = startblockind; + while (currentblock != null) { - if (!span.IsEmpty) + //read strings from the block. + int startindex = 0; + int endindex = 0; + var data = currentblock.Data; + foreach (var span in data.AsSpan().EnumerateSplit((byte)0)) { - string str = Encoding.ASCII.GetStringPooled(span); - strings.Add(str); + if (!span.IsEmpty) + { + string str = Encoding.ASCII.GetStringPooled(span); + strings.Add(str); + } } + //for (int b = 0; b < data.Length; b++) + //{ + // if (data[b] == 0) + // { + // startindex = endindex; + // endindex = b; + // if (endindex > startindex) + // { + // string str = Encoding.ASCII.GetString(data.AsSpan(startindex, endindex - startindex)); + // strings.Add(str); + // endindex++; //start next string after the 0. + // } + // } + //} + //if (endindex != data.Length - 1) + //{ + // startindex = endindex; + // endindex = data.Length - 1; + // if (endindex > startindex) + // { + // string str = Encoding.ASCII.GetString(data.AsSpan(startindex, endindex - startindex)); + // strings.Add(str); + // strings2.Add(str); + // } + //} + + currentblockind++; + if (currentblockind >= datablocks.Count) + break; //last block, can't go any further + + currentblock = datablocks[currentblockind]; + if (currentblock.StructureNameHash != (MetaName)MetaTypeName.STRING) + break; //not the right block type, can't go further } - //for (int b = 0; b < data.Length; b++) - //{ - // if (data[b] == 0) - // { - // startindex = endindex; - // endindex = b; - // if (endindex > startindex) - // { - // string str = Encoding.ASCII.GetString(data.AsSpan(startindex, endindex - startindex)); - // strings.Add(str); - // endindex++; //start next string after the 0. - // } - // } - //} - //if (endindex != data.Length - 1) - //{ - // startindex = endindex; - // endindex = data.Length - 1; - // if (endindex > startindex) - // { - // string str = Encoding.ASCII.GetString(data.AsSpan(startindex, endindex - startindex)); - // strings.Add(str); - // strings2.Add(str); - // } - //} - currentblockind++; - if (currentblockind >= datablocks.Count) - break; //last block, can't go any further - currentblock = datablocks[currentblockind]; - if (currentblock.StructureNameHash != (MetaName)MetaTypeName.STRING) - break; //not the right block type, can't go further + if (strings.Count <= 0) + { + return null; //don't return empty array... + } + return strings.ToArray(); } - - - if (strings.Count <= 0) + finally { - return null; //don't return empty array... + PooledListPool.Shared.Return(strings); } - return strings.ToArray(); } [SkipLocalsInit] @@ -4921,7 +4928,7 @@ namespace CodeWalker.GameFiles public void AddScenarioPoint(MCExtensionDefSpawnPoint p) { List newpoints = new List(); - if (ScenarioPoints != null) + if (ScenarioPoints is not null) { newpoints.AddRange(ScenarioPoints); } diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs index 68f8f08..c9a952b 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs @@ -1907,18 +1907,9 @@ namespace CodeWalker.GameFiles } - public class StringBuilderPooledObjectPolicyLogged : StringBuilderPooledObjectPolicy - { - public override bool Return(StringBuilder obj) - { - Console.WriteLine($"StringBuilder returned with capacity: {obj.Capacity} {obj.Length}"); - return base.Return(obj); - } - } - public class MetaXmlBase { - public static ObjectPool StringBuilderPool = ObjectPool.Create(new StringBuilderPooledObjectPolicyLogged { MaximumRetainedCapacity = 4 * 1024 * 1024, InitialCapacity = 1024 * 32 }); + public static ObjectPool StringBuilderPool = ObjectPool.Create(new StringBuilderPooledObjectPolicy { MaximumRetainedCapacity = 4 * 1024 * 1024, InitialCapacity = 1024 * 32 }); public const string XmlHeader = ""; diff --git a/CodeWalker.Core/GameFiles/MetaTypes/Rbf.cs b/CodeWalker.Core/GameFiles/MetaTypes/Rbf.cs index dab302f..5c241e0 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/Rbf.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/Rbf.cs @@ -450,7 +450,6 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public class RbfStructure : IRbfType, IDisposable { - private static ObjectPool> listPool = ObjectPool.Create(new DefaultPooledObjectPolicy>()); public string Name { get; set; } public PooledList? Children { get; set; } @@ -519,13 +518,13 @@ namespace CodeWalker.GameFiles internal void AddChild(IRbfType value) { - Children ??= listPool.Get(); + Children ??= PooledListPool.Shared.Get(); Children.Add(value); } internal void AddAttribute(IRbfType value) { - Attributes ??= listPool.Get(); + Attributes ??= PooledListPool.Shared.Get(); Attributes.Add(value); } @@ -546,11 +545,11 @@ namespace CodeWalker.GameFiles { if (Children is PooledList children) { - listPool.Return(children); + PooledListPool.Shared.Return(children); } if (Attributes is PooledList attributes) { - listPool.Return(attributes); + PooledListPool.Shared.Return(attributes); } GC.SuppressFinalize(this); diff --git a/CodeWalker.Core/GameFiles/Resources/Bounds.cs b/CodeWalker.Core/GameFiles/Resources/Bounds.cs index 572b9ba..53b0116 100644 --- a/CodeWalker.Core/GameFiles/Resources/Bounds.cs +++ b/CodeWalker.Core/GameFiles/Resources/Bounds.cs @@ -3300,16 +3300,7 @@ namespace CodeWalker.GameFiles } public override void WriteXml(StringBuilder sb, int indent) { - var s = string.Format("{0} m=\"{1}\" v1=\"{2}\" v2=\"{3}\" v3=\"{4}\" f1=\"{5}\" f2=\"{6}\" f3=\"{7}\"", - Type, - MaterialIndex, - vertIndex1, - vertIndex2, - vertIndex3, - vertFlag1 ? 1 : 0, - vertFlag2 ? 1 : 0, - vertFlag3 ? 1 : 0 - ); + var s = $"{Type} m=\"{MaterialIndex}\" v1=\"{vertIndex1}\" v2=\"{vertIndex2}\" v3=\"{vertIndex3}\" f1=\"{(vertFlag1 ? 1 : 0)}\" f2=\"{(vertFlag2 ? 1 : 0)}\" f3=\"{(vertFlag3 ? 1 : 0)}\""; YbnXml.SelfClosingTag(sb, indent, s); } public override void ReadXml(XmlNode node) @@ -3324,7 +3315,7 @@ namespace CodeWalker.GameFiles } public override string ToString() { - return base.ToString() + ": " + vertIndex1.ToString() + ", " + vertIndex2.ToString() + ", " + vertIndex3.ToString(); + return $"{base.ToString()}: {vertIndex1}, {vertIndex2}, {vertIndex3}"; } } [TC(typeof(EXP))] public class BoundPolygonSphere : BoundPolygon @@ -3335,20 +3326,8 @@ namespace CodeWalker.GameFiles public uint unused0 { get; set; } public uint unused1 { get; set; } - public override Vector3 BoxMin - { - get - { - return Position - sphereRadius; - } - } - public override Vector3 BoxMax - { - get - { - return Position + sphereRadius; - } - } + public override Vector3 BoxMin => Position - sphereRadius; + public override Vector3 BoxMax => Position + sphereRadius; public override Vector3 Scale { get @@ -3571,13 +3550,7 @@ namespace CodeWalker.GameFiles } public override void WriteXml(StringBuilder sb, int indent) { - var s = string.Format("{0} m=\"{1}\" v1=\"{2}\" v2=\"{3}\" radius=\"{4}\"", - Type, - MaterialIndex, - capsuleIndex1, - capsuleIndex2, - FloatUtil.ToString(capsuleRadius) - ); + var s = $"{Type} m=\"{MaterialIndex}\" v1=\"{capsuleIndex1}\" v2=\"{capsuleIndex2}\" radius=\"{FloatUtil.ToString(capsuleRadius)}\""; YbnXml.SelfClosingTag(sb, indent, s); } public override void ReadXml(XmlNode node) @@ -3587,10 +3560,7 @@ namespace CodeWalker.GameFiles capsuleIndex2 = (ushort)Xml.GetUIntAttribute(node, "v2"); capsuleRadius = Xml.GetFloatAttribute(node, "radius"); } - public override string ToString() - { - return base.ToString() + ": " + capsuleIndex1.ToString() + ", " + capsuleIndex2.ToString() + ", " + capsuleRadius.ToString(); - } + public override string ToString() => $"{base.ToString()}: {capsuleIndex1}, {capsuleIndex2}, {capsuleRadius}"; } [TC(typeof(EXP))] public class BoundPolygonBox : BoundPolygon { @@ -3790,10 +3760,7 @@ namespace CodeWalker.GameFiles boxIndex3 = (short)Xml.GetIntAttribute(node, "v3"); boxIndex4 = (short)Xml.GetIntAttribute(node, "v4"); } - public override string ToString() - { - return base.ToString() + ": " + boxIndex1.ToString() + ", " + boxIndex2.ToString() + ", " + boxIndex3.ToString() + ", " + boxIndex4.ToString(); - } + public override string ToString() => $"{base.ToString()}: {boxIndex1}, {boxIndex2}, {boxIndex3}, {boxIndex4}"; } [TC(typeof(EXP))] public class BoundPolygonCylinder : BoundPolygon { @@ -3815,20 +3782,8 @@ namespace CodeWalker.GameFiles set { if (Owner != null) Owner.SetVertexPos(cylinderIndex2, value); } } - public override Vector3 BoxMin - { - get - { - return Vector3.Min(Vertex1, Vertex2) - cylinderRadius;//not perfect but meh - } - } - public override Vector3 BoxMax - { - get - { - return Vector3.Max(Vertex1, Vertex2) + cylinderRadius;//not perfect but meh - } - } + public override Vector3 BoxMin => Vector3.Min(Vertex1, Vertex2) - cylinderRadius;//not perfect but meh + public override Vector3 BoxMax => Vector3.Max(Vertex1, Vertex2) + cylinderRadius;//not perfect but meh public override Vector3 Scale { get @@ -3853,10 +3808,7 @@ namespace CodeWalker.GameFiles } public override Vector3 Position { - get - { - return (Vertex1 + Vertex2) * 0.5f; - } + get => (Vertex1 + Vertex2) * 0.5f; set { var offset = value - Position; @@ -3962,10 +3914,7 @@ namespace CodeWalker.GameFiles cylinderIndex2 = (ushort)Xml.GetUIntAttribute(node, "v2"); cylinderRadius = Xml.GetFloatAttribute(node, "radius"); } - public override string ToString() - { - return base.ToString() + ": " + cylinderIndex1.ToString() + ", " + cylinderIndex2.ToString() + ", " + cylinderRadius.ToString(); - } + public override string ToString() => $"{base.ToString()}: {cylinderIndex1}, {cylinderIndex2}, {cylinderRadius}"; } @@ -4311,10 +4260,7 @@ namespace CodeWalker.GameFiles set { MaxX = (short)value.X; MaxY = (short)value.Y; MaxZ = (short)value.Z; } } - public override readonly string ToString() - { - return $"{NodeIndex1}, {NodeIndex2} ({NodeIndex2 - NodeIndex1} nodes)"; - } + public override readonly string ToString() => $"{NodeIndex1}, {NodeIndex2} ({NodeIndex2 - NodeIndex1} nodes)"; } [TC(typeof(EXP))] public struct BVHNode_s @@ -4339,10 +4285,7 @@ namespace CodeWalker.GameFiles set { MaxX = (short)value.X; MaxY = (short)value.Y; MaxZ = (short)value.Z; } } - public override readonly string ToString() - { - return $"{ItemId}: {ItemCount}"; - } + public override readonly string ToString() => $"{ItemId}: {ItemCount}"; } @@ -4851,46 +4794,34 @@ namespace CodeWalker.GameFiles return !(left == right); } } - [TC(typeof(EXP))] public struct BoundMaterialColour + + [TC(typeof(EXP))] + public struct BoundMaterialColour { public byte R { get; set; } public byte G { get; set; } public byte B { get; set; } public byte A { get; set; } //GIMS EVO saves this as "opacity" 0-100 - public override string ToString() - { - //return Type.ToString() + ", " + Unk0.ToString() + ", " + Unk1.ToString() + ", " + Unk2.ToString(); - return R.ToString() + ", " + G.ToString() + ", " + B.ToString() + ", " + A.ToString(); - } + public override string ToString() => $"{R}, {G}, {B}, {A}"; + //return Type.ToString() + ", " + Unk0.ToString() + ", " + Unk1.ToString() + ", " + Unk2.ToString(); } - [TC(typeof(EXP))] public struct BoundsMaterialType + + [TC(typeof(EXP))] + public struct BoundsMaterialType { public byte Index { get; set; } - public BoundsMaterialData MaterialData - { - get - { - return BoundsMaterialTypes.GetMaterial(this); - } - } + public BoundsMaterialData MaterialData => BoundsMaterialTypes.GetMaterial(this); - public override string ToString() - { - return BoundsMaterialTypes.GetMaterialName(this); - } + public override string ToString() => BoundsMaterialTypes.GetMaterialName(this); - public static implicit operator byte(BoundsMaterialType matType) - { - return matType.Index; //implicit conversion - } + public static implicit operator byte(BoundsMaterialType matType) => matType.Index; - public static implicit operator BoundsMaterialType(byte b) - { - return new BoundsMaterialType() { Index = b }; - } + public static implicit operator BoundsMaterialType(byte b) => new BoundsMaterialType() { Index = b }; } - [TC(typeof(EXP))] public class BoundsMaterialData + + [TC(typeof(EXP))] + public class BoundsMaterialData { public string Name { get; set; } public string Filter { get; set; } @@ -4918,10 +4849,7 @@ namespace CodeWalker.GameFiles public Color Colour { get; set; } - public override string ToString() - { - return Name; - } + public override string ToString() => Name; } public static class BoundsMaterialTypes diff --git a/CodeWalker.Core/GameFiles/Resources/Frag.cs b/CodeWalker.Core/GameFiles/Resources/Frag.cs index 4427771..3ab792b 100644 --- a/CodeWalker.Core/GameFiles/Resources/Frag.cs +++ b/CodeWalker.Core/GameFiles/Resources/Frag.cs @@ -98,7 +98,7 @@ namespace CodeWalker.GameFiles public string Name { get; set; } public FragBoneTransforms BoneTransforms { get; set; } public ResourcePointerArray64 GlassWindows { get; set; } - public FragPhysicsLODGroup PhysicsLODGroup { get; set; } + public FragPhysicsLODGroup? PhysicsLODGroup { get; set; } public FragDrawable DrawableCloth { get; set; } public FragVehicleGlassWindows VehicleGlassWindows { get; set; } @@ -1995,10 +1995,7 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public class FragPhysicsLODGroup : ResourceSystemBlock { - public override long BlockLength - { - get { return 48; } - } + public override long BlockLength => 48; // structure data public uint VFT { get; set; } = 1080055472; @@ -2010,9 +2007,9 @@ namespace CodeWalker.GameFiles public ulong Unknown_28h; // 0x0000000000000000 // reference data - public FragPhysicsLOD PhysicsLOD1 { get; set; } - public FragPhysicsLOD PhysicsLOD2 { get; set; } - public FragPhysicsLOD PhysicsLOD3 { get; set; } + public FragPhysicsLOD? PhysicsLOD1 { get; set; } + public FragPhysicsLOD? PhysicsLOD2 { get; set; } + public FragPhysicsLOD? PhysicsLOD3 { get; set; } public override void Read(ResourceDataReader reader, params object[] parameters) { @@ -2167,8 +2164,8 @@ namespace CodeWalker.GameFiles public FragPhysArticulatedBodyType ArticulatedBodyType { get; set; } public float[] ChildrenUnkFloats { get; set; } public FragPhysGroupNamesBlock GroupNames { get; set; } - public ResourcePointerArray64 Groups { get; set; } - public ResourcePointerArray64 Children { get; set; } + public ResourcePointerArray64? Groups { get; set; } + public ResourcePointerArray64? Children { get; set; } public FragPhysArchetype Archetype1 { get; set; } public FragPhysArchetype Archetype2 { get; set; } public Bounds Bound { get; set; } diff --git a/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs b/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs index dd59078..7ee7aba 100644 --- a/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs +++ b/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs @@ -105,6 +105,12 @@ namespace CodeWalker.GameFiles //public float Unknown_23 { get; set; } //public float Unknown_24 { get; set; } + public Matrix3_s(Vector4 row1, Vector4 row2, Vector4 row3) + { + Row1 = row1; + Row2 = row2; + Row3 = row3; + } public Matrix3_s(float[] a) { @@ -215,8 +221,8 @@ namespace CodeWalker.GameFiles return new Matrix(Column1.X, Column1.Y, Column1.Z, 0, Column2.X, Column2.Y, Column2.Z, 0, Column3.X, Column3.Y, Column3.Z, 0, Column4.X, Column4.Y, Column4.Z, 1); } - public static Matrix4F_s Identity => new Matrix4F_s(true); - public static Matrix4F_s Zero => new Matrix4F_s(false); + public readonly static Matrix4F_s Identity = new Matrix4F_s(true); + public readonly static Matrix4F_s Zero = new Matrix4F_s(false); } diff --git a/CodeWalker.Core/GameFiles/RpfFile.cs b/CodeWalker.Core/GameFiles/RpfFile.cs index a5b69f8..e3b7981 100644 --- a/CodeWalker.Core/GameFiles/RpfFile.cs +++ b/CodeWalker.Core/GameFiles/RpfFile.cs @@ -25,7 +25,7 @@ using System.Threading.Tasks; namespace CodeWalker.GameFiles { - public struct FileCounts + public record struct FileCounts { public uint Rpfs; public uint Files; @@ -54,16 +54,6 @@ namespace CodeWalker.GameFiles BinaryFiles = a.BinaryFiles + b.BinaryFiles }; } - - public static bool operator ==(in FileCounts left, in FileCounts right) - { - return left.Equals(in right); - } - - public static bool operator !=(in FileCounts left, in FileCounts right) - { - return !(left == right); - } } public class RpfFile @@ -285,7 +275,6 @@ namespace CodeWalker.GameFiles rfe.IsEncrypted = rfe.IsExtension(".ysc");//any other way to know..? } - allEntries.Add(e); } @@ -548,11 +537,18 @@ namespace CodeWalker.GameFiles } } + } - - - - + private Dictionary? FilesByFileType; + public RpfEntry[] GetFilesByFileType(string ext) + { + FilesByFileType ??= new Dictionary(); + if (!FilesByFileType.TryGetValue(ext, out var entries)) + { + entries = AllEntries.Where(p => p.IsExtension(ext)).ToArray(); + FilesByFileType[ext] = entries; + } + return entries; } diff --git a/CodeWalker.Core/GameFiles/RpfManager.cs b/CodeWalker.Core/GameFiles/RpfManager.cs index 773e7a0..152db15 100644 --- a/CodeWalker.Core/GameFiles/RpfManager.cs +++ b/CodeWalker.Core/GameFiles/RpfManager.cs @@ -64,14 +64,16 @@ namespace CodeWalker.GameFiles BaseRpfs = new List(1300); ModRpfs = new List(0); DlcRpfs = new List(3500); - AllRpfs = new List(5000); + AllRpfs ??= new List(0); DlcNoModRpfs = new List(3500); AllNoModRpfs = new List(5000); - RpfDict = new Dictionary(DefaultRpfDictCapacity, StringComparer.OrdinalIgnoreCase); - EntryDict = new Dictionary(DefaultEntryDictCapacity, StringComparer.OrdinalIgnoreCase); + RpfDict = new Dictionary(StringComparer.OrdinalIgnoreCase); + EntryDict = new Dictionary(StringComparer.OrdinalIgnoreCase); ModRpfDict = new Dictionary(StringComparer.OrdinalIgnoreCase); ModEntryDict = new Dictionary(StringComparer.OrdinalIgnoreCase); + FileCounts _fileCounts = default; + var rpfs = new ConcurrentBag(); Parallel.ForEach(allfiles, (rpfpath) => { @@ -79,21 +81,27 @@ namespace CodeWalker.GameFiles { RpfFile rf = new RpfFile(rpfpath, rpfpath.Replace(replpath, "")); - if (ExcludePaths != null) + if (ExcludePaths is not null) { bool excl = false; - for (int i = 0; i < ExcludePaths.Length; i++) + foreach(var path in ExcludePaths) { - if (rf.Path.StartsWith(ExcludePaths[i], StringComparison.OrdinalIgnoreCase)) + if (rf.Path.StartsWith(path, StringComparison.OrdinalIgnoreCase)) { excl = true; break; } } - if (excl) return; //skip files in exclude paths. + if (excl) + return; //skip files in exclude paths. } - rf.ScanStructure(updateStatus, errorLog, out _); + rf.ScanStructure(updateStatus, errorLog, out var fileCounts); + + lock(this) + { + _fileCounts += fileCounts; + } if (rf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF) { @@ -115,13 +123,12 @@ namespace CodeWalker.GameFiles return rpf.AllEntries?.Count ?? 0 + rpf.Children?.Sum(calculateSum) ?? 0; } - var minCapacity = rpfs.Sum(calculateSum); - if (minCapacity > AllRpfs.Capacity) - { - AllRpfs.Capacity = minCapacity; - } + AllRpfs.EnsureCapacity((int)_fileCounts.Rpfs); + RpfDict.EnsureCapacity((int)_fileCounts.Rpfs); + AllNoModRpfs.EnsureCapacity((int)_fileCounts.Rpfs); + EntryDict.EnsureCapacity((int)_fileCounts.Rpfs + (int)_fileCounts.Files); - foreach(var rpf in rpfs) + foreach (var rpf in rpfs) { AddRpfFile(rpf, false, false); } @@ -142,8 +149,7 @@ namespace CodeWalker.GameFiles IsInited = true; } - - + Console.WriteLine($"fileCounts: {_fileCounts}"); Console.WriteLine($"AllRpfs: {AllRpfs.Count}; RpfDict: {RpfDict.Count}; EntryDict: {EntryDict.Count}; BaseRpfs: {BaseRpfs.Count}; ModRpfs: {ModRpfs.Count}; DlcRpfs: {DlcRpfs.Count}; DlcNoModRpfs: {DlcNoModRpfs.Count}; AllNoModRpfs: {AllNoModRpfs.Count}; ModRpfDict: {ModRpfDict.Count}; ModEntryDict: {ModEntryDict.Count}"); } @@ -191,7 +197,7 @@ namespace CodeWalker.GameFiles isdlc = isdlc || (file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase) && (file.Name.StartsWith("dlc", StringComparison.OrdinalIgnoreCase) || file.Name.Equals("update.rpf", StringComparison.OrdinalIgnoreCase))); ismod = ismod || (file.Path.StartsWith("mods\\", StringComparison.OrdinalIgnoreCase)); - if (file.AllEntries != null) + if (file.AllEntries is not null) { AllRpfs.Add(file); if (!ismod) @@ -237,7 +243,7 @@ namespace CodeWalker.GameFiles } else { - EntryDict[entry.Path] = entry; + _ = EntryDict.TryAdd(entry.Path, entry); } } @@ -246,7 +252,7 @@ namespace CodeWalker.GameFiles { file.LastError = ex.ToString(); file.LastException = ex; - ErrorLog?.Invoke(entry.Path + ": " + ex.ToString()); + ErrorLog?.Invoke($"{entry.Path}: {ex}"); } } } diff --git a/CodeWalker.Core/GameFiles/Utils/GTACrypto.cs b/CodeWalker.Core/GameFiles/Utils/GTACrypto.cs index e62509d..5ba864e 100644 --- a/CodeWalker.Core/GameFiles/Utils/GTACrypto.cs +++ b/CodeWalker.Core/GameFiles/Utils/GTACrypto.cs @@ -40,6 +40,7 @@ using static System.Runtime.InteropServices.JavaScript.JSType; namespace CodeWalker.GameFiles { + [SkipLocalsInit] public class GTACrypto { @@ -232,8 +233,8 @@ namespace CodeWalker.GameFiles table[15][data[15]] ^ key[3]; - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..8]), x1 | (ulong)x2 << 32); - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 8)), x3 | (ulong)x4 << 32); + Unsafe.WriteUnaligned(ref data[0], x1 | (ulong)x2 << 32); + Unsafe.WriteUnaligned(ref data[8], x3 | (ulong)x4 << 32); //Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..4]), x1); //Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(4, 4)), x2); @@ -271,8 +272,8 @@ namespace CodeWalker.GameFiles table[12][data[12]] ^ key[3]; - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..8]), x1 | (ulong)x2 << 32); - Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 8)), x3 | (ulong)x4 << 32); + Unsafe.WriteUnaligned(ref data[0], x1 | (ulong)x2 << 32); + Unsafe.WriteUnaligned(ref data[8], x3 | (ulong)x4 << 32); //Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..4]), x1); //Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(4, 4)), x2); diff --git a/CodeWalker.Core/GameFiles/Utils/Jenk.cs b/CodeWalker.Core/GameFiles/Utils/Jenk.cs index 5ee6633..0dae0e2 100644 --- a/CodeWalker.Core/GameFiles/Utils/Jenk.cs +++ b/CodeWalker.Core/GameFiles/Utils/Jenk.cs @@ -292,6 +292,7 @@ namespace CodeWalker.GameFiles } } + [SkipLocalsInit] public static class JenkIndex { //public static ConcurrentDictionary Index = new ConcurrentDictionary(Environment.ProcessorCount * 2, 2000000); @@ -400,6 +401,7 @@ namespace CodeWalker.GameFiles } return res; } + public static string TryGetString(uint hash) { string res; @@ -417,8 +419,5 @@ namespace CodeWalker.GameFiles var res = Index.Values; return res; } - } - - } diff --git a/CodeWalker.Core/Tests/GameFileCache.cs b/CodeWalker.Core/Tests/GameFileCache.cs index de9d8b9..6ec821d 100644 --- a/CodeWalker.Core/Tests/GameFileCache.cs +++ b/CodeWalker.Core/Tests/GameFileCache.cs @@ -1171,7 +1171,7 @@ namespace CodeWalker.GameFiles } catch (Exception ex) { - UpdateStatus?.Invoke("Error! " + ex.ToString()); + UpdateStatus?.Invoke($"Error! {ex}"); errorfiles.Add(entry); } if (xmltest && (ybn != null) && (ybn.Bounds != null)) @@ -1179,18 +1179,16 @@ namespace CodeWalker.GameFiles var xml = YbnXml.GetXml(ybn); var ybn2 = XmlYbn.GetYbn(xml); var xml2 = YbnXml.GetXml(ybn2); - if (xml.Length != xml2.Length) - { } } if (savetest && (ybn != null) && (ybn.Bounds != null)) { if (entry is not RpfFileEntry fentry) - { continue; } //shouldn't happen + continue; //shouldn't happen var bytes = ybn.Save(); if (!reloadtest) - { continue; } + continue; string origlen = TextUtil.GetBytesReadable(fentry.FileSize); string bytelen = TextUtil.GetBytesReadable(bytes.Length); @@ -1200,9 +1198,9 @@ namespace CodeWalker.GameFiles RpfFile.LoadResourceFile(ybn2, bytes, 43); if (ybn2.Bounds == null) - { continue; } + continue; if (ybn2.Bounds.Type != ybn.Bounds.Type) - { continue; } + continue; //quick check of roundtrip switch (ybn2.Bounds.Type) @@ -1211,30 +1209,30 @@ namespace CodeWalker.GameFiles { var a = ybn.Bounds as BoundSphere; if (ybn2.Bounds is not BoundSphere b) - { continue; } + continue; break; } case BoundsType.Capsule: { var a = ybn.Bounds as BoundCapsule; if (ybn2.Bounds is not BoundCapsule b) - { continue; } + continue; break; } case BoundsType.Box: { var a = ybn.Bounds as BoundBox; if (ybn2.Bounds is not BoundBox b) - { continue; } + continue; break; } case BoundsType.Geometry: { var a = ybn.Bounds as BoundGeometry; if (ybn2.Bounds is not BoundGeometry b) - { continue; } + continue; if (a.Polygons?.Length != b.Polygons?.Length) - { continue; } + continue; for (int i = 0; i < a.Polygons.Length; i++) { var pa = a.Polygons[i]; @@ -1248,17 +1246,15 @@ namespace CodeWalker.GameFiles { var a = ybn.Bounds as BoundBVH; if (ybn2.Bounds is not BoundBVH b) - { continue; } + continue; if (a.BVH?.Nodes?.data_items?.Length != b.BVH?.Nodes?.data_items?.Length) { } if (a.Polygons?.Length != b.Polygons?.Length) - { continue; } + continue; for (int i = 0; i < a.Polygons.Length; i++) { var pa = a.Polygons[i]; var pb = b.Polygons[i]; - if (pa.Type != pb.Type) - { } } break; } @@ -1275,7 +1271,7 @@ namespace CodeWalker.GameFiles { var a = ybn.Bounds as BoundDisc; if (ybn2.Bounds is not BoundDisc b) - { continue; } + continue; break; } case BoundsType.Cylinder: diff --git a/CodeWalker.Core/Utils/Matrices.cs b/CodeWalker.Core/Utils/Matrices.cs index 99201f8..ca969b3 100644 --- a/CodeWalker.Core/Utils/Matrices.cs +++ b/CodeWalker.Core/Utils/Matrices.cs @@ -12,16 +12,6 @@ namespace CodeWalker public static class MatrixExtensions { - public static Vector3 MultiplyW(in this Matrix m, Vector3 v) - { - float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41; - float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42; - float z = (((m.M13 * v.X) + (m.M23 * v.Y)) + (m.M33 * v.Z)) + m.M43; - float w = (((m.M14 * v.X) + (m.M24 * v.Y)) + (m.M34 * v.Z)) + m.M44; - float iw = 1.0f / Math.Abs(w); - return new Vector3(x * iw, y * iw, z * iw); - } - public static Vector3 MultiplyW(in this Matrix m, in Vector3 v) { float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41; @@ -32,7 +22,7 @@ namespace CodeWalker return new Vector3(x * iw, y * iw, z * iw); } - public static Vector3 Multiply(in this Matrix m, Vector3 v) + public static Vector3 Multiply(in this Matrix m, in Vector3 v) { float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41; float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42; @@ -40,7 +30,7 @@ namespace CodeWalker return new Vector3(x, y, z); //this quick mul ignores W... } - public static Vector3 MultiplyRot(in this Matrix m, Vector3 v) + public static Vector3 MultiplyRot(in this Matrix m, in Vector3 v) { float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z));// + m.M41; float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z));// + m.M42; @@ -49,7 +39,7 @@ namespace CodeWalker //this quick mul ignores W and translation... } - public static Vector4 Multiply(in this Matrix m, Vector4 v) + public static Vector4 Multiply(in this Matrix m, in Vector4 v) { float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41; float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42; diff --git a/CodeWalker.Core/Utils/SharedObjectPool.cs b/CodeWalker.Core/Utils/SharedObjectPool.cs index f3f8b74..40e8691 100644 --- a/CodeWalker.Core/Utils/SharedObjectPool.cs +++ b/CodeWalker.Core/Utils/SharedObjectPool.cs @@ -1,4 +1,6 @@ -using Collections.Pooled; +using CodeWalker.GameFiles; +using Collections.Pooled; +using CommunityToolkit.Diagnostics; using CommunityToolkit.HighPerformance.Buffers; using Microsoft.Extensions.ObjectPool; using System; @@ -19,31 +21,49 @@ namespace CodeWalker.Core.Utils public class PooledListObjectPolicy : PooledObjectPolicy> { + private readonly ClearMode clearMode; + public PooledListObjectPolicy(ClearMode _clearMode = ClearMode.Auto) + { + clearMode = _clearMode; + } public PooledList Get() { - return new PooledList(); + return new PooledList(clearMode); } public override PooledList Create() { - return new PooledList(); + return new PooledList(clearMode); } public override bool Return(PooledList list) { - foreach (var entry in list.Span) - { - if (entry is IDisposable disposable) - disposable.Dispose(); - if (entry is IResettable resettable) - resettable.TryReset(); - } - list.Clear(); return true; } } + public static class PooledListPool + { + private static readonly ObjectPool> s_shared = ObjectPool.Create(new PooledListObjectPolicy(ClearMode.Never)); + public static ObjectPool> Shared => s_shared; + } + + public static class PooledListExtensions + { + public static int EnsureCapacity(this PooledList list, int capacity) + { + ArgumentOutOfRangeException.ThrowIfLessThan(capacity, 0, nameof(capacity)); + + if (list.Capacity < capacity) + { + list.Capacity = capacity; + } + + return list.Capacity; + } + } + public static class StringPoolExtension { [SkipLocalsInit] diff --git a/CodeWalker.Core/Utils/Timer.cs b/CodeWalker.Core/Utils/Timer.cs index 72a34c1..9257111 100644 --- a/CodeWalker.Core/Utils/Timer.cs +++ b/CodeWalker.Core/Utils/Timer.cs @@ -1,46 +1,99 @@ -using System; +using Microsoft.Extensions.ObjectPool; +using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text; +using System.Threading; +using System.Xml.Linq; namespace CodeWalker.Core.Utils { - public class DisposableTimer : IDisposable + public class DisposableTimer : Stopwatch, IDisposable, IResettable { - public static event Action TimerStopped; - public Stopwatch Stopwatch { get; init; } + public static event Action OnDispose; + public Stopwatch Stopwatch => this; static DisposableTimer() { #if DEBUG TimerStopped += (timeSpan, name) => Debug.WriteLine($"{name} took {timeSpan.TotalMilliseconds} ms"); #endif - TimerStopped += (timeSpan, name) => Console.WriteLine($"{name} took {timeSpan.TotalMilliseconds} ms"); + OnDispose += (timeSpan, name) => Console.WriteLine($"{name} took {timeSpan.TotalMilliseconds} ms"); } public string Name { get; private set; } - public DisposableTimer(string name) + public DisposableTimer([CallerMemberName] string name = "") : base() { - Stopwatch = Stopwatch.StartNew(); + Start(); Name = name; } + public DisposableTimer(string name, bool start) { Name = name; if (start) { - Stopwatch = Stopwatch.StartNew(); - } else - { - Stopwatch = new Stopwatch(); + Start(); } } public void Dispose() { - Stopwatch.Stop(); - TimerStopped?.Invoke(Stopwatch.Elapsed, Name ?? string.Empty); + Stop(); + OnDispose?.Invoke(Elapsed, Name ?? string.Empty); + GC.SuppressFinalize(this); + } + + public bool TryReset() + { + Reset(); + return true; + } + } + + public class SummableDisposableTimer : DisposableTimer + { + public event Action OnDispose; + + public SummableDisposableTimer([CallerMemberName] string name = "") : base(name) + { + + } + + public void Dispose() + { + Stop(); + } + } + + public class DisposableTimerSummed : IDisposable + { + private long _elapsed; + public TimeSpan TimeSpan => new TimeSpan(_elapsed); + + public string Name { get; set; } + + public DisposableTimerSummed([CallerMemberName] string name = "") + { + Name = name; + } + + public DisposableTimer GetTimer([CallerMemberName] string name = "") + { + var timer = new SummableDisposableTimer(name); + timer.OnDispose += (time, _) => + { + Interlocked.Add(ref _elapsed, time.Ticks); + }; + return timer; + } + + public void Dispose() + { + Console.WriteLine($"{Name} took {TimeSpan.TotalMilliseconds} ms"); + GC.SuppressFinalize(this); } } } diff --git a/CodeWalker.Core/Utils/TypeConverters/LinkedListConverter.cs b/CodeWalker.Core/Utils/TypeConverters/LinkedListConverter.cs new file mode 100644 index 0000000..d92f226 --- /dev/null +++ b/CodeWalker.Core/Utils/TypeConverters/LinkedListConverter.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace CodeWalker.Core.Utils.TypeConverters; + + +public class LinkedListPropertyDescripter : PropertyDescriptor +{ + private readonly LinkedListNode node; + + public LinkedListPropertyDescripter(LinkedListNode node) + : base(CSharpName(node.Value.GetType()), null) + { + this.node = node; + } + + private static string CSharpName(Type type) + { + var sb = new StringBuilder(); + var name = type.Name; + if (!type.IsGenericType) + return name; + sb.Append(name.Substring(0, name.IndexOf('`'))); + sb.Append("<"); + sb.Append(string.Join(", ", type.GetGenericArguments() + .Select(CSharpName))); + sb.Append(">"); + return sb.ToString(); + } + + public override object GetValue(object component) + { + return node.Value; + } + + public override bool IsReadOnly => true; + + public override string Name => node.Value.ToString(); + + public override Type PropertyType => node.Value.GetType(); + + public override Type ComponentType => node.List.GetType(); + + public override bool ShouldSerializeValue(object component) => false; + + public override bool CanResetValue(object component) => false; + + public override void ResetValue(object component) + { + + } + + public override void SetValue(object component, object value) + { + + } +} + + +public class LinkedListConverter : CollectionConverter +{ + public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + { + LinkedList list = value as LinkedList; + if (list == null || list.Count == 0) + return base.GetProperties(context, value, attributes); + + var items = new PropertyDescriptorCollection(null); + + foreach (var item in list) + { + var node = list.Find(item); + items.Add(new LinkedListPropertyDescripter(node)); + } + return items; + } +} + + +public class ExpandableCollectionPropertyDescriptor : PropertyDescriptor +{ + private IList collection; + private readonly int _index; + + public ExpandableCollectionPropertyDescriptor(IList coll, int idx) + : base(GetDisplayName(coll, idx), null) + { + collection = coll; + _index = idx; + } + + private static string GetDisplayName(IList list, int index) + { + return $"[{index}] " + CSharpName(list[index].GetType()); + } + + private static string CSharpName(Type type) + { + var sb = new StringBuilder(); + var name = type.Name; + if (!type.IsGenericType) + return name; + sb.Append(name.Substring(0, name.IndexOf('`'))); + sb.Append("<"); + sb.Append(string.Join(", ", type.GetGenericArguments() + .Select(CSharpName))); + sb.Append(">"); + return sb.ToString(); + } + + public override bool CanResetValue(object component) + { + return true; + } + + public override Type ComponentType + { + get { return this.collection.GetType(); } + } + + public override object GetValue(object component) + { + return collection[_index]; + } + + public override bool IsReadOnly + { + get { return false; } + } + + public override string Name + { + get { return _index.ToString(CultureInfo.InvariantCulture); } + } + + public override Type PropertyType + { + get { return collection[_index].GetType(); } + } + + public override void ResetValue(object component) + { + } + + public override bool ShouldSerializeValue(object component) + { + return true; + } + + public override void SetValue(object component, object value) + { + collection[_index] = value; + } +} + +public class ListConverter : CollectionConverter +{ + public override bool GetPropertiesSupported(ITypeDescriptorContext context) + { + return true; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + { + IList list = value as IList; + if (list == null || list.Count == 0) + return base.GetProperties(context, value, attributes); + + var items = new PropertyDescriptorCollection(null); + for (int i = 0; i < list.Count; i++) + { + object item = list[i]; + items.Add(new ExpandableCollectionPropertyDescriptor(list, i)); + } + return items; + } +} \ No newline at end of file diff --git a/CodeWalker.Core/Utils/Utils.cs b/CodeWalker.Core/Utils/Utils.cs index 590b962..08a01c3 100644 --- a/CodeWalker.Core/Utils/Utils.cs +++ b/CodeWalker.Core/Utils/Utils.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; @@ -21,8 +22,31 @@ namespace CodeWalker public static class TextUtil { + static string[] sizeSuffixes = ["B", "KB", "MB", "GB", "TB", "PB", "EB"]; - public static string GetBytesReadable(long i) + public static string GetBytesReadable(long size) + { + //shamelessly stolen from stackoverflow, and a bit mangled + + // Returns the human-readable file size for an arbitrary, 64-bit file size + // The default format is "0.### XB", e.g. "4.2 KB" or "1.434 GB" + // Get absolute value + Debug.Assert(sizeSuffixes.Length > 0); + + if (size == 0) + { + return "0 B"; + } + + var absSize = Math.Abs(size); + var fpPower = Math.Log(absSize, 1024); + var intPower = (int)fpPower; + var normSize = absSize / Math.Pow(1024, intPower); + + return $"{normSize:G4} {sizeSuffixes[intPower]}"; + } + + public static string GetBytesReadableOld(long i) { //shamelessly stolen from stackoverflow, and a bit mangled diff --git a/CodeWalker.Core/Utils/Vectors.cs b/CodeWalker.Core/Utils/Vectors.cs index cac4a6e..cbb3bcf 100644 --- a/CodeWalker.Core/Utils/Vectors.cs +++ b/CodeWalker.Core/Utils/Vectors.cs @@ -8,6 +8,18 @@ using CodeWalker.GameFiles; namespace CodeWalker { + public static class Matrices + { + public static Matrix FromVectors(in Vector4 row1, in Vector4 row2, in Vector4 row3, in Vector4 row4) + { + return new Matrix( + row1.X, row1.Y, row1.Z, row1.Z, + row2.X, row2.Y, row2.Z, row2.Z, + row3.X, row3.Y, row3.Z, row3.Z, + row4.X, row4.Y, row4.Z, row4.Z + ); + } + } public static class Vectors { public static Vector3 XYZ(in this Vector4 v) diff --git a/CodeWalker.Core/World/Scenarios.cs b/CodeWalker.Core/World/Scenarios.cs index c2a95a9..d0807df 100644 --- a/CodeWalker.Core/World/Scenarios.cs +++ b/CodeWalker.Core/World/Scenarios.cs @@ -11,9 +11,11 @@ using System.Xml.Linq; using CodeWalker.Core.Utils; using System.Runtime.InteropServices; using System.Diagnostics.Metrics; +using System.Runtime.CompilerServices; namespace CodeWalker.World { + [SkipLocalsInit] public class Scenarios { public volatile bool Inited = false; @@ -32,9 +34,10 @@ namespace CodeWalker.World Timecycle = timecycle; GameFileCache = gameFileCache; - - EnsureScenarioTypes(gameFileCache); - + using (new DisposableTimer("EnsureScenarioTypes")) + { + await EnsureScenarioTypes(gameFileCache); + } ScenarioRegions.Clear(); @@ -56,10 +59,15 @@ namespace CodeWalker.World YmtFile? manifestymt = await rpfman.GetFileAsync(manifestfilename); if (manifestymt is not null && manifestymt.CScenarioPointManifest is not null) { + var regionDefs = manifestymt.CScenarioPointManifest.RegionDefs; - foreach (var region in manifestymt.CScenarioPointManifest.RegionDefs) + ScenarioRegions.EnsureCapacity(regionDefs.Length); + using var timerSummed = new DisposableTimerSummed("LoadYmtFile"); + + await Parallel.ForAsync(0, regionDefs.Length, async (i, cancellationToken) => { - string regionfilename = region.Name.ToString() + ".ymt"; //JenkIndex lookup... ymt should have already loaded path strings into it! maybe change this... + var region = regionDefs[i]; + string regionfilename = $"{region.Name}.ymt"; //JenkIndex lookup... ymt should have already loaded path strings into it! maybe change this... string basefilename = regionfilename.Replace("platform:", "x64a.rpf"); string updfilename = regionfilename.Replace("platform:", "update\\update.rpf\\x64"); string usefilename = updfilename; @@ -68,14 +76,19 @@ namespace CodeWalker.World { usefilename = basefilename; } + YmtFile? regionymt = await rpfman.GetFileAsync(usefilename) ?? await rpfman.GetFileAsync(basefilename); + //YmtFile? regionymt = await rpfman.GetFileAsync(usefilename) ?? await rpfman.GetFileAsync(basefilename); if (regionymt is not null) { var sregion = regionymt.ScenarioRegion; - if (sregion != null) + if (sregion is not null) { - ScenarioRegions.Add(regionymt); + lock(ScenarioRegions) + { + ScenarioRegions.Add(regionymt); + } @@ -90,23 +103,19 @@ namespace CodeWalker.World //System.IO.File.WriteAllBytes("C:\\CodeWalker.Projects\\YmtTest\\AllYmts\\" + regionymt.Name, data); } } - - - } - + }); } - Inited = true; } - public static void EnsureScenarioTypes(GameFileCache gfc) + public static async Task EnsureScenarioTypes(GameFileCache gfc) { //if (ScenarioTypes == null) //{ var stypes = new ScenarioTypes(); - stypes.Load(gfc); + await stypes.LoadAsync(gfc); ScenarioTypes = stypes; //} } @@ -294,67 +303,66 @@ namespace CodeWalker.World EnsureNode(node); } - - List chainedges = new List(); - - if ((r.Paths.Chains != null) && (r.Paths.Edges != null)) + if (r.Paths.Chains is not null && r.Paths.Edges is not null) { + List chainedges = new List(); + var rp = r.Paths; var rpc = rp.Chains; var rpe = rp.Edges; + var rpeLength = rp.Edges.Length; var rpn = rp.Nodes; + var rpnLength = rpn.Length; foreach (var chain in rpc) { - chainedges.Clear(); - - if (chain.EdgeIds != null) + if (chain.EdgeIds is not null) { + chainedges.Clear(); + foreach (var edgeId in chain.EdgeIds) { - if (edgeId >= rpe.Length) - { continue; } + if (edgeId >= rpeLength) + continue; var edge = rpe[edgeId]; - if (edge.NodeIndexFrom >= rpn.Length) - { continue; } - if (edge.NodeIndexTo >= rpn.Length) - { continue; } + if (edge.NodeIndexFrom >= rpnLength) + continue; + if (edge.NodeIndexTo >= rpnLength) + continue; edge.NodeFrom = rpn[edge.NodeIndexFrom]; edge.NodeTo = rpn[edge.NodeIndexTo]; - var nfc = edge.NodeFrom?.Chain; - var ntc = edge.NodeTo?.Chain; - - if ((nfc != null) && (nfc != chain)) - { } - if ((ntc != null) && (ntc != chain)) - { } - - if (edge.NodeFrom != null) edge.NodeFrom.Chain = chain; - if (edge.NodeTo != null) edge.NodeTo.Chain = chain; + if (edge.NodeFrom is not null) + edge.NodeFrom.Chain = chain; + if (edge.NodeTo is not null) + edge.NodeTo.Chain = chain; chainedges.Add(edge); } - } - chain.Edges = chainedges.ToArray(); + chain.Edges = chainedges.ToArray(); + } + else + { + chain.Edges = []; + } } } } - if (r.Points != null) + if (r.Points is not null) { - if (r.Points.MyPoints != null) + if (r.Points.MyPoints is not null) { foreach (var point in r.Points.MyPoints) { EnsureNode(point); } } - if (r.Points.LoadSavePoints != null) + if (r.Points.LoadSavePoints is not null) { foreach (var point in r.Points.LoadSavePoints) { @@ -363,15 +371,15 @@ namespace CodeWalker.World } } - if (r.Clusters != null) //spawn groups + if (r.Clusters is not null) //spawn groups { foreach (var cluster in r.Clusters) { EnsureClusterNode(cluster); - if (cluster.Points != null) + if (cluster.Points is not null) { - if (cluster.Points.MyPoints != null) + if (cluster.Points.MyPoints is not null) { foreach (var point in cluster.Points.MyPoints) { @@ -379,7 +387,7 @@ namespace CodeWalker.World node.Cluster = cluster; } } - if (cluster.Points.LoadSavePoints != null) + if (cluster.Points.LoadSavePoints is not null) { foreach (var point in cluster.Points.LoadSavePoints) { @@ -391,13 +399,13 @@ namespace CodeWalker.World } } - if (r.EntityOverrides != null) + if (r.EntityOverrides is not null) { foreach (var overr in r.EntityOverrides) { EnsureEntityNode(overr); - if (overr.ScenarioPoints != null) + if (overr.ScenarioPoints is not null) { foreach (var point in overr.ScenarioPoints) { @@ -407,10 +415,8 @@ namespace CodeWalker.World } } } - } - //Nodes = NodeDict.Values.ToList(); } @@ -422,47 +428,46 @@ namespace CodeWalker.World public void BuildVertices() { - - List pathverts = new List(); - + PathVerts = []; uint cred = (uint)Color.Red.ToRgba(); uint cblu = (uint)Color.Blue.ToRgba(); uint cgrn = (uint)Color.Green.ToBgra(); uint cblk = (uint)Color.Black.ToBgra(); - if (Ymt?.CScenarioPointRegion is not null) + var r = Ymt?.CScenarioPointRegion?.Paths; + if (r?.Nodes is not null && r?.Chains is not null && r?.Edges is not null) { - var r = Ymt.CScenarioPointRegion; + List pathverts = new List(); - if ((r.Paths != null) && (r.Paths.Nodes != null)) + + var nodes = r.Nodes; + var nodesLength = nodes.Length; + foreach (var chain in r.Chains) { - if ((r.Paths.Chains != null) && (r.Paths.Edges != null)) - { - foreach (var chain in r.Paths.Chains) - { - if (chain.Edges == null) continue; - foreach (var edge in chain.Edges) - { - var vid1 = edge._Data.NodeIndexFrom; - var vid2 = edge._Data.NodeIndexTo; - if ((vid1 >= r.Paths.Nodes.Length) || (vid2 >= r.Paths.Nodes.Length)) continue; - var v1 = r.Paths.Nodes[vid1]; - var v2 = r.Paths.Nodes[vid2]; - byte cr1 = (v1.HasIncomingEdges) ? (byte)255 : (byte)0; - byte cr2 = (v2.HasIncomingEdges) ? (byte)255 : (byte)0; - byte cg = 0;// (chain._Data.Unk_1156691834 > 1) ? (byte)255 : (byte)0; - //cg = ((v1.Unk1 != 0) || (v2.Unk1 != 0)) ? (byte)255 : (byte)0; - //cg = (edge.Action == CScenarioChainingEdge__eAction.Unk_7865678) ? (byte)255 : (byte)0; - //cg = ((v1.UnkValTest != 0) || (v2.UnkValTest != 0)) ? (byte)255 : (byte)0; + if (chain.Edges is null) + continue; - byte cb1 = (byte)(255 - cr1); - byte cb2 = (byte)(255 - cr2); - var colour1 = (uint)new Color(cr1, cg, cb1, (byte)255).ToRgba(); - var colour2 = (uint)new Color(cr2, cg, cb2, (byte)255).ToRgba(); - pathverts.Add(new EditorVertex(v1.Position, colour1)); - pathverts.Add(new EditorVertex(v2.Position, colour2)); - } - } + foreach (var edge in chain.Edges) + { + var vid1 = edge._Data.NodeIndexFrom; + var vid2 = edge._Data.NodeIndexTo; + if ((vid1 >= nodesLength) || (vid2 >= nodesLength)) + continue; + var v1 = nodes[vid1]; + var v2 = nodes[vid2]; + byte cr1 = (v1.HasIncomingEdges) ? (byte)255 : (byte)0; + byte cr2 = (v2.HasIncomingEdges) ? (byte)255 : (byte)0; + byte cg = 0;// (chain._Data.Unk_1156691834 > 1) ? (byte)255 : (byte)0; + //cg = ((v1.Unk1 != 0) || (v2.Unk1 != 0)) ? (byte)255 : (byte)0; + //cg = (edge.Action == CScenarioChainingEdge__eAction.Unk_7865678) ? (byte)255 : (byte)0; + //cg = ((v1.UnkValTest != 0) || (v2.UnkValTest != 0)) ? (byte)255 : (byte)0; + + byte cb1 = (byte)(255 - cr1); + byte cb2 = (byte)(255 - cr2); + var colour1 = (uint)new Color(cr1, cg, cb1, (byte)255).ToRgba(); + var colour2 = (uint)new Color(cr2, cg, cb2, (byte)255).ToRgba(); + pathverts.Add(new EditorVertex(v1.Position, colour1)); + pathverts.Add(new EditorVertex(v2.Position, colour2)); } } @@ -503,28 +508,21 @@ namespace CodeWalker.World // } //} - + if (pathverts.Count > 0) + { + PathVerts = pathverts.ToArray(); + } } - - if (pathverts.Count > 0) + var _nodes = Nodes; + var count = _nodes.Count; + Vector4[] nodePositions = new Vector4[count]; + for (int i = 0; i < count; i++) { - PathVerts = pathverts.ToArray(); - } - else - { - PathVerts = []; + nodePositions[i] = new Vector4(_nodes[i].Position, 1.0f); } - - - Vector4[] nodes = new Vector4[Nodes.Count]; - for (int i = 0; i < Nodes.Count; i++) - { - nodes[i] = new Vector4(Nodes[i].Position, 1.0f); - } - - NodePositions = nodes; + NodePositions = nodePositions; } @@ -582,7 +580,6 @@ namespace CodeWalker.World exnode.LoadSavePoint = point; exnode.Position = point.Position; exnode.Orientation = point.Orientation; - NodeDict[point.Position] = exnode; Nodes.Add(exnode); } return exnode; @@ -599,7 +596,6 @@ namespace CodeWalker.World exnode = new ScenarioNode(cluster.Region?.Ymt); exnode.Cluster = cluster; exnode.Position = cluster.Position; - NodeDict[cluster.Position] = exnode; Nodes.Add(exnode); } return exnode; @@ -618,7 +614,6 @@ namespace CodeWalker.World exnode.ClusterMyPoint = point; exnode.Position = point.Position; exnode.Orientation = point.Orientation; - NodeDict[point.Position] = exnode; Nodes.Add(exnode); } return exnode; @@ -635,7 +630,6 @@ namespace CodeWalker.World exnode = new ScenarioNode(point.ScenarioRegion?.Ymt); exnode.ClusterLoadSavePoint = point; exnode.Position = point.Position; - NodeDict[point.Position] = exnode; Nodes.Add(exnode); } return exnode; @@ -653,7 +647,6 @@ namespace CodeWalker.World exnode.EntityPoint = point; exnode.Position = point.Position; exnode.Orientation = point.Orientation; - NodeDict[point.Position] = exnode; Nodes.Add(exnode); } return exnode; @@ -670,7 +663,6 @@ namespace CodeWalker.World exnode = new ScenarioNode(entity.Region?.Ymt); exnode.Entity = entity; exnode.Position = entity.Position; - NodeDict[entity.Position] = exnode; Nodes.Add(exnode); } return exnode; @@ -733,21 +725,24 @@ namespace CodeWalker.World } - if ((Region != null) && (Region.Points != null)) + if (Region is not null && Region.Points is not null) { if (n.MyPoint != null) Region.Points.AddMyPoint(n.MyPoint); if (n.LoadSavePoint != null) Region.Points.AddLoadSavePoint(n.LoadSavePoint); - if ((n.Cluster != null) && (n.Cluster.Points != null)) + if (n.Cluster is not null && n.Cluster.Points is not null) { - if (n.ClusterMyPoint != null) n.Cluster.Points.AddMyPoint(n.ClusterMyPoint); - if (n.ClusterLoadSavePoint != null) n.Cluster.Points.AddLoadSavePoint(n.ClusterLoadSavePoint); + if (n.ClusterMyPoint is not null) + n.Cluster.Points.AddMyPoint(n.ClusterMyPoint); + if (n.ClusterLoadSavePoint is not null) + n.Cluster.Points.AddLoadSavePoint(n.ClusterLoadSavePoint); } - if ((n.Entity != null) && (n.Entity.ScenarioPoints != null)) + if ((n.Entity is not null) && (n.Entity.ScenarioPoints is not null)) { - if (n.EntityPoint != null) n.Entity.AddScenarioPoint(n.EntityPoint); + if (n.EntityPoint is not null) + n.Entity.AddScenarioPoint(n.EntityPoint); } } - if ((Region != null) && (Region.Paths != null)) + if (Region is not null && Region.Paths is not null) { if (n.ChainingNode != null) { @@ -1701,37 +1696,50 @@ namespace CodeWalker.World public class ScenarioTypes { - private readonly object SyncRoot = new object(); //keep this thread-safe.. technically shouldn't be necessary, but best to be safe - - private Dictionary TypeRefs { get; set; } - private Dictionary Types { get; set; } - private Dictionary TypeGroups { get; set; } - private Dictionary PropSets { get; set; } - private Dictionary PedModelSets { get; set; } - private Dictionary VehicleModelSets { get; set; } - private Dictionary AnimGroups { get; set; } + private Dictionary? TypeRefs { get; set; } + private Dictionary? Types { get; set; } + private Dictionary? TypeGroups { get; set; } + private Dictionary? PropSets { get; set; } + private Dictionary? PedModelSets { get; set; } + private Dictionary? VehicleModelSets { get; set; } + private Dictionary? AnimGroups { get; set; } - public void Load(GameFileCache gfc) + public async Task LoadAsync(GameFileCache gfc) { - lock (SyncRoot) - { - Types = LoadTypes(gfc, "common:\\data\\ai\\scenarios.meta"); - TypeGroups = LoadTypeGroups(gfc, "common:\\data\\ai\\scenarios.meta"); - PropSets = LoadModelSets(gfc, "common:\\data\\ai\\propsets.meta"); - PedModelSets = LoadModelSets(gfc, "common:\\data\\ai\\ambientpedmodelsets.meta"); - VehicleModelSets = LoadModelSets(gfc, "common:\\data\\ai\\vehiclemodelsets.meta"); - AnimGroups = LoadAnimGroups(gfc, "common:\\data\\ai\\conditionalanims.meta"); + await Task.WhenAll([ + Task.Run(() => Types = LoadTypes(gfc, "common:\\data\\ai\\scenarios.meta")), + Task.Run(() => TypeGroups = LoadTypeGroups(gfc, "common:\\data\\ai\\scenarios.meta")), + Task.Run(() => PropSets = LoadModelSets(gfc, "common:\\data\\ai\\propsets.meta")), + Task.Run(() => PedModelSets = LoadModelSets(gfc, "common:\\data\\ai\\ambientpedmodelsets.meta")), + Task.Run(() => VehicleModelSets = LoadModelSets(gfc, "common:\\data\\ai\\vehiclemodelsets.meta")), + Task.Run(() => AnimGroups = LoadAnimGroups(gfc, "common:\\data\\ai\\conditionalanims.meta")), + ]); - TypeRefs = new Dictionary(Types.Count + TypeGroups.Count); - foreach (var kvp in Types) + TypeRefs = new Dictionary(Types.Count + TypeGroups.Count); + + lock (TypeRefs) + { + if (Types is not null) { - TypeRefs[kvp.Key] = new ScenarioTypeRef(kvp.Value); + lock (Types) + { + foreach (var (key, value) in Types) + { + TypeRefs[key] = new ScenarioTypeRef(value); + } + } } - foreach (var kvp in TypeGroups) + if (TypeGroups is not null) { - TypeRefs[kvp.Key] = new ScenarioTypeRef(kvp.Value); + lock(TypeGroups) + { + foreach (var (key, value) in TypeGroups) + { + TypeRefs[key] = new ScenarioTypeRef(value); + } + } } } } @@ -1764,6 +1772,9 @@ namespace CodeWalker.World var typesxml = xml.DocumentElement; var items = typesxml.SelectNodes("Scenarios/Item"); + if (items is null) + return types; + foreach (XmlNode item in items) { var typestr = Xml.GetStringAttribute(item, "type"); @@ -1821,6 +1832,9 @@ namespace CodeWalker.World var typesxml = xml.DocumentElement; var items = typesxml.SelectNodes("ScenarioTypeGroups/Item"); + if (items is null) + return types; + foreach (XmlNode item in items) { ScenarioTypeGroup group = new ScenarioTypeGroup(); @@ -1853,6 +1867,9 @@ namespace CodeWalker.World var setsxml = xml.DocumentElement; var items = setsxml.SelectNodes("ModelSets/Item"); + if (items is null) + return sets; + var noneset = new AmbientModelSet(); noneset.Name = "NONE"; noneset.NameHash = JenkHash.GenHash("none"); @@ -1880,7 +1897,7 @@ namespace CodeWalker.World var xml = LoadXml(gfc, filename); - if (xml?.DocumentElement == null) + if (xml?.DocumentElement is null) { return groups; } @@ -1888,6 +1905,9 @@ namespace CodeWalker.World var setsxml = xml.DocumentElement; var items = setsxml.SelectNodes("ConditionalAnimsGroup/Item"); + if (items is null) + return groups; + foreach (XmlNode item in items) { ConditionalAnimsGroup group = new ConditionalAnimsGroup(); @@ -1910,153 +1930,161 @@ namespace CodeWalker.World public ScenarioTypeRef? GetScenarioTypeRef(uint hash) { - lock (SyncRoot) + if (TypeRefs is null) + return null; + + lock (TypeRefs) { - if (TypeRefs == null) - return null; - ScenarioTypeRef st; - TypeRefs.TryGetValue(hash, out st); + TypeRefs.TryGetValue(hash, out var st); return st; } } public ScenarioType? GetScenarioType(uint hash) { - lock (SyncRoot) + if (Types is null) + return null; + + lock (Types) { - if (Types == null) - return null; Types.TryGetValue(hash, out var st); return st; } } public ScenarioTypeGroup? GetScenarioTypeGroup(uint hash) { - lock (SyncRoot) + if (TypeGroups is null) + return null; + + lock (TypeGroups) { - if (TypeGroups == null) - return null; TypeGroups.TryGetValue(hash, out var tg); return tg; } } public AmbientModelSet? GetPropSet(uint hash) { - lock (SyncRoot) + if (PropSets is null) + return null; + + lock (PropSets) { - if (PropSets == null) - return null; PropSets.TryGetValue(hash, out var ms); return ms; } } public AmbientModelSet? GetPedModelSet(uint hash) { - lock (SyncRoot) + if (PedModelSets is null) + return null; + + lock(PedModelSets) { - if (PedModelSets == null) - return null; - if(!PedModelSets.TryGetValue(hash, out var ms)) + ref var ms = ref CollectionsMarshal.GetValueRefOrAddDefault(PedModelSets, hash, out var exists); + if (!exists) { string s_hash = hash.ToString("X"); ms = new AmbientModelSet(); ms.Name = $"UNKNOWN PED MODELSET ({s_hash})"; ms.NameHash = new MetaHash(hash); ms.Models = []; - PedModelSets.Add(hash, ms); } return ms; } } public AmbientModelSet? GetVehicleModelSet(uint hash) { - lock (SyncRoot) + + if (VehicleModelSets is null) + return null; + lock(VehicleModelSets) { - if (VehicleModelSets == null) - return null; - if(!VehicleModelSets.TryGetValue(hash, out var ms)) + ref var ms = ref CollectionsMarshal.GetValueRefOrAddDefault(VehicleModelSets, hash, out var exists); + if (!exists) { string s_hash = hash.ToString("X"); ms = new AmbientModelSet(); ms.Name = $"UNKNOWN VEHICLE MODELSET ({s_hash})"; ms.NameHash = new MetaHash(hash); ms.Models = []; - VehicleModelSets.Add(hash, ms); } return ms; } } public ConditionalAnimsGroup? GetAnimGroup(uint hash) { - lock (SyncRoot) + if (AnimGroups == null) + return null; + + lock(AnimGroups) { - if (AnimGroups == null) - return null; - ConditionalAnimsGroup ag; - AnimGroups.TryGetValue(hash, out ag); + AnimGroups.TryGetValue(hash, out var ag); return ag; } } public ScenarioTypeRef[] GetScenarioTypeRefs() { - lock (SyncRoot) + if (TypeRefs is null) + return []; + + lock(TypeRefs) { - if (TypeRefs == null) - return []; return TypeRefs.Values.ToArray(); } } public ScenarioType[] GetScenarioTypes() { - lock (SyncRoot) + if (Types is null) + return []; + + lock (Types) { - if (Types == null) - return []; return Types.Values.ToArray(); } } public ScenarioTypeGroup[] GetScenarioTypeGroups() { - lock (SyncRoot) + if (TypeGroups is null) + return []; + lock (TypeGroups) { - if (TypeGroups == null) - return []; return TypeGroups.Values.ToArray(); } } public AmbientModelSet[] GetPropSets() { - lock (SyncRoot) + if (PropSets is null) + return []; + lock (PropSets) { - if (PropSets == null) - return []; return PropSets.Values.ToArray(); } } public AmbientModelSet[] GetPedModelSets() { - lock (SyncRoot) + if (PedModelSets is null) + return []; + lock (PedModelSets) { - if (PedModelSets == null) - return []; return PedModelSets.Values.ToArray(); } } public AmbientModelSet[] GetVehicleModelSets() { - lock (SyncRoot) + if (VehicleModelSets is null) + return []; + lock (VehicleModelSets) { - if (VehicleModelSets == null) - return []; return VehicleModelSets.Values.ToArray(); } } public ConditionalAnimsGroup[] GetAnimGroups() { - lock (SyncRoot) + if (AnimGroups is null) + return []; + + lock (AnimGroups) { - if (AnimGroups == null) - return []; return AnimGroups.Values.ToArray(); } } @@ -2174,7 +2202,7 @@ namespace CodeWalker.World NameHash = JenkHash.GenHashLower(Name); var models = node.SelectNodes("Models/Item"); - var modellist = new List(); + var modellist = new List(models.Count); foreach (XmlNode item in models) { AmbientModel model = new AmbientModel(); diff --git a/CodeWalker.Core/World/Space.cs b/CodeWalker.Core/World/Space.cs index 2ffa429..3cb1a4c 100644 --- a/CodeWalker.Core/World/Space.cs +++ b/CodeWalker.Core/World/Space.cs @@ -2,10 +2,13 @@ using CodeWalker.GameFiles; using Collections.Pooled; using CommunityToolkit.HighPerformance; +using Microsoft.Extensions.ObjectPool; using SharpDX; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; @@ -60,34 +63,32 @@ namespace CodeWalker.World GameFileCache = gameFileCache; + var task = Task.Run(() => InitNodeGrid("Space Init")); + updateStatus?.Invoke("Scanning manifests..."); - InitManifestData(); + InitManifestData("Space Init"); updateStatus?.Invoke("Scanning caches..."); - await InitCacheDataAsync(); + await InitCacheDataAsync("Space Init"); updateStatus?.Invoke("Building map data store..."); - InitMapDataStore(); + InitMapDataStore("Space Init"); updateStatus?.Invoke("Building bounds store..."); - InitBoundsStore(); - - - updateStatus?.Invoke("Loading paths..."); - - InitNodeGrid(); - + InitBoundsStore("Space Init"); updateStatus?.Invoke("Loading nav meshes..."); - InitNavGrid(); + InitNavGrid("Space Init"); + + await task; Inited = true; @@ -95,9 +96,9 @@ namespace CodeWalker.World } - private void InitManifestData() + private void InitManifestData([CallerMemberName] string callerName = "") { - using var _ = new DisposableTimer("InitManifestData"); + using var _ = new DisposableTimer($"{callerName} -> {nameof(InitManifestData)}"); interiorLookup.Clear(); interiorManifest.Clear(); ymaptimes.Clear(); @@ -105,7 +106,7 @@ namespace CodeWalker.World dataGroupDict.Clear(); var manifests = GameFileCache.AllManifests; - foreach (var manifest in manifests) + foreach (var manifest in manifests.AsSpan()) { //build interior lookup - maps child->parent interior bounds @@ -166,10 +167,10 @@ namespace CodeWalker.World } - private async Task InitCacheDataAsync() + private async Task InitCacheDataAsync([CallerMemberName] string callerName = "") { //build the grid from the cached data - using var _ = new DisposableTimer("InitCacheDataAsync"); + using var _ = new DisposableTimer($"{callerName} -> {nameof(InitCacheDataAsync)}"); var caches = GameFileCache.AllCacheFiles; nodedict = new Dictionary(6000); //List intlist = new List(); @@ -252,8 +253,8 @@ namespace CodeWalker.World - using var ymapTimer = new DisposableTimer("ymaps"); - using var ybnTimer = new DisposableTimer("ybns"); + using var ymapTimer = new DisposableTimer("ymaps", false); + using var ybnTimer = new DisposableTimer("ybns", false); using(new DisposableTimer("maprpfs.Values")) { //try and generate the cache data for uncached ymaps... mainly for mod maps! @@ -264,6 +265,7 @@ namespace CodeWalker.World { if (entry.IsExtension(".ymap")) { + ymapTimer.Stopwatch.Start(); if (!nodedict.ContainsKey(new MetaHash(entry.ShortNameHash))) { //non-cached ymap. mostly only mods... but some interesting test things also @@ -277,9 +279,11 @@ namespace CodeWalker.World } } } + ymapTimer.Stopwatch.Stop(); } if (entry.IsExtension(".ybn")) { + ybnTimer.Stopwatch.Start(); MetaHash ehash = new MetaHash(entry.ShortNameHash); if (!usedboundsdict.ContainsKey(ehash)) { @@ -297,6 +301,7 @@ namespace CodeWalker.World } } } + ybnTimer.Stopwatch.Stop(); } } } @@ -306,33 +311,29 @@ namespace CodeWalker.World } - private void InitMapDataStore() + private void InitMapDataStore([CallerMemberName] string callerName = "") { - using var _ = new DisposableTimer("InitMapDataStore"); + using var _ = new DisposableTimer($"{callerName} -> {nameof(InitMapDataStore)}"); MapDataStore = new SpaceMapDataStore(); MapDataStore.Init(nodedict.Values); } - private void InitBoundsStore() + private void InitBoundsStore([CallerMemberName] string callerName = "") { - using var _ = new DisposableTimer("InitBoundsStore"); + using var _ = new DisposableTimer($"{callerName} -> {nameof(InitBoundsStore)}"); BoundsStore = new SpaceBoundsStore(); BoundsStore.Init(boundsdict.Values); - } - private void InitNodeGrid() + private Dictionary GetYndEntries() { - using var _ = new DisposableTimer("InitNodeGrid"); - NodeGrid = new SpaceNodeGrid(); - AllYnds.Clear(); - + using var addRpfYndTimer = new DisposableTimer($"{nameof(InitNodeGrid)} -> AddRpfYnds"); var rpfman = GameFileCache.RpfMan; Dictionary yndentries = new Dictionary(); - foreach (var rpffile in GameFileCache.BaseRpfs) //load nodes from base rpfs + foreach (var rpffile in GameFileCache.BaseRpfs.AsSpan()) //load nodes from base rpfs { AddRpfYnds(rpffile, yndentries); } @@ -341,27 +342,39 @@ namespace CodeWalker.World var updrpf = rpfman.FindRpfFile("update\\update.rpf"); //load nodes from patch area... if (updrpf is not null) { - foreach (var rpffile in updrpf.Children) + foreach (var rpffile in updrpf.Children.Span) { AddRpfYnds(rpffile, yndentries); } } - foreach (var dlcrpf in GameFileCache.DlcActiveRpfs) //load nodes from current dlc rpfs + foreach (var dlcrpf in GameFileCache.DlcActiveRpfs.AsSpan()) //load nodes from current dlc rpfs { if (dlcrpf.Path.StartsWith("x64", StringComparison.OrdinalIgnoreCase)) continue; //don't override update.rpf YNDs with x64 ones! *hack - foreach (var rpffile in dlcrpf.Children) + foreach (var rpffile in dlcrpf.Children.Span) { AddRpfYnds(rpffile, yndentries); } } } + return yndentries; + } + + private async Task InitNodeGrid([CallerMemberName] string callerName = "") + { + using var _ = new DisposableTimer($"{callerName} -> {nameof(InitNodeGrid)}"); + NodeGrid = new SpaceNodeGrid(); + AllYnds.Clear(); + + var yndentries = GetYndEntries(); + + var addRpfYndTimer = new DisposableTimer($"{nameof(InitNodeGrid)} -> BuildNodeGrid"); Vector3 corner = new Vector3(-8192, -8192, -2048); Vector3 cellsize = new Vector3(512, 512, 4096); - for (int x = 0; x < NodeGrid.CellCountX; x++) + await Parallel.ForAsync(0, NodeGrid.CellCountX, async (x, cancellationToken) => { for (int y = 0; y < NodeGrid.CellCountY; y++) { @@ -370,14 +383,17 @@ namespace CodeWalker.World uint fnhash = JenkHash.GenHash(fname); if (yndentries.TryGetValue(fnhash, out RpfFileEntry? fentry)) { - cell.Ynd = RpfManager.GetFile(fentry); + cell.Ynd = await RpfManager.GetFileAsync(fentry); cell.Ynd.BBMin = corner + (cellsize * new Vector3(x, y, 0)); cell.Ynd.BBMax = cell.Ynd.BBMin + cellsize; cell.Ynd.CellX = x; cell.Ynd.CellY = y; cell.Ynd.Loaded = true; - AllYnds[fnhash] = cell.Ynd; + lock(AllYnds) + { + AllYnds[fnhash] = cell.Ynd; + } #region node flags test @@ -458,19 +474,24 @@ namespace CodeWalker.World } } - } + }); + addRpfYndTimer.Dispose(); + addRpfYndTimer = new DisposableTimer($"{nameof(InitNodeGrid)} -> BuildYndData"); //join the dots.... //StringBuilder sb = new StringBuilder(); - List tverts = new List(); - using PooledList tlinks = new PooledList(); - using PooledList nlinks = new PooledList(); - foreach (var ynd in AllYnds.Values) - { - BuildYndData(ynd, tverts, tlinks, nlinks); + var allYnds = AllYnds.Values.ToArray(); - //sb.Append(ynd.nodestr); - } + Parallel.ForEach(allYnds, BuildYndData); + + //foreach (var ynd in AllYnds.Values) + //{ + // BuildYndData(ynd); + + // //sb.Append(ynd.nodestr); + //} + + addRpfYndTimer.Dispose(); //string str = sb.ToString(); } @@ -482,11 +503,12 @@ namespace CodeWalker.World NodeGrid.UpdateYnd(ynd); } - private void AddRpfYnds(RpfFile rpffile, Dictionary yndentries) + private static void AddRpfYnds(RpfFile rpffile, Dictionary yndentries) { if (rpffile.AllEntries is null) return; - foreach (var entry in rpffile.AllEntries) + + foreach (var entry in rpffile.AllEntries.Span) { if (entry is RpfFileEntry fentry) { @@ -498,19 +520,20 @@ namespace CodeWalker.World } } - public void BuildYndLinks(YndFile ynd, IList? tlinks = null, IList? nlinks = null) + public void BuildYndLinks(YndFile ynd) { var ynodes = ynd.Nodes; var nodes = ynd.NodeDictionary?.Nodes; var links = ynd.NodeDictionary?.Links; - if (ynodes is null || nodes is null || links is null) return; + if (ynodes is null || nodes is null || links is null) + return; int nodecount = ynodes.Length; //build the links arrays. - tlinks ??= new PooledList(); - nlinks ??= new PooledList(); + var tlinks = PooledListPool.Shared.Get(); + var nlinks = PooledListPool.Shared.Get(); tlinks.Clear(); for (int i = 0; i < nodecount; i++) @@ -524,24 +547,24 @@ namespace CodeWalker.World var llid = linkid + l; if (llid >= links.Length) continue; var link = links[llid]; - YndNode tnode; + YndNode? tnode; if (link.AreaID == node.AreaID) { if (link.NodeID >= ynodes.Length) - { continue; } + continue; + tnode = ynodes[link.NodeID]; } else { tnode = NodeGrid.GetYndNode(link.AreaID, link.NodeID); if (tnode == null) - { continue; } - if ((Math.Abs(tnode.Ynd.CellX - ynd.CellX) > 1) || (Math.Abs(tnode.Ynd.CellY - ynd.CellY) > 1)) - { /*continue;*/ } //non-adjacent cell? seems to be the carrier problem... + continue; + //if ((Math.Abs(tnode.Ynd.CellX - ynd.CellX) > 1) || (Math.Abs(tnode.Ynd.CellY - ynd.CellY) > 1)) + //{ /*continue;*/ } //non-adjacent cell? seems to be the carrier problem... } - YndLink yl = new YndLink(); - yl.Init(ynd, node, tnode, link); + YndLink yl = new YndLink(ynd, node, tnode, link); tlinks.Add(yl); nlinks.Add(yl); } @@ -549,10 +572,13 @@ namespace CodeWalker.World } ynd.Links = tlinks.ToArray(); + PooledListPool.Shared.Return(tlinks); + PooledListPool.Shared.Return(nlinks); } - public void BuildYndVerts(YndFile ynd, YndNode[]? selectedNodes, IList? tverts = null) + + public void BuildYndVerts(YndFile ynd, YndNode[]? selectedNodes = null) { - var laneColour = (uint) new Color4(0f, 0f, 1f, 1f).ToRgba(); + var laneColour = 4294901760; // (uint) new Color4(0f, 0f, 1f, 1f).ToRgba(); var ynodes = ynd.Nodes; if (ynodes is null) return; @@ -560,7 +586,7 @@ namespace CodeWalker.World int nodecount = ynodes.Length; //build the main linked vertex array (used by the renderable to draw the lines). - tverts ??= new PooledList(); + var tverts = PooledListPool.Shared.Get(); tverts.Clear(); for (int i = 0; i < nodecount; i++) { @@ -569,14 +595,12 @@ namespace CodeWalker.World continue; - var nvert = new EditorVertex(node.Position, (uint)node.Colour.ToRgba()); + var nvert = new EditorVertex(node.Position, (uint)node.ColourRgba); - - for (int l = 0; l < node.Links.Length; l++) + foreach(var yl in node.Links) { - YndLink yl = node.Links[l]; var laneDir = yl.GetDirection(); - var laneDirCross = Vector3.Cross(laneDir, Vector3.UnitZ); + var laneDirCross = Vectors.Cross(in laneDir, in Vector3.UnitZ); var laneWidth = yl.GetLaneWidth(); var laneHalfWidth = laneWidth / 2; var offset = yl.IsTwoWay() @@ -587,9 +611,9 @@ namespace CodeWalker.World var tnode = yl.Node2; - if (tnode == null) + if (tnode is null) continue; //invalid links could hit here - var tvert = new EditorVertex(tnode.Position, (uint)tnode.Colour.ToRgba()); + var tvert = new EditorVertex(tnode.Position, (uint)tnode.ColourRgba); tverts.Add(nvert); tverts.Add(tvert); @@ -619,6 +643,8 @@ namespace CodeWalker.World } ynd.LinkedVerts = tverts.ToArray(); + PooledListPool.Shared.Return(tverts); + ynd.UpdateTriangleVertices(selectedNodes); } public void BuildYndJuncs(YndFile ynd) @@ -631,8 +657,7 @@ namespace CodeWalker.World for (int i = 0; i < junccount; i++) { var junc = yjuncs[i]; - var cell = NodeGrid.GetCell(junc.RefData.AreaID); - if (cell?.Ynd?.Nodes is null) + if (!NodeGrid.TryGetCell(junc.RefData.AreaID, out var cell) || cell?.Ynd?.Nodes is null) continue; var jynd = cell.Ynd; @@ -656,15 +681,14 @@ namespace CodeWalker.World } } - public void BuildYndData(YndFile ynd, IList? tverts = null, IList? tlinks = null, IList? nlinks = null) + public void BuildYndData(YndFile ynd) { - - BuildYndLinks(ynd, tlinks, nlinks); + ArgumentNullException.ThrowIfNull(ynd, nameof(ynd)); + BuildYndLinks(ynd); BuildYndJuncs(ynd); - BuildYndVerts(ynd, null, tverts); - + BuildYndVerts(ynd); } public YndFile[] GetYndFilesThatDependOnYndFile(YndFile file) @@ -723,9 +747,9 @@ namespace CodeWalker.World } - private void InitNavGrid() + private void InitNavGrid([CallerMemberName] string callerName = "") { - using var _ = new DisposableTimer("InitNavGrid"); + using var _ = new DisposableTimer($"{callerName} -> InitNavGrid"); NavGrid = new SpaceNavGrid(); var rpfman = GameFileCache.RpfMan; @@ -774,16 +798,14 @@ namespace CodeWalker.World private void AddRpfYnvs(RpfFile rpffile, Dictionary ynventries) { - if (rpffile.AllEntries == null) return; - foreach (var entry in rpffile.AllEntries) + if (rpffile.AllEntries is null) + return; + foreach (var entry in rpffile.AllEntries.Span) { - if (entry is RpfFileEntry) + if (entry is RpfFileEntry fentry) { - RpfFileEntry fentry = entry as RpfFileEntry; if (entry.IsExtension(".ynv")) { - if (ynventries.ContainsKey(entry.NameHash)) - { } ynventries[entry.NameHash] = fentry; } } @@ -794,10 +816,13 @@ namespace CodeWalker.World public void Update(float elapsed) { - if (!Inited) return; - if (BoundsStore == null) return; + if (!Inited) + return; + if (BoundsStore is null) + return; - if (elapsed > 0.1f) elapsed = 0.1f; + if (elapsed > 0.1f) + elapsed = 0.1f; Collisions.Clear(); @@ -806,11 +831,13 @@ namespace CodeWalker.World EnabledEntities.Clear(); foreach (var e in PersistentEntities) { - if (e.Enabled) EnabledEntities.Add(e); + if (e.Enabled) + EnabledEntities.Add(e); } foreach (var e in TemporaryEntities) { - if (e.Enabled) EnabledEntities.Add(e); + if (e.Enabled) + EnabledEntities.Add(e); } @@ -1044,9 +1071,9 @@ namespace CodeWalker.World { if (ymapweathertypes.TryGetValue(ymaphash, out var weathers)) { - for (int i = 0; i < weathers.Length; i++) + foreach(var _weather in weathers) { - if (weathers[i] == weather) + if (_weather == weather) return true; } return false; @@ -1057,10 +1084,9 @@ namespace CodeWalker.World public void GetVisibleYmaps(Camera cam, int hour, MetaHash weather, Dictionary ymaps) { - if (!Inited) - return; - if (MapDataStore is null) + if (!Inited || MapDataStore is null) return; + CurrentHour = hour; CurrentWeather = weather; var items = MapDataStore.GetItems(in cam.Position); @@ -1156,7 +1182,7 @@ namespace CodeWalker.World { var hash = cell.YnvEntry.ShortNameHash; var ynv = (hash > 0) ? GameFileCache.GetYnv(hash) : null; - if ((ynv != null) && (ynv.Loaded)) + if (ynv != null && ynv.Loaded) { ynvs.Add(ynv); } @@ -1196,8 +1222,8 @@ namespace CodeWalker.World continue; } //already a closer hit - YbnFile ybn = GameFileCache.GetYbn(bound.Name); - if (ybn == null) + YbnFile? ybn = GameFileCache.GetYbn(bound.Name); + if (ybn is null) { continue; } //ybn not found? @@ -1917,10 +1943,7 @@ namespace CodeWalker.World { RootNode = new SpaceBoundsStoreNode(); RootNode.Owner = this; - foreach (var item in items) - { - RootNode.Add(item); - } + RootNode.AddRange(items); RootNode.TrySplit(SplitThreshold); } @@ -1932,6 +1955,7 @@ namespace CodeWalker.World return VisibleItems; } + public List GetItems(ref Ray ray, bool[]? layers = null) { VisibleItems.Clear(); @@ -1941,44 +1965,58 @@ namespace CodeWalker.World return VisibleItems; } } + public class SpaceBoundsStoreNode { - public SpaceBoundsStore Owner = null; - public SpaceBoundsStoreNode[] Children = null; - public List Items = null; + public SpaceBoundsStore? Owner = null; + public SpaceBoundsStoreNode[]? Children = null; + public PooledList? Items = null; public Vector3 BBMin = new Vector3(float.MaxValue); public Vector3 BBMax = new Vector3(float.MinValue); public int Depth = 0; public void Add(BoundsStoreItem item) { - if (Items == null) - { - Items = new List(); - } - BBMin = Vector3.Min(BBMin, item.Min); - BBMax = Vector3.Max(BBMax, item.Max); + Items ??= PooledListPool.Shared.Get(); + + Vectors.Min(in BBMin, in item.Min, out BBMin); + Vectors.Max(in BBMax, in item.Max, out BBMax); Items.Add(item); } + public void AddRange(IEnumerable items) + { + Items ??= PooledListPool.Shared.Get(); + if (items.TryGetNonEnumeratedCount(out var count)) + { + Items.EnsureCapacity(Items.Count + count); + } + foreach (var item in items) + { + Vectors.Min(in BBMin, in item.Min, out BBMin); + Vectors.Max(in BBMax, in item.Max, out BBMax); + Items.Add(item); + } + } + public void TrySplit(int threshold) { - if ((Items == null) || (Items.Count <= threshold)) - { return; } + if (Items is null || Items.Count <= threshold) + return; Children = new SpaceBoundsStoreNode[4]; - var newItems = new List(); + var newItems = PooledListPool.Shared.Get(); var ncen = (BBMax + BBMin) * 0.5f; var next = (BBMax - BBMin) * 0.5f; var nsiz = Math.Max(next.X, next.Y); var nsizh = nsiz * 0.5f; - foreach (var item in Items) + foreach (var item in Items.Span) { - var imin = item.Min; - var imax = item.Max; + ref readonly var imin = ref item.Min; + ref readonly var imax = ref item.Max; var icen = (imax + imin) * 0.5f; var iext = (imax - imin) * 0.5f; var isiz = Math.Max(iext.X, iext.Y); @@ -1991,7 +2029,7 @@ namespace CodeWalker.World { var cind = ((icen.X > ncen.X) ? 1 : 0) + ((icen.Y > ncen.Y) ? 2 : 0); var c = Children[cind]; - if (c == null) + if (c is null) { c = new SpaceBoundsStoreNode(); c.Owner = Owner; @@ -2007,25 +2045,28 @@ namespace CodeWalker.World Children[i]?.TrySplit(threshold); } + if (Items is not null) + { + PooledListPool.Shared.Return(Items); + } + Items = newItems; } - public void GetItems(in Vector3 min, in Vector3 max, List items, bool[] layers = null) + public void GetItems(in Vector3 min, in Vector3 max, List items, bool[]? layers = null) { - if ((max.X >= BBMin.X) && (min.X <= BBMax.X) && (max.Y >= BBMin.Y) && (min.Y <= BBMax.Y)) + if (max.X >= BBMin.X && min.X <= BBMax.X && max.Y >= BBMin.Y && min.Y <= BBMax.Y) { if (Items != null) { - for (int i = 0; i < Items.Count; i++) + foreach(var item in Items.Span) { - var item = Items[i]; - - if ((layers != null) && (item.Layer < 3) && (!layers[item.Layer])) + if (layers is not null && item.Layer < 3 && !layers[item.Layer]) { continue; } - if ((max.X >= item.Min.X) && (min.X <= item.Max.X) && (max.Y >= item.Min.Y) && (min.Y <= item.Max.Y)) + if (max.X >= item.Min.X && min.X <= item.Max.X && max.Y >= item.Min.Y && min.Y <= item.Max.Y) { items.Add(item); } @@ -2040,6 +2081,7 @@ namespace CodeWalker.World } } } + public void GetItems(ref Ray ray, List items, bool[]? layers = null) { var box = new BoundingBox(BBMin, BBMax); @@ -2047,10 +2089,8 @@ namespace CodeWalker.World { if (Items is not null) { - for (int i = 0; i < Items.Count; i++) + foreach(var item in Items.Span) { - var item = Items[i]; - if (layers is not null && item.Layer < 3 && !layers[item.Layer]) { continue; @@ -2101,23 +2141,37 @@ namespace CodeWalker.World } } - public SpaceNodeGridCell GetCell(int id) + public SpaceNodeGridCell? GetCell(int id) { int x = id % CellCountX; int y = id / CellCountX; - if ((x >= 0) && (x < CellCountX) && (y >= 0) && (y < CellCountY)) + if (x >= 0 && x < CellCountX && y >= 0 && y < CellCountY) { return Cells[x, y]; } return null; } + public bool TryGetCell(int id, [MaybeNullWhen(false)] out SpaceNodeGridCell cell) + { + int x = id % CellCountX; + int y = id / CellCountX; + if (x >= 0 && x < CellCountX && y >= 0 && y < CellCountY) + { + cell = Cells[x, y]; + return true; + } + + cell = default; + return false; + } + public SpaceNodeGridCell? GetCellForPosition(in Vector3 position) { var x = (int)((position.X - CornerX) / CellSize); var y = (int)((position.Y - CornerY) / CellSize); - if ((x >= 0) && (x < CellCountX) && (y >= 0) && (y < CellCountY)) + if (x >= 0 && x < CellCountX && y >= 0 && y < CellCountY) { return Cells[x, y]; } @@ -2128,11 +2182,14 @@ namespace CodeWalker.World public YndNode? GetYndNode(ushort areaid, ushort nodeid) { - var cell = GetCell(areaid); - if (cell?.Ynd?.Nodes is null) + if (!TryGetCell(areaid, out var cell) || cell?.Ynd?.Nodes is null) + { return null; + } + if (nodeid >= cell.Ynd.Nodes.Length) - { return null; } + return null; + return cell.Ynd.Nodes[nodeid]; } @@ -2160,7 +2217,7 @@ namespace CodeWalker.World public int Y; public int ID; - public YndFile Ynd; + public YndFile? Ynd; public SpaceNodeGridCell(int x, int y) { diff --git a/CodeWalker.Core/World/TimecycleMods.cs b/CodeWalker.Core/World/TimecycleMods.cs index 230a67e..bc961b9 100644 --- a/CodeWalker.Core/World/TimecycleMods.cs +++ b/CodeWalker.Core/World/TimecycleMods.cs @@ -1,4 +1,5 @@ -using CodeWalker.GameFiles; +using CodeWalker.Core.Utils; +using CodeWalker.GameFiles; using System; using System.Collections.Generic; using System.ComponentModel; @@ -96,7 +97,7 @@ namespace CodeWalker.World JenkIndex.Ensure(namel); nameHash = JenkHash.GenHash(namel); - List vals = new List(); + var vals = PooledListPool.Shared.Get(); foreach (XmlNode valnode in node.ChildNodes) { if (!(valnode is XmlElement)) continue; @@ -108,7 +109,7 @@ namespace CodeWalker.World Dict[val.name] = val; } Values = vals.ToArray(); - + PooledListPool.Shared.Return(vals); } public override string ToString() diff --git a/CodeWalker.WinForms/FormUtils.cs b/CodeWalker.WinForms/FormUtils.cs index df4036f..8e3a3be 100644 --- a/CodeWalker.WinForms/FormUtils.cs +++ b/CodeWalker.WinForms/FormUtils.cs @@ -503,7 +503,7 @@ namespace CodeWalker action?.Invoke(args); } - public static SwitchToUiAwaitable SwitchToUi(this Form form) + public static SwitchToUiAwaitable SwitchToUiContext(this Form form) { return new SwitchToUiAwaitable(form); } diff --git a/CodeWalker.WinForms/Utils/TypeConverters.cs b/CodeWalker.WinForms/Utils/TypeConverters.cs new file mode 100644 index 0000000..a20f869 --- /dev/null +++ b/CodeWalker.WinForms/Utils/TypeConverters.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace CodeWalker.WinForms.Utils; + + +public class LinkedListPropertyDescripter : PropertyDescriptor +{ + private readonly LinkedListNode node; + + public LinkedListPropertyDescripter(LinkedListNode node) + : base(CSharpName(node.Value.GetType()), null) + { + this.node = node; + } + + private static string CSharpName(Type type) + { + var sb = new StringBuilder(); + var name = type.Name; + if (!type.IsGenericType) + return name; + sb.Append(name.Substring(0, name.IndexOf('`'))); + sb.Append("<"); + sb.Append(string.Join(", ", type.GetGenericArguments() + .Select(CSharpName))); + sb.Append(">"); + return sb.ToString(); + } + + public override object GetValue(object component) + { + return node.Value; + } + + public override bool IsReadOnly => true; + + public override string Name => node.Value.ToString(); + + public override Type PropertyType => node.Value.GetType(); + + public override Type ComponentType => node.List.GetType(); + + public override bool ShouldSerializeValue(object component) => false; + + public override bool CanResetValue(object component) => false; + + public override void ResetValue(object component) + { + + } + + public override void SetValue(object component, object value) + { + + } +} + + +public class LinkedListConverter : CollectionConverter +{ + public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true; + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + { + LinkedList list = value as LinkedList; + if (list == null || list.Count == 0) + return base.GetProperties(context, value, attributes); + + var items = new PropertyDescriptorCollection(null); + + foreach(var item in list) + { + var node = list.Find(item); + items.Add(new LinkedListPropertyDescripter(node)); + } + return items; + } +} + + +public class ExpandableCollectionPropertyDescriptor : PropertyDescriptor +{ + private IList collection; + private readonly int _index; + + public ExpandableCollectionPropertyDescriptor(IList coll, int idx) + : base(GetDisplayName(coll, idx), null) + { + collection = coll; + _index = idx; + } + + private static string GetDisplayName(IList list, int index) + { + return $"[{index}] " + CSharpName(list[index].GetType()); + } + + private static string CSharpName(Type type) + { + var sb = new StringBuilder(); + var name = type.Name; + if (!type.IsGenericType) + return name; + sb.Append(name.Substring(0, name.IndexOf('`'))); + sb.Append("<"); + sb.Append(string.Join(", ", type.GetGenericArguments() + .Select(CSharpName))); + sb.Append(">"); + return sb.ToString(); + } + + public override bool CanResetValue(object component) + { + return true; + } + + public override Type ComponentType + { + get { return this.collection.GetType(); } + } + + public override object GetValue(object component) + { + return collection[_index]; + } + + public override bool IsReadOnly + { + get { return false; } + } + + public override string Name + { + get { return _index.ToString(CultureInfo.InvariantCulture); } + } + + public override Type PropertyType + { + get { return collection[_index].GetType(); } + } + + public override void ResetValue(object component) + { + } + + public override bool ShouldSerializeValue(object component) + { + return true; + } + + public override void SetValue(object component, object value) + { + collection[_index] = value; + } +} + +public class ListConverter : CollectionConverter +{ + public override bool GetPropertiesSupported(ITypeDescriptorContext context) + { + return true; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) + { + IList list = value as IList; + if (list == null || list.Count == 0) + return base.GetProperties(context, value, attributes); + + var items = new PropertyDescriptorCollection(null); + for (int i = 0; i < list.Count; i++) + { + object item = list[i]; + items.Add(new ExpandableCollectionPropertyDescriptor(list, i)); + } + return items; + } +} \ No newline at end of file diff --git a/CodeWalker/ExploreForm.cs b/CodeWalker/ExploreForm.cs index fa91962..bb950be 100644 --- a/CodeWalker/ExploreForm.cs +++ b/CodeWalker/ExploreForm.cs @@ -292,7 +292,7 @@ namespace CodeWalker BoundsMaterialTypes.Init(FileCache); UpdateStatus?.Invoke("Loading scenario types..."); - Scenarios.EnsureScenarioTypes(FileCache); + await Scenarios.EnsureScenarioTypes(FileCache); UpdateStatus?.Invoke("File cache loaded."); } @@ -2548,7 +2548,8 @@ namespace CodeWalker collectDrawable(drawable); collectTextures(drawable); } - } else if (entry.IsExtension(".ytd")) + } + else if (entry.IsExtension(".ytd")) { UpdateStatus?.Invoke(entry.Path); YtdFile ytd = RpfFile.GetFile(entry, fileData); @@ -2605,7 +2606,7 @@ namespace CodeWalker foreach (var tp in texparams) { //MetaXml.WriteCustomItemArray(sb, tp.Value.Textures, 3, "Texture"); - MetaXml.OpenTag(sb, 3, string.Format(otstr, ((ShaderParamNames)tp.Key).ToString(), "Texture")); + MetaXml.OpenTag(sb, 3, $"Item name=\"{(ShaderParamNames)tp.Key}\""); foreach(var texture in tp.Value.Textures) { @@ -2618,15 +2619,15 @@ namespace CodeWalker { var svp = s.GetSortedList(vp.Value); var defval = svp.FirstOrDefault(); - MetaXml.SelfClosingTag(sb, 3, string.Format(otstr, ((ShaderParamNames)vp.Key).ToString(), "Vector") + " " + FloatUtil.GetVector4XmlString(defval)); + MetaXml.SelfClosingTag(sb, 3, $"Item name=\"{(ShaderParamNames)vp.Key}\" type=\"Vector\" {FloatUtil.GetVector4XmlString(in defval)}"); } foreach (var ap in arrparams) { var defval = ap.Value.FirstOrDefault(); - MetaXml.OpenTag(sb, 3, string.Format(otstr, ((ShaderParamNames)ap.Key).ToString(), "Array")); + MetaXml.OpenTag(sb, 3, $"Item name=\"{(ShaderParamNames)ap.Key}\" type=\"Array\""); foreach (var vec in defval) { - MetaXml.SelfClosingTag(sb, 4, "Value " + FloatUtil.GetVector4XmlString(vec)); + MetaXml.SelfClosingTag(sb, 4, $"Value {FloatUtil.GetVector4XmlString(in vec)}"); } MetaXml.CloseTag(sb, 3, "Item"); } @@ -5248,7 +5249,15 @@ namespace CodeWalker ImageIndex = 1; //FOLDER imageIndex var ic = fld.GetItemCount(); fileSize = ic; - fileSizeText = $"{ic} item{((ic != 1) ? "s" : "")}"; + if (ic != 1) + { + fileSizeText = $"{ic} items"; + } + else + { + fileSizeText = $"{ic} item"; + } + } } else diff --git a/CodeWalker/Forms/ModelForm.cs b/CodeWalker/Forms/ModelForm.cs index 159fd97..5e14fff 100644 --- a/CodeWalker/Forms/ModelForm.cs +++ b/CodeWalker/Forms/ModelForm.cs @@ -1147,7 +1147,7 @@ namespace CodeWalker.Forms int ih = (int)fh; int im = v - (ih * 60); if (ih == 24) ih = 0; - TimeOfDayLabel.Text = string.Format("{0:00}:{1:00}", ih, im); + TimeOfDayLabel.Text = $"{ih:00}:{im:00}"; } @@ -1432,7 +1432,7 @@ namespace CodeWalker.Forms var tgnode = tmnode.Nodes.Add(gname); tgnode.Tag = geom; - if ((geom.Shader != null) && (geom.Shader.ParametersList != null) && (geom.Shader.ParametersList.Hashes != null)) + if (geom.Shader?.ParametersList?.Hashes is not null) { var pl = geom.Shader.ParametersList; var h = pl.Hashes; @@ -1446,9 +1446,9 @@ namespace CodeWalker.Forms var tstr = tex.Name.Trim(); if (tex is Texture t) { - tstr = string.Format("{0} ({1}x{2}, embedded)", tex.Name, t.Width, t.Height); + tstr = $"{tex.Name} ({t.Width}x{t.Height}, embedded)"; } - var tnode = tgnode.Nodes.Add(hash.ToString().Trim() + ": " + tstr); + var tnode = tgnode.Nodes.Add($"{hash}: {tstr}"); tnode.Tag = tex; } } diff --git a/CodeWalker/PedsForm.cs b/CodeWalker/PedsForm.cs index 574a793..e5eafa8 100644 --- a/CodeWalker/PedsForm.cs +++ b/CodeWalker/PedsForm.cs @@ -1091,7 +1091,7 @@ namespace CodeWalker int ih = (int)fh; int im = v - (ih * 60); if (ih == 24) ih = 0; - TimeOfDayLabel.Text = string.Format("{0:00}:{1:00}", ih, im); + TimeOfDayLabel.Text = $"{ih:00}:{im:00}"; } diff --git a/CodeWalker/Project/Panels/EditAudioZonePanel.cs b/CodeWalker/Project/Panels/EditAudioZonePanel.cs index 01f5585..327fe5e 100644 --- a/CodeWalker/Project/Panels/EditAudioZonePanel.cs +++ b/CodeWalker/Project/Panels/EditAudioZonePanel.cs @@ -102,7 +102,7 @@ namespace CodeWalker.Project.Panels UnkVec1TextBox.Text = FloatUtil.GetVector4String(z.UnkVec1); UnkVec2TextBox.Text = FloatUtil.GetVector4String(z.UnkVec2); UnkVec3TextBox.Text = FloatUtil.GetVector2String(z.UnkVec3); - UnkBytesTextBox.Text = string.Format("{0}, {1}, {2}", z.Unk14, z.Unk15, z.Unk16); + UnkBytesTextBox.Text = $"{z.Unk14}, {z.Unk15}, {z.Unk16}"; Flags0TextBox.Text = z.Flags0.Hex; Flags1TextBox.Text = z.Flags1.Hex; Unk13TextBox.Text = z.Unk13.Hex; @@ -110,7 +110,7 @@ namespace CodeWalker.Project.Panels SceneTextBox.Text = z.Scene.ToString(); StringBuilder sb = new StringBuilder(); - if (z.Rules != null) + if (z.Rules is not null) { foreach (var hash in z.Rules) { diff --git a/CodeWalker/Project/Panels/EditYndNodePanel.cs b/CodeWalker/Project/Panels/EditYndNodePanel.cs index 8879545..d536063 100644 --- a/CodeWalker/Project/Panels/EditYndNodePanel.cs +++ b/CodeWalker/Project/Panels/EditYndNodePanel.cs @@ -78,7 +78,7 @@ namespace CodeWalker.Project.Panels else { populatingui = true; - var n = CurrentPathNode.RawData; + var n = CurrentPathNode._RawData; //YndNodePanel.Enabled = true; PathNodeDeleteButton.Enabled = ProjectForm.YndExistsInProject(CurrentYndFile); PathNodeAddToProjectButton.Enabled = !PathNodeDeleteButton.Enabled; diff --git a/CodeWalker/Project/Panels/GenerateNavMeshPanel.cs b/CodeWalker/Project/Panels/GenerateNavMeshPanel.cs index 2a8fe4f..a95fa94 100644 --- a/CodeWalker/Project/Panels/GenerateNavMeshPanel.cs +++ b/CodeWalker/Project/Panels/GenerateNavMeshPanel.cs @@ -812,11 +812,7 @@ namespace CodeWalker.Project.Panels }); } - - - var statf = "{0} hit tests, {1} hits, {2} new polys"; - var stats = string.Format(statf, hitTestCount, hitCount, newCount); - UpdateStatus("Process complete. " + stats); + UpdateStatus($"Process complete. {hitTestCount} hit tests, {hitCount} hits, {newCount} new polys"); GenerateComplete(); }); } diff --git a/CodeWalker/Project/Panels/ProjectExplorerPanel.cs b/CodeWalker/Project/Panels/ProjectExplorerPanel.cs index 2adf5d6..a87770f 100644 --- a/CodeWalker/Project/Panels/ProjectExplorerPanel.cs +++ b/CodeWalker/Project/Panels/ProjectExplorerPanel.cs @@ -615,16 +615,16 @@ namespace CodeWalker.Project.Panels - if ((ynd.Nodes != null) && (ynd.Nodes.Length > 0)) + if (ynd.Nodes is not null && ynd.Nodes.Length > 0) { - var nodesnode = node.Nodes.Add("Nodes (" + ynd.Nodes.Length.ToString() + ")"); + var nodesnode = node.Nodes.Add($"Nodes ({ynd.Nodes.Length})"); nodesnode.Name = "Nodes"; nodesnode.Tag = ynd; var nodes = ynd.Nodes; for (int i = 0; i < nodes.Length; i++) { var ynode = nodes[i]; - var nnode = ynode.RawData; + var nnode = ynode._RawData; var tnode = nodesnode.Nodes.Add(nnode.ToString()); tnode.Tag = ynode; } @@ -2315,7 +2315,7 @@ namespace CodeWalker.Project.Panels var tn = FindYmapTreeNode(ymap); if (tn != null) { - await this.SwitchToUi(); + await this.SwitchToUiContext(); tn.Text = ymap.RpfFileEntry?.Name ?? ymap.Name; } } @@ -2357,7 +2357,7 @@ namespace CodeWalker.Project.Panels var tn = FindYnvTreeNode(ynv); if (tn != null) { - await this.SwitchToUi(); + await this.SwitchToUiContext(); tn.Text = ynv.RpfFileEntry?.Name ?? ynv.Name; } } @@ -2366,7 +2366,7 @@ namespace CodeWalker.Project.Panels var tn = FindTrainTrackTreeNode(track); if (tn != null) { - await this.SwitchToUi(); + await this.SwitchToUiContext(); tn.Text = track.RpfFileEntry?.Name ?? track.Name; } } @@ -2375,7 +2375,7 @@ namespace CodeWalker.Project.Panels var tn = FindScenarioTreeNode(scenarios); if (tn != null) { - await this.SwitchToUi(); + await this.SwitchToUiContext(); tn.Text = scenarios.RpfFileEntry?.Name ?? scenarios.Name; } } @@ -2384,7 +2384,7 @@ namespace CodeWalker.Project.Panels var tn = FindAudioRelTreeNode(rel); if (tn != null) { - await this.SwitchToUi(); + await this.SwitchToUiContext(); tn.Text = rel.RpfFileEntry?.Name ?? rel.Name; } } @@ -2393,7 +2393,7 @@ namespace CodeWalker.Project.Panels var tn = FindArchetypeTreeNode(archetype); if (tn != null) { - await this.SwitchToUi(); + await this.SwitchToUiContext(); tn.Text = archetype._BaseArchetypeDef.ToString(); } } diff --git a/CodeWalker/Project/ProjectForm.cs b/CodeWalker/Project/ProjectForm.cs index 95e8a47..a4a5845 100644 --- a/CodeWalker/Project/ProjectForm.cs +++ b/CodeWalker/Project/ProjectForm.cs @@ -34,7 +34,7 @@ namespace CodeWalker.Project public RpfManager RpfMan { get; private set; } - public bool IsProjectLoaded => CurrentProjectFile != null; + public bool IsProjectLoaded => CurrentProjectFile is not null; public ProjectFile? CurrentProjectFile; private MapSelection[]? CurrentMulti; @@ -99,17 +99,17 @@ namespace CodeWalker.Project public readonly object ProjectSyncRoot = new object(); - private Dictionary visibleybns = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary visibleynds = new Dictionary(); - private Dictionary visibleynvs = new Dictionary(); - private Dictionary visibletrains = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary visiblescenarios = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary visiblemloentities = new Dictionary(); - private Dictionary visibleaudiofiles = new Dictionary(); + private readonly Dictionary visibleybns = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary visibleynds = new Dictionary(); + private readonly Dictionary visibleynvs = new Dictionary(); + private readonly Dictionary visibletrains = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary visiblescenarios = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary visiblemloentities = new Dictionary(); + private readonly Dictionary visibleaudiofiles = new Dictionary(); - private Dictionary projectybns = new Dictionary();//used for handling interior ybns + private readonly Dictionary projectybns = new Dictionary();//used for handling interior ybns - private List interiorslist = new List(); //used for handling interiors ybns + private readonly List interiorslist = new List(); //used for handling interiors ybns private bool ShowProjectItemInProcess = false; @@ -120,7 +120,7 @@ namespace CodeWalker.Project InitializeComponent(); - SetTheme(Settings.Default.ProjectWindowTheme, false); + _ = SetTheme(Settings.Default.ProjectWindowTheme, false); ShowDefaultPanels(); if (!GameFileCache.IsInited) @@ -264,15 +264,23 @@ namespace CodeWalker.Project return null; } - public void ShowDefaultPanels() + public async void ShowDefaultPanels() { - ShowProjectExplorer(); - ShowWelcomePanel(); + try + { + await ShowProjectExplorer(); + await ShowWelcomePanel(); + } + catch(Exception ex) + { + Console.WriteLine(ex); + } } - public void ShowProjectExplorer() + public async ValueTask ShowProjectExplorer() { - if ((ProjectExplorer == null) || (ProjectExplorer.IsDisposed) || (ProjectExplorer.Disposing)) + await this.SwitchToUiContext(); + if (ProjectExplorer is null || ProjectExplorer.IsDisposed || ProjectExplorer.Disposing) { ProjectExplorer = new ProjectExplorerPanel(this); ProjectExplorer.OnItemSelected += ProjectExplorer_OnItemSelected; @@ -285,8 +293,9 @@ namespace CodeWalker.Project ProjectExplorer.Show(); } } - public void ShowWelcomePanel() + public async ValueTask ShowWelcomePanel() { + await this.SwitchToUiContext(); ShowPreviewPanel(() => new WelcomePanel()); } public void ShowPreviewPanel(Func createFunc, Action? updateAction = null) where T : ProjectPanel @@ -7498,7 +7507,7 @@ namespace CodeWalker.Project var curybn = bounds?.GetRootYbn(); var eray = mray; - if (hidegtavmap && (curybn != null)) + if (hidegtavmap && (curybn is not null)) { curHit.Clear(); } @@ -7506,7 +7515,7 @@ namespace CodeWalker.Project lock (ProjectSyncRoot) { - if (renderitems && (CurrentProjectFile != null)) + if (renderitems && CurrentProjectFile is not null) { for (int i = 0; i < CurrentProjectFile.YbnFiles.Count; i++) { @@ -8707,8 +8716,6 @@ namespace CodeWalker.Project await ymap.LoadAsync(data); ymap.InitYmapEntityArchetypes(GameFileCache); //this needs to be done after calling YmapFile.Load() - - GameFileCache?.AddProjectFile(ymap); } private async Task LoadYtypFromFileAsync(YtypFile ytyp, string filename) { diff --git a/CodeWalker/Project/UndoStep.cs b/CodeWalker/Project/UndoStep.cs index 435effb..0482630 100644 --- a/CodeWalker/Project/UndoStep.cs +++ b/CodeWalker/Project/UndoStep.cs @@ -884,7 +884,8 @@ namespace CodeWalker.Project } - if (Bounds != sel.CollisionBounds) wf.SelectObject(Bounds); + if (Bounds != sel.CollisionBounds) + wf.SelectObject(Bounds); wf.SetWidgetPosition(p); UpdateGraphics(wf); diff --git a/CodeWalker/Properties/PublishProfiles/ClickOnceProfile.pubxml b/CodeWalker/Properties/PublishProfiles/ClickOnceProfile.pubxml index 4e719da..0b78087 100644 --- a/CodeWalker/Properties/PublishProfiles/ClickOnceProfile.pubxml +++ b/CodeWalker/Properties/PublishProfiles/ClickOnceProfile.pubxml @@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - 1 + 2 1.0.0.* True Release diff --git a/CodeWalker/Rendering/Renderable.cs b/CodeWalker/Rendering/Renderable.cs index 1846dd8..7b40b4e 100644 --- a/CodeWalker/Rendering/Renderable.cs +++ b/CodeWalker/Rendering/Renderable.cs @@ -13,6 +13,7 @@ using SharpDX; using System.Threading; using System.Diagnostics; using Collections.Pooled; +using System.Diagnostics.CodeAnalysis; namespace CodeWalker.Rendering { @@ -76,11 +77,11 @@ namespace CodeWalker.Rendering public YtdFile[]? HDtxds; public bool AllTexturesLoaded = false; - public RenderableModel[] HDModels = Array.Empty(); + public RenderableModel[] HDModels = []; public RenderableModel[]? MedModels; public RenderableModel[]? LowModels; public RenderableModel[]? VlowModels; - public RenderableModel[] AllModels = Array.Empty(); + public RenderableModel[] AllModels = []; public float LodDistanceHigh; public float LodDistanceMed; @@ -96,9 +97,9 @@ namespace CodeWalker.Rendering public bool HasAnims = false; public double CurrentAnimTime = 0; - public YcdFile ClipDict; - public ClipMapEntry ClipMapEntry; - public Expression Expression; + public YcdFile? ClipDict; + public ClipMapEntry? ClipMapEntry; + public Expression? Expression; public Dictionary? ModelBoneLinks; public bool EnableRootMotion = false; //used to toggle whether or not to include root motion when playing animations @@ -370,19 +371,19 @@ namespace CodeWalker.Rendering { if (distance > LodDistanceVLow) { - return Array.Empty(); + return []; } else if (distance > LodDistanceLow) { - return VlowModels ?? Array.Empty(); + return VlowModels ?? []; } else if (distance > LodDistanceMed) { - return LowModels ?? Array.Empty(); + return LowModels ?? []; } else if (distance > LodDistanceHigh) { - return MedModels ?? Array.Empty(); + return MedModels ?? []; } else { @@ -434,7 +435,7 @@ namespace CodeWalker.Rendering LoadQueued = false; } - public override string ToString() + public override string? ToString() { return Key.ToString(); } @@ -442,48 +443,46 @@ namespace CodeWalker.Rendering public void ResetBoneTransforms() { - if (Skeleton == null) return; + if (Skeleton is null) + return; Skeleton.ResetBoneTransforms(); UpdateBoneTransforms(); } private void UpdateBoneTransforms() { - if (Skeleton?.Bones?.Items == null) return; + if (Skeleton?.Bones?.Items is null) + return; Skeleton.UpdateBoneTransforms(); var bones = Skeleton.Bones?.Items; var bonetransforms = Skeleton.BoneTransforms; + if (AllModels is null || AllModels.Length == 0 || bones is null) + return; - var drawbl = Key; - if (AllModels == null) return; - for (int i = 0; i < AllModels.Length; i++) + foreach(var model in AllModels) { - var model = AllModels[i]; - if (model?.Geometries == null) continue; - for (int g = 0; g < model.Geometries.Length; g++) + if (model?.Geometries is null) + continue; + + foreach(var geom in model.Geometries) { - var geom = model.Geometries[g]; var boneids = geom?.DrawableGeom?.BoneIds; - if (boneids == null) continue; + if (boneids is null) + continue; + if (boneids.Length != bones.Length) { var idc = boneids.Length; - if (geom.BoneTransforms == null) - { - geom.BoneTransforms = new Matrix3_s[idc]; - } + geom.BoneTransforms ??= new Matrix3_s[idc]; + for (int b = 0; b < idc; b++) { var id = boneids[b]; if (id < bonetransforms.Length) { geom.BoneTransforms[b] = bonetransforms[id]; - if (id != b) - { } } - else - { } } } } @@ -500,12 +499,13 @@ namespace CodeWalker.Rendering realTime = ClipMapEntry.PlayTime; } - if (CurrentAnimTime == realTime) return;//already updated this! + if (CurrentAnimTime == realTime) + return;//already updated this! CurrentAnimTime = realTime; EnableRootMotion = ClipMapEntry?.EnableRootMotion ?? false; - if (ClipMapEntry != null) + if (ClipMapEntry is not null) { UpdateAnim(ClipMapEntry); //animate skeleton/models } @@ -551,15 +551,7 @@ namespace CodeWalker.Rendering } private void UpdateAnim(Animation anim, float t) { - if (anim is null) - { - return; - } - if (anim.BoneIds?.data_items is null) - { - return; - } - if (anim.Sequences?.data_items is null) + if (anim?.BoneIds?.data_items is null || anim.Sequences?.data_items is null) { return; } @@ -567,8 +559,6 @@ namespace CodeWalker.Rendering bool interpolate = true; //how to know? eg. cs4_14_hickbar_anim shouldn't var frame = anim.GetFramePosition(t); - - var dwbl = this.Key; var skel = Skeleton; var bones = skel?.BonesSorted;//.Bones?.Items;// if (bones is null) @@ -585,19 +575,15 @@ namespace CodeWalker.Rendering var boneid = boneiditem.BoneId; var track = boneiditem.Track; - if (Expression?.BoneTracksDict != null) + if (Expression?.BoneTracksDict is not null) { - var exprbt = new ExpressionTrack() { BoneId = boneid, Track = track, Flags = boneiditem.Unk0 }; - var exprbtmap = exprbt; - - if ((track == 24) || (track == 25) || (track == 26)) + if (track == 24 || track == 25 || track == 26) { - if (Expression.BoneTracksDict.TryGetValue(exprbt, out exprbtmap)) + var exprbt = new ExpressionTrack() { BoneId = boneid, Track = track, Flags = boneiditem.Unk0 }; + if (Expression.BoneTracksDict.TryGetValue(exprbt, out var exprbtmap)) { boneid = exprbtmap.BoneId; } - else - { } } } @@ -676,21 +662,20 @@ namespace CodeWalker.Rendering } } - for (int i = 0; i < bones.Length; i++) + foreach(var bone in bones) { - var bone = bones[i]; var tag = bone.Tag; - switch (bone.Tag) + tag = tag switch { - case 23639: tag = 58271; break; //RB_L_ThighRoll: SKEL_L_Thigh - case 6442: tag = 51826; break; //RB_R_ThighRoll: SKEL_R_Thigh - //case 61007: tag = 61163; break; //RB_L_ForeArmRoll: SKEL_L_Forearm //NOT GOOD - //case 5232: tag = 45509; break; //RB_L_ArmRoll: SKEL_L_UpperArm - } - if ((tag != bone.Tag) && (tag != bone.Parent?.Tag)) + 23639 => (ushort)58271, + 6442 => (ushort)51826, + //61007 => 61163; //RB_L_ForeArmRoll: SKEL_L_Forearm //NOT GOOD + //5232 => 45509; //RB_L_ArmRoll: SKEL_L_UpperArm + _ => tag, + }; + if (tag != bone.Tag && tag != bone.Parent?.Tag) { - var obone = bone; - if (skel.BonesMap.TryGetValue(tag, out obone)) + if (skel.BonesMap.TryGetValue(tag, out var obone)) { bone.AnimRotation = obone.AnimRotation; } @@ -699,9 +684,8 @@ namespace CodeWalker.Rendering if (ModelBoneLinks is not null) { - for (int i = 0; i < bones.Length; i++) + foreach (var bone in bones) { - var bone = bones[i]; bone.UpdateAnimTransform(); bone.UpdateSkinTransform(); @@ -713,7 +697,6 @@ namespace CodeWalker.Rendering continue; bmodel.Transform = bone.AnimTransform; - } } } @@ -871,11 +854,11 @@ namespace CodeWalker.Rendering public uint VertexDataSize { get; set; } public uint IndexDataSize { get; set; } public uint TotalDataSize { get; set; } - public TextureBase[] Textures; - public Texture[] TexturesHD; - public RenderableTexture?[]? RenderableTextures; - public RenderableTexture?[]? RenderableTexturesHD; - public ShaderParamNames[] TextureParamHashes; + public TextureBase[]? Textures; + public Texture?[]? TexturesHD { get; set; } + public RenderableTexture?[]? RenderableTextures { get; set; } + public RenderableTexture?[]? RenderableTexturesHD { get; set; } + public ShaderParamNames[]? TextureParamHashes { get; set; } public PrimitiveTopology Topology { get; set; } public bool IsFragment = false; public bool IsEmissive { get; set; } = false; @@ -904,11 +887,14 @@ namespace CodeWalker.Rendering public float HeightOpacity { get; set; } = 0; //for terrainfoam public bool HDTextureEnable = true; public bool globalAnimUVEnable = false; - public ClipMapEntry ClipMapEntryUV = null; + public ClipMapEntry? ClipMapEntryUV = null; public bool isHair = false; public bool disableRendering = false; - public Matrix3_s[] BoneTransforms = null; + public Matrix3_s[]? BoneTransforms = null; + + [MemberNotNullWhen(true, nameof(Textures), nameof(RenderableTextures), nameof(RenderableTexturesHD), nameof(TexturesHD), nameof(TextureParamHashes))] + public bool HasTextures => Textures is not null; public static ShaderParamNames[] GetTextureSamplerList() { @@ -1128,8 +1114,6 @@ namespace CodeWalker.Rendering RenderableTexturesHD = new RenderableTexture[texs.Count]; //these will get populated at render time. } } - - } public void Load(Device device) diff --git a/CodeWalker/Rendering/Renderer.cs b/CodeWalker/Rendering/Renderer.cs index 9c91f6d..355d78c 100644 --- a/CodeWalker/Rendering/Renderer.cs +++ b/CodeWalker/Rendering/Renderer.cs @@ -1938,7 +1938,7 @@ namespace CodeWalker.Rendering { var rndbl = GetArchetypeRenderable(ent.Archetype); ent.LodManagerRenderable = rndbl; - if (rndbl != null) + if (rndbl is not null) { RenderEntities.Add(ent); } @@ -2427,22 +2427,21 @@ namespace CodeWalker.Rendering - private Renderable GetArchetypeRenderable(Archetype arch) + private Renderable? GetArchetypeRenderable(Archetype arch) { - if (arch == null) + if (arch is null) return null; - Renderable rndbl = null; - if (!ArchetypeRenderables.TryGetValue(arch, out rndbl)) + if (!ArchetypeRenderables.TryGetValue(arch, out Renderable? rndbl)) { var drawable = gameFileCache.TryGetDrawable(arch); rndbl = TryGetRenderable(arch, drawable); - if (rndbl != null && rndbl.IsLoaded) + if (rndbl is not null && rndbl.IsLoaded) { ArchetypeRenderables[arch] = rndbl; } } - if ((rndbl != null) && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload)) + if (rndbl is not null && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload)) { return rndbl; } @@ -2454,9 +2453,7 @@ namespace CodeWalker.Rendering public void RenderYmap(YmapFile ymap) { - if (ymap is null) - return; - if (!ymap.Loaded) + if (ymap is null || !ymap.Loaded) return; if (ymap.AllEntities.Length > 0 && ymap.RootEntities.Length > 0) @@ -2493,15 +2490,15 @@ namespace CodeWalker.Rendering } } - if (rendercars && ymap.CarGenerators != null && ymap.CarGenerators.Length > 0) + if (rendercars && ymap.CarGenerators is not null && ymap.CarGenerators.Length > 0) { RenderYmapCarGenerators(ymap); } - if (rendergrass && ymap.GrassInstanceBatches != null && ymap.GrassInstanceBatches.Length > 0) + if (rendergrass && ymap.GrassInstanceBatches is not null && ymap.GrassInstanceBatches.Length > 0) { RenderYmapGrass(ymap); } - if (renderdistlodlights && timecycle.IsNightTime && ymap.DistantLODLights != null) + if (renderdistlodlights && timecycle.IsNightTime && ymap.DistantLODLights is not null) { RenderYmapDistantLODLights(ymap); } @@ -2715,14 +2712,13 @@ namespace CodeWalker.Rendering var maxdist = 200 * renderworldDetailDistMult; var maxdist2 = maxdist * maxdist; - for (int i = 0; i < ymap.CarGenerators.Length; i++) + foreach(var cg in ymap.CarGenerators) { - var cg = ymap.CarGenerators[i]; - var bscent = cg.Position - camera.Position; float bsrad = cg._CCarGen.perpendicularLength; if (bscent.LengthSquared() > maxdist2) continue; //don't render distant cars.. + if (!camera.ViewFrustum.ContainsSphereNoClipNoOpt(ref bscent, bsrad)) { continue; //frustum cull cars... @@ -2737,297 +2733,296 @@ namespace CodeWalker.Rendering } + private void RenderWheels(Archetype? arch, YmapEntityDef ent, FragType fragment, uint txdhash = 0, ClipMapEntry? animClip = null) + { + if (fragment.PhysicsLODGroup?.PhysicsLOD1?.Children?.data_items is null) + return; + + var pl1 = fragment.PhysicsLODGroup.PhysicsLOD1; + //var groupnames = pl1?.GroupNames?.data_items; + //var groups = pl1.Groups?.data_items; + + FragDrawable? wheel_f = null; + FragDrawable? wheel_r = null; + foreach (var pch in pl1.Children.data_items) + { + //var groupname = pch.GroupNameHash; + //if ((pl1.Groups?.data_items != null) && (i < pl1.Groups.data_items.Length)) + //{ + // //var group = pl1.Groups.data_items[i]; + //} + + if (pch.Drawable1 is not null && pch.Drawable1.AllModels.Length != 0) + { + + switch (pch.BoneTag) + { + case 27922: //wheel_lf + case 26418: //wheel_rf + wheel_f = pch.Drawable1; + break; + case 29921: //wheel_lm1 + case 29922: //wheel_lm2 + case 29923: //wheel_lm3 + case 27902: //wheel_lr + case 5857: //wheel_rm1 + case 5858: //wheel_rm2 + case 5859: //wheel_rm3 + case 26398: //wheel_rr + wheel_r = pch.Drawable1; + break; + default: + + RenderDrawable(pch.Drawable1, arch, ent, txdhash, null, null, animClip); + + break; + } + + } + else + { } + if (pch.Drawable2 is not null && pch.Drawable2.AllModels.Length != 0) + { + RenderDrawable(pch.Drawable2, arch, ent, txdhash, null, null, animClip); + } + else + { } + } + + if (wheel_f is not null || wheel_r != null) + { + for (int i = 0; i < pl1.Children.data_items.Length; i++) + { + var pch = pl1.Children.data_items[i]; + FragDrawable dwbl = pch.Drawable1; + FragDrawable? dwblcopy = null; + switch (pch.BoneTag) + { + case 27922: //wheel_lf + case 26418: //wheel_rf + dwblcopy = wheel_f ?? wheel_r; + break; + case 29921: //wheel_lm1 + case 29922: //wheel_lm2 + case 29923: //wheel_lm3 + case 27902: //wheel_lr + case 5857: //wheel_rm1 + case 5858: //wheel_rm2 + case 5859: //wheel_rm3 + case 26398: //wheel_rr + dwblcopy = wheel_r ?? wheel_f; + break; + default: + break; + } + //switch (pch.GroupNameHash) + //{ + // case 3311608449: //wheel_lf + // case 1705452237: //wheel_lm1 + // case 1415282742: //wheel_lm2 + // case 3392433122: //wheel_lm3 + // case 133671269: //wheel_rf + // case 2908525601: //wheel_rm1 + // case 2835549038: //wheel_rm2 + // case 4148013026: //wheel_rm3 + // dwblcopy = wheel_f != null ? wheel_f : wheel_r; + // break; + // case 1695736278: //wheel_lr + // case 1670111368: //wheel_rr + // dwblcopy = wheel_r != null ? wheel_r : wheel_f; + // break; + // default: + // break; + //} + + if (dwblcopy is not null) + { + if (dwbl is not null) + { + if (dwbl != dwblcopy && dwbl.AllModels.Length == 0) + { + dwbl.Owner = dwblcopy; + dwbl.AllModels = dwblcopy.AllModels; //hopefully this is all that's need to render, otherwise drawable is actually getting edited! + //dwbl.DrawableModelsHigh = dwblcopy.DrawableModelsHigh; + //dwbl.DrawableModelsMedium = dwblcopy.DrawableModelsMedium; + //dwbl.DrawableModelsLow = dwblcopy.DrawableModelsLow; + //dwbl.DrawableModelsVeryLow = dwblcopy.DrawableModelsVeryLow; + //dwbl.VertexDecls = dwblcopy.VertexDecls; + } + + RenderDrawable(dwbl, arch, ent, txdhash /*, null, null, animClip*/); + + } + else + { } + } + else + { } + } + } + } - public bool RenderFragment(Archetype arch, YmapEntityDef ent, FragType f, uint txdhash = 0, ClipMapEntry animClip = null) + private static readonly uint ColourBlue = (uint)Color.Blue.ToRgba(); + private static readonly uint ColourRed = (uint)Color.Red.ToRgba(); + private void RenderFragmentWindows(Archetype? arch, YmapEntityDef ent, FragType fragment, uint txdhash = 0, ClipMapEntry? animClip = null) + { + if (!renderfragwindows) + return; + + var eori = Quaternion.Identity; + var epos = Vector3.Zero; + if (ent is not null) + { + eori = ent.Orientation; + epos = ent.Position; + } + + if (fragment.GlassWindows?.data_items is not null) + { + foreach(var gw in fragment.GlassWindows.data_items) + { + var projt = gw.ProjectionRow1;//row0? or row3? maybe investigate more + var proju = gw.ProjectionRow2;//row1 of XYZ>UV projection + var projv = gw.ProjectionRow3;//row2 of XYZ>UV projection + //var unk01 = new Vector2(gw.UnkFloat13, gw.UnkFloat14);//offset? + //var unk02 = new Vector2(gw.UnkFloat15, gw.UnkFloat16);//scale? sum of this and above often gives integers eg 1, 6 + //var thick = gw.Thickness; //thickness of the glass + //var unkuv = new Vector2(gw.UnkFloat18, gw.UnkFloat19); //another scale in UV space..? + //var tangt = gw.Tangent;//direction of surface tangent + //var bones = f.Drawable?.Skeleton?.Bones?.Items; //todo: use bones instead? + var grp = gw.Group; + var grplod = gw.GroupLOD; + var xforms = grplod?.FragTransforms?.Matrices; + var xoffs = Vector3.Zero; + if (grp is not null && xforms is not null && grp.ChildIndex < xforms.Length && grplod is not null) + { + var xform = xforms[grp.ChildIndex]; + xoffs = xform.TranslationVector + grplod.PositionOffset; + } + var m = new Matrix( + projt.X, projt.Y, projt.Z, 0, + proju.X, proju.Y, proju.Z, 0, + projv.X, projv.Y, projv.Z, 0, + xoffs.X, xoffs.Y, xoffs.Z, 1 + ); + //m.Row1 = new Vector4(projt, 0); + //m.Row2 = new Vector4(proju, 0); + //m.Row3 = new Vector4(projv, 0); + //m.Row4 = new Vector4(xoffs, 1); + + var v0 = m.Multiply(new Vector3(1, 0, 0)); + var v1 = m.Multiply(new Vector3(1, 0, 1)); + var v2 = m.Multiply(new Vector3(1, 1, 1)); + var v3 = m.Multiply(new Vector3(1, 1, 0)); + var c0 = eori.Multiply(in v0) + epos; + var c1 = eori.Multiply(in v1) + epos; + var c2 = eori.Multiply(in v2) + epos; + var c3 = eori.Multiply(in v3) + epos; + RenderSelectionLine(in c0, in c1, ColourBlue); + RenderSelectionLine(in c1, in c2, ColourBlue); + RenderSelectionLine(in c2, in c3, ColourBlue); + RenderSelectionLine(in c3, in c0, ColourBlue); + //RenderSelectionLine(c0, c0 + tangt, colred); + } + } + if (fragment.VehicleGlassWindows?.Windows is not null) + { + foreach(var vgw in fragment.VehicleGlassWindows.Windows) + { + //var grp = vgw.Group; + //var grplod = vgw.GroupLOD; + var m = vgw.Projection; + m.M44 = 1.0f; + m.Transpose(); + m.Invert();//ouch + var min = (new Vector3(0, 0, 0)); + var max = (new Vector3(vgw.ShatterMapWidth, vgw.ItemDataCount, 1)); + var v0 = m.MultiplyW(new Vector3(min.X, min.Y, 0)); + var v1 = m.MultiplyW(new Vector3(min.X, max.Y, 0)); + var v2 = m.MultiplyW(new Vector3(max.X, max.Y, 0)); + var v3 = m.MultiplyW(new Vector3(max.X, min.Y, 0)); + var c0 = eori.Multiply(in v0) + epos; + var c1 = eori.Multiply(in v1) + epos; + var c2 = eori.Multiply(in v2) + epos; + var c3 = eori.Multiply(in v3) + epos; + RenderSelectionLine(in c0, in c1, ColourBlue); + RenderSelectionLine(in c1, in c2, ColourBlue); + RenderSelectionLine(in c2, in c3, ColourBlue); + RenderSelectionLine(in c3, in c0, ColourBlue); + if (vgw.ShatterMap != null) + { + var width = vgw.ShatterMapWidth; + var height = vgw.ShatterMap.Length; + for (int y = 0; y < height; y++) + { + var smr = vgw.ShatterMap[y]; + for (int x = 0; x < width; x++) + { + var v = smr.GetValue(x); + if (v < 0 || v > 255) + continue; + var col = (uint)(new Color(v, v, v, 127).ToRgba()); + v0 = m.MultiplyW(new Vector3(x, y, 0)); + v1 = m.MultiplyW(new Vector3(x, y + 1, 0)); + v2 = m.MultiplyW(new Vector3(x + 1, y + 1, 0)); + v3 = m.MultiplyW(new Vector3(x + 1, y, 0)); + c0 = eori.Multiply(v0) + epos; + c1 = eori.Multiply(v1) + epos; + c2 = eori.Multiply(v2) + epos; + c3 = eori.Multiply(v3) + epos; + RenderSelectionQuad(c0, c1, c2, c3, col);//extra ouch + } + } + } + + } + } + } + + public bool RenderFragment(Archetype? arch, YmapEntityDef ent, FragType fragment, uint txdhash = 0, ClipMapEntry? animClip = null) { - RenderDrawable(f.Drawable, arch, ent, txdhash, null, null, animClip); + RenderDrawable(fragment.Drawable, arch, ent, txdhash, null, null, animClip); - if (f.DrawableCloth != null) //cloth + if (fragment.DrawableCloth is not null) //cloth { - RenderDrawable(f.DrawableCloth, arch, ent, txdhash, null, null, animClip); + RenderDrawable(fragment.DrawableCloth, arch, ent, txdhash, null, null, animClip); } //vehicle wheels... - if ((f.PhysicsLODGroup != null) && (f.PhysicsLODGroup.PhysicsLOD1 != null)) + RenderWheels(arch, ent, fragment, txdhash, animClip); + + bool isselected = SelectionFlagsTestAll || (fragment.Drawable == SelectedDrawable); + if (isselected && fragment.DrawableArray?.data_items is not null) { - var pl1 = f.PhysicsLODGroup.PhysicsLOD1; - //var groupnames = pl1?.GroupNames?.data_items; - var groups = pl1?.Groups?.data_items; - - FragDrawable wheel_f = null; - FragDrawable wheel_r = null; - - if (pl1.Children?.data_items != null) + foreach(var draw in fragment.DrawableArray.data_items) { - for (int i = 0; i < pl1.Children.data_items.Length; i++) - { - var pch = pl1.Children.data_items[i]; - - //var groupname = pch.GroupNameHash; - //if ((pl1.Groups?.data_items != null) && (i < pl1.Groups.data_items.Length)) - //{ - // //var group = pl1.Groups.data_items[i]; - //} - - if ((pch.Drawable1 != null) && (pch.Drawable1.AllModels.Length != 0)) - { - - switch (pch.BoneTag) - { - case 27922: //wheel_lf - case 26418: //wheel_rf - wheel_f = pch.Drawable1; - break; - case 29921: //wheel_lm1 - case 29922: //wheel_lm2 - case 29923: //wheel_lm3 - case 27902: //wheel_lr - case 5857: //wheel_rm1 - case 5858: //wheel_rm2 - case 5859: //wheel_rm3 - case 26398: //wheel_rr - wheel_r = pch.Drawable1; - break; - default: - - RenderDrawable(pch.Drawable1, arch, ent, txdhash, null, null, animClip); - - break; - } - - } - else - { } - if ((pch.Drawable2 != null) && (pch.Drawable2.AllModels.Length != 0)) - { - RenderDrawable(pch.Drawable2, arch, ent, txdhash, null, null, animClip); - } - else - { } - } - - if ((wheel_f != null) || (wheel_r != null)) - { - for (int i = 0; i < pl1.Children.data_items.Length; i++) - { - var pch = pl1.Children.data_items[i]; - FragDrawable dwbl = pch.Drawable1; - FragDrawable dwblcopy = null; - switch (pch.BoneTag) - { - case 27922: //wheel_lf - case 26418: //wheel_rf - dwblcopy = wheel_f != null ? wheel_f : wheel_r; - break; - case 29921: //wheel_lm1 - case 29922: //wheel_lm2 - case 29923: //wheel_lm3 - case 27902: //wheel_lr - case 5857: //wheel_rm1 - case 5858: //wheel_rm2 - case 5859: //wheel_rm3 - case 26398: //wheel_rr - dwblcopy = wheel_r != null ? wheel_r : wheel_f; - break; - default: - break; - } - //switch (pch.GroupNameHash) - //{ - // case 3311608449: //wheel_lf - // case 1705452237: //wheel_lm1 - // case 1415282742: //wheel_lm2 - // case 3392433122: //wheel_lm3 - // case 133671269: //wheel_rf - // case 2908525601: //wheel_rm1 - // case 2835549038: //wheel_rm2 - // case 4148013026: //wheel_rm3 - // dwblcopy = wheel_f != null ? wheel_f : wheel_r; - // break; - // case 1695736278: //wheel_lr - // case 1670111368: //wheel_rr - // dwblcopy = wheel_r != null ? wheel_r : wheel_f; - // break; - // default: - // break; - //} - - if (dwblcopy != null) - { - if (dwbl != null) - { - if ((dwbl != dwblcopy) && (dwbl.AllModels.Length == 0)) - { - dwbl.Owner = dwblcopy; - dwbl.AllModels = dwblcopy.AllModels; //hopefully this is all that's need to render, otherwise drawable is actually getting edited! - //dwbl.DrawableModelsHigh = dwblcopy.DrawableModelsHigh; - //dwbl.DrawableModelsMedium = dwblcopy.DrawableModelsMedium; - //dwbl.DrawableModelsLow = dwblcopy.DrawableModelsLow; - //dwbl.DrawableModelsVeryLow = dwblcopy.DrawableModelsVeryLow; - //dwbl.VertexDecls = dwblcopy.VertexDecls; - } - - RenderDrawable(dwbl, arch, ent, txdhash /*, null, null, animClip*/); - - } - else - { } - } - else - { } - } - } - - } - } - - - bool isselected = SelectionFlagsTestAll || (f.Drawable == SelectedDrawable); - if (isselected) - { - var darr = f.DrawableArray?.data_items; - if (darr != null) - { - for (int i = 0; i < darr.Length; i++) - { - RenderDrawable(darr[i], arch, ent, txdhash, null, null, animClip); - } - } - } - - - - if (renderfragwindows) - { - var colblu = (uint)(new Color(0, 0, 255, 255).ToRgba()); - var colred = (uint)(new Color(255, 0, 0, 255).ToRgba()); - var eori = Quaternion.Identity; - var epos = Vector3.Zero; - if (ent != null) - { - eori = ent.Orientation; - epos = ent.Position; - } - - if (f.GlassWindows?.data_items != null) - { - for (int i = 0; i < f.GlassWindows.data_items.Length; i++) - { - var gw = f.GlassWindows.data_items[i]; - var projt = gw.ProjectionRow1;//row0? or row3? maybe investigate more - var proju = gw.ProjectionRow2;//row1 of XYZ>UV projection - var projv = gw.ProjectionRow3;//row2 of XYZ>UV projection - //var unk01 = new Vector2(gw.UnkFloat13, gw.UnkFloat14);//offset? - //var unk02 = new Vector2(gw.UnkFloat15, gw.UnkFloat16);//scale? sum of this and above often gives integers eg 1, 6 - //var thick = gw.Thickness; //thickness of the glass - //var unkuv = new Vector2(gw.UnkFloat18, gw.UnkFloat19); //another scale in UV space..? - //var tangt = gw.Tangent;//direction of surface tangent - //var bones = f.Drawable?.Skeleton?.Bones?.Items; //todo: use bones instead? - var grp = gw.Group; - var grplod = gw.GroupLOD; - var xforms = grplod?.FragTransforms?.Matrices; - var xoffs = Vector3.Zero; - if ((grp != null) && (xforms != null) && (grp.ChildIndex < xforms.Length) && (grplod != null)) - { - var xform = xforms[grp.ChildIndex]; - xoffs = xform.TranslationVector + grplod.PositionOffset; - } - var m = new Matrix(); - m.Row1 = new Vector4(projt, 0); - m.Row2 = new Vector4(proju, 0); - m.Row3 = new Vector4(projv, 0); - m.Row4 = new Vector4(xoffs, 1); - var v0 = m.Multiply(new Vector3(1, 0, 0)); - var v1 = m.Multiply(new Vector3(1, 0, 1)); - var v2 = m.Multiply(new Vector3(1, 1, 1)); - var v3 = m.Multiply(new Vector3(1, 1, 0)); - var c0 = eori.Multiply(v0) + epos; - var c1 = eori.Multiply(v1) + epos; - var c2 = eori.Multiply(v2) + epos; - var c3 = eori.Multiply(v3) + epos; - RenderSelectionLine(c0, c1, colblu); - RenderSelectionLine(c1, c2, colblu); - RenderSelectionLine(c2, c3, colblu); - RenderSelectionLine(c3, c0, colblu); - //RenderSelectionLine(c0, c0 + tangt, colred); - } - } - if (f.VehicleGlassWindows?.Windows != null) - { - for (int i = 0; i < f.VehicleGlassWindows.Windows.Length; i++) - { - var vgw = f.VehicleGlassWindows.Windows[i]; - //var grp = vgw.Group; - //var grplod = vgw.GroupLOD; - var m = vgw.Projection; - m.M44 = 1.0f; - m.Transpose(); - m.Invert();//ouch - var min = (new Vector3(0, 0, 0)); - var max = (new Vector3(vgw.ShatterMapWidth, vgw.ItemDataCount, 1)); - var v0 = m.MultiplyW(new Vector3(min.X, min.Y, 0)); - var v1 = m.MultiplyW(new Vector3(min.X, max.Y, 0)); - var v2 = m.MultiplyW(new Vector3(max.X, max.Y, 0)); - var v3 = m.MultiplyW(new Vector3(max.X, min.Y, 0)); - var c0 = eori.Multiply(v0) + epos; - var c1 = eori.Multiply(v1) + epos; - var c2 = eori.Multiply(v2) + epos; - var c3 = eori.Multiply(v3) + epos; - RenderSelectionLine(c0, c1, colblu); - RenderSelectionLine(c1, c2, colblu); - RenderSelectionLine(c2, c3, colblu); - RenderSelectionLine(c3, c0, colblu); - if (vgw.ShatterMap != null) - { - var width = vgw.ShatterMapWidth; - var height = vgw.ShatterMap.Length; - for (int y = 0; y < height; y++) - { - var smr = vgw.ShatterMap[y]; - for (int x = 0; x < width; x++) - { - var v = smr.GetValue(x); - if (v < 0 || v > 255) - continue; - var col = (uint)(new Color(v, v, v, 127).ToRgba()); - v0 = m.MultiplyW(new Vector3(x, y, 0)); - v1 = m.MultiplyW(new Vector3(x, y+1, 0)); - v2 = m.MultiplyW(new Vector3(x+1, y+1, 0)); - v3 = m.MultiplyW(new Vector3(x+1, y, 0)); - c0 = eori.Multiply(v0) + epos; - c1 = eori.Multiply(v1) + epos; - c2 = eori.Multiply(v2) + epos; - c3 = eori.Multiply(v3) + epos; - RenderSelectionQuad(c0, c1, c2, c3, col);//extra ouch - } - } - } - - } + RenderDrawable(draw, arch, ent, txdhash, null, null, animClip); } } + RenderFragmentWindows(arch, ent, fragment, txdhash, animClip); return true; } - public bool RenderArchetype(Archetype arche, YmapEntityDef entity, Renderable rndbl = null, bool cull = true, ClipMapEntry animClip = null) + public bool RenderArchetype(Archetype arche, YmapEntityDef entity, Renderable? rndbl = null, bool cull = true, ClipMapEntry? animClip = null) { //enqueue a single archetype for rendering. - if (arche == null) return false; + if (arche is null) + return false; - Vector3 entpos = (entity != null) ? entity.Position : Vector3.Zero; + Vector3 entpos = (entity is not null) ? entity.Position : Vector3.Zero; Vector3 camrel = entpos - camera.Position; Quaternion orientation = Quaternion.Identity; Vector3 scale = Vector3.One; Vector3 bscent = camrel; - if (entity != null) + if (entity is not null) { orientation = entity.Orientation; scale = entity.Scale; @@ -3051,19 +3046,14 @@ namespace CodeWalker.Rendering if (boundsmode == BoundsShaderMode.Sphere) { - if ((bsrad < renderboundsmaxrad) && (dist < renderboundsmaxdist)) + if (bsrad < renderboundsmaxrad && dist < renderboundsmaxdist) { - MapSphere ms = new MapSphere - { - CamRelPos = bscent, - Radius = bsrad, - }; - BoundingSpheres.Add(ms); + BoundingSpheres.Add(new MapSphere(bscent, bsrad)); } } if (boundsmode == BoundsShaderMode.Box) { - if ((dist < renderboundsmaxdist)) + if (dist < renderboundsmaxdist) { BoundingBoxes.Add( new MapBox( @@ -3077,16 +3067,13 @@ namespace CodeWalker.Rendering } } - - - bool res = false; - if (rndbl == null) + if (rndbl is null) { var drawable = gameFileCache.TryGetDrawable(arche); rndbl = TryGetRenderable(arche, drawable); } - if (rndbl == null || !rndbl.IsLoaded) + if (rndbl is null || !rndbl.IsLoaded) { return false; } @@ -3099,14 +3086,14 @@ namespace CodeWalker.Rendering } - res = RenderRenderable(rndbl, arche, entity); + bool res = RenderRenderable(rndbl, arche, entity); //fragments have extra drawables! need to render those too... TODO: handle fragments properly... if (rndbl.Key is FragDrawable fd) { var frag = fd.OwnerFragment; - if ((frag != null) && (frag.DrawableCloth != null)) //cloth... + if (frag is not null && frag.DrawableCloth != null) //cloth... { rndbl = TryGetRenderable(arche, frag.DrawableCloth); if (rndbl != null && rndbl.IsLoaded) @@ -3121,14 +3108,14 @@ namespace CodeWalker.Rendering return res; } - public bool RenderDrawable(DrawableBase drawable, Archetype arche, YmapEntityDef entity, uint txdHash = 0, TextureDictionary? txdExtra = null, Texture? diffOverride = null, ClipMapEntry? animClip = null, ClothInstance? cloth = null, Expression? expr = null) + public bool RenderDrawable(DrawableBase drawable, Archetype? arche, YmapEntityDef entity, uint txdHash = 0, TextureDictionary? txdExtra = null, Texture? diffOverride = null, ClipMapEntry? animClip = null, ClothInstance? cloth = null, Expression? expr = null) { //enqueue a single drawable for rendering. if (drawable is null) return false; - Renderable rndbl = TryGetRenderable(arche, drawable, txdHash, txdExtra, diffOverride); + Renderable? rndbl = TryGetRenderable(arche, drawable, txdHash, txdExtra, diffOverride); if (rndbl is null || !rndbl.IsLoaded) return false; @@ -3176,11 +3163,10 @@ namespace CodeWalker.Rendering Vector3 bbmax = (arche != null) ? arche.BBMax : rndbl.Key.BoundingBoxMax; Vector3 bscen = (arche != null) ? arche.BSCenter : rndbl.Key.BoundingCenter; float radius = (arche != null) ? arche.BSRadius : rndbl.Key.BoundingSphereRadius; - float distance = 0;//(camrel + bscen).Length(); - bool interiorent = false; bool castshadow = true; - if (entity != null) + float distance; + if (entity is not null) { position = entity.Position; scale = entity.Scale; @@ -3191,8 +3177,7 @@ namespace CodeWalker.Rendering bscen = entity.BSCenter; camrel += position; distance = entity.Distance; - castshadow = (entity.MloParent == null);//don't cast sun/moon shadows if this is an interior entity - optimisation! - interiorent = (entity.MloParent != null); + castshadow = (entity.MloParent is null);//don't cast sun/moon shadows if this is an interior entity - optimisation! } else { @@ -3213,7 +3198,7 @@ namespace CodeWalker.Rendering { rndbl.UpdateAnims(currentRealTime); } - if (rndbl.Cloth != null) + if (rndbl.Cloth is not null) { rndbl.Cloth.Update(currentRealTime); } @@ -3246,7 +3231,7 @@ namespace CodeWalker.Rendering RenderSkeleton(rndbl, entity); } - if (renderlights && Shaders.deferred && (rndbl.Lights != null)) + if (renderlights && Shaders.deferred && rndbl.Lights is not null) { entity?.EnsureLights(rndbl.Key); @@ -3286,7 +3271,7 @@ namespace CodeWalker.Rendering bool retval = true;// false; - if ((rndbl.AllTexturesLoaded || !waitforchildrentoload)) + if (rndbl.AllTexturesLoaded || !waitforchildrentoload) { RenderableGeometryInst rginst = new RenderableGeometryInst(); rginst.Inst.Renderable = rndbl; @@ -3305,10 +3290,8 @@ namespace CodeWalker.Rendering RenderableModel[] models = isselected ? rndbl.HDModels : rndbl.GetModels(distance); - for (int mi = 0; mi < models.Length; mi++) + foreach(var model in models) { - var model = models[mi]; - if (isselected) { if (SelectionModelDrawFlags.ContainsKey(model.DrawableModel)) @@ -3322,9 +3305,8 @@ namespace CodeWalker.Rendering continue; } //filter out reflection proxy models... - for (int gi = 0; gi < model.Geometries.Length; gi++) + foreach(var geom in model.Geometries) { - var geom = model.Geometries[gi]; var dgeom = geom.DrawableGeom; if (dgeom.UpdateRenderableParameters) //when edited by material editor @@ -3361,21 +3343,18 @@ namespace CodeWalker.Rendering return retval; } - - - - public void RenderCar(Vector3 pos, Quaternion ori, MetaHash modelHash, MetaHash modelSetHash, bool valign = false) + public void RenderCar(Vector3 pos, in Quaternion ori, MetaHash modelHash, MetaHash modelSetHash, bool valign = false) { uint carhash = modelHash; - if ((carhash == 0) && (modelSetHash != 0)) + if (carhash == 0 && modelSetHash != 0) { //find the pop group... and choose a vehicle.. var stypes = Scenarios.ScenarioTypes; - if (stypes != null) + if (stypes is not null) { var modelset = stypes.GetVehicleModelSet(modelSetHash); - if ((modelset != null) && (modelset.Models != null) && (modelset.Models.Length > 0)) + if (modelset is not null && modelset.Models is not null && modelset.Models.Length > 0) { carhash = JenkHash.GenHash(modelset.Models[0].NameLower); } @@ -3383,8 +3362,8 @@ namespace CodeWalker.Rendering } if (carhash == 0) carhash = 418536135; //"infernus" - YftFile caryft = gameFileCache.GetYft(carhash); - if ((caryft != null) && (caryft.Loaded) && (caryft.Fragment != null)) + YftFile? caryft = gameFileCache.GetYft(carhash); + if (caryft is not null && caryft.Loaded && caryft.Fragment is not null) { if (valign) { @@ -3515,15 +3494,14 @@ namespace CodeWalker.Rendering { RenderDrawable(drawable, null, ped.RenderEntity, 0, td, texture, ac, cloth, expr); } - - } public void RenderHideEntity(YmapEntityDef ent) { - var hash = ent?.EntityHash ?? 0; - if (hash == 0) return; + var hash = ent.EntityHash; + if (hash == 0) + return; HideEntities[hash] = ent; } @@ -3601,7 +3579,7 @@ namespace CodeWalker.Rendering - private Renderable? TryGetRenderable(Archetype arche, DrawableBase drawable, uint txdHash = 0, TextureDictionary? txdExtra = null, Texture? diffOverride = null) + private Renderable? TryGetRenderable(Archetype? arche, DrawableBase drawable, uint txdHash = 0, TextureDictionary? txdExtra = null, Texture? diffOverride = null) { if (drawable is null) return null; @@ -3628,7 +3606,7 @@ namespace CodeWalker.Rendering if (rndbl is null) return null; - if ((clipDict != 0) && (rndbl.ClipDict == null)) + if (clipDict != 0 && rndbl.ClipDict is null) { var ycd = gameFileCache.GetYcd(clipDict); if (ycd is not null && ycd.Loaded) @@ -3753,7 +3731,7 @@ namespace CodeWalker.Rendering foreach (var geom in model.Geometries) { - if (geom.Textures != null) + if (geom.HasTextures) { for (int i = 0; i < geom.Textures.Length; i++) { @@ -3863,7 +3841,7 @@ namespace CodeWalker.Rendering RenderableTexture? rhdtex = null; if (renderhdtextures) { - Texture hdtex = geom.TexturesHD[i]; + Texture? hdtex = geom.TexturesHD[i]; if (hdtex is null) { //look for a replacement HD texture... @@ -3919,6 +3897,7 @@ namespace CodeWalker.Rendering public readonly Archetype Archetype = archetype; public readonly YmapEntityDef Entity = entity; } + public readonly struct RenderedBoundComposite(RenderableBoundComposite boundComp, YmapEntityDef entity) { public readonly RenderableBoundComposite BoundComp = boundComp; @@ -4006,7 +3985,7 @@ namespace CodeWalker.Rendering } if (ymap.Parent != pymap) { - Console.WriteLine($"Connected ymap {ymap.Name} to parent {pymap.Name}"); + Console.WriteLine($"Connected ymap {ymap.Name} {ymap.FilePath} to parent {pymap.Name} {pymap.FilePath}"); ymap.ConnectToParent(pymap); } } @@ -4025,8 +4004,11 @@ namespace CodeWalker.Rendering { var ymap = CurrentYmaps[remYmap]; CurrentYmaps.Remove(remYmap); + + Console.WriteLine($"Removing ymap {ymap.Name} {ymap.FilePath}"); + var remEnts = ymap.LodManagerOldEntities ?? ymap.AllEntities; - if (remEnts != null) // remove this ymap's entities from the tree..... + if (remEnts is not null) // remove this ymap's entities from the tree..... { for (int i = 0; i < remEnts.Length; i++) { @@ -4063,11 +4045,14 @@ namespace CodeWalker.Rendering } if (ymap._CMapData.parent != 0 && ymap.Parent is null) //skip adding ymaps until parents are available { + // We have to make sure to re-add children when map get's reloaded (this breaks LOD's for project files otherwise) + CurrentYmaps.Remove(key, out _); continue; } if (CurrentYmaps.TryAdd(key, ymap)) { - foreach(var ent in ymap.AllEntities) + Console.WriteLine($"Adding ymap {ymap.Name} {ymap.FilePath}"); + foreach (var ent in ymap.AllEntities) { if (ent.Parent is not null) { diff --git a/CodeWalker/Rendering/Shaders/CableShader.cs b/CodeWalker/Rendering/Shaders/CableShader.cs index 1dac663..6fca4a5 100644 --- a/CodeWalker/Rendering/Shaders/CableShader.cs +++ b/CodeWalker/Rendering/Shaders/CableShader.cs @@ -106,12 +106,9 @@ namespace CodeWalker.Rendering PSSceneVars = new GpuVarsBuffer(device); PSGeomVars = new GpuVarsBuffer(device); - //supported layout - requires Position, Normal, Colour, Texcoord layouts.Add(VertexType.Default, new InputLayout(device, vsbytes, VertexTypeGTAV.GetLayout(VertexType.Default))); - - texsampler = new SamplerState(device, new SamplerStateDescription() { AddressU = TextureAddressMode.Wrap, diff --git a/CodeWalker/Rendering/Shaders/MarkerShader.cs b/CodeWalker/Rendering/Shaders/MarkerShader.cs index 16450bc..4e500ce 100644 --- a/CodeWalker/Rendering/Shaders/MarkerShader.cs +++ b/CodeWalker/Rendering/Shaders/MarkerShader.cs @@ -76,7 +76,6 @@ namespace CodeWalker.Rendering MinimumLod = 0, MipLodBias = 0, }); - } diff --git a/CodeWalker/Rendering/Shaders/ShadowShader.cs b/CodeWalker/Rendering/Shaders/ShadowShader.cs index c5ed379..1dd5f02 100644 --- a/CodeWalker/Rendering/Shaders/ShadowShader.cs +++ b/CodeWalker/Rendering/Shaders/ShadowShader.cs @@ -167,12 +167,7 @@ namespace CodeWalker.Rendering defaultBoneMatrices = new Matrix3_s[255]; for (int i = 0; i < 255; i++) { - defaultBoneMatrices[i] = new Matrix3_s - { - Row1 = Vector4.UnitX, - Row2 = Vector4.UnitY, - Row3 = Vector4.UnitZ - }; + defaultBoneMatrices[i] = new Matrix3_s(Vector4.UnitX, Vector4.UnitY, Vector4.UnitZ); } } diff --git a/CodeWalker/Rendering/Utils/ShaderExtensions.cs b/CodeWalker/Rendering/Utils/ShaderExtensions.cs index 44a91e0..f893326 100644 --- a/CodeWalker/Rendering/Utils/ShaderExtensions.cs +++ b/CodeWalker/Rendering/Utils/ShaderExtensions.cs @@ -28,7 +28,6 @@ namespace CodeWalker.Rendering.Utils } else { - Console.WriteLine(folder); return folder; } } diff --git a/CodeWalker/Tools/BinarySearchForm.cs b/CodeWalker/Tools/BinarySearchForm.cs index 4bfe8ed..c8c1558 100644 --- a/CodeWalker/Tools/BinarySearchForm.cs +++ b/CodeWalker/Tools/BinarySearchForm.cs @@ -112,7 +112,9 @@ namespace CodeWalker.Tools string searchfolder = FileSearchFolderTextBox.Text; AbortOperation = false; - if (InProgress) return; + if (InProgress) + return; + if (searchfolder.Length == 0) { MessageBox.Show("Please select a folder..."); @@ -165,10 +167,10 @@ namespace CodeWalker.Tools InProgress = true; - Task.Run(() => + _ = Task.Run(() => { - FileSearchAddResult("Searching " + searchfolder + "..."); + FileSearchAddResult($"Searching {searchfolder}..."); string[] filenames = Directory.GetFiles(searchfolder); @@ -195,13 +197,13 @@ namespace CodeWalker.Tools if (hitlen1 == bytelen) { - FileSearchAddResult(finf.Name + ":" + (i - bytelen)); + FileSearchAddResult($"{finf.Name}:{i - bytelen}"); matchcount++; hitlen1 = 0; } if (hitlen2 == bytelen) { - FileSearchAddResult(finf.Name + ":" + (i - bytelen)); + FileSearchAddResult($"{finf.Name}:{i - bytelen}"); matchcount++; hitlen2 = 0; } @@ -218,7 +220,7 @@ namespace CodeWalker.Tools } - FileSearchAddResult(string.Format("Search complete. {0} results found.", matchcount)); + FileSearchAddResult($"Search complete. {matchcount} results found."); FileSearchComplete(); InProgress = false; }); @@ -479,7 +481,7 @@ namespace CodeWalker.Tools { continue; } } - UpdateStatus(string.Format("{0} - Searching {1}/{2} : {3}", duration.ToString(@"hh\:mm\:ss"), curfile, totfiles, fentry.Path)); + UpdateStatus($"{duration:hh\\:mm\\:ss} - Searching {curfile}/{totfiles} : {fentry.Path}"); byte[] filebytes = fentry.File.ExtractFile(fentry); if (filebytes == null) continue; @@ -655,7 +657,7 @@ namespace CodeWalker.Tools { if (entry is RpfDirectoryEntry rde) { - FileInfoLabel.Text = rde.Path + " (Directory)"; + FileInfoLabel.Text = $"{rde.Path} (Directory)"; DataTextBox.Text = "[Please select a data file]"; } else @@ -678,7 +680,7 @@ namespace CodeWalker.Tools byte[] data = rfe.File.ExtractFile(rfe); int datalen = (data != null) ? data.Length : 0; - FileInfoLabel.Text = rfe.Path + " (" + typestr + " file) - " + TextUtil.GetBytesReadable(datalen); + FileInfoLabel.Text = $"{rfe.Path} ({typestr} file) - {TextUtil.GetBytesReadable(datalen)}"; if (ShowLargeFileContentsCheckBox.Checked || (datalen < 524287)) //512K diff --git a/CodeWalker/Tools/BrowseForm.cs b/CodeWalker/Tools/BrowseForm.cs index 83a2994..80bf9f0 100644 --- a/CodeWalker/Tools/BrowseForm.cs +++ b/CodeWalker/Tools/BrowseForm.cs @@ -1097,7 +1097,7 @@ namespace CodeWalker.Tools { continue; } } - UpdateStatus(string.Format("{0} - Searching {1}/{2} : {3}", duration.ToString(@"hh\:mm\:ss"), curfile, totfiles, fentry.Path)); + UpdateStatus($"{duration:hh\\:mm\\:ss} - Searching {curfile}/{totfiles} : {fentry.Path}"); byte[] filebytes = fentry.File.ExtractFile(fentry); if (filebytes == null) continue; diff --git a/CodeWalker/Tools/ExtractTexForm.cs b/CodeWalker/Tools/ExtractTexForm.cs index ea9fefa..404efca 100644 --- a/CodeWalker/Tools/ExtractTexForm.cs +++ b/CodeWalker/Tools/ExtractTexForm.cs @@ -1,6 +1,7 @@ using CodeWalker.GameFiles; using CodeWalker.Properties; using CodeWalker.Utils; +using CommunityToolkit.Diagnostics; using System; using System.Collections.Generic; using System.ComponentModel; @@ -64,20 +65,17 @@ namespace CodeWalker.Tools } - private void UpdateExtractStatus(string text) + private async void UpdateExtractStatus(string text) { try { - if (InvokeRequired) - { - Invoke(new Action(() => { UpdateExtractStatus(text); })); - } - else - { - ExtractStatusLabel.Text = text; - } + await this.SwitchToUiContext(); + ExtractStatusLabel.Text = text; + } + catch(Exception ex) + { + Console.WriteLine(ex); } - catch { } } private void ExtractButton_Click(object sender, EventArgs e) @@ -91,12 +89,12 @@ namespace CodeWalker.Tools } if (!Directory.Exists(FolderTextBox.Text)) { - MessageBox.Show("Folder doesn't exist: " + FolderTextBox.Text); + MessageBox.Show($"Folder doesn't exist: {FolderTextBox.Text}"); return; } if (!Directory.Exists(OutputFolderTextBox.Text)) { - MessageBox.Show("Folder doesn't exist: " + OutputFolderTextBox.Text); + MessageBox.Show($"Folder doesn't exist: {OutputFolderTextBox.Text}"); return; } //if (Directory.GetFiles(OutputFolderTextBox.Text, "*.ysc", SearchOption.AllDirectories).Length > 0) @@ -118,7 +116,7 @@ namespace CodeWalker.Tools bool bydd = YddCheckBox.Checked; bool byft = YftCheckBox.Checked; - Task.Run(() => + Task.Run(async () => { UpdateExtractStatus("Keys loaded."); @@ -146,22 +144,39 @@ namespace CodeWalker.Tools if (bytd && entry.IsExtension(".ytd")) { UpdateExtractStatus(entry.Path); - YtdFile ytd = RpfManager.GetFile(entry); - if (ytd is null) throw new Exception("Couldn't load file."); - if (ytd.TextureDict is null) throw new Exception("Couldn't load texture dictionary."); - if (ytd.TextureDict.Textures is null) throw new Exception("Couldn't load texture dictionary texture array."); - if (ytd.TextureDict.Textures.data_items is null) throw new Exception("Texture dictionary had no entries..."); + YtdFile? ytd = await RpfManager.GetFileAsync(entry); + if (ytd is null) + { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load ytd file {entry.Path ?? entry.File.Path}"); + } + if (ytd.TextureDict is null) + { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load texture dictionary for file {entry.Path ?? entry.File.Path}"); + } + if (ytd.TextureDict.Textures is null) + { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load texture dictionary texture array for file {entry.Path ?? entry.File.Path}"); + } + if (ytd.TextureDict.Textures.data_items is null) + { + ThrowHelper.ThrowInvalidOperationException($"Texture dictionary had no entries... for file {entry.Path ?? entry.File.Path}"); + } foreach (var tex in ytd.TextureDict.Textures.data_items) { - SaveTexture(tex, entry, outputpath); + await SaveTextureAsync(tex, entry, outputpath); } } else if (bydr && entry.IsExtension(".ydr")) { UpdateExtractStatus(entry.Path); - YdrFile ydr = RpfManager.GetFile(entry); - if (ydr is null) throw new Exception("Couldn't load file."); - if (ydr.Drawable is null) throw new Exception("Couldn't load drawable."); + YdrFile? ydr = await RpfManager.GetFileAsync(entry); + if (ydr is null) + { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load ydr file {entry.Path ?? entry.File.Path}"); + } + if (ydr.Drawable is null) { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load drawable. array for file {entry.Path ?? entry.File.Path}"); + } if (ydr.Drawable.ShaderGroup is not null) { var ydrtd = ydr.Drawable.ShaderGroup.TextureDictionary; @@ -169,7 +184,7 @@ namespace CodeWalker.Tools { foreach (var tex in ydrtd.Textures.data_items) { - SaveTexture(tex, entry, outputpath); + await SaveTextureAsync(tex, entry, outputpath); } } } @@ -177,22 +192,30 @@ namespace CodeWalker.Tools else if (bydd && entry.IsExtension(".ydd")) { UpdateExtractStatus(entry.Path); - YddFile ydd = RpfManager.GetFile(entry); - if (ydd == null) throw new Exception("Couldn't load file."); + YddFile? ydd = await RpfManager.GetFileAsync(entry); + + if (ydd is null) + { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load ydd file {entry.Path ?? entry.File.Path}"); + } + //if (ydd.DrawableDict == null) throw new Exception("Couldn't load drawable dictionary."); //if (ydd.DrawableDict.Drawables == null) throw new Exception("Drawable dictionary had no items..."); //if (ydd.DrawableDict.Drawables.data_items == null) throw new Exception("Drawable dictionary had no items..."); - if ((ydd.Dict==null)||(ydd.Dict.Count==0)) throw new Exception("Drawable dictionary had no items..."); + if (ydd.Dict is null || ydd.Dict.Count == 0) + { + ThrowHelper.ThrowInvalidDataException("Drawable dictionary has no items..."); + } foreach (var drawable in ydd.Dict.Values) { if (drawable.ShaderGroup != null) { var ydrtd = drawable.ShaderGroup.TextureDictionary; - if ((ydrtd != null) && (ydrtd.Textures != null) && (ydrtd.Textures.data_items != null)) + if (ydrtd?.Textures?.data_items != null) { foreach (var tex in ydrtd.Textures.data_items) { - SaveTexture(tex, entry, outputpath); + await SaveTextureAsync(tex, entry, outputpath); } } } @@ -201,9 +224,15 @@ namespace CodeWalker.Tools else if (byft && entry.IsExtension(".yft")) { UpdateExtractStatus(entry.Path); - YftFile yft = RpfManager.GetFile(entry); - if (yft == null) throw new Exception("Couldn't load file."); - if (yft.Fragment == null) throw new Exception("Couldn't load fragment."); + YftFile? yft = await RpfManager.GetFileAsync(entry); + if (yft is null) + { + ThrowHelper.ThrowInvalidOperationException($"Couldn't load yft file {entry.Path ?? entry.File.Path}"); + } + if (yft.Fragment is null) + { + ThrowHelper.ThrowInvalidDataException($"Couldn't load fragment for file {entry.Path ?? entry.File.Path}"); + } if (yft.Fragment.Drawable != null) { if (yft.Fragment.Drawable.ShaderGroup != null) @@ -213,7 +242,7 @@ namespace CodeWalker.Tools { foreach (var tex in ydrtd.Textures.data_items) { - SaveTexture(tex, entry, outputpath); + await SaveTextureAsync(tex, entry, outputpath); } } } @@ -222,7 +251,8 @@ namespace CodeWalker.Tools } catch (Exception ex) { - string err = entry.Name + ": " + ex.Message; + Console.WriteLine(ex); + string err = $"{entry.Name}: {ex.Message}"; UpdateExtractStatus(err); errsb.AppendLine(err); } @@ -237,14 +267,14 @@ namespace CodeWalker.Tools }); } - private void SaveTexture(Texture tex, RpfEntry entry, string folder) + private static async Task SaveTextureAsync(Texture tex, RpfEntry entry, string folder) { //DirectXTex byte[] dds = DDSIO.GetDDSFile(tex); - string bpath = folder + "\\" + entry.Name + "_" + tex.Name; + string bpath = $"{folder}\\{entry.Name}_{tex.Name}"; string fpath = bpath + ".dds"; int c = 1; while (File.Exists(fpath)) @@ -253,8 +283,7 @@ namespace CodeWalker.Tools c++; } - File.WriteAllBytes(fpath, dds); - + await File.WriteAllBytesAsync(fpath, dds); } diff --git a/CodeWalker/Tools/JenkIndForm.cs b/CodeWalker/Tools/JenkIndForm.cs index dd92597..d4f6865 100644 --- a/CodeWalker/Tools/JenkIndForm.cs +++ b/CodeWalker/Tools/JenkIndForm.cs @@ -167,7 +167,7 @@ namespace CodeWalker.Tools if (hasstr && hastxt) { - MatchTextBox.Text = string.Format("JenkIndex match:\r\n{0}\r\nGlobalText match:\r\n{1}", str, txt); + MatchTextBox.Text = $"JenkIndex match:\r\n{str}\r\nGlobalText match:\r\n{txt}"; } else if (hasstr) { diff --git a/CodeWalker/Utils/MapUtils.cs b/CodeWalker/Utils/MapUtils.cs index 334afda..62cd20e 100644 --- a/CodeWalker/Utils/MapUtils.cs +++ b/CodeWalker/Utils/MapUtils.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using SharpDX; using SharpDX.Direct3D11; using CodeWalker.Utils; +using CodeWalker.Core.Utils; namespace CodeWalker { @@ -49,7 +50,7 @@ namespace CodeWalker } catch (Exception ex) { - errorAction("Could not load map icon " + Filepath + " for " + Name + "!\n\n" + ex.ToString()); + errorAction($"Could not load map icon {Filepath} for {Name}!\n\n{ex}"); } } @@ -75,42 +76,48 @@ namespace CodeWalker public class MapMarker { - public MapIcon Icon { get; set; } + public MapIcon? Icon { get; set; } public Vector3 WorldPos { get; set; } //actual world pos public Vector3 CamRelPos { get; set; } //updated per frame public Vector3 ScreenPos { get; set; } //position on screen (updated per frame) - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public List Properties { get; set; } //additional data public bool IsMovable { get; set; } public float Distance { get; set; } //length of CamRelPos, updated per frame - public void Parse(string s) + public void Parse(ReadOnlySpan s) { Vector3 p = new Vector3(0.0f); - string[] ss = s.Split(','); - if (ss.Length > 1) + + var enumerator = s.EnumerateSplit(','); + + if (!enumerator.MoveNext()) + return; + + FloatUtil.TryParse(enumerator.Current.Trim(), out p.X); + + if (!enumerator.MoveNext()) + return; + + FloatUtil.TryParse(enumerator.Current.Trim(), out p.Y); + + if (enumerator.MoveNext()) { - FloatUtil.TryParse(ss[0].Trim(), out p.X); - FloatUtil.TryParse(ss[1].Trim(), out p.Y); - } - if (ss.Length > 2) - { - FloatUtil.TryParse(ss[2].Trim(), out p.Z); - } - if (ss.Length > 3) - { - Name = ss[3].Trim(); - } - else - { - Name = string.Empty; - } - for (int i = 4; i < ss.Length; i++) - { - if (Properties == null) Properties = new List(); - Properties.Add(ss[i].Trim()); + FloatUtil.TryParse(enumerator.Current.Trim(), out p.Z); } + WorldPos = p; + + if (enumerator.MoveNext()) + { + Name = enumerator.Current.Trim().ToString(); + } + + while(enumerator.MoveNext()) + { + Properties ??= new List(); + Properties.Add(enumerator.Current.Trim().ToString()); + } } public override string ToString() @@ -118,12 +125,12 @@ namespace CodeWalker string cstr = Get3DWorldPosString(); if (!string.IsNullOrEmpty(Name)) { - cstr += ", " + Name; - if (Properties != null) + cstr += $", {Name}"; + if (Properties is not null) { foreach (string prop in Properties) { - cstr += ", " + prop; + cstr += $", {prop}"; } } } @@ -132,11 +139,11 @@ namespace CodeWalker public string Get2DWorldPosString() { - return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", WorldPos.X, WorldPos.Y); + return string.Create(CultureInfo.InvariantCulture, $"{WorldPos.X}, {WorldPos.Y}"); } public string Get3DWorldPosString() { - return string.Format(CultureInfo.InvariantCulture, "{0}, {1}, {2}", WorldPos.X, WorldPos.Y, WorldPos.Z); + return string.Create(CultureInfo.InvariantCulture, $"{WorldPos.X}, {WorldPos.Y}, {WorldPos.Z}"); } diff --git a/CodeWalker/VehicleForm.cs b/CodeWalker/VehicleForm.cs index f86988b..5f8accd 100644 --- a/CodeWalker/VehicleForm.cs +++ b/CodeWalker/VehicleForm.cs @@ -551,9 +551,9 @@ namespace CodeWalker var tstr = tex.Name.Trim(); if (tex is Texture t) { - tstr = string.Format("{0} ({1}x{2}, embedded)", tex.Name, t.Width, t.Height); + tstr = $"{tex.Name} ({t.Width}x{t.Height}, embedded)"; } - var tnode = tgnode.Nodes.Add(hash.ToString().Trim() + ": " + tstr); + var tnode = tgnode.Nodes.Add($"{hash}: {tstr}"); tnode.Tag = tex; } } @@ -750,7 +750,7 @@ namespace CodeWalker int ih = (int)fh; int im = v - (ih * 60); if (ih == 24) ih = 0; - TimeOfDayLabel.Text = string.Format("{0:00}:{1:00}", ih, im); + TimeOfDayLabel.Text = $"{ih:00}:{im:00}"; } diff --git a/CodeWalker/World/CutsceneForm.cs b/CodeWalker/World/CutsceneForm.cs index d28ed25..d0aa2a6 100644 --- a/CodeWalker/World/CutsceneForm.cs +++ b/CodeWalker/World/CutsceneForm.cs @@ -767,7 +767,7 @@ namespace CodeWalker.World { if (obj.Enabled == false) continue; - if (obj.HideEntity != null) + if (obj.HideEntity is not null) { renderer.RenderHideEntity(obj.HideEntity); } diff --git a/CodeWalker/World/WorldSearchForm.cs b/CodeWalker/World/WorldSearchForm.cs index 33e2ac0..dd5564e 100644 --- a/CodeWalker/World/WorldSearchForm.cs +++ b/CodeWalker/World/WorldSearchForm.cs @@ -162,7 +162,7 @@ namespace CodeWalker.World LastUpdate.Restart(); try { - await this.SwitchToUi(); + await this.SwitchToUiContext(); ArchetypeSearchStatusLabel.Text = text; } catch(Exception ex) @@ -175,7 +175,7 @@ namespace CodeWalker.World { try { - await this.SwitchToUi(); + await this.SwitchToUiContext(); if (ArchetypeResults.Contains(arch)) return; ArchetypeResults.Add(arch); @@ -191,7 +191,7 @@ namespace CodeWalker.World { try { - await this.SwitchToUi(); + await this.SwitchToUiContext(); ArchetypeSearchTextBox.Enabled = true; ArchetypeSearchButton.Enabled = true; ArchetypeSearchAbortButton.Enabled = false; @@ -416,7 +416,7 @@ namespace CodeWalker.World LastUpdate.Restart(); try { - await this.SwitchToUi(); + await this.SwitchToUiContext(); EntitySearchStatusLabel.Text = text; } catch (Exception ex) @@ -429,7 +429,7 @@ namespace CodeWalker.World { try { - await this.SwitchToUi(); + await this.SwitchToUiContext(); if (EntityResults.Contains(ent)) return; EntityResults.Add(ent); @@ -445,7 +445,7 @@ namespace CodeWalker.World { try { - await this.SwitchToUi(); + await this.SwitchToUiContext(); EntitySearchTextBox.Enabled = true; EntitySearchButton.Enabled = true; EntitySearchAbortButton.Enabled = false; diff --git a/CodeWalker/WorldForm.cs b/CodeWalker/WorldForm.cs index eb4e12a..c40bddb 100644 --- a/CodeWalker/WorldForm.cs +++ b/CodeWalker/WorldForm.cs @@ -20,6 +20,7 @@ using CodeWalker.Tools; using CodeWalker.WinForms; using CodeWalker.Core.Utils; using System.Data; +using CommunityToolkit.HighPerformance; namespace CodeWalker { @@ -42,12 +43,12 @@ namespace CodeWalker public bool Pauserendering { get; set; } volatile bool initialised = false; - Stopwatch frametimer = new Stopwatch(); + readonly Stopwatch frametimer = new Stopwatch(); Space space; - Camera camera; - Timecycle timecycle; - Weather weather; - Clouds clouds; + readonly Camera camera; + readonly Timecycle timecycle; + readonly Weather weather; + readonly Clouds clouds; private Water water; Water Water => water ??= new Water(); @@ -78,7 +79,7 @@ namespace CodeWalker bool rendermaps = false; bool renderworld = false; - int startupviewmode = 0; //0=world, 1=ymap, 2=model + readonly int startupviewmode = 0; //0=world, 1=ymap, 2=model string modelname = "dt1_tc_dufo_core";//"dt1_11_fount_decal";//"v_22_overlays";// string[] ymaplist; @@ -91,7 +92,7 @@ namespace CodeWalker WorldControlMode ControlMode = WorldControlMode.Free; - object MouseControlSyncRoot = new object(); + readonly object MouseControlSyncRoot = new object(); int MouseControlX = 0; int MouseControlY = 0; int MouseControlWheel = 0; @@ -117,56 +118,56 @@ namespace CodeWalker List Icons; - MapIcon MarkerIcon = null; - MapIcon LocatorIcon = null; - MapMarker LocatorMarker = null; - MapMarker GrabbedMarker = null; - MapMarker SelectedMarker = null; - MapMarker MousedMarker = null; - List Markers = new List(); - List SortedMarkers = new List(); - List MarkerBatch = new List(); + MapIcon? MarkerIcon = null; + MapIcon? LocatorIcon = null; + MapMarker? LocatorMarker = null; + MapMarker? GrabbedMarker = null; + MapMarker? SelectedMarker = null; + MapMarker? MousedMarker = null; + readonly List Markers = new List(); + readonly List SortedMarkers = new List(); + readonly List MarkerBatch = new List(); bool RenderLocator = false; - object markersyncroot = new object(); - object markersortedsyncroot = new object(); + readonly object markersyncroot = new object(); + readonly object markersortedsyncroot = new object(); bool rendercollisionmeshes = Settings.Default.ShowCollisionMeshes; - List collisionitems = new List(); - List collisionybns = new List(); - Dictionary collisioninteriors = new Dictionary(); + readonly List collisionitems = new List(); + readonly List collisionybns = new List(); + readonly Dictionary collisioninteriors = new Dictionary(); int collisionmeshrange = Settings.Default.CollisionMeshRange; - bool[] collisionmeshlayers = { true, true, true }; + readonly bool[] collisionmeshlayers = { true, true, true }; - Dictionary renderworldVisibleYmapDict = new Dictionary(); + readonly Dictionary renderworldVisibleYmapDict = new Dictionary(); bool worldymaptimefilter = true; bool worldymapweatherfilter = true; bool renderpathbounds = true; bool renderpaths = false; - List renderpathynds = new List(); + readonly List renderpathynds = new List(); bool renderwaterquads = true; bool rendertraintracks = false; - List rendertraintracklist = new List(); + readonly List rendertraintracklist = new List(); bool rendernavmeshes = false; - List rendernavmeshynvs = new List(); + readonly List rendernavmeshynvs = new List(); - bool renderscenariobounds = false; - bool renderscenarios = false; - List renderscenariolist = new List(); + readonly bool renderscenariobounds = false; + readonly bool renderscenarios = false; + readonly List renderscenariolist = new List(); bool renderpopzones = false; - bool renderheightmaps = false; - bool renderwatermaps = false; + readonly bool renderheightmaps = false; + readonly bool renderwatermaps = false; - bool renderaudiozones = false; + readonly bool renderaudiozones = false; bool renderaudioouterbounds = true; - List renderaudfilelist = new List(); - List renderaudplacementslist = new List(); + readonly List renderaudfilelist = new List(); + readonly List renderaudplacementslist = new List(); bool MapViewEnabled = false; int MapViewDragX = 0; @@ -175,12 +176,12 @@ namespace CodeWalker bool MouseSelectEnabled = false; bool ShowSelectionBounds = true; - bool SelectByGeometry = false; //select by geometry needs more work + readonly bool SelectByGeometry = false; //select by geometry needs more work MapSelection CurMouseHit = new MapSelection(); MapSelection LastMouseHit = new MapSelection(); MapSelection PrevMouseHit = new MapSelection(); - bool MouseRayCollisionEnabled = true; + readonly bool MouseRayCollisionEnabled = true; bool MouseRayCollisionVisible = false; SpaceRayIntersectResult MouseRayCollision = new SpaceRayIntersectResult(); @@ -192,15 +193,15 @@ namespace CodeWalker public MapSelection CurrentMapSelection { get { return SelectedItem; } } - TransformWidget Widget = new TransformWidget(); + readonly TransformWidget Widget = new TransformWidget(); TransformWidget GrabbedWidget = null; bool ShowWidget = true; ProjectForm ProjectForm = null; - Stack UndoSteps = new Stack(); - Stack RedoSteps = new Stack(); + readonly Stack UndoSteps = new Stack(); + readonly Stack RedoSteps = new Stack(); Vector3 UndoStartPosition; Quaternion UndoStartRotation; Vector3 UndoStartScale; @@ -218,7 +219,7 @@ namespace CodeWalker CutsceneForm CutsceneForm = null; - InputManager Input = new InputManager(); + readonly InputManager Input = new InputManager(); @@ -570,7 +571,7 @@ namespace CodeWalker int ih = (int)fh; int im = v - (ih * 60); if (ih == 24) ih = 0; - TimeOfDayLabel.Text = string.Format("{0:00}:{1:00}", ih, im); + TimeOfDayLabel.Text = $"{ih:00}:{im:00}"; } private void UpdateControlInputs(float elapsed) @@ -1962,9 +1963,9 @@ namespace CodeWalker } } - public void UpdatePathYndGraphics(YndFile ynd, bool fullupdate) + public void UpdatePathYndGraphics(YndFile? ynd, bool fullupdate) { - if (ynd == null) + if (ynd is null) { return; } @@ -2462,7 +2463,7 @@ namespace CodeWalker } private void UpdateMouseHitsFromRenderer() { - foreach (var rd in Renderer.RenderedDrawables) + foreach (ref var rd in Renderer.RenderedDrawables.AsSpan()) { UpdateMouseHits(rd.Drawable, rd.Archetype, rd.Entity); } @@ -2481,7 +2482,8 @@ namespace CodeWalker } private void UpdateMouseHitsFromProject() { - if (ProjectForm == null) return; + if (ProjectForm is null) + return; if (SelectionMode == MapSelectionMode.Collision) { @@ -2499,9 +2501,8 @@ namespace CodeWalker //first test the bounding sphere for mouse hit.. Quaternion orinv; Ray mraytrn; - float hitdist = 0.0f; int geometryIndex = 0; - DrawableGeometry geometry = null; + DrawableGeometry? geometry = null; BoundingBox geometryAABB = new BoundingBox(); BoundingSphere bsph = new BoundingSphere(); BoundingBox bbox = new BoundingBox(); @@ -2509,13 +2510,15 @@ namespace CodeWalker Quaternion orientation = Quaternion.Identity; Vector3 scale = Vector3.One; Vector3 camrel = -camera.Position; - if (entity != null) + + if (entity is not null) { orientation = entity.Orientation; scale = entity.Scale; camrel += entity.Position; } - if (arche != null) + + if (arche is not null) { bsph.Center = camrel + orientation.Multiply(arche.BSCenter);//could use entity.BSCenter bsph.Radius = arche.BSRadius; @@ -2529,23 +2532,21 @@ namespace CodeWalker bbox.Minimum = drawable.BoundingBoxMin * scale; bbox.Maximum = drawable.BoundingBoxMax * scale; } + bool mousespherehit = camera.MouseRay.Intersects(ref bsph); - if ((SelectionMode == MapSelectionMode.EntityExtension) || (SelectionMode == MapSelectionMode.ArchetypeExtension)) + float hitdist; + if (SelectionMode == MapSelectionMode.EntityExtension || SelectionMode == MapSelectionMode.ArchetypeExtension) { //transform the mouse ray into the entity space. orinv = Quaternion.Invert(orientation); - mraytrn = new Ray - { - Position = orinv.Multiply(camera.MouseRay.Position - camrel), - Direction = orinv.Multiply(camera.MouseRay.Direction) - }; + mraytrn = new Ray(orinv.Multiply(camera.MouseRay.Position - camrel), orinv.Multiply(camera.MouseRay.Direction)); if (SelectionMode == MapSelectionMode.EntityExtension) { - if ((entity != null) && (entity.Extensions != null)) + if (entity?.Extensions is not null) { for (int i = 0; i < entity.Extensions.Length; i++) { @@ -2616,15 +2617,20 @@ namespace CodeWalker bool usegeomboxes = SelectByGeometry; var dmodels = drawable.DrawableModels?.High; - if (dmodels == null) - { usegeomboxes = false; } - if (usegeomboxes) + if (dmodels is null) + { + usegeomboxes = false; + } + if (usegeomboxes && dmodels is not null) { for (int i = 0; i < dmodels.Length; i++) { var m = dmodels[i]; - if (m.BoundsData == null) - { usegeomboxes = false; break; } + if (m.BoundsData is null) + { + usegeomboxes = false; + break; + } } } @@ -2632,15 +2638,9 @@ namespace CodeWalker //transform the mouse ray into the entity space. orinv = Quaternion.Invert(orientation); - mraytrn = new Ray - { - Position = orinv.Multiply(camera.MouseRay.Position - camrel), - Direction = orinv.Multiply(camera.MouseRay.Direction) - }; - hitdist = 0.0f; + mraytrn = new Ray(orinv.Multiply(camera.MouseRay.Position - camrel), orinv.Multiply(camera.MouseRay.Direction)); - - if (usegeomboxes) + if (usegeomboxes && dmodels is not null) { //geometry bounding boxes version float ghitdist = float.MaxValue; @@ -2701,8 +2701,8 @@ namespace CodeWalker bool outerhit = false; if (mraytrn.Intersects(ref bbox, out hitdist)) //test primary box { - bool firsthit = (CurMouseHit.EntityDef == null); - if (firsthit || (hitdist > 0.0f)) //ignore when inside the box.. + bool firsthit = CurMouseHit.EntityDef is null; + if (firsthit || hitdist > 0.0f) //ignore when inside the box.. { bool nearer = (hitdist < CurMouseHit.HitDist); //closer than the last.. bool radsm = true; @@ -2721,7 +2721,9 @@ namespace CodeWalker } } if (!outerhit) - { return; } //no hit. + { + return; + } //no hit. } @@ -2822,14 +2824,7 @@ namespace CodeWalker var tcm = ymap.TimeCycleModifiers[i]; if ((((tcm.BBMin + tcm.BBMax) * 0.5f) - camera.Position).Length() > dmax) continue; - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = tcm.BBMin, - BBMax = tcm.BBMax, - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; + MapBox mb = new MapBox(-camera.Position, tcm.BBMin, tcm.BBMax, Quaternion.Identity, Vector3.One); Renderer.BoundingBoxes.Add(mb); bbox.Minimum = mb.BBMin; @@ -2848,22 +2843,11 @@ namespace CodeWalker for (int i = 0; i < ymap.CarGenerators.Length; i++) { var cg = ymap.CarGenerators[i]; - MapBox mb = new MapBox - { - CamRelPos = cg.Position - camera.Position, - BBMin = cg.BBMin, - BBMax = cg.BBMax, - Orientation = cg.Orientation, - Scale = Vector3.One, - }; + MapBox mb = new MapBox(cg.Position - camera.Position, cg.BBMin, cg.BBMax, cg.Orientation, Vector3.One); Renderer.BoundingBoxes.Add(mb); Quaternion orinv = Quaternion.Invert(cg.Orientation); - Ray mraytrn = new Ray - { - Position = orinv.Multiply(camera.MouseRay.Position - mb.CamRelPos), - Direction = orinv.Multiply(mray.Direction) - }; + Ray mraytrn = new Ray(orinv.Multiply(camera.MouseRay.Position - mb.CamRelPos), orinv.Multiply(mray.Direction)); bbox.Minimum = mb.BBMin; bbox.Maximum = mb.BBMax; if (mraytrn.Intersects(ref bbox, out hitdist) && (hitdist < CurMouseHit.HitDist) && (hitdist > 0)) @@ -2885,14 +2869,7 @@ namespace CodeWalker { if (SelectedItem.MloEntityDef == ent) continue; - MapBox mb = new MapBox - { - CamRelPos = ent.Position - camera.Position, - BBMin = /*ent?.BBMin ??*/ new Vector3(-1.5f), - BBMax = /*ent?.BBMax ??*/ new Vector3(1.5f), - Orientation = ent?.Orientation ?? Quaternion.Identity, - Scale = /*ent?.Scale ??*/ Vector3.One - }; + MapBox mb = new MapBox(ent.Position - camera.Position, new Vector3(-1.5f), new Vector3(1.5f), ent?.Orientation ?? Quaternion.Identity, Vector3.One); Renderer.BoundingBoxes.Add(mb); Quaternion orinv = Quaternion.Invert(mb.Orientation); @@ -2914,16 +2891,10 @@ namespace CodeWalker for (int i = 0; i < ymap.GrassInstanceBatches.Length; i++) { var gb = ymap.GrassInstanceBatches[i]; - if ((gb.Position - camera.Position).Length() > dmax) continue; + if ((gb.Position - camera.Position).Length() > dmax) + continue; - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = gb.AABBMin, - BBMax = gb.AABBMax, - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; + MapBox mb = new MapBox(-camera.Position, gb.AABBMin, gb.AABBMax, Quaternion.Identity, Vector3.One); Renderer.BoundingBoxes.Add(mb); bbox.Minimum = mb.BBMin; @@ -2942,18 +2913,9 @@ namespace CodeWalker var ll = ymap.LODLights; if ((((ll.BBMin + ll.BBMax) * 0.5f) - camera.Position).Length() <= dmax) { + Renderer.BoundingBoxes.Add(new MapBox(-camera.Position, ll.BBMin, ll.BBMax, Quaternion.Identity, Vector3.One)); - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = ll.BBMin, - BBMax = ll.BBMax, - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; - Renderer.BoundingBoxes.Add(mb); - - if (ll.BVH != null) + if (ll.BVH is not null) { UpdateMouseHits(ll.BVH, ref mray); } @@ -2964,15 +2926,9 @@ namespace CodeWalker var dll = ymap.DistantLODLights; if ((((dll.BBMin + dll.BBMax) * 0.5f) - camera.Position).Length() <= dmax) { - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = dll.BBMin, - BBMax = dll.BBMax, - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; - Renderer.BoundingBoxes.Add(mb); + Renderer.BoundingBoxes.Add( + new MapBox(-camera.Position, dll.BBMin, dll.BBMax, Quaternion.Identity, Vector3.One) + ); } } if ((SelectionMode == MapSelectionMode.Occlusion) && (ymap.BoxOccluders != null)) @@ -2980,18 +2936,14 @@ namespace CodeWalker for (int i = 0; i < ymap.BoxOccluders.Length; i++) { var bo = ymap.BoxOccluders[i]; - if ((bo.Position - camera.Position).Length() > dmax) continue; + var deltaPosition = bo.Position - camera.Position; + + if (deltaPosition.Length() > dmax) + continue; Renderer.RenderBasePath(bo); - MapBox mb = new MapBox - { - CamRelPos = bo.Position - camera.Position, - BBMin = bo.BBMin, - BBMax = bo.BBMax, - Orientation = bo.Orientation, - Scale = Vector3.One - }; + MapBox mb = new MapBox(deltaPosition, bo.BBMin, bo.BBMax, bo.Orientation, Vector3.One); //Renderer.BoundingBoxes.Add(mb); Quaternion orinv = Quaternion.Invert(bo.Orientation); @@ -3022,7 +2974,7 @@ namespace CodeWalker Renderer.RenderBasePath(om); var hittri = om.RayIntersect(ref mray, ref hitdist); - if ((hittri != null) && (hitdist < CurMouseHit.HitDist)) + if (hittri is not null && hitdist < CurMouseHit.HitDist) { CurMouseHit.BoxOccluder = null; CurMouseHit.OccludeModelTri = hittri; @@ -3080,64 +3032,51 @@ namespace CodeWalker } private void UpdateMouseHits(List ynvs) { - if (SelectionMode != MapSelectionMode.NavMesh) return; + if (SelectionMode != MapSelectionMode.NavMesh) + return; - Ray mray = new Ray - { - Position = camera.MouseRay.Position + camera.Position, - Direction = camera.MouseRay.Direction - }; + Ray mray = new Ray(camera.MouseRay.Position + camera.Position, camera.MouseRay.Direction); foreach (var ynv in ynvs) { if (renderpathbounds) { - if (ynv.Nav == null) continue; - if (ynv.Nav.SectorTree == null) continue; + if (ynv.Nav?.SectorTree is null) + continue; - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = ynv.Nav.SectorTree.AABBMin.XYZ(), - BBMax = ynv.Nav.SectorTree.AABBMax.XYZ(), - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; - Renderer.BoundingBoxes.Add(mb); + Renderer.BoundingBoxes.Add( + new MapBox(-camera.Position, ynv.Nav.SectorTree.AABBMin.XYZ(), ynv.Nav.SectorTree.AABBMax.XYZ(), Quaternion.Identity, Vector3.One) + ); } - if (ynv.BVH != null) + if (ynv.BVH is not null) { UpdateMouseHits(ynv.BVH, ref mray); } //if ((CurMouseHit.NavPoint != null) || (CurMouseHit.NavPortal != null)) continue; - if ((ynv.Nav != null) && (ynv.Vertices != null) && (ynv.Indices != null) && (ynv.Polys != null)) + if (ynv.Nav is not null && ynv.Vertices is not null && ynv.Indices is not null && ynv.Polys is not null) { - UpdateMouseHits(ynv, ynv.Nav.SectorTree, ynv.Nav.SectorTree, in mray); + UpdateMouseHits(ynv, ynv.Nav.SectorTree, ynv.Nav.SectorTree, ref mray); } } } - private void UpdateMouseHits(YnvFile ynv, NavMeshSector navsector, NavMeshSector rootsec, in Ray mray) + private void UpdateMouseHits(YnvFile ynv, NavMeshSector navsector, NavMeshSector rootsec, ref Ray mray) { - if (navsector == null) return; + if (navsector is null) + return; float hitdist = float.MaxValue; - BoundingBox bbox = new BoundingBox - { - Minimum = navsector.AABBMin.XYZ(), - Maximum = navsector.AABBMax.XYZ() - }; + BoundingBox bbox = new BoundingBox(navsector.AABBMin.XYZ(), navsector.AABBMax.XYZ()); - if (rootsec != null) //apparently the Z values are incorrect :( + if (rootsec is not null) //apparently the Z values are incorrect :( { bbox.Minimum.Z = rootsec.AABBMin.Z; bbox.Maximum.Z = rootsec.AABBMax.Z; } - float fhd; - if (mray.Intersects(ref bbox, out fhd)) //ray intersects this node... check children for hits! + if (mray.Intersects(ref bbox)) //ray intersects this node... check children for hits! { ////test vis //MapBox mb = new MapBox(); @@ -3149,51 +3088,45 @@ namespace CodeWalker //BoundingBoxes.Add(mb); - if (navsector.SubTree1 != null) + if (navsector.SubTree1 is not null) { - UpdateMouseHits(ynv, navsector.SubTree1, rootsec, in mray); + UpdateMouseHits(ynv, navsector.SubTree1, rootsec, ref mray); } - if (navsector.SubTree2 != null) + if (navsector.SubTree2 is not null) { - UpdateMouseHits(ynv, navsector.SubTree2, rootsec, in mray); + UpdateMouseHits(ynv, navsector.SubTree2, rootsec, ref mray); } - if (navsector.SubTree3 != null) + if (navsector.SubTree3 is not null) { - UpdateMouseHits(ynv, navsector.SubTree3, rootsec, in mray); + UpdateMouseHits(ynv, navsector.SubTree3, rootsec, ref mray); } - if (navsector.SubTree4 != null) + if (navsector.SubTree4 is not null) { - UpdateMouseHits(ynv, navsector.SubTree4, rootsec, in mray); + UpdateMouseHits(ynv, navsector.SubTree4, rootsec, ref mray); } - if ((navsector.Data != null) && (navsector.Data.PolyIDs != null)) + if (navsector.Data is not null && navsector.Data.PolyIDs is not null) { - BoundingBox cbox = new BoundingBox - { - Minimum = bbox.Minimum - camera.Position, - Maximum = bbox.Maximum - camera.Position - }; - var polys = ynv.Polys; var polyids = navsector.Data.PolyIDs; for (int i = 0; i < polyids.Length; i++) { var polyid = polyids[i]; if (polyid >= polys.Count) - { continue; } + continue; var poly = polys[polyid]; var ic = poly._RawData.IndexCount; var startid = poly._RawData.IndexID; var endid = startid + ic; if (startid >= ynv.Indices.Count) - { continue; } + continue; if (endid > ynv.Indices.Count) - { continue; } + continue; var vc = ynv.Vertices.Count; var startind = ynv.Indices[startid]; if (startind >= vc) - { continue; } + continue; Vector3 p0 = ynv.Vertices[startind]; @@ -3205,7 +3138,7 @@ namespace CodeWalker int ind1 = ynv.Indices[tid + 1]; int ind2 = ynv.Indices[tid + 2]; if ((ind1 >= vc) || (ind2 >= vc)) - { continue; } + continue; Vector3 p1 = ynv.Vertices[ind1]; Vector3 p2 = ynv.Vertices[ind2]; @@ -3227,29 +3160,20 @@ namespace CodeWalker } private void UpdateMouseHits(List ynds) { - if (SelectionMode != MapSelectionMode.Path) return; + if (SelectionMode != MapSelectionMode.Path) + return; - Ray mray = new Ray - { - Position = camera.MouseRay.Position + camera.Position, - Direction = camera.MouseRay.Direction - }; + Ray mray = new Ray(camera.MouseRay.Position + camera.Position, camera.MouseRay.Direction); foreach (var ynd in ynds) { if (renderpathbounds) { - float minz = (ynd.BVH != null) ? ynd.BVH.Box.Minimum.Z : 0.0f; - float maxz = (ynd.BVH != null) ? ynd.BVH.Box.Maximum.Z : 0.0f; - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = new Vector3(ynd.BBMin.X, ynd.BBMin.Y, minz), - BBMax = new Vector3(ynd.BBMax.X, ynd.BBMax.Y, maxz), - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; - Renderer.BoundingBoxes.Add(mb); + float minz = ynd.BVH?.Box.Minimum.Z ?? 0.0f; + float maxz = ynd.BVH?.Box.Maximum.Z ?? 0.0f; + Renderer.BoundingBoxes.Add( + new MapBox(-camera.Position, new Vector3(ynd.BBMin.X, ynd.BBMin.Y, minz), new Vector3(ynd.BBMax.X, ynd.BBMax.Y, maxz), Quaternion.Identity, Vector3.One) + ); } if (ynd.BVH != null) @@ -3259,21 +3183,21 @@ namespace CodeWalker } - if (SelectedItem.PathNode != null) + if (SelectedItem.PathNode is not null) { float linkrad = 0.25f; var n = SelectedItem.PathNode; - if (n.Links != null) + if (n.Links is not null) { foreach (var ln in n.Links) { - if (ln.Node2 == null) continue;//invalid links can hit here... + if (ln.Node2 is null) + continue;//invalid links can hit here... Vector3 dv = n.Position - ln.Node2.Position; float dl = dv.Length(); Vector3 dir = dv * (1.0f / dl); Vector3 dup = Vector3.UnitZ; - MapBox mb = new MapBox(); int lanestot = ln.LaneCountForward + ln.LaneCountBackward; float lanewidth = ln.GetLaneWidth(); @@ -3293,11 +3217,7 @@ namespace CodeWalker } - mb.CamRelPos = n.Position - camera.Position; - mb.BBMin = new Vector3(-linkrad - outer, -linkrad, 0.0f); - mb.BBMax = new Vector3(linkrad - inner, linkrad, dl); - mb.Orientation = Quaternion.Invert(Quaternion.RotationLookAtRH(dir, dup)); - mb.Scale = Vector3.One; + var mb = new MapBox(n.Position - camera.Position, new Vector3(-linkrad - outer, -linkrad, 0.0f), new Vector3(linkrad - inner, linkrad, dl), Quaternion.Invert(Quaternion.RotationLookAtRH(dir, dup)), Vector3.One); if (ln == SelectedItem.PathLink) { Renderer.HilightBoxes.Add(mb); @@ -3313,13 +3233,10 @@ namespace CodeWalker } private void UpdateMouseHits(List tracks) { - if (SelectionMode != MapSelectionMode.TrainTrack) return; + if (SelectionMode != MapSelectionMode.TrainTrack) + return; - Ray mray = new Ray - { - Position = camera.MouseRay.Position + camera.Position, - Direction = camera.MouseRay.Direction - }; + Ray mray = new Ray(camera.MouseRay.Position + camera.Position, camera.MouseRay.Direction); foreach (var track in tracks) { @@ -3354,16 +3271,10 @@ namespace CodeWalker float dl = dv.Length(); Vector3 dir = dv * (1.0f / dl); Vector3 dup = Vector3.UnitZ; - MapBox mb = new MapBox - { - CamRelPos = n.Position - camera.Position, - BBMin = new Vector3(-linkrad, -linkrad, 0.0f), - BBMax = new Vector3(linkrad, linkrad, dl), - Orientation = Quaternion.Invert(Quaternion.RotationLookAtRH(dir, dup)), - Scale = Vector3.One, - }; - Renderer.BoundingBoxes.Add(mb); + Renderer.BoundingBoxes.Add( + new MapBox(n.Position - camera.Position, new Vector3(-linkrad, -linkrad, 0.0f), new Vector3(linkrad, linkrad, dl), Quaternion.Invert(Quaternion.RotationLookAtRH(dir, dup)), Vector3.One) + ); } } } @@ -3371,40 +3282,32 @@ namespace CodeWalker } private void UpdateMouseHits(List scenarios) { - if (SelectionMode != MapSelectionMode.Scenario) return; + if (SelectionMode != MapSelectionMode.Scenario) + return; - Ray mray = new Ray - { - Position = camera.MouseRay.Position + camera.Position, - Direction = camera.MouseRay.Direction - }; + Ray mray = new Ray(camera.MouseRay.Position + camera.Position, camera.MouseRay.Direction); foreach (var scenario in scenarios) { var sr = scenario.ScenarioRegion; - if (sr == null) continue; + if (sr is null) + continue; if (renderscenariobounds) { - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = sr?.BVH?.Box.Minimum ?? Vector3.Zero, - BBMax = sr?.BVH?.Box.Maximum ?? Vector3.Zero, - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; - Renderer.BoundingBoxes.Add(mb); + Renderer.BoundingBoxes.Add( + new MapBox(-camera.Position, sr.BVH?.Box.Minimum ?? Vector3.Zero, sr.BVH?.Box.Maximum ?? Vector3.Zero, Quaternion.Identity, Vector3.One) + ); } - if (sr.BVH != null) + if (sr.BVH is not null) { UpdateMouseHits(sr.BVH, ref mray); } } - if (SelectedItem.ScenarioNode != null) //move this stuff to renderselection..? + if (SelectedItem.ScenarioNode is not null) //move this stuff to renderselection..? { var n = SelectedItem.ScenarioNode; var nc = n.ChainingNode?.Chain; @@ -3434,14 +3337,7 @@ namespace CodeWalker var sr = SelectedItem.ScenarioNode.Ymt.ScenarioRegion; //if (renderscenariobounds) { - MapBox mb = new MapBox - { - CamRelPos = -camera.Position, - BBMin = sr?.BVH?.Box.Minimum ?? Vector3.Zero, - BBMax = sr?.BVH?.Box.Maximum ?? Vector3.Zero, - Orientation = Quaternion.Identity, - Scale = Vector3.One - }; + MapBox mb = new MapBox(-camera.Position, sr?.BVH?.Box.Minimum ?? Vector3.Zero, sr?.BVH?.Box.Maximum ?? Vector3.Zero, Quaternion.Identity, Vector3.One); if (renderscenariobounds) { Renderer.HilightBoxes.Add(mb); @@ -3453,84 +3349,50 @@ namespace CodeWalker } - if (ncl != null) + if (ncl is not null) { - //hilight the cluster itself - MapBox mb = new MapBox - { - Scale = Vector3.One, - BBMin = new Vector3(-0.5f), - BBMax = new Vector3(0.5f), - CamRelPos = ncl.Position - camera.Position, - Orientation = Quaternion.Identity, - }; - - Renderer.HilightBoxes.Add(mb); - + Renderer.HilightBoxes.Add( + new MapBox(ncl.Position - camera.Position, new Vector3(-0.5f), new Vector3(0.5f), Quaternion.Identity, Vector3.One) + ); //show boxes for points in the cluster - if ((ncl.Points != null) && (ncl.Points.MyPoints != null)) + if (ncl.Points?.MyPoints is not null) { foreach (var clpoint in ncl.Points.MyPoints) { if (clpoint == n.ClusterMyPoint) continue; //don't highlight the selected node... - mb = new MapBox - { - Scale = Vector3.One, - BBMin = new Vector3(-0.5f), - BBMax = new Vector3(0.5f), - CamRelPos = clpoint.Position - camera.Position, - Orientation = clpoint.Orientation, - }; - Renderer.BoundingBoxes.Add(mb); + Renderer.BoundingBoxes.Add( + new MapBox(clpoint.Position - camera.Position, new Vector3(-0.5f), new Vector3(0.5f), clpoint.Orientation, Vector3.One) + ); } } } - - - } - - - - } - private void UpdateMouseHits(PathBVHNode pathbvhnode, in Ray mray) + + private void UpdateMouseHits(PathBVHNode pathbvhnode, ref Ray mray) { float nrad = 0.5f; - float hitdist = float.MaxValue; - BoundingSphere bsph = new BoundingSphere - { - Radius = nrad - }; + BoundingSphere bsph = new BoundingSphere(default, nrad); - BoundingBox bbox = new BoundingBox - { - Minimum = pathbvhnode.Box.Minimum - nrad, - Maximum = pathbvhnode.Box.Maximum + nrad - }; + BoundingBox bbox = new BoundingBox(pathbvhnode.Box.Minimum - nrad, pathbvhnode.Box.Maximum + nrad); + BoundingBox nbox = new BoundingBox(new Vector3(-nrad), new Vector3(nrad)); - BoundingBox nbox = new BoundingBox + if (mray.Intersects(ref bbox, out float _)) //ray intersects this node... check children for hits! { - Minimum = new Vector3(-nrad), - Maximum = new Vector3(nrad) - }; - - float fhd; - if (mray.Intersects(ref bbox, out fhd)) //ray intersects this node... check children for hits! - { - if ((pathbvhnode.Node1 != null) && (pathbvhnode.Node2 != null)) //node is split. recurse + if (pathbvhnode.Node1 is not null && pathbvhnode.Node2 is not null) //node is split. recurse { - UpdateMouseHits(pathbvhnode.Node1, in mray); - UpdateMouseHits(pathbvhnode.Node2, in mray); + UpdateMouseHits(pathbvhnode.Node1, ref mray); + UpdateMouseHits(pathbvhnode.Node2, ref mray); } - else if (pathbvhnode.Nodes != null) //leaf node. test contaned pathnodes + else if (pathbvhnode.Nodes is not null) //leaf node. test contaned pathnodes { foreach (var n in pathbvhnode.Nodes) { bsph.Center = n.Position; + float hitdist; if (mray.Intersects(ref bsph, out hitdist) && (hitdist < CurMouseHit.HitDist) && (hitdist > 0)) { CurMouseHit.PathNode = n as YndNode; @@ -4390,10 +4252,9 @@ namespace CodeWalker await LoadWorldAsync(); } } - Invoke(new Action(() => - { - Cursor = Cursors.Default; - })); + + await this.SwitchToUiContext(); + Cursor = Cursors.Default; }); } @@ -4420,9 +4281,10 @@ namespace CodeWalker Settings.Default.Save(); } } - catch + catch(Exception ex) { MessageBox.Show("Keys not found! This shouldn't happen."); + Console.WriteLine(ex); Close(); return; } @@ -4512,8 +4374,8 @@ namespace CodeWalker StatusLabel.Text = text; } - private Stopwatch lastStatusUpdate = Stopwatch.StartNew(); - private TimeSpan updateInterval = TimeSpan.FromSeconds(0.05); + private readonly Stopwatch lastStatusUpdate = Stopwatch.StartNew(); + private readonly TimeSpan updateInterval = TimeSpan.FromSeconds(0.05); private Renderer renderer; private void UpdateStatus(string text) @@ -4616,49 +4478,36 @@ namespace CodeWalker Console.WriteLine(ex); } } - private void UpdateDlcListComboBox(List dlcnames) + private async void UpdateDlcListComboBox(List dlcnames) { try { - if (InvokeRequired) + await this.SwitchToUiContext(); + DlcLevelComboBox.Items.Clear(); + foreach (var dlcname in dlcnames) { - BeginInvoke(UpdateDlcListComboBox, dlcnames); + DlcLevelComboBox.Items.Add(dlcname); + } + if (string.IsNullOrEmpty(GameFileCache.SelectedDlc)) + { + DlcLevelComboBox.SelectedIndex = dlcnames.Count - 1; } else { - DlcLevelComboBox.Items.Clear(); - foreach (var dlcname in dlcnames) - { - DlcLevelComboBox.Items.Add(dlcname); - } - if (string.IsNullOrEmpty(GameFileCache.SelectedDlc)) - { - DlcLevelComboBox.SelectedIndex = dlcnames.Count - 1; - } - else - { - int idx = DlcLevelComboBox.FindString(GameFileCache.SelectedDlc); - DlcLevelComboBox.SelectedIndex = (idx > 0) ? idx : (dlcnames.Count - 1); - } + int idx = DlcLevelComboBox.FindString(GameFileCache.SelectedDlc); + DlcLevelComboBox.SelectedIndex = (idx > 0) ? idx : (dlcnames.Count - 1); } } catch (Exception ex) { Console.WriteLine(ex); } } - private void LogError(string text) + private async void LogError(string text) { try { - if (InvokeRequired) - { - Invoke(LogError, text); - } - else - { - ConsoleTextBox.AppendText(text + "\r\n"); - Console.WriteLine(text); - //MessageBox.Show(text); - } + Console.WriteLine(text); + await this.SwitchToUiContext(); + ConsoleTextBox.AppendText($"{text}\r\n"); } catch (Exception ex) { Console.WriteLine(ex); @@ -4782,16 +4631,16 @@ namespace CodeWalker public MapMarker AddMarker(Vector3 pos, string name, bool addtotxtbox = false) { - string str = pos.X.ToString() + ", " + pos.Y.ToString() + ", " + pos.Z.ToString(); + string str = $"{pos.X}, {pos.Y}, {pos.Z}"; if (!string.IsNullOrEmpty(name)) { - str += ", " + name; + str += $", {name}"; } if (addtotxtbox) { StringBuilder sb = new StringBuilder(); sb.Append(MultiFindTextBox.Text); - if ((sb.Length > 0) && (!MultiFindTextBox.Text.EndsWith("\n"))) + if (sb.Length > 0 && !MultiFindTextBox.Text.EndsWith('\n')) { sb.AppendLine(); } @@ -4806,7 +4655,7 @@ namespace CodeWalker lock (markersyncroot) { MapMarker m = new MapMarker(); - m.Parse(markerstr.Trim()); + m.Parse(markerstr); m.Icon = MarkerIcon; Markers.Add(m); @@ -5286,19 +5135,22 @@ namespace CodeWalker } private async ValueTask Save() { - if (ProjectForm == null) return; + if (ProjectForm is null) + return; await ProjectForm.Save(); } private async ValueTask SaveAll() { - if (ProjectForm == null) return; + if (ProjectForm is null) + return; await ProjectForm.SaveAll(); } private void AddItem() { - if (ProjectForm == null) return; + if (ProjectForm is null) + return; switch (SelectionMode) { case MapSelectionMode.Entity: ProjectForm.NewEntity(); break; @@ -6152,7 +6004,7 @@ namespace CodeWalker CameraPositionTextBox.Text = FloatUtil.GetVector3StringFormat(camera.Position, "0.##"); } - private bool isRenderedLoaded = false; + private readonly bool isRenderedLoaded = false; private async void WorldForm_Load(object sender, EventArgs e) { await InitAsync();