Decompiled source of Strain o nator v0.1.2

UserLibs/SharpGLTF.Core.dll

Decompiled 21 hours ago
using System;
using System.Buffers.Binary;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.CodeAnalysis;
using SharpGLTF.Animations;
using SharpGLTF.Collections;
using SharpGLTF.Diagnostics;
using SharpGLTF.IO;
using SharpGLTF.Memory;
using SharpGLTF.Reflection;
using SharpGLTF.Schema2;
using SharpGLTF.Transforms;
using SharpGLTF.Validation;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyMetadata("IsTrimmable", "True")]
[assembly: AssemblyCompany("Vicente Penades")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Vicente Penades")]
[assembly: AssemblyDescription("SharpGLTF is a C# library for reading and writing glTF2 3D models")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+acbc151e70d65ceb8bec24fa95f0067d0e5ed933")]
[assembly: AssemblyProduct("SharpGLTF.Core")]
[assembly: AssemblyTitle("SharpGLTF.Core")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/vpenades/SharpGLTF")]
[assembly: InternalsVisibleTo("SharpGLTF.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100318e6698f4f41b26736d5881759c6afde44d17c06b63d2e383c915ca1fed6ec1b6eb2e1c38013feb2320224ace549b7701e22e6e0f7770e0d72b5c199f9e969bf2c2668c0ebeb035e539fb534d9ef24053aa9997d2d72534a6c1da8ab58e353ed6eb62ad9eb1f4a044893d7a50a226e4701d4e472a7dc1918caee6794d9c4cae")]
[assembly: InternalsVisibleTo("SharpGLTF.Toolkit.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100318e6698f4f41b26736d5881759c6afde44d17c06b63d2e383c915ca1fed6ec1b6eb2e1c38013feb2320224ace549b7701e22e6e0f7770e0d72b5c199f9e969bf2c2668c0ebeb035e539fb534d9ef24053aa9997d2d72534a6c1da8ab58e353ed6eb62ad9eb1f4a044893d7a50a226e4701d4e472a7dc1918caee6794d9c4cae")]
[assembly: InternalsVisibleTo("SharpGLTF.Ext.3DTiles.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100318e6698f4f41b26736d5881759c6afde44d17c06b63d2e383c915ca1fed6ec1b6eb2e1c38013feb2320224ace549b7701e22e6e0f7770e0d72b5c199f9e969bf2c2668c0ebeb035e539fb534d9ef24053aa9997d2d72534a6c1da8ab58e353ed6eb62ad9eb1f4a044893d7a50a226e4701d4e472a7dc1918caee6794d9c4cae")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsUnmanagedAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
}
namespace SharpGLTF
{
	[DebuggerStepThrough]
	internal static class Guard
	{
		private static readonly IReadOnlyList<char> _InvalidRelativePathChars = (from c in Path.GetInvalidFileNameChars()
			where c != '/' && c != '\\'
			select c).ToArray();

		public static void NotNullOrEmpty(string target, string parameterName, string message = "")
		{
			if (target == null)
			{
				throw new ArgumentNullException(parameterName, message);
			}
			if (!string.IsNullOrWhiteSpace(target))
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = parameterName + " cannot be null or empty and cannot contain only blanks.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void FileNameMustBeValid(string fileName, string parameterName, string message = "")
		{
			NotNullOrEmpty(fileName, parameterName, message);
			char[] invalid = Path.GetInvalidFileNameChars();
			if (!fileName.Any((char c) => invalid.Contains(c)))
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = fileName + " is invalid or does not exist.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void FilePathMustBeValid(string filePath, string parameterName, string message = "")
		{
			NotNullOrEmpty(filePath, parameterName, message);
			filePath = Path.GetFullPath(filePath);
			bool flag = false;
			flag |= filePath.EndsWith(new string(Path.DirectorySeparatorChar, 1), StringComparison.Ordinal);
			if (!(flag | filePath.EndsWith(new string(Path.AltDirectorySeparatorChar, 1), StringComparison.Ordinal)))
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = filePath + " is invalid or does not exist.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void FilePathMustExist(string filePath, string parameterName, string message = "")
		{
			if (File.Exists(filePath))
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = filePath + " is invalid or does not exist.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void DirectoryPathMustExist(string dirPath, string parameterName, string message = "")
		{
			if (Directory.Exists(dirPath))
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = dirPath + " is invalid or does not exist.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void MustExist(FileInfo finfo, string parameterName, string message = "")
		{
			if (finfo == null)
			{
				throw new ArgumentNullException("finfo");
			}
			if (finfo.Exists)
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = finfo.FullName + " is invalid or does not exist.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void MustExist(DirectoryInfo dinfo, string parameterName, string message = "")
		{
			if (dinfo == null)
			{
				throw new ArgumentNullException("dinfo");
			}
			if (dinfo.Exists)
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = dinfo.FullName + " is invalid or does not exist.";
			}
			throw new ArgumentException(message, parameterName);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void NotNull(object target, string parameterName)
		{
			if (target == null)
			{
				throw new ArgumentNullException(parameterName);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void NotNull(object target, string parameterName, string message)
		{
			if (target == null)
			{
				throw new ArgumentNullException(parameterName, message);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static void MustBeNull(object target, string parameterName, string message = "")
		{
			if (target == null)
			{
				return;
			}
			throw new ArgumentException(parameterName, message);
		}

		public static void NotNullOrEmpty<T>(IEnumerable<T> target, string parameterName, string message = "")
		{
			if (target == null)
			{
				throw new ArgumentNullException(parameterName, message);
			}
			if (target.Any())
			{
				return;
			}
			if (string.IsNullOrWhiteSpace(message))
			{
				message = parameterName + " cannot be empty.";
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void MustBeEqualTo<TValue>(TValue value, TValue expected, string parameterName) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(expected) == 0)
			{
				return;
			}
			throw new ArgumentException(parameterName, $"{parameterName} {value} must be equal to {expected}.");
		}

		public static void MustBePositiveAndMultipleOf(int value, int padding, string parameterName, string message = "")
		{
			if (value < 0)
			{
				if (string.IsNullOrWhiteSpace(message))
				{
					message = parameterName + " must not be negative";
				}
				throw new ArgumentOutOfRangeException(message, parameterName);
			}
			if (value % padding != 0)
			{
				if (string.IsNullOrWhiteSpace(message))
				{
					message = $"{parameterName} is {value}; expected to be multiple of {padding}";
				}
				throw new ArgumentOutOfRangeException(message, parameterName);
			}
		}

		public static void MustBeLessThan<TValue>(TValue value, TValue max, string parameterName) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(max) < 0)
			{
				return;
			}
			throw new ArgumentOutOfRangeException(parameterName, $"{parameterName} {value} must be less than {max}.");
		}

		public static void MustBeLessThanOrEqualTo<TValue>(TValue value, TValue max, string parameterName) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(max) > 0)
			{
				throw new ArgumentOutOfRangeException(parameterName, $"{parameterName} {value} must be less than or equal to {max}.");
			}
		}

		public static void MustBeGreaterThan<TValue>(TValue value, TValue min, string parameterName) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(min) > 0)
			{
				return;
			}
			throw new ArgumentOutOfRangeException(parameterName, $"Value {value} must be greater than {min}.");
		}

		public static void MustBeGreaterThanOrEqualTo<TValue>(TValue value, TValue min, string parameterName) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(min) >= 0)
			{
				return;
			}
			throw new ArgumentOutOfRangeException(parameterName, $"{parameterName} {value} must be greater than or equal to {min}.");
		}

		public static void MustBeBetweenOrEqualTo<TValue>(TValue value, TValue minInclusive, TValue maxInclusive, string parameterName) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(minInclusive) >= 0 && value.CompareTo(maxInclusive) <= 0)
			{
				return;
			}
			throw new ArgumentOutOfRangeException(parameterName, $"{parameterName} {value} must be greater than or equal to {minInclusive} and less than or equal to {maxInclusive}.");
		}

		public static void IsTrue(bool target, string parameterName, string message = "")
		{
			if (target)
			{
				return;
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void IsFalse(bool target, string parameterName, string message = "")
		{
			if (!target)
			{
				return;
			}
			throw new ArgumentException(message, parameterName);
		}

		public static void IsValidURI(string parameterName, string gltfURI, params string[] validHeaders)
		{
			if (string.IsNullOrEmpty(gltfURI))
			{
				return;
			}
			foreach (string text in validHeaders)
			{
				if (gltfURI.StartsWith(text, StringComparison.OrdinalIgnoreCase))
				{
					string value = text + ",";
					if (gltfURI.StartsWith(value, StringComparison.OrdinalIgnoreCase) || gltfURI.StartsWith(text + ";base64,", StringComparison.OrdinalIgnoreCase))
					{
						return;
					}
					throw new ArgumentException(parameterName + " has invalid URI '" + gltfURI + "'.");
				}
			}
			if (gltfURI.Any((char c) => _InvalidRelativePathChars.Contains(c)))
			{
				throw new ArgumentException("Invalid URI '" + gltfURI + "'.");
			}
			if (Uri.TryCreate(gltfURI, UriKind.RelativeOrAbsolute, out Uri _))
			{
				return;
			}
			throw new ArgumentException("Invalid URI '" + gltfURI + "'.");
		}

		public static void MustShareLogicalParent(LogicalChildOfRoot a, LogicalChildOfRoot b, string parameterName)
		{
			MustShareLogicalParent(a?.LogicalParent, "LogicalParent", b, parameterName);
		}

		public static void MustShareLogicalParent(ModelRoot a, string aName, LogicalChildOfRoot b, string bName)
		{
			if (a == null)
			{
				throw new ArgumentNullException(aName);
			}
			if (b == null)
			{
				throw new ArgumentNullException(bName);
			}
			if (a != b.LogicalParent)
			{
				throw new ArgumentException("LogicalParent mismatch", bName);
			}
		}

		public static void HasDynamicallyAccessedMembers(Type t, bool hasConstructors, bool hasMethods, bool hasProperties, bool hasFields, string parameterName)
		{
			DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes = DynamicallyAccessedMemberTypes.None;
			if (hasConstructors)
			{
				dynamicallyAccessedMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
			}
			if (hasMethods)
			{
				dynamicallyAccessedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods;
			}
			if (hasProperties)
			{
				dynamicallyAccessedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties;
			}
			if (hasFields)
			{
				dynamicallyAccessedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields;
			}
			DynamicallyAccessedMembersAttribute customAttribute = t.GetCustomAttribute<DynamicallyAccessedMembersAttribute>();
			if (customAttribute == null || (customAttribute.MemberTypes & dynamicallyAccessedMemberTypes) != dynamicallyAccessedMemberTypes)
			{
				throw new ArgumentException($"{parameterName} {t.Name} must define  #if NET6_0_OR_GREATER  [DynamicallyAccessedMembers({dynamicallyAccessedMemberTypes})] #endif");
			}
		}
	}
	[DebuggerStepThrough]
	internal static class GuardAll
	{
		public static void NotNull<T>(IEnumerable<T> collection, string parameterName, string message = "")
		{
			Guard.NotNull(collection, "collection");
			foreach (T item in collection)
			{
				Guard.NotNull(item, parameterName, message);
			}
		}

		public static void AreTrue(IEnumerable<bool> collection, string parameterName, Func<int, string> messageFunc = null)
		{
			Guard.NotNull(collection, "collection");
			int num = 0;
			foreach (bool item in collection)
			{
				Guard.IsTrue(item, parameterName, messageFunc?.Invoke(num) ?? string.Empty);
				num++;
			}
		}

		public static void MustBeEqualTo<TValue>(IEnumerable<TValue> collection, TValue expected, string parameterName) where TValue : IComparable<TValue>
		{
			Guard.NotNull(collection, "collection");
			foreach (TValue item in collection)
			{
				Guard.MustBeEqualTo(item, expected, parameterName);
			}
		}

		public static void MustBeGreaterThan<TValue>(IEnumerable<TValue> collection, TValue minExclusive, string parameterName) where TValue : IComparable<TValue>
		{
			Guard.NotNull(collection, "collection");
			foreach (TValue item in collection)
			{
				Guard.MustBeGreaterThan(item, minExclusive, parameterName);
			}
		}

		public static void MustBeLessThan<TValue>(IEnumerable<TValue> collection, TValue maxExclusive, string parameterName) where TValue : IComparable<TValue>
		{
			Guard.NotNull(collection, "collection");
			foreach (TValue item in collection)
			{
				Guard.MustBeLessThan(item, maxExclusive, parameterName);
			}
		}

		public static void MustBeLessThanOrEqualTo<TValue>(IEnumerable<TValue> collection, TValue maxInclusive, string parameterName) where TValue : IComparable<TValue>
		{
			Guard.NotNull(collection, "collection");
			foreach (TValue item in collection)
			{
				Guard.MustBeLessThanOrEqualTo(item, maxInclusive, parameterName);
			}
		}

		public static void MustBeGreaterThanOrEqualTo<TValue>(IEnumerable<TValue> collection, TValue minInclusive, string parameterName) where TValue : IComparable<TValue>
		{
			Guard.NotNull(collection, "collection");
			foreach (TValue item in collection)
			{
				Guard.MustBeGreaterThanOrEqualTo(item, minInclusive, parameterName);
			}
		}

		public static void MustBeBetweenOrEqualTo<TValue>(IEnumerable<TValue> collection, TValue minInclusive, TValue maxInclusive, string parameterName) where TValue : IComparable<TValue>
		{
			Guard.NotNull(collection, "collection");
			foreach (TValue item in collection)
			{
				Guard.MustBeBetweenOrEqualTo(item, minInclusive, maxInclusive, parameterName);
			}
		}
	}
	internal static class _Extensions
	{
		private readonly struct _ListSelect<TSource, TResult> : IReadOnlyList<TResult>, IEnumerable<TResult>, IEnumerable, IReadOnlyCollection<TResult>
		{
			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			private readonly IReadOnlyList<TSource> _List;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			private readonly Func<TSource, TResult> _Selector;

			public TResult this[int index] => _Selector(_List[index]);

			public int Count => _List.Count;

			public _ListSelect(IReadOnlyList<TSource> list, Func<TSource, TResult> selector)
			{
				_List = list;
				_Selector = selector;
			}

			public IEnumerator<TResult> GetEnumerator()
			{
				foreach (TSource item in _List)
				{
					yield return _Selector(item);
				}
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				foreach (TSource item in _List)
				{
					yield return _Selector(item);
				}
			}
		}

		private readonly struct _CollectionSelect<TSource, TResult> : IReadOnlyCollection<TResult>, IEnumerable<TResult>, IEnumerable
		{
			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			private readonly IReadOnlyCollection<TSource> _List;

			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			private readonly Func<TSource, TResult> _Selector;

			public int Count => _List.Count;

			public _CollectionSelect(IReadOnlyCollection<TSource> list, Func<TSource, TResult> selector)
			{
				_List = list;
				_Selector = selector;
			}

			public IEnumerator<TResult> GetEnumerator()
			{
				foreach (TSource item in _List)
				{
					yield return _Selector(item);
				}
			}

			IEnumerator IEnumerable.GetEnumerator()
			{
				foreach (TSource item in _List)
				{
					yield return _Selector(item);
				}
			}
		}

		private const float _UnitLengthThresholdVec3 = 0.00674f;

		private const float _UnitLengthThresholdVec4 = 0.00769f;

		private const float _UnitSumThresholdStep = 0.0039216f;

		internal static bool IsMultipleOf(this int value, int mult)
		{
			return value % mult == 0;
		}

		internal static int WordPadded(this int length)
		{
			int num = length & 3;
			return length + ((num != 0) ? (4 - num) : 0);
		}

		internal static bool _IsFinite(this float value)
		{
			return float.IsFinite(value);
		}

		internal static bool _IsFinite(this Vector2 v)
		{
			if (v.X._IsFinite())
			{
				return v.Y._IsFinite();
			}
			return false;
		}

		internal static bool _IsFinite(this Vector3 v)
		{
			if (v.X._IsFinite() && v.Y._IsFinite())
			{
				return v.Z._IsFinite();
			}
			return false;
		}

		internal static bool _IsFinite(this in Vector4 v)
		{
			if (v.X._IsFinite() && v.Y._IsFinite() && v.Z._IsFinite())
			{
				return v.W._IsFinite();
			}
			return false;
		}

		internal static bool _IsFinite(this in Matrix4x4 v)
		{
			if (!v.M11._IsFinite() || !v.M12._IsFinite() || !v.M13._IsFinite() || !v.M14._IsFinite())
			{
				return false;
			}
			if (!v.M21._IsFinite() || !v.M22._IsFinite() || !v.M23._IsFinite() || !v.M24._IsFinite())
			{
				return false;
			}
			if (!v.M31._IsFinite() || !v.M32._IsFinite() || !v.M33._IsFinite() || !v.M34._IsFinite())
			{
				return false;
			}
			if (!v.M41._IsFinite() || !v.M42._IsFinite() || !v.M43._IsFinite() || !v.M44._IsFinite())
			{
				return false;
			}
			return true;
		}

		internal static bool _IsFinite(this Quaternion v)
		{
			if (v.X._IsFinite() && v.Y._IsFinite() && v.Z._IsFinite())
			{
				return v.W._IsFinite();
			}
			return false;
		}

		internal static Vector3 WithLength(this Vector3 v, float len)
		{
			return Vector3.Normalize(v) * len;
		}

		internal static bool IsNormalized(this Vector3 normal)
		{
			if (!normal._IsFinite())
			{
				return false;
			}
			return Math.Abs(normal.Length() - 1f) <= 0.00674f;
		}

		internal static bool IsNormalized(this Quaternion rotation)
		{
			if (!rotation._IsFinite())
			{
				return false;
			}
			return Math.Abs(rotation.Length() - 1f) <= 0.00769f;
		}

		internal static Quaternion AsQuaternion(this Vector4 v)
		{
			return new Quaternion(v.X, v.Y, v.Z, v.W);
		}

		internal static Quaternion Sanitized(this Quaternion q)
		{
			if (!q.IsNormalized())
			{
				return Quaternion.Normalize(q);
			}
			return q;
		}

		internal static bool IsInRange(this Vector3 value, Vector3 min, Vector3 max)
		{
			if (value.X < min.X || value.X > max.X)
			{
				return false;
			}
			if (value.Y < min.Y || value.Y > max.Y)
			{
				return false;
			}
			if (value.Z < min.Z || value.Z > max.Z)
			{
				return false;
			}
			return true;
		}

		internal static bool IsInRange(this Vector4 value, Vector4 min, Vector4 max)
		{
			if (value.X < min.X || value.X > max.X)
			{
				return false;
			}
			if (value.Y < min.Y || value.Y > max.Y)
			{
				return false;
			}
			if (value.Z < min.Z || value.Z > max.Z)
			{
				return false;
			}
			if (value.W < min.W || value.W > max.W)
			{
				return false;
			}
			return true;
		}

		internal static bool IsRound(this Vector4 value)
		{
			Vector4 vector = new Vector4((int)value.X, (int)value.Y, (int)value.Z, (int)value.W);
			return value - vector == Vector4.Zero;
		}

		internal static void ValidateNormal(this Vector3 normal, string msg)
		{
			if (!normal._IsFinite())
			{
				throw new NotFiniteNumberException(msg + " is invalid.");
			}
			if (!normal.IsNormalized())
			{
				throw new ArithmeticException(msg + " is not unit length.");
			}
		}

		internal static void ValidateTangent(this Vector4 tangent, string msg)
		{
			if (tangent.W != 1f && tangent.W != -1f)
			{
				throw new ArithmeticException(msg);
			}
			new Vector3(tangent.X, tangent.Y, tangent.Z).ValidateNormal(msg);
		}

		internal static Vector3 SanitizeNormal(this Vector3 normal)
		{
			if (normal == Vector3.Zero)
			{
				return Vector3.UnitX;
			}
			if (!normal.IsNormalized())
			{
				return Vector3.Normalize(normal);
			}
			return normal;
		}

		internal static bool IsValidTangent(this Vector4 tangent)
		{
			if (tangent.W != 1f && tangent.W != -1f)
			{
				return false;
			}
			return new Vector3(tangent.X, tangent.Y, tangent.Z).IsNormalized();
		}

		internal static Vector4 SanitizeTangent(this Vector4 tangent)
		{
			Vector3 value = new Vector3(tangent.X, tangent.Y, tangent.Z).SanitizeNormal();
			float num = (float.IsNaN(tangent.W) ? 1f : tangent.W);
			return new Vector4(value, (num > 0f) ? 1 : (-1));
		}

		internal static Matrix4x4 Inverse(this in Matrix4x4 src)
		{
			if (!Matrix4x4.Invert(src, out var result))
			{
				Guard.IsTrue(target: false, "src", "Matrix cannot be inverted.");
			}
			if (src.M44 == 1f)
			{
				result.M44 = 1f;
			}
			return result;
		}

		internal static bool IsValid(this in Matrix4x4 matrix, Matrix4x4Factory.MatrixCheck check, float tolerance = 0f)
		{
			return Matrix4x4Factory.IsValid(in matrix, check, tolerance);
		}

		public static bool AreSameReference<T>(this (T x, T y) refs, out bool result) where T : class
		{
			if (refs.x == refs.y)
			{
				result = true;
				return true;
			}
			if (refs.x == null)
			{
				result = false;
				return true;
			}
			if (refs.y == null)
			{
				result = false;
				return true;
			}
			result = false;
			return false;
		}

		internal static int GetContentHashCode<T>(this IEnumerable<T> collection, int count = int.MaxValue)
		{
			if (collection == null)
			{
				return 0;
			}
			int num = 0;
			if (collection is IReadOnlyList<T> readOnlyList)
			{
				count = Math.Min(count, readOnlyList.Count);
				for (int i = 0; i < count; i++)
				{
					num ^= readOnlyList[i]?.GetHashCode() ?? 0;
					num *= 17;
				}
				return num;
			}
			foreach (T item in collection.Take(count))
			{
				num ^= item?.GetHashCode() ?? 0;
				num *= 17;
			}
			return num;
		}

		internal static ArraySegment<T> Slice<T>(this T[] array, int offset)
		{
			return new ArraySegment<T>(array, offset, array.Length - offset);
		}

		internal static T[] CloneArray<T>(this T[] srcArray)
		{
			if (srcArray == null)
			{
				return null;
			}
			T[] array = new T[srcArray.Length];
			srcArray.CopyTo(array, 0);
			return array;
		}

		internal static void Fill<T>(this IList<T> collection, T value)
		{
			for (int i = 0; i < collection.Count; i++)
			{
				collection[i] = value;
			}
		}

		internal static void Fill<T>(this T[] array, T value)
		{
			array.AsSpan().Fill(value);
		}

		internal static IReadOnlyList<T> EnsureList<T>(this IEnumerable<T> collection)
		{
			if (!(collection is IReadOnlyList<T> result))
			{
				return collection.ToList();
			}
			return result;
		}

		internal static bool IsEmpty<T>(this IReadOnlyList<T> list)
		{
			return list.Count == 0;
		}

		internal static int IndexOf<T>(this IReadOnlyList<T> collection, T value)
		{
			int count = collection.Count;
			for (int i = 0; i < count; i++)
			{
				if (object.Equals(collection[i], value))
				{
					return i;
				}
			}
			return -1;
		}

		internal static int IndexOf<T>(this IReadOnlyList<T> collection, Predicate<T> predicate)
		{
			int count = collection.Count;
			for (int i = 0; i < count; i++)
			{
				if (predicate(collection[i]))
				{
					return i;
				}
			}
			return -1;
		}

		internal static int IndexOf<T>(this IReadOnlyList<T> collection, T[] subset) where T : IEquatable<T>
		{
			int num = collection.Count - subset.Length;
			for (int i = 0; i < num; i++)
			{
				bool flag = false;
				for (int j = 0; j < subset.Length && collection[i + j].Equals(subset[j]); j++)
				{
					flag = true;
				}
				if (flag)
				{
					return i;
				}
			}
			return -1;
		}

		internal static void CopyTo<T>(this T[] src, int srcOffset, IList<T> dst, int dstOffset, int count)
		{
			ArraySegment<T> src2 = new ArraySegment<T>(src);
			src2.CopyTo(srcOffset, dst, dstOffset, count);
		}

		internal static void CopyTo<T>(this ArraySegment<T> src, int srcOffset, IList<T> dst, int dstOffset, int count)
		{
			if (dst is T[] destinationArray)
			{
				Array.Copy(src.Array, src.Offset + srcOffset, destinationArray, dstOffset, count);
				return;
			}
			for (int i = 0; i < count; i++)
			{
				dst[dstOffset + i] = src.Array[src.Offset + srcOffset + i];
			}
		}

		internal static void AddRange<Tin, Tout>(this IList<Tout> dst, IEnumerable<Tin> src, Converter<Tin, Tout> cvt)
		{
			foreach (Tin item in src)
			{
				dst.Add(cvt(item));
			}
		}

		internal static IEnumerable<T> ConcatElements<T>(this IEnumerable<T> collection, params T[] elements)
		{
			return collection.Concat(elements.Where((T item) => item != null));
		}

		public static void SanitizeNormals(this IList<Vector3> normals)
		{
			for (int i = 0; i < normals.Count; i++)
			{
				if (!normals[i].IsNormalized())
				{
					normals[i] = normals[i].SanitizeNormal();
				}
			}
		}

		public static void SanitizeTangents(this IList<Vector4> tangents)
		{
			for (int i = 0; i < tangents.Count; i++)
			{
				if (!tangents[i].IsValidTangent())
				{
					tangents[i] = tangents[i].SanitizeTangent();
				}
			}
		}

		public static IReadOnlyList<TResult> SelectList<TSource, TResult>(this IReadOnlyList<TSource> collection, Func<TSource, TResult> selector)
		{
			return new _ListSelect<TSource, TResult>(collection, selector);
		}

		public static IReadOnlyCollection<TResult> SelectCollection<TSource, TResult>(this IReadOnlyCollection<TSource> collection, Func<TSource, TResult> selector)
		{
			return new _CollectionSelect<TSource, TResult>(collection, selector);
		}

		public static string ToDebugString(this EncodingType encoding, DimensionType dimensions, bool normalized)
		{
			string text = string.Empty;
			switch (encoding)
			{
			case EncodingType.BYTE:
				text += "SByte";
				break;
			case EncodingType.FLOAT:
				text += "Float";
				break;
			case EncodingType.SHORT:
				text += "SShort";
				break;
			case EncodingType.UNSIGNED_BYTE:
				text += "UByte";
				break;
			case EncodingType.UNSIGNED_INT:
				text += "UInt";
				break;
			case EncodingType.UNSIGNED_SHORT:
				text += "UShort";
				break;
			}
			switch (dimensions)
			{
			case DimensionType.VEC2:
				text += "2";
				break;
			case DimensionType.VEC3:
				text += "3";
				break;
			case DimensionType.VEC4:
				text += "4";
				break;
			case DimensionType.MAT2:
				text += "2x2";
				break;
			case DimensionType.MAT3:
				text += "3x3";
				break;
			case DimensionType.MAT4:
				text += "4x4";
				break;
			}
			if (normalized)
			{
				text = "Norm" + text;
			}
			return text;
		}

		public static int ByteLength(this IndexEncodingType encoding)
		{
			return encoding switch
			{
				IndexEncodingType.UNSIGNED_BYTE => 1, 
				IndexEncodingType.UNSIGNED_SHORT => 2, 
				IndexEncodingType.UNSIGNED_INT => 4, 
				_ => throw new NotImplementedException(), 
			};
		}

		public static int ByteLength(this EncodingType encoding)
		{
			return encoding switch
			{
				EncodingType.BYTE => 1, 
				EncodingType.SHORT => 2, 
				EncodingType.FLOAT => 4, 
				EncodingType.UNSIGNED_BYTE => 1, 
				EncodingType.UNSIGNED_SHORT => 2, 
				EncodingType.UNSIGNED_INT => 4, 
				_ => throw new NotImplementedException(), 
			};
		}

		public static EncodingType ToComponent(this IndexEncodingType t)
		{
			return t switch
			{
				IndexEncodingType.UNSIGNED_BYTE => EncodingType.UNSIGNED_BYTE, 
				IndexEncodingType.UNSIGNED_SHORT => EncodingType.UNSIGNED_SHORT, 
				IndexEncodingType.UNSIGNED_INT => EncodingType.UNSIGNED_INT, 
				_ => throw new NotSupportedException(), 
			};
		}

		public static IndexEncodingType ToIndex(this EncodingType t)
		{
			return t switch
			{
				EncodingType.UNSIGNED_BYTE => IndexEncodingType.UNSIGNED_BYTE, 
				EncodingType.UNSIGNED_SHORT => IndexEncodingType.UNSIGNED_SHORT, 
				EncodingType.UNSIGNED_INT => IndexEncodingType.UNSIGNED_INT, 
				_ => throw new NotSupportedException(), 
			};
		}

		public static int DimCount(this DimensionType dimension)
		{
			return dimension switch
			{
				DimensionType.SCALAR => 1, 
				DimensionType.VEC2 => 2, 
				DimensionType.VEC3 => 3, 
				DimensionType.VEC4 => 4, 
				DimensionType.MAT2 => 4, 
				DimensionType.MAT3 => 9, 
				DimensionType.MAT4 => 16, 
				_ => throw new NotImplementedException(), 
			};
		}

		public static DimensionType ToDimension(this int len)
		{
			return len switch
			{
				1 => DimensionType.SCALAR, 
				2 => DimensionType.VEC2, 
				3 => DimensionType.VEC3, 
				4 => DimensionType.VEC4, 
				9 => DimensionType.MAT3, 
				16 => DimensionType.MAT4, 
				_ => throw new NotImplementedException(), 
			};
		}

		public static DimensionType ToDimension(this Type t)
		{
			if (t == typeof(sbyte))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(byte))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(short))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(ushort))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(int))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(uint))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(float))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(double))
			{
				return DimensionType.SCALAR;
			}
			if (t == typeof(Vector2))
			{
				return DimensionType.VEC2;
			}
			if (t == typeof(Vector3))
			{
				return DimensionType.VEC3;
			}
			if (t == typeof(Vector4))
			{
				return DimensionType.VEC4;
			}
			if (t == typeof(Quaternion))
			{
				return DimensionType.VEC4;
			}
			if (t == typeof(Matrix4x4))
			{
				return DimensionType.MAT4;
			}
			throw new NotImplementedException();
		}

		public static int GetPrimitiveVertexSize(this PrimitiveType ptype)
		{
			return ptype switch
			{
				PrimitiveType.POINTS => 1, 
				PrimitiveType.LINES => 2, 
				PrimitiveType.LINE_LOOP => 2, 
				PrimitiveType.LINE_STRIP => 2, 
				PrimitiveType.TRIANGLES => 3, 
				PrimitiveType.TRIANGLE_FAN => 3, 
				PrimitiveType.TRIANGLE_STRIP => 3, 
				_ => throw new NotImplementedException(), 
			};
		}

		public static IEnumerable<(int A, int B)> GetLinesIndices(this PrimitiveType ptype, int vertexCount)
		{
			return ptype.GetLinesIndices(from item in Enumerable.Range(0, vertexCount)
				select (uint)item);
		}

		public static IEnumerable<(int A, int B, int C)> GetTrianglesIndices(this PrimitiveType ptype, int vertexCount)
		{
			return ptype.GetTrianglesIndices(from item in Enumerable.Range(0, vertexCount)
				select (uint)item);
		}

		public static IEnumerable<(int A, int B)> GetLinesIndices(this PrimitiveType ptype, IEnumerable<uint> sourceIndices)
		{
			if (ptype == PrimitiveType.LINES)
			{
				using (IEnumerator<uint> ptr = sourceIndices.GetEnumerator())
				{
					while (ptr.MoveNext())
					{
						uint current = ptr.Current;
						if (!ptr.MoveNext())
						{
							break;
						}
						uint current2 = ptr.Current;
						if (!_IsDegeneratedSegment(current, current2))
						{
							yield return (A: (int)current, B: (int)current2);
						}
					}
				}
				yield break;
			}
			throw new NotImplementedException();
		}

		public static IEnumerable<(int A, int B, int C)> GetTrianglesIndices(this PrimitiveType ptype, IEnumerable<uint> sourceIndices)
		{
			switch (ptype)
			{
			case PrimitiveType.TRIANGLES:
			{
				using (IEnumerator<uint> ptr = sourceIndices.GetEnumerator())
				{
					while (ptr.MoveNext())
					{
						uint current = ptr.Current;
						if (!ptr.MoveNext())
						{
							break;
						}
						uint current2 = ptr.Current;
						if (!ptr.MoveNext())
						{
							break;
						}
						uint current3 = ptr.Current;
						if (!_IsDegeneratedTriangle(current, current2, current3))
						{
							yield return (A: (int)current, B: (int)current2, C: (int)current3);
						}
					}
				}
				break;
			}
			case PrimitiveType.TRIANGLE_FAN:
			{
				using IEnumerator<uint> ptr = sourceIndices.GetEnumerator();
				if (!ptr.MoveNext())
				{
					break;
				}
				uint a = ptr.Current;
				if (!ptr.MoveNext())
				{
					break;
				}
				uint num2 = ptr.Current;
				while (ptr.MoveNext())
				{
					uint c = ptr.Current;
					if (!_IsDegeneratedTriangle(a, num2, c))
					{
						yield return (A: (int)a, B: (int)num2, C: (int)c);
					}
					num2 = c;
				}
				break;
			}
			case PrimitiveType.TRIANGLE_STRIP:
			{
				using IEnumerator<uint> ptr = sourceIndices.GetEnumerator();
				if (!ptr.MoveNext())
				{
					break;
				}
				uint num = ptr.Current;
				if (!ptr.MoveNext())
				{
					break;
				}
				uint a = ptr.Current;
				bool reversed = false;
				while (ptr.MoveNext())
				{
					uint c = ptr.Current;
					if (!_IsDegeneratedTriangle(num, a, c))
					{
						if (reversed)
						{
							yield return (A: (int)a, B: (int)num, C: (int)c);
						}
						else
						{
							yield return (A: (int)num, B: (int)a, C: (int)c);
						}
					}
					num = a;
					a = c;
					reversed = !reversed;
				}
				break;
			}
			default:
				throw new NotImplementedException();
			}
		}

		private static bool _IsDegeneratedSegment(uint a, uint b)
		{
			return a == b;
		}

		private static bool _IsDegeneratedTriangle(uint a, uint b, uint c)
		{
			if (a == b)
			{
				return true;
			}
			if (a == c)
			{
				return true;
			}
			if (b == c)
			{
				return true;
			}
			return false;
		}

		public static bool TryGetUnderlayingArray<T>(this ArraySegment<T> segment, out T[] array)
		{
			array = segment.Array ?? Array.Empty<T>();
			if (segment.Offset == 0)
			{
				return segment.Count == segment.Array.Length;
			}
			return false;
		}

		public static ArraySegment<byte> ToArraySegment(this MemoryStream m)
		{
			if (!m.TryGetBuffer(out var buffer))
			{
				return new ArraySegment<byte>(m.ToArray());
			}
			return buffer;
		}

		public static byte[] GetPaddedContent(this byte[] content)
		{
			if (content == null)
			{
				return null;
			}
			if (content.Length.IsMultipleOf(4))
			{
				return content;
			}
			int num = content.Length % 4;
			num = ((num != 0) ? (4 - num) : 0);
			byte[] array = new byte[content.Length + num];
			content.CopyTo(array, 0);
			return array;
		}

		public static byte[] TryParseBase64Unchecked(this string uri, params string[] prefixes)
		{
			if (uri == null)
			{
				return null;
			}
			if (!uri.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			foreach (string prefix in prefixes)
			{
				byte[] array = _TryParseBase64Unchecked(uri, prefix);
				if (array != null)
				{
					return array;
				}
			}
			return null;
		}

		private static byte[] _TryParseBase64Unchecked(string uri, string prefix)
		{
			if (!uri.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			string text = uri.Substring(prefix.Length);
			if (text.StartsWith(";base64,", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(";base64,".Length);
				return Convert.FromBase64String(text);
			}
			if (text.StartsWith(','))
			{
				text = text.Substring(1);
				if (text.Length == 1)
				{
					return new byte[1] { byte.Parse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture) };
				}
				throw new NotImplementedException();
			}
			throw new NotImplementedException();
		}

		public static Uri ToUri(this UriKind kind, string value)
		{
			Uri uri = new Uri(value, kind);
			if (!uri.IsAbsoluteUri || uri.IsFile)
			{
				value = value.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
				uri = new Uri(value, kind);
			}
			return uri;
		}

		[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(JsonValue))]
		[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(JsonArray))]
		[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(JsonObject))]
		public static JsonNode DeepClone(this JsonNode node)
		{
			if (node == null)
			{
				throw new ArgumentNullException("node");
			}
			JsonNode jsonNode = null;
			if (!(node is JsonValue node2))
			{
				if (!(node is JsonArray node3))
				{
					if (node is JsonObject node4)
					{
						jsonNode = node4.Deserialize<JsonObject>();
					}
				}
				else
				{
					jsonNode = node3.Deserialize<JsonArray>();
				}
			}
			else
			{
				jsonNode = node2.Deserialize<JsonValue>();
			}
			if (jsonNode == null)
			{
				throw new NotImplementedException();
			}
			return jsonNode;
		}

		public static bool DeepEquals(this JsonNode x, JsonNode y, double precission)
		{
			if (x == y)
			{
				return true;
			}
			if (x == null)
			{
				return false;
			}
			if (y == null)
			{
				return false;
			}
			if (x is JsonValue jsonValue && y is JsonValue jsonValue2)
			{
				if (jsonValue.TryGetValue<int>(out var value) && jsonValue2.TryGetValue<int>(out var value2))
				{
					return value == value2;
				}
				if (jsonValue.TryGetValue<long>(out var value3) && jsonValue2.TryGetValue<long>(out var value4))
				{
					return value3 == value4;
				}
				if (jsonValue.TryGetValue<float>(out var value5) && jsonValue2.TryGetValue<float>(out var value6))
				{
					return (double)Math.Abs(value5 - value6) <= precission;
				}
				if (jsonValue.TryGetValue<double>(out var value7) && jsonValue2.TryGetValue<double>(out var value8))
				{
					return Math.Abs(value7 - value8) <= precission;
				}
				return jsonValue.ToJsonString() == jsonValue2.ToJsonString();
			}
			if (x is JsonArray jsonArray && y is JsonArray jsonArray2)
			{
				if (jsonArray.Count != jsonArray2.Count)
				{
					return false;
				}
				for (int i = 0; i < jsonArray.Count; i++)
				{
					if (!jsonArray[i].DeepEquals(jsonArray2[i], precission))
					{
						return false;
					}
				}
				return true;
			}
			if (x is JsonObject jsonObject && y is JsonObject jsonObject2)
			{
				if (jsonObject.Count != jsonObject2.Count)
				{
					return false;
				}
				foreach (KeyValuePair<string, JsonNode> item in jsonObject)
				{
					if (!jsonObject2.TryGetPropertyValue(item.Key, out JsonNode jsonNode))
					{
						return false;
					}
					if (!item.Value.DeepEquals(jsonNode, precission))
					{
						return false;
					}
				}
				return true;
			}
			return false;
		}
	}
}
namespace SharpGLTF.Validation
{
	public class ModelException : Exception
	{
		private readonly JsonSerializable _Target;

		internal string MessageSuffix { get; set; }

		public override string Message
		{
			get
			{
				if (string.IsNullOrWhiteSpace(MessageSuffix))
				{
					return base.Message;
				}
				return base.Message + MessageSuffix;
			}
		}

		private string _Generator
		{
			get
			{
				ModelRoot modelRoot = null;
				if (_Target is ModelRoot modelRoot2)
				{
					modelRoot = modelRoot2;
				}
				if (_Target is LogicalChildOfRoot logicalChildOfRoot)
				{
					modelRoot = logicalChildOfRoot.LogicalParent;
				}
				return modelRoot?.Asset?.Generator ?? string.Empty;
			}
		}

		internal ModelException(JsonSerializable target, string message)
			: base(_CreateBaseMessage(target, message))
		{
			_Target = target;
		}

		internal ModelException(JsonSerializable target, Exception ex)
			: base(_CreateBaseMessage(target, ex.Message), ex)
		{
			_Target = target;
		}

		private static string _CreateBaseMessage(JsonSerializable target, string message)
		{
			if (target == null)
			{
				return message;
			}
			TypeInfo typeInfo = target.GetType().GetTypeInfo();
			PropertyInfo property = typeInfo.GetProperty("LogicalIndex");
			int num = ((property != null) ? ((int)property.GetValue(target)) : (-1));
			if (num >= 0)
			{
				return $"{typeInfo.Name}[{num}] {message}";
			}
			return typeInfo.Name + " " + message;
		}

		internal static void _Decorate(Exception ex)
		{
			if (ex is ModelException { _Generator: var generator } ex2)
			{
				if (generator.Contains("SHARPGLTF", StringComparison.InvariantCultureIgnoreCase))
				{
					ex2.MessageSuffix = "Model generated by <" + generator + "> seems to be malformed.";
				}
				else
				{
					ex2.MessageSuffix = "Model generated by <" + generator + "> seems to be malformed; Please, check the file at https://github.khronos.org/glTF-Validator/";
				}
			}
		}
	}
	public class SchemaException : ModelException
	{
		internal SchemaException(JsonSerializable target, string message)
			: base(target, message)
		{
		}

		internal SchemaException(JsonSerializable target, JsonException rex)
			: base(target, rex)
		{
		}
	}
	public class SemanticException : ModelException
	{
		internal SemanticException(JsonSerializable target, string message)
			: base(target, message)
		{
		}
	}
	public class LinkException : ModelException
	{
		internal LinkException(JsonSerializable target, string message)
			: base(target, message)
		{
		}
	}
	public class DataException : ModelException
	{
		internal DataException(JsonSerializable target, string message)
			: base(target, message)
		{
		}
	}
	[StructLayout(LayoutKind.Auto)]
	[DebuggerDisplay("{_Current}")]
	[DebuggerStepThrough]
	public readonly struct ValidationContext
	{
		private readonly ModelRoot _Root;

		private readonly ValidationMode _Mode;

		private readonly JsonSerializable _Current;

		public ModelRoot Root => _Root;

		public bool TryFix => _Mode == ValidationMode.TryFix;

		public ValidationContext(ValidationResult result)
		{
			Guard.NotNull(result, "result");
			_Root = result.Root;
			_Mode = result.Mode;
			_Current = null;
		}

		internal ValidationContext(ValidationContext context, JsonSerializable target)
		{
			_Root = context._Root;
			_Mode = context._Mode;
			_Current = target;
		}

		public ValidationContext GetContext(JsonSerializable target)
		{
			return new ValidationContext(this, target);
		}

		[DebuggerStepThrough]
		internal void _SchemaThrow(ValueLocation pname, string msg)
		{
			throw new SchemaException(_Current, $"{pname}: {msg}");
		}

		public ValidationContext IsTrue(ValueLocation parameterName, bool value, string msg)
		{
			if (!value)
			{
				_SchemaThrow(parameterName, msg);
			}
			return this;
		}

		public ValidationContext NotNull(ValueLocation parameterName, object target)
		{
			if (target == null)
			{
				_SchemaThrow(parameterName, "must not be null.");
			}
			return this;
		}

		public ValidationContext MustBeNull(ValueLocation parameterName, object target)
		{
			if (target != null)
			{
				_SchemaThrow(parameterName, "must be null.");
			}
			return this;
		}

		public ValidationContext IsDefined<T>(ValueLocation parameterName, T value) where T : class
		{
			if (value == null)
			{
				_SchemaThrow(parameterName, "must be defined.");
			}
			return this;
		}

		public ValidationContext IsDefined<T>(ValueLocation parameterName, T? value) where T : struct
		{
			if (!value.HasValue)
			{
				_SchemaThrow(parameterName, "must be defined.");
			}
			return this;
		}

		public ValidationContext IsUndefined<T>(ValueLocation parameterName, T value) where T : class
		{
			if (value != null)
			{
				_SchemaThrow(parameterName, "must NOT be defined.");
			}
			return this;
		}

		public ValidationContext IsUndefined<T>(ValueLocation parameterName, T? value) where T : struct
		{
			if (value.HasValue)
			{
				_SchemaThrow(parameterName, "must NOT be defined.");
			}
			return this;
		}

		public ValidationContext AreSameReference<TRef>(ValueLocation parameterName, TRef value, TRef expected) where TRef : class
		{
			if (value != expected)
			{
				_SchemaThrow(parameterName, $"{value} and {expected} must be the same.");
			}
			return this;
		}

		public ValidationContext AreEqual<TValue>(ValueLocation parameterName, TValue value, TValue expected) where TValue : IEquatable<TValue>
		{
			if (!value.Equals(expected))
			{
				_SchemaThrow(parameterName, $"{value} must be equal to {expected}.");
			}
			return this;
		}

		public ValidationContext IsLess<TValue>(ValueLocation parameterName, TValue value, TValue max) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(max) >= 0)
			{
				_SchemaThrow(parameterName, $"{value} must be less than {max}.");
			}
			return this;
		}

		public ValidationContext IsLessOrEqual<TValue>(ValueLocation parameterName, TValue value, TValue max) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(max) > 0)
			{
				_SchemaThrow(parameterName, $"{value} must be less or equal to {max}.");
			}
			return this;
		}

		public ValidationContext IsGreater<TValue>(ValueLocation parameterName, TValue value, TValue min) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(min) <= 0)
			{
				_SchemaThrow(parameterName, $"{value} must be greater than {min}.");
			}
			return this;
		}

		public ValidationContext IsDefaultOrWithin<TValue>(ValueLocation parameterName, TValue? value, TValue minInclusive, TValue maxInclusive) where TValue : unmanaged, IComparable<TValue>
		{
			if (!value.HasValue)
			{
				return this;
			}
			if (value.Value.CompareTo(minInclusive) < 0)
			{
				_SchemaThrow(parameterName, $"{value} must be greater or equal to {minInclusive}.");
			}
			if (value.Value.CompareTo(maxInclusive) > 0)
			{
				_SchemaThrow(parameterName, $"{value} must be less or equal to {maxInclusive}.");
			}
			return this;
		}

		public ValidationContext IsGreaterOrEqual<TValue>(ValueLocation parameterName, TValue value, TValue min) where TValue : IComparable<TValue>
		{
			if (value.CompareTo(min) < 0)
			{
				_SchemaThrow(parameterName, $"{value} must be greater or equal to {min}.");
			}
			return this;
		}

		public ValidationContext IsMultipleOf(ValueLocation parameterName, int value, int multiple)
		{
			if (value % multiple != 0)
			{
				_SchemaThrow(parameterName, $"Value {value} is not a multiple of {multiple}.");
			}
			return this;
		}

		public ValidationContext NonNegative(ValueLocation parameterName, int? value)
		{
			if (value.GetValueOrDefault() < 0)
			{
				_SchemaThrow(parameterName, "must be a non-negative integer.");
			}
			return this;
		}

		public ValidationContext IsNullOrValidURI(ValueLocation parameterName, string gltfURI, params string[] validHeaders)
		{
			if (gltfURI == null)
			{
				return this;
			}
			return IsValidURI(parameterName, gltfURI, validHeaders);
		}

		public ValidationContext IsValidURI(ValueLocation parameterName, string gltfURI, params string[] validHeaders)
		{
			Guard.NotNull(validHeaders, "validHeaders");
			try
			{
				Guard.IsValidURI(parameterName, gltfURI, validHeaders);
				return this;
			}
			catch (ArgumentException ex)
			{
				_SchemaThrow(parameterName, ex.Message);
			}
			return this;
		}

		[DebuggerStepThrough]
		internal void _LinkThrow(ValueLocation pname, string msg)
		{
			throw new LinkException(_Current, $"{pname}: {msg}");
		}

		public ValidationContext EnumsAreEqual<TValue>(ValueLocation parameterName, TValue value, TValue expected) where TValue : Enum
		{
			if (!value.Equals(expected))
			{
				_LinkThrow(parameterName, $"{value} must be equal to {expected}.");
			}
			return this;
		}

		public ValidationContext IsNullOrIndex<T>(ValueLocation parameterName, int? index, IReadOnlyList<T> array)
		{
			return IsNullOrInRange(parameterName, index, 1, array);
		}

		public ValidationContext IsNullOrInRange<T>(ValueLocation parameterName, int? offset, int length, IReadOnlyList<T> array)
		{
			if (!offset.HasValue)
			{
				return this;
			}
			NonNegative($"{parameterName}.offset", offset.Value);
			IsGreater($"{parameterName}.length", length, 0);
			if (array == null)
			{
				_LinkThrow(parameterName, $".{offset} exceeds the number of available items (null).");
				return this;
			}
			if (offset > array.Count - length)
			{
				if (length == 1)
				{
					_LinkThrow(parameterName, $".{offset} exceeds the number of available items ({array.Count}).");
				}
				else
				{
					_LinkThrow(parameterName, $".{offset}+{length} exceeds the number of available items ({array.Count}).");
				}
			}
			return this;
		}

		public ValidationContext IsAnyOf<T>(ValueLocation parameterName, T value, params T[] values)
		{
			string separator = " ";
			if (!values.Contains(value))
			{
				_LinkThrow(parameterName, $"value {value} is not one of [{string.Join(separator, values)}].");
			}
			return this;
		}

		public ValidationContext IsAnyOf(ValueLocation parameterName, AttributeFormat value, params AttributeFormat[] values)
		{
			string separator = " ";
			if (!values.Contains(value))
			{
				_LinkThrow(parameterName, $"value {value._GetDebuggerDisplay()} is not one of [{string.Join(separator, values.Select((AttributeFormat item) => item._GetDebuggerDisplay()))}].");
			}
			return this;
		}

		public ValidationContext IsSetCollection<T>(ValueLocation parameterName, IEnumerable<T> collection) where T : class
		{
			int num = 0;
			if (collection == null)
			{
				_LinkThrow(parameterName, "must not be null.");
				return this;
			}
			HashSet<T> hashSet = new HashSet<T>();
			foreach (T item in collection)
			{
				if (item == null)
				{
					_LinkThrow((name: parameterName, index: num), "Is NULL.");
				}
				if (hashSet.Contains(item))
				{
					_LinkThrow((name: parameterName, index: num), "Is duplicated.");
				}
				hashSet.Add(item);
				num++;
			}
			return this;
		}

		[DebuggerStepThrough]
		private void _DataThrow(ValueLocation pname, string msg)
		{
			throw new DataException(_Current, $"{pname}: {msg}");
		}

		public ValidationContext IsInRange<T>(ValueLocation pname, T value, T minInclusive, T maxInclusive) where T : IComparable<T>
		{
			if (value.CompareTo(minInclusive) == -1)
			{
				_DataThrow(pname, $"is below minimum {minInclusive} value: {value}");
			}
			if (value.CompareTo(maxInclusive) == 1)
			{
				_DataThrow(pname, $"is above maximum {maxInclusive} value: {value}");
			}
			return this;
		}

		public ValidationContext IsNullOrMatrix(ValueLocation pname, Matrix4x4? matrix, bool mustInvert = true, bool mustDecompose = true)
		{
			if (!matrix.HasValue)
			{
				return this;
			}
			return IsMatrix(pname, matrix.Value, mustInvert, mustDecompose);
		}

		public ValidationContext IsNullOrMatrix4x3(ValueLocation pname, Matrix4x4? matrix, bool mustInvert = true, bool mustDecompose = true)
		{
			if (!matrix.HasValue)
			{
				return this;
			}
			return IsMatrix4x3(pname, matrix.Value, mustInvert, mustDecompose);
		}

		public ValidationContext IsPosition(ValueLocation pname, in Vector3 position)
		{
			if (!position._IsFinite())
			{
				_DataThrow(pname, "Invalid Position");
			}
			return this;
		}

		public ValidationContext IsNormal(ValueLocation pname, in Vector3 normal)
		{
			if (!normal.IsNormalized())
			{
				_DataThrow(pname, "Invalid Normal");
			}
			return this;
		}

		public ValidationContext IsRotation(ValueLocation pname, in Quaternion rotation)
		{
			if (!rotation.IsNormalized())
			{
				_DataThrow(pname, "Invalid Rotation");
			}
			return this;
		}

		public ValidationContext IsMatrix(ValueLocation pname, in Matrix4x4 matrix, bool mustInvert = true, bool mustDecompose = true)
		{
			Matrix4x4Factory.MatrixCheck matrixCheck = Matrix4x4Factory.MatrixCheck.NonZero;
			if (mustInvert)
			{
				matrixCheck |= Matrix4x4Factory.MatrixCheck.Invertible;
			}
			if (mustDecompose)
			{
				matrixCheck |= Matrix4x4Factory.MatrixCheck.Decomposable;
			}
			if (!matrix.IsValid(matrixCheck))
			{
				_DataThrow(pname, "Invalid Matrix");
			}
			return this;
		}

		public ValidationContext IsMatrix4x3(ValueLocation pname, in Matrix4x4 matrix, bool mustInvert = true, bool mustDecompose = true)
		{
			Matrix4x4Factory.MatrixCheck matrixCheck = Matrix4x4Factory.MatrixCheck.IdentityColumn4;
			if (mustInvert)
			{
				matrixCheck |= Matrix4x4Factory.MatrixCheck.Invertible;
			}
			if (mustDecompose)
			{
				matrixCheck |= Matrix4x4Factory.MatrixCheck.Decomposable;
			}
			if (!matrix.IsValid(matrixCheck))
			{
				_DataThrow(pname, "Invalid Matrix");
			}
			return this;
		}

		public ValidationContext ArePositions(ValueLocation pname, IReadOnlyList<Vector3> positions)
		{
			Guard.NotNull(positions, "positions");
			for (int i = 0; i < positions.Count; i++)
			{
				IsPosition((name: pname, index: i), positions[i]);
			}
			return this;
		}

		public ValidationContext AreNormals(ValueLocation pname, IReadOnlyList<Vector3> normals)
		{
			Guard.NotNull(normals, "normals");
			for (int i = 0; i < normals.Count; i++)
			{
				IsNormal((name: pname, index: i), normals[i]);
			}
			return this;
		}

		public ValidationContext AreTangents(ValueLocation pname, IReadOnlyList<Vector4> tangents)
		{
			Guard.NotNull(tangents, "tangents");
			for (int i = 0; i < tangents.Count; i++)
			{
				if (!tangents[i].IsValidTangent())
				{
					_DataThrow((name: pname, index: i), "Invalid Tangent");
				}
			}
			return this;
		}

		public ValidationContext AreRotations(ValueLocation pname, IReadOnlyList<Quaternion> rotations)
		{
			Guard.NotNull(rotations, "rotations");
			for (int i = 0; i < rotations.Count; i++)
			{
				if (!rotations[i].IsNormalized())
				{
					_DataThrow((name: pname, index: i), "Invalid Rotation");
				}
			}
			return this;
		}

		public ValidationContext AreJoints(ValueLocation pname, IReadOnlyList<Vector4> joints, int skinsMaxJointCount)
		{
			Guard.NotNull(joints, "joints");
			for (int i = 0; i < joints.Count; i++)
			{
				Vector4 v = joints[i];
				if (!v._IsFinite())
				{
					_DataThrow((name: pname, index: i), "Is not finite");
				}
				if (v.X < 0f || v.X >= (float)skinsMaxJointCount)
				{
					_DataThrow((name: pname, index: i), "Is out of bounds");
				}
				if (v.Y < 0f || v.Y >= (float)skinsMaxJointCount)
				{
					_DataThrow((name: pname, index: i), "Is out of bounds");
				}
				if (v.Z < 0f || v.Z >= (float)skinsMaxJointCount)
				{
					_DataThrow((name: pname, index: i), "Is out of bounds");
				}
				if (v.W < 0f || v.W >= (float)skinsMaxJointCount)
				{
					_DataThrow((name: pname, index: i), "Is out of bounds");
				}
			}
			return this;
		}

		public ValidationContext That(Action action)
		{
			Guard.NotNull(action, "action");
			try
			{
				action();
			}
			catch (ArgumentException ex)
			{
				_DataThrow(ex.ParamName, ex.Message);
			}
			return this;
		}

		public ValidationContext That(bool result, string paramName, string msg)
		{
			if (!result)
			{
				_DataThrow(paramName, msg);
			}
			return this;
		}
	}
	public readonly struct ValueLocation
	{
		private readonly string _Name;

		private readonly int _Index;

		public static implicit operator ValueLocation(int index)
		{
			return new ValueLocation(string.Empty, index);
		}

		public static implicit operator ValueLocation(int? index)
		{
			return new ValueLocation(string.Empty, index.GetValueOrDefault());
		}

		public static implicit operator ValueLocation(string name)
		{
			return new ValueLocation(name);
		}

		public static implicit operator ValueLocation((string name, int index) tuple)
		{
			return new ValueLocation(tuple.name, tuple.index);
		}

		public static implicit operator ValueLocation((string name, int? index) tuple)
		{
			return new ValueLocation(tuple.name, tuple.index.GetValueOrDefault());
		}

		public static implicit operator string(ValueLocation location)
		{
			return location.ToString();
		}

		private ValueLocation(string name, int idx1 = -1)
		{
			_Name = name;
			_Index = idx1;
		}

		public override string ToString()
		{
			if (_Index >= 0)
			{
				return $"{_Name}[{_Index}]";
			}
			return _Name;
		}

		public string ToString(JsonSerializable target, string message)
		{
			return ToString(target) + " " + message;
		}

		public string ToString(JsonSerializable target)
		{
			if (target == null)
			{
				return ToString();
			}
			string text = target.GetType().Name;
			PropertyInfo property = target.GetType().GetProperty("LogicalIndex");
			if (property != null)
			{
				object value = property.GetValue(target);
				text += $"[{value}]";
			}
			return text + ToString();
		}
	}
	public enum ValidationMode
	{
		Skip,
		TryFix,
		Strict
	}
	[DebuggerStepThrough]
	public sealed class ValidationResult
	{
		private readonly ModelRoot _Root;

		private readonly ValidationMode _Mode;

		private readonly bool _InstantThrow;

		private readonly List<Exception> _Errors = new List<Exception>();

		public ModelRoot Root => _Root;

		public ValidationMode Mode => _Mode;

		public IEnumerable<Exception> Errors => _Errors;

		public bool HasErrors => _Errors.Count > 0;

		public ValidationResult(ModelRoot root, ValidationMode mode, bool instantThrow = false)
		{
			_Root = root;
			_Mode = mode;
			_InstantThrow = instantThrow;
		}

		public ValidationContext GetContext()
		{
			return new ValidationContext(this);
		}

		public void SetSchemaError(EndOfStreamException ex)
		{
			Guard.NotNull(ex, "ex");
			SetError(new SchemaException(null, ex.Message));
		}

		public void SetSchemaError(ModelRoot model, string error)
		{
			SetError(new SchemaException(model, error));
		}

		public void SetSchemaError(ModelRoot model, JsonException ex)
		{
			SetError(new SchemaException(model, ex));
		}

		public void SetModelError(FormatException ex)
		{
			SetError(new ModelException(null, ex));
		}

		public void SetModelError(ModelRoot model, ArgumentException ex)
		{
			SetError(new ModelException(model, ex));
		}

		public void SetError(ModelException ex)
		{
			if (_InstantThrow)
			{
				throw ex;
			}
			_Errors.Add(ex);
		}
	}
}
namespace SharpGLTF.Transforms
{
	[DebuggerDisplay("AffineTransform {ToDebuggerDisplayString(),nq}")]
	public readonly struct AffineTransform : IEquatable<AffineTransform>
	{
		private const string _CannotDecomposeError = "Matrix is invalid or skewed.";

		private const string _RequiresSRTError = "Needs to be in SRT representation. Call GetDecomposed() first.";

		public static readonly AffineTransform Identity = new AffineTransform(null, null, null);

		private const int DATA_UNDEFINED = 0;

		private const int DATA_SRT = 1;

		private const int DATA_MAT = 2;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly int _Representation;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M11;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M12;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M13;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M21;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M22;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M23;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M31;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M32;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly float _M33;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private readonly Vector3 _Translation;

		public bool IsValid
		{
			get
			{
				if (_Representation == 0)
				{
					return false;
				}
				if (!Translation._IsFinite())
				{
					return false;
				}
				if (!_M11._IsFinite())
				{
					return false;
				}
				if (!_M12._IsFinite())
				{
					return false;
				}
				if (!_M13._IsFinite())
				{
					return false;
				}
				if (!_M21._IsFinite())
				{
					return false;
				}
				if (!_M22._IsFinite())
				{
					return false;
				}
				if (!_M23._IsFinite())
				{
					return false;
				}
				if (!_M31._IsFinite())
				{
					return false;
				}
				if (!_M32._IsFinite())
				{
					return false;
				}
				if (!_M33._IsFinite())
				{
					return false;
				}
				return true;
			}
		}

		public bool IsMatrix => _Representation == 2;

		public bool IsSRT => _Representation == 1;

		public Vector3 Scale => _GetScale();

		public Quaternion Rotation => _GetRotation();

		public Vector3 Translation => _Translation;

		public Matrix4x4 Matrix => _GetMatrix();

		public bool IsLosslessDecomposable
		{
			get
			{
				_VerifyDefined();
				if (IsSRT)
				{
					return true;
				}
				if (_M11 != 0f)
				{
					return false;
				}
				if (_M12 == 0f)
				{
					return false;
				}
				if (_M13 == 0f)
				{
					return false;
				}
				if (_M21 == 0f)
				{
					return false;
				}
				if (_M22 != 0f)
				{
					return false;
				}
				if (_M23 == 0f)
				{
					return false;
				}
				if (_M31 == 0f)
				{
					return false;
				}
				if (_M32 == 0f)
				{
					return false;
				}
				if (_M33 != 0f)
				{
					return false;
				}
				return true;
			}
		}

		public bool IsDecomposable
		{
			get
			{
				Vector3 scale;
				Quaternion rotation;
				Vector3 translation;
				if (IsMatrix)
				{
					return Matrix4x4.Decompose(_GetMatrix(), out scale, out rotation, out translation);
				}
				return true;
			}
		}

		public bool IsIdentity
		{
			get
			{
				if (IsSRT)
				{
					if (Translation != Vector3.Zero)
					{
						return false;
					}
					if (_M11 != 1f)
					{
						return false;
					}
					if (_M12 != 1f)
					{
						return false;
					}
					if (_M13 != 1f)
					{
						return false;
					}
					if (_M21 != 0f)
					{
						return false;
					}
					if (_M22 != 0f)
					{
						return false;
					}
					if (_M23 != 0f)
					{
						return false;
					}
					if (_M31 != 1f)
					{
						return false;
					}
				}
				else if (IsMatrix)
				{
					if (Translation != Vector3.Zero)
					{
						return false;
					}
					if (_M11 != 1f)
					{
						return false;
					}
					if (_M12 != 0f)
					{
						return false;
					}
					if (_M13 != 0f)
					{
						return false;
					}
					if (_M21 != 0f)
					{
						return false;
					}
					if (_M22 != 1f)
					{
						return false;
					}
					if (_M23 != 0f)
					{
						return false;
					}
					if (_M31 != 0f)
					{
						return false;
					}
					if (_M32 != 0f)
					{
						return false;
					}
					if (_M33 != 1f)
					{
						return false;
					}
				}
				else
				{
					_VerifyDefined();
				}
				return true;
			}
		}

		internal string ToDebuggerDisplayString()
		{
			if (!IsValid)
			{
				return "INVALID";
			}
			if (IsIdentity)
			{
				return "IDENTITY";
			}
			if (TryDecompose(out var transform))
			{
				Vector3 vector = transform._GetScale();
				Quaternion quaternion = transform._GetRotation();
				string text = string.Empty;
				Vector3 vector2 = Vector3.Max(Vector3.Zero, vector - Vector3.One);
				if (vector2.X > 1E-06f || vector2.Y > 1E-06f || vector2.Z > 1E-06f)
				{
					text += $"\ud835\udc12:{vector} ";
				}
				if (quaternion != Quaternion.Identity)
				{
					text += $"\ud835\udc11:{quaternion} ";
				}
				if (transform.Translation != Vector3.Zero)
				{
					text += $"\ud835\udebb:{transform.Translation} ";
				}
				return text;
			}
			return "Skewed Matrix ";
		}

		public static implicit operator AffineTransform((Quaternion r, Vector3 t) xform)
		{
			return new AffineTransform(null, xform.r, xform.t);
		}

		public static implicit operator AffineTransform(Matrix4x4 matrix)
		{
			return new AffineTransform(matrix);
		}

		public static implicit operator AffineTransform(Quaternion rotation)
		{
			return new AffineTransform(rotation);
		}

		public static AffineTransform CreateDecomposed(Matrix4x4 matrix)
		{
			if (!Matrix4x4.Decompose(matrix, out var scale, out var rotation, out var translation))
			{
				throw new ArgumentException("Can't decompose", "matrix");
			}
			return new AffineTransform(scale, rotation, translation);
		}

		public static AffineTransform CreateFromAny(Matrix4x4? matrix, Vector3? scale, Quaternion? rotation, Vector3? translation)
		{
			if (matrix.HasValue)
			{
				Guard.MustBeNull(scale, "scale");
				Guard.MustBeNull(scale, "rotation");
				Guard.MustBeNull(scale, "translation");
				return new AffineTransform(matrix.Value);
			}
			return new AffineTransform(scale, rotation, translation);
		}

		public AffineTransform WithScale(Vector3 scale)
		{
			if (_Representation == 0)
			{
				return new AffineTransform(scale, null, null);
			}
			AffineTransform affineTransform = this;
			if (affineTransform.IsMatrix)
			{
				affineTransform = affineTransform.GetDecomposed();
			}
			return new AffineTransform(scale, affineTransform.Rotation, affineTransform.Translation);
		}

		public AffineTransform WithRotation(Quaternion rotation)
		{
			if (_Representation == 0)
			{
				return new AffineTransform(null, rotation, null);
			}
			AffineTransform affineTransform = this;
			if (affineTransform.IsMatrix)
			{
				affineTransform = affineTransform.GetDecomposed();
			}
			return new AffineTransform(affineTransform.Scale, rotation, affineTransform.Translation);
		}

		public AffineTransform WithTranslation(Vector3 translation)
		{
			if (_Representation == 0)
			{
				return new AffineTransform(null, null, translation);
			}
			if (IsSRT)
			{
				return new AffineTransform(Scale, Rotation, translation);
			}
			Matrix4x4 matrix = Matrix;
			matrix.Translation = translation;
			return matrix;
		}

		public AffineTransform(Vector3? scale, Quaternion? rotation, Vector3? translation)
			: this(scale ?? Vector3.One, rotation ?? Quaternion.Identity, translation ?? Vector3.Zero)
		{
		}

		public AffineTransform(Quaternion rotation)
			: this(Vector3.One, rotation, Vector3.Zero)
		{
		}

		public AffineTransform(Quaternion rotation, Vector3 translation)
			: this(Vector3.One, rotation, translation)
		{
		}

		public AffineTransform(Vector3 scale, Quaternion rotation, Vector3 translation)
		{
			rotation = rotation.Sanitized();
			Guard.IsTrue(scale._IsFinite(), "scale");
			Guard.IsTrue(rotation._IsFinite(), "rotation");
			Guard.IsTrue(translation._IsFinite(), "translation");
			_Representation = 1;
			_M11 = scale.X;
			_M12 = scale.Y;
			_M13 = scale.Z;
			_M21 = rotation.X;
			_M22 = rotation.Y;
			_M23 = rotation.Z;
			_M31 = rotation.W;
			_M32 = 0f;
			_M33 = 0f;
			_Translation = translation;
		}

		public AffineTransform(Matrix4x4 matrix)
		{
			_Representation = 2;
			_M11 = matrix.M11;
			_M12 = matrix.M12;
			_M13 = matrix.M13;
			_M21 = matrix.M21;
			_M22 = matrix.M22;
			_M23 = matrix.M23;
			_M31 = matrix.M31;
			_M32 = matrix.M32;
			_M33 = matrix.M33;
			_Translation = matrix.Translation;
		}

		public override int GetHashCode()
		{
			return _Translation.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			if (obj is AffineTransform other)
			{
				return Equals(other);
			}
			return false;
		}

		public bool Equals(AffineTransform other)
		{
			if (_Representation != other._Representation)
			{
				return false;
			}
			if (_Translation != other._Translation)
			{
				return false;
			}
			if (_M11 != other._M11)
			{
				return false;
			}
			if (_M12 != other._M12)
			{
				return false;
			}
			if (_M13 != other._M13)
			{
				return false;
			}
			if (_M21 != other._M21)
			{
				return false;
			}
			if (_M22 != other._M22)
			{
				return false;
			}
			if (_M23 != other._M23)
			{
				return false;
			}
			if (_M31 != other._M31)
			{
				return false;
			}
			if (IsMatrix && other.IsMatrix)
			{
				if (_M32 != other._M32)
				{
					return false;
				}
				if (_M33 != other._M33)
				{
					return false;
				}
			}
			return true;
		}

		public static bool operator ==(in AffineTransform a, in AffineTransform b)
		{
			return a.Equals(b);
		}

		public static bool operator !=(in AffineTransform a, in AffineTransform b)
		{
			return !a.Equals(b);
		}

		public static bool AreGeometricallyEquivalent(in AffineTransform a, in AffineTransform b, float tolerance = 1E-05f)
		{
			Vector3 value = Transform(Vector3.UnitX, in a);
			Vector3 value2 = Transform(Vector3.UnitY, in a);
			Vector3 value3 = Transform(Vector3.UnitZ, in a);
			Vector3 value4 = Transform(Vector3.UnitX, in b);
			Vector3 value5 = Transform(Vector3.UnitY, in b);
			Vector3 value6 = Transform(Vector3.UnitZ, in b);
			if (Vector3.Distance(value, value4) > tolerance)
			{
				return false;
			}
			if (Vector3.Distance(value2, value5) > tolerance)
			{
				return false;
			}
			if (Vector3.Distance(value3, value6) > tolerance)
			{
				return false;
			}
			return true;
		}

		public AffineTransform GetDecomposed()
		{
			if (!TryDecompose(out var transform))
			{
				throw new InvalidOperationException("Matrix is invalid or skewed.");
			}
			return transform;
		}

		public bool TryDecompose(out AffineTransform transform)
		{
			if (IsSRT)
			{
				transform = this;
				return true;
			}
			if (IsLosslessDecomposable)
			{
				transform = new AffineTransform(new Vector3(_M11, _M22, _M33), Quaternion.Identity, Translation);
				return true;
			}
			Vector3 scale;
			Quaternion rotation;
			Vector3 translation;
			bool flag = Matrix4x4.Decompose(Matrix, out scale, out rotation, out translation);
			transform = (flag ? new AffineTransform(scale, rotation, translation) : this);
			return flag;
		}

		public bool TryDecompose(out Vector3 scale, out Quaternion rotation, out Vector3 translation)
		{
			if (IsSRT)
			{
				scale = _GetScale();
				rotation = _GetRotation();
				translation = _Translation;
				return true;
			}
			if (IsLosslessDecomposable)
			{
				scale = new Vector3(_M11, _M22, _M33);
				rotation = Quaternion.Identity;
				translation = _Translation;
				return true;
			}
			return Matrix4x4.Decompose(Matrix, out scale, out rotation, out translation);
		}

		public static AffineTransform Blend(ReadOnlySpan<AffineTransform> transforms, ReadOnlySpan<float> weights)
		{
			if (transforms.Length == 0)
			{
				return Identity;
			}
			if (transforms.Length == 1)
			{
				return transforms[0];
			}
			Vector3 zero = Vector3.Zero;
			Quaternion quaternion = default(Quaternion);
			Vector3 zero2 = Vector3.Zero;
			float num = 0f;
			for (int i = 0; i < transforms.Length; i++)
			{
				Guard.IsTrue(transforms[i].IsValid, "transforms");
				Guard.IsTrue(transforms[i].TryDecompose(out var scale, out var rotation, out var translation), $"Can't decompose [{i}]");
				float num2 = weights[i];
				zero += scale * num2;
				zero2 += translation * num2;
				num += num2;
				quaternion = ((i != 0) ? Quaternion.Slerp(rotation, quaternion, num2 / num) : rotation);
			}
			quaternion = Quaternion.Normalize(quaternion);
			return new AffineTransform(zero, quaternion, zero2);
		}

		public static AffineTransform operator *(in AffineTransform a, in AffineTransform b)
		{
			return Multiply(in a, in b);
		}

		public static AffineTransform Multiply(in AffineTransform a, in AffineTransform b)
		{
			Guard.IsFalse(a._Representation == 0, "a");
			Guard.IsFalse(b._Representation == 0, "b");
			if (a.IsMatrix || b.IsMatrix)
			{
				return new AffineTransform(a.Matrix * b.Matrix);
			}
			Vector3 scale = b.Scale;
			if (scale.X != scale.Y || scale.X != scale.Z)
			{
				return new AffineTransform(a.Matrix * b.Matrix);
			}
			Vector3 scale2 = scale * a.Scale;
			Quaternion rotation = b.Rotation;
			Quaternion rotation2 = Quaternion.Multiply(rotation, a.Rotation);
			Vector3 translation = b.Translation + _Vector3Transform(a.Translation * scale, rotation);
			return new AffineTransform(scale2, rotation2, translation);
		}

		public static bool TryInvert(in AffineTransform xform, out AffineTransform inverse)
		{
			if (xform.IsMatrix)
			{
				if (Matrix4x4.Invert(xform.Matrix, out var result))
				{
					result.M44 = 1f;
					inverse = result;
					return true;
				}
				inverse = default(AffineTransform);
				return false;
			}
			if (xform.IsSRT)
			{
				if (xform.Rotation.IsIdentity)
				{
					Vector3 vector = Vector3.One / xform.Scale;
					Vector3 translation = -vector * xform.Translation;
					inverse = new AffineTransform(vector, Quaternion.Identity, translation);
				}
				else
				{
					if (xform.Scale.X != xform.Scale.Y || xform.Scale.Y != xform.Scale.Z)
					{
						return TryInvert((AffineTransform)xform.Matrix, out inverse);
					}
					float num = 1f / xform.Scale.X;
					Quaternion quaternion = Quaternion.Normalize(Quaternion.Conjugate(xform.Rotation));
					Vector3 translation2 = (0f - num) * _Vector3Transform(xform.Translation, quaternion);
					inverse = new AffineTransform(new Vector3(num), quaternion, translation2);
				}
				return true;
			}
			inverse = default(AffineTransform);
			return false;
		}

		private static Vector3 Transform(Vector3 vector, in AffineTransform xform)
		{
			if (xform.IsMatrix)
			{
				return Vector3.Transform(vector, xform.Matrix);
			}
			if (xform.IsSRT)
			{
				vector *= xform.Scale;
				vector = _Vector3Transform(vector, xform.Rotation);
				vector += xform.Translation;
				return vector;
			}
			throw new ArgumentException("Undefined transform", "xform");
		}

		public static Vector3 TransformNormal(Vector3 vector, in AffineTransform xform)
		{
			if (xform.IsMatrix)
			{
				return Vector3.TransformNormal(vector, xform.Matrix);
			}
			if (xform.IsSRT)
			{
				vector *= xform.Scale;
				vector = _Vector3Transform(vector, xform.Rotation);
				return vector;
			}
			throw new ArgumentException("Undefined transform", "xform");
		}

		private void _VerifyDefined()
		{
			if (_Representation == 0)
			{
				throw new InvalidOperationException("Undefined");
			}
		}

		private Matrix4x4 _GetMatrix()
		{
			if (IsMatrix)
			{
				return new Matrix4x4(_M11, _M12, _M13, 0f, _M21, _M22, _M23, 0f, _M31, _M32, _M33, 0f, _Translation.X, _Translation.Y, _Translation.Z, 1f);
			}
			if (IsSRT)
			{
				Matrix4x4 result = Matrix4x4.CreateScale(Scale) * Matrix4x4.CreateFromQuaternion(Rotation);
				result.Translation = Translation;
				return result;
			}
			_VerifyDefined();
			return default(Matrix4x4);
		}

		private Vector3 _GetScale()
		{
			if (IsSRT)
			{
				return new Vector3(_M11, _M12, _M13);
			}
			throw new InvalidOperationException("Needs to be in SRT representation. Call GetDecomposed() first.");
		}

		private Quaternion _GetRotation()
		{
			if (IsSRT)
			{
				return new Quaternion(_M21, _M22, _M23, _M31);
			}
			throw new InvalidOperationException("Needs to be in SRT representation. Call GetDecomposed() first.");
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private static Vector3 _Vector3Transform(Vector3 v, Quaternion q)
		{
			Vector3 vector = new Vector3(q.X, q.Y, q.Z);
			float w = q.W;
			return 2f * Vector3.Dot(vector, v) * vector + (w * w - Vector3.Dot(vector, vector)) * v + 2f * w * Vector3.Cross(vector, v);
		}
	}
	[DebuggerDisplay("{Index} = {Weight}")]
	internal readonly struct IndexWeight : IEquatable<IndexWeight>
	{
		public readonly int Index;

		public readonly float Weight;

		public static implicit operator IndexWeight((int Index, float Weight) pair)
		{
			return new IndexWeight(pair.Index, pair.Weight);
		}

		public static implicit operator IndexWeight(KeyValuePair<int, float> pair)
		{
			return new IndexWeight(pair.Key, pair.Value);
		}

		public IndexWeight((int Index, float Weight) pair)
		{
			(Index, Weight) = pair;
		}

		public IndexWeight(KeyValuePair<int, float> pair)
		{
			Index = pair.Key;
			Weight = pair.Value;
		}

		public IndexWeight(int i, float w)
		{
			Index = i;
			Weight = w;
		}

		public override int GetHashCode()
		{
			return Index.GetHashCode() ^ Weight.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			if (obj is IndexWeight other)
			{
				return Equals(other);
			}
			return false;
		}

		public bool Equals(IndexWeight other)
		{
			if (Index == other.Index)
			{
				return Weight == other.Weight;
			}
			return false;
		}

		public bool IsGreaterThan(in IndexWeight other)
		{
			float num = Math.Abs(Weight);
			float num2 = Math.Abs(other.Weight);
			if (num > num2)
			{
				return true;
			}
			if (num == num2 && Index < other.Index)
			{
				return true;
			}
			return false;
		}

		public static IndexWeight operator +(IndexWeight a, IndexWeight b)
		{
			if (a.Index != b.Index)
			{
				throw new InvalidOperationException("b");
			}
			return new IndexWeight(a.Index, a.Weight + b.Weight);
		}

		public static IndexWeight operator +(IndexWeight a, float w)
		{
			return new IndexWeight(a.Index, a.Weight + w);
		}

		public static bool IsWellFormed(ReadOnlySpan<IndexWeight> iw, out string err)
		{
			for (int i = 0; i < iw.Length; i++)
			{
				IndexWeight indexWeight = iw[i];
				if (indexWeight.Weight == 0f)
				{
					if (indexWeight.Index != 0)
					{
						err = "weightless items must have index 0.";
						return false;
					}
					continue;
				}
				for (int j = 0; j < i; j++)
				{
					IndexWeight indexWeight2 = iw[j];
					if (indexWeight2.Weight != 0f && indexWeight.Index == indexWeight2.Index)
					{
						err = "indices must be unique.";
						return false;
					}
				}
			}
			err = null;
			return true;
		}

		public static int InsertSorted(Span<IndexWeight> buffer, int length, IndexWeight item)
		{
			if (item.Weight == 0f)
			{
				return length;
			}
			for (int i = 0; i < length; i++)
			{
				if (buffer[i].Index != item.Index)
				{
					continue;
				}
				buffer[i] += item;
				IndexWeight indexWeight;
				for (; i > 1; indexWeight = buffer[i - 1], buffer[i - 1] = buffer[i], buffer[i] = indexWeight, i--)
				{
					switch (Math.Abs(buffer[i - 1].Weight).CompareTo(buffer[i].Weight))
					{
					case 0:
						if (buffer[i - 1].Index >= item.Index)
						{
							continue;
						}
						break;
					default:
						continue;
					case 1:
						break;
					}
					break;
				}
				return length;
			}
			int num = length;
			float value = Math.Abs(item.Weight);
			for (int j = 0; j < length; j++)
			{
				int num2 = Math.Abs(buffer[j].Weight).CompareTo(value);
				if (num2 != 1 && (num2 != 0 || buffer[j].Index >= item.Index))
				{
					num = j;
					break;
				}
			}
			if (num >= buffer.Length)
			{
				return buffer.Length;
			}
			length = Math.Min(length + 1, buffer.Length);
			for (int num3 = length - 1; num3 > num; num3--)
			{
				buffer[num3] = buffer[num3 - 1];
			}
			buffer[num] = item;
			return length;
		}

		public static int InsertUnsorted(Span<IndexWeight> sparse, in Vector4 idx0123, in Vector4 wgt0123)
		{
			int num = 0;
			if (wgt0123.X != 0f)
			{
				sparse[0] = (Index: (int)idx0123.X, Weight: wgt0123.X);
				num++;
			}
			if (wgt0123.Y != 0f)
			{
				int num2 = (int)idx0123.Y;
				if (num == 1 && sparse[0].Index == num2)
				{
					sparse[0] += (IndexWeight)(Index: num2, Weight: wgt0123.Y);
				}
				else
				{
					sparse[num++] = (Index: num2, Weight: wgt0123.Y);
				}
			}
			if (wgt0123.Z != 0f)
			{
				int num3 = (int)idx0123.Z;
				if (num > 0 && sparse[0].Index == num3)
				{
					sparse[0] += (IndexWeight)(Index: num3, Weight: wgt0123.Z);
				}
				else if (num > 1 && sparse[1].Index == num3)
				{
					sparse[1] += (IndexWeight)(Index: num3, Weight: wgt0123.Z);
				}
				else
				{
					sparse[num++] = (Index: num3, Weight: wgt0123.Z);
				}
			}
			if (wgt0123.W != 0f)
			{
				int num4 = (int)idx0123.W;
				if (num > 0 && sparse[0].Index == num4)
				{
					sparse[0] += (IndexWeight)(Index: num4, Weight: wgt0123.W);
				}
				else if (num > 1 && sparse[1].Index == num4)
				{
					sparse[1] += (IndexWeight)(Index: num4, Weight: wgt0123.W);
				}
				else if (num > 2 && sparse[2].Index == num4)
				{
					sparse[2] += (IndexWeight)(Index: num4, Weight: wgt0123.W);
				}
				else
				{
					sparse[num++] = (Index: num4, Weight: wgt0123.W);
				}
			}
			return num;
		}

		public static int InsertUnsorted(Span<IndexWeight> buffer, int length, IndexWeight item)
		{
			if (item.Weight == 0f)
			{
				return length;
			}
			for (int i = 0; i < length; i++)
			{
				if (buffer[i].Index == item.Index)
				{
					float num = buffer[i].Weight + item.Weight;
					buffer[i] = ((num == 0f) ? default(IndexWeight) : new IndexWeight(item.Index, num));
					return length;
				}
			}
			if (length < buffer.Length)
			{
				buffer[length] = item;
				return length + 1;
			}
			int num2 = -1;
			IndexWeight other = item;
			for (int j = 0; j < buffer.Length; j++)
			{
				if (!buffer[j].IsGreaterThan(in other))
				{
					num2 = j;
					other = buffer[j];
				}
			}
			if (num2 >= 0)
			{
				buffer[num2] = item;
			}
			return length;
		}

		public static int CopyTo(in SparseWeight8 src, Span<int> dstIndices, Span<float> dstWeights, int dstLength)
		{
			foreach (IndexWeight item in src._GetPairs())
			{
				int num = dstIndices.Slice(0, dstLength).IndexOf(item.Index);
				if (num < 0)
				{
					dstIndices[dstLength] = item.Index;
					dstWeights[dstLength] = item.Weight;
					dstLength++;
				}
				else
				{
					dstWeights[num] += item.Weight;
				}
			}
			return dstLength;
		}

		public static void BubbleSortByWeight(Span<IndexWeight> pairs)
		{
			for (int i = 0; i < pairs.Length - 1; i++)
			{
				bool flag = true;
				for (int j = 1; j < pairs.Length; j++)
				{
					int index = j - 1;
					IndexWeight indexWeight = pairs[index];
					IndexWeight other = pairs[j];
					if (!indexWeight.IsGreaterThan(in other))
					{
						pairs[index] = other;
						pairs[j] = indexWeight;
						flag = false;
					}
				}
				if (flag)
				{
					break;
				}
			}
		}

		public static void BubbleSortByIndex(Span<IndexWeight> pairs)
		{
			for (int i = 0; i < pairs.Length - 1; i++)
			{
				bool flag = true;
				for (int j = 1; j < pairs.Length; j++)
				{
					int index = j - 1;
					IndexWeight indexWeight = pairs[index];
					IndexWeight indexWeight2 = pairs[j];
					if (indexWeight.Index >= indexWeight2.Index && (indexWeight.Index != indexWeight2.Index || !(indexWeight.Weight > indexWeight2.Weight)))
					{
						pairs[index] = indexWeight2;
						pairs[j] = indexWeight;
						flag = false;
					}
				}
				if (flag)
				{
					break;
				}
			}
		}
	}
	[DebuggerTypeProxy(typeof(_Matrix4x4DoubleProxy))]
	public struct Matrix4x4Double : IEquatable<Matrix4x4Double>
	{
		private static readonly Matrix4x4Double _identity = new Matrix4x4Double(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0);

		public double M11;

		public double M12;

		public double M13;

		public double M14;

		public double M21;

		public double M22;

		public double M23;

		public double M24;

		public double M31;

		public double M32;

		public double M33;

		public double M34;

		public double M41;

		public double M42;

		public double M43;

		public double M44;

		public static Matrix4x4Double Identity => _identity;

		public (double x, double y, double z) Translation
		{
			get
			{
				return (x: M41, y: M42, z: M43);
			}
			set
			{
				(M41, M42, M43) = value;
			}
		}

		public Matrix4x4Double(double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44)
		{
			M11 = m11;
			M12 = m12;
			M13 = m13;
			M14 = m14;
			M21 = m21;
			M22 = m22;
			M23 = m23;
			M24 = m24;
			M31 = m31;
			M32 = m32;
			M33 = m33;
			M34 = m34;
			M41 = m41;
			M42 = m42;
			M43 = m43;
			M44 = m44;
		}

		public Matrix4x4Double(Matrix4x4 other)
		{
			M11 = other.M11;
			M12 = other.M12;
			M13 = other.M13;
			M14 = other.M14;
			M21 = other.M21;
			M22 = other.M22;
			M23 = other.M23;
			M24 = other.M24;
			M31 = other.M31;
			M32 = other.M32;
			M33 = other.M33;
			M34 = other.M34;
			M41 = other.M41;
			M42 = other.M42;
			M43 = other.M43;
			M44 = other.M44;
		}

		public static Matrix4x4Double CreateTranslation(double xPosition, double yPosition, double zPosition)
		{
			Matrix4x4Double result = default(Matrix4x4Double);
			result.M11 = 1.0;
			result.M12 = 0.0;
			result.M13 = 0.0;
			result.M14 = 0.0;
			result.M21 = 0.0;
			result.M22 = 1.0;
			result.M23 = 0.0;
			result.M24 = 0.0;
			result.M31 = 0.0;
			result.M32 = 0.0;
			result.M33 = 1.0;
			result.M34 = 0.0;
			result.M41 = xPosition;
			result.M42 = yPosition;
			result.M43 = zPosition;
			result.M44 = 1.0;
			return result;
		}

		public static Matrix4x4Double CreateScale(double xScale, double yScale, double zScale)
		{
			Matrix4x4Double result = default(Matrix4x4Double);
			result.M11 = xScale;
			result.M12 = 0.0;
			result.M13 = 0.0;
			result.M14 = 0.0;
			result.M21 = 0.0;
			result.M22 = yScale;
			result.M23 = 0.0;
			result.M24 = 0.0;
			result.M31 = 0.0;
			result.M32 = 0.0;
			result.M33 = zScale;
			result.M34 = 0.0;
			result.M41 = 0.0;
			result.M42 = 0.0;
			result.M43 = 0.0;
			result.M44 = 1.0;
			return result;
		}

		public static Matrix4x4Double CreateFromQuaternion(Quaternion quaternion)
		{
			double num = quaternion.X;
			double num2 = quaternion.Y;
			double num3 = quaternion.Z;
			double num4 = quaternion.W;
			double num5 = num * num;
			double num6 = num2 * num2;
			double num7 = num3 * num3;
			double num8 = num * num2;
			double num9 = num3 * num4;
			double num10 = num3 * num;
			double num11 = num2 * num4;
			double num12 = num2 * num3;
			double num13 = num * num4;
			Matrix4x4Double result = default(Matrix4x4Double);
			result.M11 = 1.0 - 2.0 * (num6 + num7);
			result.M12 = 2.0 * (num8 + num9);
			result.M13 = 2.0 * (num10 - num11);
			result.M14 = 0.0;
			result.M21 = 2.0 * (num8 - num9);
			result.M22 = 1.0 - 2.0 * (num7 + num5);
			result.M23 = 2.0 * (num12 + num13);
			result.M24 = 0.0;
			result.M31 = 2.0 * (num10 + num11);
			result.M32 = 2.0 * (num12 - num13);
			result.M33 = 1.0 - 2.0 * (num6 + num5);
			result.M34 = 0.0;
			result.M41 = 0.0;
			result.M42 = 0.0;
			result.M43 = 0.0;
			result.M44 = 1.0;
			return result;
		}

		public static explicit operator Matrix4x4(Matrix4x4Double mat)
		{
			return new Matrix4x4((float)mat.M11, (float)mat.M12, (float)mat.M13, (float)mat.M14, (float)mat.M21, (float)mat.M22, (float)mat.M23, (float)mat.M24, (float)mat.M31, (float)mat.M32, (float)mat.M33, (float)mat.M34, (float)mat.M41, (float)mat.M42, (float)mat.M43, (float)mat.M44);
		}

		public static implicit operator Matrix4x4Double(Matrix4x4 mat)
		{
			return new Matrix4x4Double(mat.M11, mat.M12, mat.M13, mat.M14, mat.M21, mat.M22, mat.M23, mat.M24, mat.M31, mat.M32, mat.M33, mat.M34, mat.M41, mat.M42, mat.M43, mat.M44);
		}

		public override readonly int GetHashCode()
		{
			return M11.GetHashCode() + M12.GetHashCode() + M13.GetHashCode() + M14.GetHashCode() + M21.GetHashCode() + M22.GetHashCode() + M23.GetHashCode() + M24.GetHashCode() + M31.GetHashCode() + M32.GetHashCode() + M33.GetHashCode() + M34.GetHashCode() + M41.GetHashCode() + M42.GetHashCode() + M43.GetHashCode() + M44.GetHashCode();
		}

		public static bool operator ==(Matrix4x4Double value1, Matrix4x4Double value2)
		{
			if (value1.M11 == value2.M11 && value1.M22 == value2.M22 && value1.M33 == value2.M33 && value1.M44 == value2.M44 && value1.M12 == value2.M12 && value1.M13 == value2.M13 && value1.M14 == value2.M14 && value1.M21 == value2.M21 && value1.M23 == value2.M23 && value1.M24 == value2.M24 && value1.M31 == value2.M31 && value1.M32 == value2.M32 && value1.M34 == value2.M34 && value1.M41 == value2.M41 && value1.M42 == value2.M42)
			{
				return value1.M43 == value2.M43;
			}
			return false;
		}

		public static bool operator !=(Matrix4x4Double value1, Matrix4x4Double value2)
		{
			if (value1.M11 == value2.M11 && value1.M12 == value2.M12 && value1.M13 == value2.M13 && value1.M14 == value2.M14 && value1.M21 == value2.M21 && value1.M22 == value2.M22 && value1.M23 == value2.M23 && value1.M24 == value2.M24 && value1.M31 == value2.M31 && value1.M32 == value2.M32 && value1.M33 == value2.M33 && value1.M34 == value2.M34 && value1.M41 == value2.M41 && value1.M42 == value2.M42 && value1.M43 == value2.M43)
			{
				return value1.M44 != value2.M44;
			}
			return true;
		}

		public readonly bool Equals(Matrix4x4Double other)
		{
			return this == other;
		}

		public override readonly bool Equals(object obj)
		{
			if (obj is Matrix4x4Double matrix4x4Double)
			{
				return this == matrix4x4Double;
			}
			return false;
		}

		public static bool Invert(Matrix4x4Double matrix, out Matrix4x4Double result)
		{
			double m = matrix.M11;
			double m2 = matrix.M12;
			double m3 = matrix.M13;
			double m4 = matrix.M14;
			double m5 = matrix.M21;
			double m6 = matrix.M22;
			double m7 = matrix.M23;
			double m8 = matrix.M24;
			double m9 = matrix.M31;
			double m10 = matrix.M32;
			double m11 = matrix.M33;
			double m12 = matrix.M34;
			double m13 = matrix.M41;
			double m14 = matrix.M42;
			double m15 = matrix.M43;
			double m16 = matrix.M44;
			double num = m11 * m16 - m12 * m15;
			double num2 = m10 * m16 - m12 * m14;
			double num3 = m10 * m15 - m11 * m14;
			double num4 = m9 * m16 - m12 * m13;
			double num5 = m9 * m15 - m11 * m13;
			double num6 = m9 * m14 - m10 * m13;
			double num7 = m6 * num - m7 * num2 + m8 * num3;
			double num8 = 0.0 - (m5 * num - m7 * num4 + m8 * num5);
			double num9 = m5 * num2 - m6 * num4 + m8 * num6;
			double num10 = 0.0 - (m5 * num3 - m6 * num5 + m7 * num6);
			double num11 = m * num7 + m2 * num8 + m3 * num9 + m4 * num10;
			if (Math.Abs(num11) < double.Epsilon)
			{
				result = new Matrix4x4Double(double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN, double.NaN);
				return false;
			}
			double num12 = 1.0 / num11;
			result.M11 = num7 * num12;
			result.M21 = num8 * num12;
			result.M31 = num9 * num12;
			result.M41 = num10 * num12;
			result.M12 = (0.0 - (m2 * num - m3 * num2 + m4 * num3)) * num12;
			result.M22 = (m * num - m3 * num4 + m4 * num5) * num12;
			result.M32 = (0.0 - (m * num2 - m2 * num4 + m4 * num6)) * num12;
			result.M42 = (m * num3 - m2 * num5 + m3 * num6) * num12;
			double num13 = m7 * m16 - m8 * m15;
			double num14 = m6 * m16 - m8 * m14;
			double num15 = m6 * m15 - m7 * m14;
			double num16 = m5 * m16 - m8 * m13;
			double num17 = m5 * m15 - m7 * m13;
			double num18 = m5 * m14 - m6 * m13;
			result.M13 = (m2 * num13 - m3 * num14 + m4 * num15) * num12;
			result.M23 = (0.0 - (m * num13 - m3 * num16 + m4 * num17)) * num12;
			result.M33 = (m * num14 - m2 * num16 + m4 * num18) * num12;
			result.M43 = (0.0 - (m * num15 - m2 * num17 + m3 * num18)) * num12;
			double num19 = m7 * m12 - m8 * m11;
			double num20 = m6 * m12 - m8 * m10;
			double num21 = m6 * m11 - m7 * m10;
			double num22 = m5 * m12 - m8 * m9;
			double num23 = m5 * m11 - m7 * m9;
			double num24 = m5 * m10 - m6 * m9;
			result.M14 = (0.0 - (m2 * num19 - m3 * num20 + m4 * num21)) * num12;
			result.M24 = (m * num19 - m3 * num22 + m4 * num23) * num12;
			result.M34 = (0.0 - (m * num20 - m2 * num22 + m4 * num24)) * num12;
			result.M44 = (m * num21 - m2 * num23 + m3 * num24) * num12;
			return true;
		}

		public static Matrix4x4Double Multiply(Matrix4x4Double value1, Matrix4x4Double value2)
		{
			return value1 * value2;
		}

		public static Matrix4x4Double operator *(Matrix4x4Double value1, Matrix4x4Double value2)
		{
			Matrix4x4Double result = default(Matrix4x4Double);
			result.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21 + value1.M13 * value2.M31 + value1.M14 * value2.M41;
			result.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22 + value1.M13 * value2.M32 + value1.M14 * value2.M42;
			result.M13 = value1.M11 * value2.M13 + value1.M12 * value2.M23 + value1.M13 * value2.M33 + value1.M14 * value2.M43;
			result.M14 = value1.M11 * value2.M14 + value1.M12 * value2.M24 + value1.M13 * value2.M34 + value1.M14 * value2.M44;
			result.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21 + value1.M23 * value2.M31 + value1.M24 * value2.M41;
			result.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22 + value1.M23 * value2.M32 + value1.M24 * value2.M42;
			result.M23 = value1.M21 * value2.M13 + value1.M22 * value2.M23 + value1.M23 * value2.M33 + value1.M24 * value2.M43;
			result.M24 = value1.M21 * value2.M14 + value1.M22 * value2.M24 + value1.M23 * value2.M34 + value1.M24 * value2.M44;
			result.M31 = value1.M31 * value2.M11 + value1.M32 * value2.M21 + value1.M33 * value2.M31 + value1.M34 * value2.M41;
			result.M32 = value1.M31 * value2.M12 + value1.M32 * value2.M22 + value1.M33 * value2.M32 + value1.M34 * value2.M42;
			result.M33 = value1.M31 * value2.M13 + value1.M32 * value2.M23 + value1.M33 * value2.M33 + value1.M34 * value2.M43;
			result.M34 = value1.M31 * value2.M14 + value1.M32 * value2.M24 + value1.M33 * value2.M34 + value1.M34 * value2.M44;
			result.M41 = value1.M41 * value2.M11 + value1.M42 * value2.M21 + value1.M43 * value2.M31 + value1.M44 * value2.M41;
			result.M42 = value1.M41 * value2.M12 + value1.M42 * value2.M22 + value1.M43 * value2.M32 + value1.M44 * value2.M42;
			result.M43 = value1.M41 * value2.M13 + value1.M42 * value2.M23 + value1.M43 * value2.M33 + value1.M44 * value2.M43;
			result.M44 = value1.M41 * value2.M14 + value1.M42 * value2.M24 + value1.M43 * value2.M34 + value1.M44 * value2.M44;
			return result;
		}
	}
	public static class Matrix4x4Factory
	{
		[Flags]
		public enum MatrixCheck
		{
			None = 0,
			Finite = 1,
			NonZero = 2,
			Identity = 4,
			IdentityColumn4 = 8,
			Invertible = 0x10,
			Decomposable = 0x20,
			PositiveDeterminant = 0x40,
			LocalTransform = 0x3A,
			WorldTransform = 0x1A,
			InverseBindMatrix = 0x1A
		}

		private static MatrixCheck _Validate(in Matrix4x4 matrix, MatrixCheck check, float tolerance = 0f)
		{
			if (!matrix._IsFinite())
			{
				return MatrixCheck.Finite;
			}
			Matrix4x4 result;
			if (check.HasFlag(MatrixCheck.NonZero))
			{
				Matrix4x4 matrix4x = matrix;
				result = default(Matrix4x4);
				if (matrix4x == result)
				{
					return MatrixCheck.NonZero;
				}
			}
			if (check.HasFlag(MatrixCheck.Identity) && matrix != Matrix4x4.Identity)
			{
				return MatrixCheck.Identity;
			}
			if (check.HasFlag(MatrixCheck.IdentityColumn4))
			{
				if (matrix.M14 != 0f)
				{
					return MatrixCheck.IdentityColumn4;
				}
				if (matrix.M24 != 0f)
				{
					return MatrixCheck.IdentityColumn4;
				}
				if (matrix.M34 != 0f)
				{
					return MatrixCheck.IdentityColumn4;
				}
				if (tolerance == 0f)
				{
					if (matrix.M44 != 1f)
					{
						return MatrixCheck.IdentityColumn4;
					}
				}
				else if (Math.Abs(matrix.M44 - 1f) > tolerance)
				{
					return MatrixCheck.IdentityColumn4;
				}
			}
			if (check.HasFlag(MatrixCheck.Invertible) && !Matrix4x4.Invert(matrix, out result))
			{
				return MatrixCheck.Invertible;
			}
			if (check.HasFlag(MatrixCheck.Decomposable) && !Matrix4x4.Decompose(matrix, out var _, out var _, out var _))
			{
				return MatrixCheck.Decomposable;
			}
			if (check.HasFlag(MatrixCheck.PositiveDeterminant) && matrix.GetDeterminant() <= 0f)
			{
				return MatrixCheck.PositiveDeterminant;
			}
			return MatrixCheck.None;
		}

		public static bool IsValid(in Matrix4x4 matrix, MatrixCheck check, float tolerance = 0f)
		{
			return _Validate(in matrix, check, tolerance) == MatrixCheck.None;
		}

		[DebuggerStepThrough]
		public static void GuardMatrix(string argName, Matrix4x4 matrix, MatrixCheck check, float tolerance = 0f)
		{
			MatrixCheck matrixCheck = _Validate(in matrix, check, tolerance);
			if (matrixCheck != MatrixCheck.None)
			{
				throw new ArgumentException($"Invalid Matrix. Fail: {matrixCheck}", argName);
			}
		}

		public static Matrix4x4 CreateFromRows(Vector3 rowX, Vector3 rowY, Vector3 rowZ)
		{
			return new Matrix4x4(rowX.X, rowX.Y, rowX.Z, 0f, rowY.X, rowY.Y, rowY.Z, 0f, rowZ.X, rowZ.Y, rowZ.Z, 0f, 0f, 0f, 0f, 1f);
		}

		public static Matrix4x4 CreateFromRows(Vector3 rowX, Vector3 rowY, Vector3 rowZ, Vector3 translation)
		{
			return new Matrix4x4(rowX.X, rowX.Y, rowX.Z, 0f, rowY.X, rowY.Y, rowY.Z, 0f, rowZ.X, rowZ.Y, rowZ.Z, 0f, translation.X, translation.Y, translation.Z, 1f);
		}

		public static Matrix4x4 CreateFrom(Matrix4x4? transform, Vector3? scale, Quaternion? rotation, Vector3? translation)
		{
			if (transform.HasValue)
			{
				return transform.Value;
			}
			return new AffineTransform(scale, rotation, translation).Matrix;
		}

		public static Matrix4x4 LocalToWorld(in Matrix4x4 parentWorld, in Matrix4x4 childLocal)
		{
			GuardMatrix("parentWorld", parentWorld, MatrixCheck.WorldTransform);
			GuardMatrix("childLocal", childLocal, MatrixCheck.LocalTransform);
			return childLocal * parentWorld;
		}

		public static Matrix4x4 WorldToLocal(in Matrix4x4 parentWorld, in Matrix4x4 childWorld)
		{
			GuardMatrix("parentWorld", parentWorld, MatrixCheck.WorldTransform);
			GuardMatrix("childWorld", childWorld, MatrixCheck.WorldTransform);
			return childWorld * parentWorld.Inverse();
		}

		public static void NormalizeMatrix(ref Matrix4x4 xform)
		{
			Vector3 vector = new Vector3(xform.M11, xform.M12, xform.M13);
			Vector3 vector2 = new Vector3(xform.M21, xform.M22, xform.M23);
			Vector3 vector3 = new Vector3(xform.M31, xform.M32, xform.M33);
			float num = vector.Length();
			float num2 = vector2.Length();
			float num3 = vector3.Length();
			vector /= num;
			vector2 /= num2;
			vector3 /= num3;
			float num4 = Math.Abs(Vector3.Dot(vector, vector2));
			float num5 = Math.Abs(Vector3.Dot(vector, vector3));
			float num6 = Math.Abs(Vector3.Dot(vector2, vector3));
			float num7 = num4 + num5;
			float num8 = num4 + num6;
			float num9 = num5 + num6;
			if (num7 < num8 && num7 < num9)
			{
				if (num8 < num9)
				{
					vector3 = Vector3.Cross(vector, vector2);
					vector2 = Vector3.Cross(vector3, vector);
				}
				else
				{
					vector2 = Vector3.Cross(vector3, vector);
					vector3 = Vector3.Cross(vector, vector2);
				}
			}
			else if (num8 < num7 && num8 < num9)
			{
				if (num7 < num9)
				{
					vector3 = Vector3.Cross(vector, vector2);
					vector = Vector3.Cross(vector2, vector3);
				}
				else
				{
					vector = Vector3.Cross(vector2, vector3);
					vector3 = Vector3.Cross(vector, vector2);
				}
			}
			else if (num7 < num8)
			{
				vector2 = Vector3.Cross(vector3, vector);
				vector = Vector3.Cross(vector2, vector3);
			}
			else
			{
				vector = Vector3.Cross(vector2, vector3);
				vector2 = Vector3.Cross(vector3, vector);
			}
			vector *= num;
			vector2 *= num2;
			vector3 *= num3;
			xform.M11 = vector.X;
			xform.M12 = vector.Y;
			xform.M13 = vector.Z;
			xform.M21 = vector2.X;
			xform.M22 = vector2.Y;
			xform.M23 = vector2.Z;
			xform.M31 = vector3.X;
			xform.M32 = vector3.Y;
			xform.M33 = vector3.Z;
		}
	}
	public interface IMaterialTransform
	{
		Vector2 MorphTexCoord(Vector2 texCoord, IReadOnlyList<Vector2> morphTargets);

		Vector4 MorphColors(Vector4 color, IReadOnlyList<Vector4> morphTargets);
	}
	public interface IGeometryTransform
	{
		bool Visible { get; }

		bool FlipFaces { get; }

		Vector3 TransformPosition(Vector3 localPosition, IReadOnlyList<Vector3> positionDeltas, in SparseWeight8 skinWeights);

		Vector3 TransformNormal(Vector3 localNormal, IReadOnlyList<Vector3> normalDeltas, in SparseWeight8 skinWeights);

		Vector4 TransformTangent(Vector4 tangent, IReadOnlyList<Vector3> tangentDeltas, in SparseWeight8 skinWeights);
	}
	public interface IGeometryInstancing
	{
		int InstancesCount { get; }

		IReadOnlyList<RigidTransform> WorldTransforms { get; }
	}
	public abstract class MorphTransform : IMaterialTransform
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private SparseWeight8 _Weights;

		public const int COMPLEMENT_INDEX = 65536;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private bool _AbsoluteMorphTargets;

		public SparseWeight8 MorphWeights => _Weights;

		public bool AbsoluteMorphTargets => _AbsoluteMorphTargets;

		protected MorphTransform()
		{
			Update(default(SparseWeight8));
		}

		protected MorphTransform(SparseWeight8 morphWeights, bool useAbsoluteMorphTargets)
		{
			Update(in morphWeights, useAbsoluteMorphTargets);
		}

		public void Update(in SparseWeight8 morphWeights, bool useAbsoluteMorphTargets = false)
		{
			_AbsoluteMorphTargets = useAbsoluteMorphTargets;
			if (morphWeights.IsWeightless)
			{
				_Weights = SparseWeight8.Create((65536, 1f));
			}
			else
			{
				_Weights = morphWeights.GetNormalizedWithComplement(65536);
			}
		}

		protected Vector2 MorphVectors(Vector2 value, IReadOnlyList<Vector2> morphTargets)
		{
			if (morphTargets == null || morphTargets.Count == 0)
			{
				return value;
			}
			if (_Weights.Index0 == 65536 && _Weights.Weight0 == 1f)
			{
				return value;
			}
			Vector2 zero = Vector2.Zero;
			if (_AbsoluteMorphTargets)
			{
				foreach (var nonZeroWeight in _Weights.GetNonZeroWeig