mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-16 20:17:30 +08:00
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
This commit is contained in:
parent
ed68bd59fd
commit
da3dc2f8f3
@ -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<uint, MetaHash>(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<char> 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<byte> data)
|
||||
{
|
||||
Unsafe.WriteUnaligned(ref data[0], new Int128(ulong.MaxValue, ulong.MaxValue));
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
public static void WriteData(Span<byte> data)
|
||||
{
|
||||
Unsafe.WriteUnaligned<ulong>(ref data[0], ulong.MaxValue);
|
||||
Unsafe.WriteUnaligned<ulong>(ref data[8], ulong.MaxValue);
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
public static void WriteDataOld(Span<byte> data)
|
||||
{
|
||||
Unsafe.WriteUnaligned<ulong>(ref MemoryMarshal.GetReference(data[..8]), ulong.MaxValue);
|
||||
Unsafe.WriteUnaligned<ulong>(ref MemoryMarshal.GetReference(data.Slice(8, 8)), ulong.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[SkipLocalsInit]
|
||||
public static uint GenHashLowerDirect(ReadOnlySpan<char> 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<char> 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<char> data)
|
||||
{
|
||||
Span<char> 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<SimpleType3> getPooledListClass()
|
||||
{
|
||||
var list = new PooledList<SimpleType3>();
|
||||
@ -343,22 +598,25 @@ namespace CodeWalker.Benchmarks
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Benchmark]
|
||||
public void ReverseEndianness()
|
||||
[SkipLocalsInit]
|
||||
public void WriteData()
|
||||
{
|
||||
//BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<float, uint>(ushorts), MemoryMarshal.Cast<float, uint>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<CacheFileDate>();
|
||||
using var allMapNodes = new PooledList<MapDataStoreNode>();
|
||||
using var allCInteriorProxies = new PooledList<CInteriorProxy>();
|
||||
var allMapNodes = PooledListPool<MapDataStoreNode>.Shared.Get();
|
||||
using var allCInteriorProxies = new PooledList<CInteriorProxy>();;
|
||||
using var allBoundsStoreItems = new PooledList<BoundsStoreItem>();
|
||||
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<MapDataStoreNode>.Shared.Return(allMapNodes);
|
||||
|
||||
MapNodeDict = new Dictionary<uint, MapDataStoreNode>();
|
||||
var rootMapNodes = new List<MapDataStoreNode>();
|
||||
var rootMapNodes = PooledListPool<MapDataStoreNode>.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<MapDataStoreNode>.Shared.Return(rootMapNodes);
|
||||
|
||||
|
||||
|
||||
BoundsStoreDict = new Dictionary<MetaHash, BoundsStoreItem>();
|
||||
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<MapDataStoreNode> ChildrenList; //used when building the array
|
||||
private PooledList<MapDataStoreNode>? ChildrenList; //used when building the array
|
||||
|
||||
public CInteriorProxy[] InteriorProxies { get; set; }
|
||||
private List<CInteriorProxy> InteriorProxyList;
|
||||
private PooledList<CInteriorProxy>? 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<MapDataStoreNode>();
|
||||
ChildrenList ??= PooledListPool<MapDataStoreNode>.Shared.Get();
|
||||
ChildrenList.Add(child);
|
||||
}
|
||||
public void ChildrenListToArray()
|
||||
@ -907,12 +889,13 @@ namespace CodeWalker.GameFiles
|
||||
if (ChildrenList is not null)
|
||||
{
|
||||
Children = ChildrenList.ToArray();
|
||||
PooledListPool<MapDataStoreNode>.Shared.Return(ChildrenList);
|
||||
ChildrenList = null; //plz get this GC
|
||||
}
|
||||
}
|
||||
public void AddInteriorToList(CInteriorProxy iprx)
|
||||
{
|
||||
InteriorProxyList ??= new List<CInteriorProxy>();
|
||||
InteriorProxyList ??= PooledListPool<CInteriorProxy>.Shared.Get();
|
||||
InteriorProxyList.Add(iprx);
|
||||
}
|
||||
public void InteriorProxyListToArray()
|
||||
@ -920,6 +903,7 @@ namespace CodeWalker.GameFiles
|
||||
if (InteriorProxyList is not null)
|
||||
{
|
||||
InteriorProxies = InteriorProxyList.ToArray();
|
||||
PooledListPool<CInteriorProxy>.Shared.Return(InteriorProxyList);
|
||||
InteriorProxyList = null; //plz get this GC
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<uint, Drawable> Dict { get; set; }
|
||||
public Drawable[] Drawables { get; set; }
|
||||
public Dictionary<uint, Drawable>? Dict { get; set; }
|
||||
public Drawable[]? Drawables { get; set; }
|
||||
|
||||
public YddFile() : base(null, GameFileType.Ydd)
|
||||
{
|
||||
|
@ -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<YmapFile> parent = new WeakReference<YmapFile>(null);
|
||||
private WeakReference<YmapFile?> parent = new WeakReference<YmapFile?>(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<YmapEntityDef> roots = new List<YmapEntityDef>(instcount);
|
||||
List<YmapEntityDef> alldefs = new List<YmapEntityDef>(instcount);
|
||||
List<YmapEntityDef> mlodefs = null;
|
||||
var roots = PooledListPool<YmapEntityDef>.Shared.Get();
|
||||
var alldefs = PooledListPool<YmapEntityDef>.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<YmapEntityDef>(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<YmapEntityDef>.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<YmapEntityDef>();
|
||||
|
||||
foreach(var ent in AllEntities)
|
||||
PooledListPool<YmapEntityDef>.Shared.Return(roots);
|
||||
|
||||
foreach(var ent in allEntities)
|
||||
{
|
||||
ent.Extensions = MetaTypes.GetExtensions(Meta, in ent._CEntityDef.extensions) ?? Array.Empty<MetaWrapper>();
|
||||
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<YmapEntityDef> LodManagerChildren { get; set; } = null;
|
||||
[TypeConverter(typeof(LinkedListConverter<YmapEntityDef>))]
|
||||
public LinkedList<YmapEntityDef>? 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();
|
||||
|
@ -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<Vector4>();
|
||||
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<EditorVertex> verts = new PooledList<EditorVertex>(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<EditorVertex> verts = PooledListPool<EditorVertex>.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<EditorVertex>.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<EditorVertex> verts = new PooledList<EditorVertex>();
|
||||
PooledList<EditorVertex> verts = PooledListPool<EditorVertex>.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<EditorVertex>.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<YndLink>();
|
||||
}
|
||||
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<YndFile>();
|
||||
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<PooledList<BasePathNode>> basePathNodeListPool => PooledListPool<BasePathNode>.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<BasePathNode> l1 = new PooledList<BasePathNode>(Nodes.Length);
|
||||
using PooledList<BasePathNode> l2 = new PooledList<BasePathNode>(Nodes.Length);
|
||||
PooledList<BasePathNode> l1 = PooledListPool<BasePathNode>.Shared.Get();
|
||||
PooledList<BasePathNode> l2 = PooledListPool<BasePathNode>.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<BasePathNode>.Shared.Return(l1);
|
||||
var nodesL2 = l2.ToArray();
|
||||
PooledListPool<BasePathNode>.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();
|
||||
|
@ -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<GameFileCacheKey>, 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,
|
||||
}
|
||||
|
||||
|
||||
|
@ -1052,11 +1052,12 @@ namespace CodeWalker.GameFiles
|
||||
YftDict ??= new Dictionary<uint, RpfFileEntry>(40000);
|
||||
YcdDict ??= new Dictionary<uint, RpfFileEntry>(20000);
|
||||
YedDict ??= new Dictionary<uint, RpfFileEntry>(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();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1931,64 +1931,71 @@ namespace CodeWalker.GameFiles
|
||||
return null; //couldn't find the strings data section.
|
||||
}
|
||||
|
||||
using PooledList<string> strings = new PooledList<string>();
|
||||
var currentblock = startblock;
|
||||
int currentblockind = startblockind;
|
||||
while (currentblock != null)
|
||||
PooledList<string> strings = PooledListPool<string>.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<string>.Shared.Return(strings);
|
||||
}
|
||||
return strings.ToArray();
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
@ -4921,7 +4928,7 @@ namespace CodeWalker.GameFiles
|
||||
public void AddScenarioPoint(MCExtensionDefSpawnPoint p)
|
||||
{
|
||||
List<MCExtensionDefSpawnPoint> newpoints = new List<MCExtensionDefSpawnPoint>();
|
||||
if (ScenarioPoints != null)
|
||||
if (ScenarioPoints is not null)
|
||||
{
|
||||
newpoints.AddRange(ScenarioPoints);
|
||||
}
|
||||
|
@ -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<StringBuilder> StringBuilderPool = ObjectPool.Create<StringBuilder>(new StringBuilderPooledObjectPolicyLogged { MaximumRetainedCapacity = 4 * 1024 * 1024, InitialCapacity = 1024 * 32 });
|
||||
public static ObjectPool<StringBuilder> StringBuilderPool = ObjectPool.Create<StringBuilder>(new StringBuilderPooledObjectPolicy { MaximumRetainedCapacity = 4 * 1024 * 1024, InitialCapacity = 1024 * 32 });
|
||||
public const string XmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||||
|
||||
|
||||
|
@ -450,7 +450,6 @@ namespace CodeWalker.GameFiles
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class RbfStructure : IRbfType, IDisposable
|
||||
{
|
||||
private static ObjectPool<PooledList<IRbfType>> listPool = ObjectPool.Create(new DefaultPooledObjectPolicy<PooledList<IRbfType>>());
|
||||
public string Name { get; set; }
|
||||
|
||||
public PooledList<IRbfType>? Children { get; set; }
|
||||
@ -519,13 +518,13 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
internal void AddChild(IRbfType value)
|
||||
{
|
||||
Children ??= listPool.Get();
|
||||
Children ??= PooledListPool<IRbfType>.Shared.Get();
|
||||
Children.Add(value);
|
||||
}
|
||||
|
||||
internal void AddAttribute(IRbfType value)
|
||||
{
|
||||
Attributes ??= listPool.Get();
|
||||
Attributes ??= PooledListPool<IRbfType>.Shared.Get();
|
||||
Attributes.Add(value);
|
||||
}
|
||||
|
||||
@ -546,11 +545,11 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
if (Children is PooledList<IRbfType> children)
|
||||
{
|
||||
listPool.Return(children);
|
||||
PooledListPool<IRbfType>.Shared.Return(children);
|
||||
}
|
||||
if (Attributes is PooledList<IRbfType> attributes)
|
||||
{
|
||||
listPool.Return(attributes);
|
||||
PooledListPool<IRbfType>.Shared.Return(attributes);
|
||||
}
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -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
|
||||
|
@ -98,7 +98,7 @@ namespace CodeWalker.GameFiles
|
||||
public string Name { get; set; }
|
||||
public FragBoneTransforms BoneTransforms { get; set; }
|
||||
public ResourcePointerArray64<FragGlassWindow> 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<FragPhysTypeGroup> Groups { get; set; }
|
||||
public ResourcePointerArray64<FragPhysTypeChild> Children { get; set; }
|
||||
public ResourcePointerArray64<FragPhysTypeGroup>? Groups { get; set; }
|
||||
public ResourcePointerArray64<FragPhysTypeChild>? Children { get; set; }
|
||||
public FragPhysArchetype Archetype1 { get; set; }
|
||||
public FragPhysArchetype Archetype2 { get; set; }
|
||||
public Bounds Bound { get; set; }
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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<string, RpfEntry[]>? FilesByFileType;
|
||||
public RpfEntry[] GetFilesByFileType(string ext)
|
||||
{
|
||||
FilesByFileType ??= new Dictionary<string, RpfEntry[]>();
|
||||
if (!FilesByFileType.TryGetValue(ext, out var entries))
|
||||
{
|
||||
entries = AllEntries.Where(p => p.IsExtension(ext)).ToArray();
|
||||
FilesByFileType[ext] = entries;
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
|
@ -64,14 +64,16 @@ namespace CodeWalker.GameFiles
|
||||
BaseRpfs = new List<RpfFile>(1300);
|
||||
ModRpfs = new List<RpfFile>(0);
|
||||
DlcRpfs = new List<RpfFile>(3500);
|
||||
AllRpfs = new List<RpfFile>(5000);
|
||||
AllRpfs ??= new List<RpfFile>(0);
|
||||
DlcNoModRpfs = new List<RpfFile>(3500);
|
||||
AllNoModRpfs = new List<RpfFile>(5000);
|
||||
RpfDict = new Dictionary<string, RpfFile>(DefaultRpfDictCapacity, StringComparer.OrdinalIgnoreCase);
|
||||
EntryDict = new Dictionary<string, RpfEntry>(DefaultEntryDictCapacity, StringComparer.OrdinalIgnoreCase);
|
||||
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
|
||||
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
|
||||
ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
FileCounts _fileCounts = default;
|
||||
|
||||
var rpfs = new ConcurrentBag<RpfFile>();
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -292,6 +292,7 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
public static class JenkIndex
|
||||
{
|
||||
//public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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<T> : PooledObjectPolicy<PooledList<T>>
|
||||
{
|
||||
private readonly ClearMode clearMode;
|
||||
public PooledListObjectPolicy(ClearMode _clearMode = ClearMode.Auto)
|
||||
{
|
||||
clearMode = _clearMode;
|
||||
}
|
||||
public PooledList<T> Get()
|
||||
{
|
||||
return new PooledList<T>();
|
||||
return new PooledList<T>(clearMode);
|
||||
}
|
||||
|
||||
public override PooledList<T> Create()
|
||||
{
|
||||
return new PooledList<T>();
|
||||
return new PooledList<T>(clearMode);
|
||||
}
|
||||
|
||||
public override bool Return(PooledList<T> 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<T>
|
||||
{
|
||||
private static readonly ObjectPool<PooledList<T>> s_shared = ObjectPool.Create(new PooledListObjectPolicy<T>(ClearMode.Never));
|
||||
public static ObjectPool<PooledList<T>> Shared => s_shared;
|
||||
}
|
||||
|
||||
public static class PooledListExtensions
|
||||
{
|
||||
public static int EnsureCapacity<T>(this PooledList<T> list, int capacity)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(capacity, 0, nameof(capacity));
|
||||
|
||||
if (list.Capacity < capacity)
|
||||
{
|
||||
list.Capacity = capacity;
|
||||
}
|
||||
|
||||
return list.Capacity;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StringPoolExtension
|
||||
{
|
||||
[SkipLocalsInit]
|
||||
|
@ -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<TimeSpan, string> TimerStopped;
|
||||
public Stopwatch Stopwatch { get; init; }
|
||||
public static event Action<TimeSpan, string> 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<TimeSpan, string> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
186
CodeWalker.Core/Utils/TypeConverters/LinkedListConverter.cs
Normal file
186
CodeWalker.Core/Utils/TypeConverters/LinkedListConverter.cs
Normal file
@ -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<T> : PropertyDescriptor
|
||||
{
|
||||
private readonly LinkedListNode<T> node;
|
||||
|
||||
public LinkedListPropertyDescripter(LinkedListNode<T> 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<T> : CollectionConverter
|
||||
{
|
||||
public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
|
||||
|
||||
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
|
||||
{
|
||||
LinkedList<T> list = value as LinkedList<T>;
|
||||
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<T>(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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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<YmtFile>(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<YmtFile>(usefilename) ?? await rpfman.GetFileAsync<YmtFile>(basefilename);
|
||||
//YmtFile? regionymt = await rpfman.GetFileAsync<YmtFile>(usefilename) ?? await rpfman.GetFileAsync<YmtFile>(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<MCScenarioChainingEdge> chainedges = new List<MCScenarioChainingEdge>();
|
||||
|
||||
if ((r.Paths.Chains != null) && (r.Paths.Edges != null))
|
||||
if (r.Paths.Chains is not null && r.Paths.Edges is not null)
|
||||
{
|
||||
List<MCScenarioChainingEdge> chainedges = new List<MCScenarioChainingEdge>();
|
||||
|
||||
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<EditorVertex> pathverts = new List<EditorVertex>();
|
||||
|
||||
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<EditorVertex> pathverts = new List<EditorVertex>();
|
||||
|
||||
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<uint, ScenarioTypeRef> TypeRefs { get; set; }
|
||||
private Dictionary<uint, ScenarioType> Types { get; set; }
|
||||
private Dictionary<uint, ScenarioTypeGroup> TypeGroups { get; set; }
|
||||
private Dictionary<uint, AmbientModelSet> PropSets { get; set; }
|
||||
private Dictionary<uint, AmbientModelSet> PedModelSets { get; set; }
|
||||
private Dictionary<uint, AmbientModelSet> VehicleModelSets { get; set; }
|
||||
private Dictionary<uint, ConditionalAnimsGroup> AnimGroups { get; set; }
|
||||
private Dictionary<uint, ScenarioTypeRef>? TypeRefs { get; set; }
|
||||
private Dictionary<uint, ScenarioType>? Types { get; set; }
|
||||
private Dictionary<uint, ScenarioTypeGroup>? TypeGroups { get; set; }
|
||||
private Dictionary<uint, AmbientModelSet>? PropSets { get; set; }
|
||||
private Dictionary<uint, AmbientModelSet>? PedModelSets { get; set; }
|
||||
private Dictionary<uint, AmbientModelSet>? VehicleModelSets { get; set; }
|
||||
private Dictionary<uint, ConditionalAnimsGroup>? 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<uint, ScenarioTypeRef>(Types.Count + TypeGroups.Count);
|
||||
foreach (var kvp in Types)
|
||||
TypeRefs = new Dictionary<uint, ScenarioTypeRef>(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<AmbientModel>();
|
||||
var modellist = new List<AmbientModel>(models.Count);
|
||||
foreach (XmlNode item in models)
|
||||
{
|
||||
AmbientModel model = new AmbientModel();
|
||||
|
@ -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<MetaHash, MapDataStoreNode>(6000);
|
||||
//List<BoundsStoreItem> intlist = new List<BoundsStoreItem>();
|
||||
@ -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<uint, RpfFileEntry> GetYndEntries()
|
||||
{
|
||||
using var _ = new DisposableTimer("InitNodeGrid");
|
||||
NodeGrid = new SpaceNodeGrid();
|
||||
AllYnds.Clear();
|
||||
|
||||
using var addRpfYndTimer = new DisposableTimer($"{nameof(InitNodeGrid)} -> AddRpfYnds");
|
||||
var rpfman = GameFileCache.RpfMan;
|
||||
Dictionary<uint, RpfFileEntry> yndentries = new Dictionary<uint, RpfFileEntry>();
|
||||
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<YndFile>(fentry);
|
||||
cell.Ynd = await RpfManager.GetFileAsync<YndFile>(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<EditorVertex> tverts = new List<EditorVertex>();
|
||||
using PooledList<YndLink> tlinks = new PooledList<YndLink>();
|
||||
using PooledList<YndLink> nlinks = new PooledList<YndLink>();
|
||||
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<uint, RpfFileEntry> yndentries)
|
||||
private static void AddRpfYnds(RpfFile rpffile, Dictionary<uint, RpfFileEntry> 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<YndLink>? tlinks = null, IList<YndLink>? 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<YndLink>();
|
||||
nlinks ??= new PooledList<YndLink>();
|
||||
var tlinks = PooledListPool<YndLink>.Shared.Get();
|
||||
var nlinks = PooledListPool<YndLink>.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<YndLink>.Shared.Return(tlinks);
|
||||
PooledListPool<YndLink>.Shared.Return(nlinks);
|
||||
}
|
||||
public void BuildYndVerts(YndFile ynd, YndNode[]? selectedNodes, IList<EditorVertex>? 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<EditorVertex>();
|
||||
var tverts = PooledListPool<EditorVertex>.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<EditorVertex>.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<EditorVertex>? tverts = null, IList<YndLink>? tlinks = null, IList<YndLink>? 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<uint, RpfFileEntry> 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<MetaHash, YmapFile> 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<BoundsStoreItem> 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<BoundsStoreItem> Items = null;
|
||||
public SpaceBoundsStore? Owner = null;
|
||||
public SpaceBoundsStoreNode[]? Children = null;
|
||||
public PooledList<BoundsStoreItem>? 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<BoundsStoreItem>();
|
||||
}
|
||||
BBMin = Vector3.Min(BBMin, item.Min);
|
||||
BBMax = Vector3.Max(BBMax, item.Max);
|
||||
Items ??= PooledListPool<BoundsStoreItem>.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<BoundsStoreItem> items)
|
||||
{
|
||||
Items ??= PooledListPool<BoundsStoreItem>.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<BoundsStoreItem>();
|
||||
var newItems = PooledListPool<BoundsStoreItem>.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<BoundsStoreItem>.Shared.Return(Items);
|
||||
}
|
||||
|
||||
Items = newItems;
|
||||
}
|
||||
|
||||
public void GetItems(in Vector3 min, in Vector3 max, List<BoundsStoreItem> items, bool[] layers = null)
|
||||
public void GetItems(in Vector3 min, in Vector3 max, List<BoundsStoreItem> 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<BoundsStoreItem> 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)
|
||||
{
|
||||
|
@ -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<TimecycleModValue> vals = new List<TimecycleModValue>();
|
||||
var vals = PooledListPool<TimecycleModValue>.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<TimecycleModValue>.Shared.Return(vals);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
@ -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);
|
||||
}
|
||||
|
186
CodeWalker.WinForms/Utils/TypeConverters.cs
Normal file
186
CodeWalker.WinForms/Utils/TypeConverters.cs
Normal file
@ -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<T> : PropertyDescriptor
|
||||
{
|
||||
private readonly LinkedListNode<T> node;
|
||||
|
||||
public LinkedListPropertyDescripter(LinkedListNode<T> 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<T> : CollectionConverter
|
||||
{
|
||||
public override bool GetPropertiesSupported(ITypeDescriptorContext context) => true;
|
||||
|
||||
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
|
||||
{
|
||||
LinkedList<T> list = value as LinkedList<T>;
|
||||
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<T>(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;
|
||||
}
|
||||
}
|
@ -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<YtdFile>(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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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}";
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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<string, YbnFile> visibleybns = new Dictionary<string, YbnFile>(StringComparer.OrdinalIgnoreCase);
|
||||
private Dictionary<int, YndFile> visibleynds = new Dictionary<int, YndFile>();
|
||||
private Dictionary<int, YnvFile> visibleynvs = new Dictionary<int, YnvFile>();
|
||||
private Dictionary<string, TrainTrack> visibletrains = new Dictionary<string, TrainTrack>(StringComparer.OrdinalIgnoreCase);
|
||||
private Dictionary<string, YmtFile> visiblescenarios = new Dictionary<string, YmtFile>(StringComparer.OrdinalIgnoreCase);
|
||||
private Dictionary<uint, YmapEntityDef> visiblemloentities = new Dictionary<uint, YmapEntityDef>();
|
||||
private Dictionary<uint, RelFile> visibleaudiofiles = new Dictionary<uint, RelFile>();
|
||||
private readonly Dictionary<string, YbnFile> visibleybns = new Dictionary<string, YbnFile>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<int, YndFile> visibleynds = new Dictionary<int, YndFile>();
|
||||
private readonly Dictionary<int, YnvFile> visibleynvs = new Dictionary<int, YnvFile>();
|
||||
private readonly Dictionary<string, TrainTrack> visibletrains = new Dictionary<string, TrainTrack>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<string, YmtFile> visiblescenarios = new Dictionary<string, YmtFile>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<uint, YmapEntityDef> visiblemloentities = new Dictionary<uint, YmapEntityDef>();
|
||||
private readonly Dictionary<uint, RelFile> visibleaudiofiles = new Dictionary<uint, RelFile>();
|
||||
|
||||
private Dictionary<uint, YbnFile> projectybns = new Dictionary<uint, YbnFile>();//used for handling interior ybns
|
||||
private readonly Dictionary<uint, YbnFile> projectybns = new Dictionary<uint, YbnFile>();//used for handling interior ybns
|
||||
|
||||
private List<YmapEntityDef> interiorslist = new List<YmapEntityDef>(); //used for handling interiors ybns
|
||||
private readonly List<YmapEntityDef> interiorslist = new List<YmapEntityDef>(); //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<T>(Func<T> createFunc, Action<T>? 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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ApplicationRevision>1</ApplicationRevision>
|
||||
<ApplicationRevision>2</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.*</ApplicationVersion>
|
||||
<BootstrapperEnabled>True</BootstrapperEnabled>
|
||||
<Configuration>Release</Configuration>
|
||||
|
@ -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<RenderableModel>();
|
||||
public RenderableModel[] HDModels = [];
|
||||
public RenderableModel[]? MedModels;
|
||||
public RenderableModel[]? LowModels;
|
||||
public RenderableModel[]? VlowModels;
|
||||
public RenderableModel[] AllModels = Array.Empty<RenderableModel>();
|
||||
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<ushort, RenderableModel>? 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<RenderableModel>();
|
||||
return [];
|
||||
}
|
||||
else if (distance > LodDistanceLow)
|
||||
{
|
||||
return VlowModels ?? Array.Empty<RenderableModel>();
|
||||
return VlowModels ?? [];
|
||||
}
|
||||
else if (distance > LodDistanceMed)
|
||||
{
|
||||
return LowModels ?? Array.Empty<RenderableModel>();
|
||||
return LowModels ?? [];
|
||||
}
|
||||
else if (distance > LodDistanceHigh)
|
||||
{
|
||||
return MedModels ?? Array.Empty<RenderableModel>();
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -106,12 +106,9 @@ namespace CodeWalker.Rendering
|
||||
PSSceneVars = new GpuVarsBuffer<CableShaderPSSceneVars>(device);
|
||||
PSGeomVars = new GpuVarsBuffer<CableShaderPSGeomVars>(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,
|
||||
|
@ -76,7 +76,6 @@ namespace CodeWalker.Rendering
|
||||
MinimumLod = 0,
|
||||
MipLodBias = 0,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ namespace CodeWalker.Rendering.Utils
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(folder);
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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<YtdFile>(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<YtdFile>(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<YdrFile>(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<YdrFile>(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<YddFile>(entry);
|
||||
if (ydd == null) throw new Exception("Couldn't load file.");
|
||||
YddFile? ydd = await RpfManager.GetFileAsync<YddFile>(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<YftFile>(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<YftFile>(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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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<string> 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<char> 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<string>();
|
||||
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<string>();
|
||||
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}");
|
||||
}
|
||||
|
||||
|
||||
|
@ -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}";
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user