#define DEBUG using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; 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 Assimp.Configs; using Assimp.Unmanaged; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("AssimpNet")] [assembly: AssemblyDescription("A .NET Wrapper for the Open Asset Import (Assimp) library.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Nicholas Woodfield")] [assembly: AssemblyProduct("AssimpNet")] [assembly: AssemblyCopyright("Copyright © 2012-2020 AssimpNet - Nicholas Woodfield")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("a2aee677-907b-4ec9-a9d4-a52d60542083")] [assembly: AssemblyFileVersion("5.0.0")] [assembly: AssemblyInformationalVersion("5.0.0-beta1")] [assembly: TargetFramework(".NETFramework,Version=v4.0", FrameworkDisplayName = ".NET Framework 4")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("5.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } } namespace Assimp { public sealed class Animation : IMarshalable { private string m_name; private double m_duration; private double m_ticksPerSecond; private List m_nodeChannels; private List m_meshChannels; private List m_meshMorphChannels; public string Name { get { return m_name; } set { m_name = value; } } public double DurationInTicks { get { return m_duration; } set { m_duration = value; } } public double TicksPerSecond { get { return m_ticksPerSecond; } set { m_ticksPerSecond = value; } } public bool HasNodeAnimations => m_nodeChannels.Count > 0; public int NodeAnimationChannelCount => m_nodeChannels.Count; public List NodeAnimationChannels => m_nodeChannels; public bool HasMeshAnimations => m_meshChannels.Count > 0; public int MeshAnimationChannelCount => m_meshChannels.Count; public int MeshMorphAnimationChannelCount => m_meshMorphChannels.Count; public List MeshAnimationChannels => m_meshChannels; public List MeshMorphAnimationChannels => m_meshMorphChannels; bool IMarshalable.IsNativeBlittable => true; public Animation() { m_name = string.Empty; m_duration = 0.0; m_ticksPerSecond = 0.0; m_nodeChannels = new List(); m_meshChannels = new List(); m_meshMorphChannels = new List(); } void IMarshalable.ToNative(IntPtr thisPtr, out AiAnimation nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.Duration = m_duration; nativeValue.TicksPerSecond = m_ticksPerSecond; nativeValue.NumChannels = (uint)NodeAnimationChannelCount; nativeValue.NumMeshChannels = (uint)MeshAnimationChannelCount; nativeValue.NumMeshMorphChannels = (uint)MeshMorphAnimationChannelCount; nativeValue.Channels = IntPtr.Zero; nativeValue.MeshChannels = IntPtr.Zero; nativeValue.MeshMorphChannels = IntPtr.Zero; if (nativeValue.NumChannels != 0) { nativeValue.Channels = MemoryHelper.ToNativeArray(m_nodeChannels.ToArray(), arrayOfPointers: true); } if (nativeValue.NumMeshChannels != 0) { nativeValue.MeshChannels = MemoryHelper.ToNativeArray(m_meshChannels.ToArray(), arrayOfPointers: true); } if (nativeValue.NumMeshMorphChannels != 0) { nativeValue.MeshMorphChannels = MemoryHelper.ToNativeArray(m_meshMorphChannels.ToArray(), arrayOfPointers: true); } } void IMarshalable.FromNative(in AiAnimation nativeValue) { m_nodeChannels.Clear(); m_meshChannels.Clear(); m_meshMorphChannels.Clear(); m_name = AiString.GetString(in nativeValue.Name); m_duration = nativeValue.Duration; m_ticksPerSecond = nativeValue.TicksPerSecond; if (nativeValue.NumChannels != 0 && nativeValue.Channels != IntPtr.Zero) { m_nodeChannels.AddRange(MemoryHelper.FromNativeArray(nativeValue.Channels, (int)nativeValue.NumChannels, arrayOfPointers: true)); } if (nativeValue.NumMeshChannels != 0 && nativeValue.MeshChannels != IntPtr.Zero) { m_meshChannels.AddRange(MemoryHelper.FromNativeArray(nativeValue.MeshChannels, (int)nativeValue.NumMeshChannels, arrayOfPointers: true)); } if (nativeValue.NumMeshMorphChannels != 0 && nativeValue.MeshMorphChannels != IntPtr.Zero) { m_meshMorphChannels.AddRange(MemoryHelper.FromNativeArray(nativeValue.MeshMorphChannels, (int)nativeValue.NumMeshMorphChannels, arrayOfPointers: true)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiAnimation aiAnimation = MemoryHelper.Read(nativeValue); if (aiAnimation.NumChannels != 0 && aiAnimation.Channels != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiAnimation.Channels, (int)aiAnimation.NumChannels, NodeAnimationChannel.FreeNative, arrayOfPointers: true); } if (aiAnimation.NumMeshChannels != 0 && aiAnimation.MeshChannels != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiAnimation.MeshChannels, (int)aiAnimation.NumMeshChannels, MeshAnimationChannel.FreeNative, arrayOfPointers: true); } if (aiAnimation.NumMeshMorphChannels != 0 && aiAnimation.MeshMorphChannels != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiAnimation.MeshMorphChannels, (int)aiAnimation.NumMeshMorphChannels, MeshMorphAnimationChannel.FreeNative, arrayOfPointers: true); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public sealed class AssimpContext : IDisposable { private bool m_isDisposed; private Dictionary m_configs; private IOSystem m_ioSystem; private ExportFormatDescription[] m_exportFormats; private string[] m_importFormats; private ImporterDescription[] m_importerDescrs; private float m_scale = 1f; private float m_xAxisRotation = 0f; private float m_yAxisRotation = 0f; private float m_zAxisRotation = 0f; private bool m_buildMatrix = false; private Matrix4x4 m_scaleRot = Matrix4x4.Identity; private IntPtr m_propStore = IntPtr.Zero; public bool IsDisposed => m_isDisposed; public float Scale { get { return m_scale; } set { if (m_scale != value) { m_scale = value; m_buildMatrix = true; } } } public float XAxisRotation { get { return m_xAxisRotation; } set { if (m_xAxisRotation != value) { m_xAxisRotation = value; m_buildMatrix = true; } } } public float YAxisRotation { get { return m_yAxisRotation; } set { if (m_yAxisRotation != value) { m_yAxisRotation = value; m_buildMatrix = true; } } } public float ZAxisRotation { get { return m_zAxisRotation; } set { if (m_zAxisRotation != value) { m_zAxisRotation = value; m_buildMatrix = true; } } } public bool UsingCustomIOSystem => m_ioSystem != null && !m_ioSystem.IsDisposed; public Dictionary PropertyConfigurations => m_configs; public AssimpContext() { m_configs = new Dictionary(); } public Scene ImportFileFromStream(Stream stream, string formatHint = null) { return ImportFileFromStream(stream, PostProcessSteps.None, formatHint); } public Scene ImportFileFromStream(Stream stream, PostProcessSteps postProcessFlags, string formatHint = null) { CheckDisposed(); if (stream == null || !stream.CanRead) { throw new AssimpException("stream", "Can't read from the stream it's null or write-only"); } IntPtr intPtr = IntPtr.Zero; PrepareImport(); try { intPtr = AssimpLibrary.Instance.ImportFileFromStream(stream, PostProcessSteps.None, formatHint, m_propStore); if (intPtr == IntPtr.Zero) { throw new AssimpException("Error importing file: " + AssimpLibrary.Instance.GetErrorString()); } TransformScene(intPtr); if (postProcessFlags != PostProcessSteps.None) { intPtr = AssimpLibrary.Instance.ApplyPostProcessing(intPtr, postProcessFlags); } return Scene.FromUnmanagedScene(intPtr); } finally { CleanupImport(); if (intPtr != IntPtr.Zero) { AssimpLibrary.Instance.ReleaseImport(intPtr); } } } public Scene ImportFile(string file) { return ImportFile(file, PostProcessSteps.None); } public Scene ImportFile(string file, PostProcessSteps postProcessFlags) { CheckDisposed(); IntPtr intPtr = IntPtr.Zero; IntPtr fileIO = IntPtr.Zero; if (UsingCustomIOSystem) { fileIO = m_ioSystem.AiFileIO; } else if (string.IsNullOrEmpty(file) || !File.Exists(file)) { throw new FileNotFoundException("Filename was null or could not be found", file); } PrepareImport(); try { intPtr = AssimpLibrary.Instance.ImportFile(file, PostProcessSteps.None, fileIO, m_propStore); if (intPtr == IntPtr.Zero) { throw new AssimpException("Error importing file: " + AssimpLibrary.Instance.GetErrorString()); } TransformScene(intPtr); if (postProcessFlags != PostProcessSteps.None) { intPtr = AssimpLibrary.Instance.ApplyPostProcessing(intPtr, postProcessFlags); } return Scene.FromUnmanagedScene(intPtr); } finally { CleanupImport(); if (intPtr != IntPtr.Zero) { AssimpLibrary.Instance.ReleaseImport(intPtr); } } } public bool ExportFile(Scene scene, string fileName, string exportFormatId) { return ExportFile(scene, fileName, exportFormatId, PostProcessSteps.None); } public bool ExportFile(Scene scene, string fileName, string exportFormatId, PostProcessSteps preProcessing) { CheckDisposed(); IntPtr zero = IntPtr.Zero; IntPtr intPtr = IntPtr.Zero; if (scene == null) { throw new ArgumentNullException("scene", "Scene must exist."); } if (!TestIfExportIdIsValid(exportFormatId)) { return false; } try { intPtr = Scene.ToUnmanagedScene(scene); ReturnCode returnCode = AssimpLibrary.Instance.ExportScene(intPtr, exportFormatId, fileName, zero, preProcessing); return returnCode == ReturnCode.Success; } finally { if (intPtr != IntPtr.Zero) { Scene.FreeUnmanagedScene(intPtr); } } } public ExportDataBlob ExportToBlob(Scene scene, string exportFormatId) { return ExportToBlob(scene, exportFormatId, PostProcessSteps.None); } public ExportDataBlob ExportToBlob(Scene scene, string exportFormatId, PostProcessSteps preProcessing) { CheckDisposed(); IntPtr zero = IntPtr.Zero; IntPtr intPtr = IntPtr.Zero; if (scene == null) { throw new ArgumentNullException("scene", "Scene must exist."); } if (!TestIfExportIdIsValid(exportFormatId)) { return null; } try { intPtr = Scene.ToUnmanagedScene(scene); return AssimpLibrary.Instance.ExportSceneToBlob(intPtr, exportFormatId, preProcessing); } finally { if (intPtr != IntPtr.Zero) { Scene.FreeUnmanagedScene(intPtr); } } } public bool ConvertFromFileToFile(string inputFilename, string outputFilename, string exportFormatId) { return ConvertFromFileToFile(inputFilename, PostProcessSteps.None, outputFilename, exportFormatId, PostProcessSteps.None); } public bool ConvertFromFileToFile(string inputFilename, string outputFilename, string exportFormatId, PostProcessSteps exportProcessSteps) { return ConvertFromFileToFile(inputFilename, PostProcessSteps.None, outputFilename, exportFormatId, exportProcessSteps); } public bool ConvertFromFileToFile(string inputFilename, PostProcessSteps importProcessSteps, string outputFilename, string exportFormatId, PostProcessSteps exportProcessSteps) { CheckDisposed(); if (!TestIfExportIdIsValid(exportFormatId)) { return false; } IntPtr intPtr = IntPtr.Zero; IntPtr fileIO = IntPtr.Zero; if (UsingCustomIOSystem) { fileIO = m_ioSystem.AiFileIO; } else if (string.IsNullOrEmpty(inputFilename) || !File.Exists(inputFilename)) { throw new FileNotFoundException("Filename was null or could not be found", inputFilename); } PrepareImport(); try { intPtr = AssimpLibrary.Instance.ImportFile(inputFilename, PostProcessSteps.None, fileIO, m_propStore); if (intPtr == IntPtr.Zero) { throw new AssimpException("Error importing file: " + AssimpLibrary.Instance.GetErrorString()); } TransformScene(intPtr); if (importProcessSteps != PostProcessSteps.None) { intPtr = AssimpLibrary.Instance.ApplyPostProcessing(intPtr, importProcessSteps); } ReturnCode returnCode = AssimpLibrary.Instance.ExportScene(intPtr, exportFormatId, outputFilename, fileIO, exportProcessSteps); return returnCode == ReturnCode.Success; } finally { CleanupImport(); if (intPtr != IntPtr.Zero) { AssimpLibrary.Instance.ReleaseImport(intPtr); } } } public ExportDataBlob ConvertFromFileToBlob(string inputFilename, string exportFormatId) { return ConvertFromFileToBlob(inputFilename, PostProcessSteps.None, exportFormatId, PostProcessSteps.None); } public ExportDataBlob ConvertFromFileToBlob(string inputFilename, string exportFormatId, PostProcessSteps exportProcessSteps) { return ConvertFromFileToBlob(inputFilename, PostProcessSteps.None, exportFormatId, exportProcessSteps); } public ExportDataBlob ConvertFromFileToBlob(string inputFilename, PostProcessSteps importProcessSteps, string exportFormatId, PostProcessSteps exportProcessSteps) { CheckDisposed(); if (!TestIfExportIdIsValid(exportFormatId)) { return null; } IntPtr intPtr = IntPtr.Zero; IntPtr fileIO = IntPtr.Zero; if (UsingCustomIOSystem) { fileIO = m_ioSystem.AiFileIO; } else if (string.IsNullOrEmpty(inputFilename) || !File.Exists(inputFilename)) { throw new FileNotFoundException("Filename was null or could not be found", inputFilename); } PrepareImport(); try { intPtr = AssimpLibrary.Instance.ImportFile(inputFilename, PostProcessSteps.None, fileIO, m_propStore); if (intPtr == IntPtr.Zero) { throw new AssimpException("Error importing file: " + AssimpLibrary.Instance.GetErrorString()); } TransformScene(intPtr); if (importProcessSteps != PostProcessSteps.None) { intPtr = AssimpLibrary.Instance.ApplyPostProcessing(intPtr, importProcessSteps); } return AssimpLibrary.Instance.ExportSceneToBlob(intPtr, exportFormatId, exportProcessSteps); } finally { CleanupImport(); if (intPtr != IntPtr.Zero) { AssimpLibrary.Instance.ReleaseImport(intPtr); } } } public bool ConvertFromStreamToFile(Stream inputStream, string importFormatHint, string outputFilename, string exportFormatId) { return ConvertFromStreamToFile(inputStream, importFormatHint, PostProcessSteps.None, outputFilename, exportFormatId, PostProcessSteps.None); } public bool ConvertFromStreamToFile(Stream inputStream, string importFormatHint, string outputFilename, string exportFormatId, PostProcessSteps exportProcessSteps) { return ConvertFromStreamToFile(inputStream, importFormatHint, PostProcessSteps.None, outputFilename, exportFormatId, exportProcessSteps); } public bool ConvertFromStreamToFile(Stream inputStream, string importFormatHint, PostProcessSteps importProcessSteps, string outputFilename, string exportFormatId, PostProcessSteps exportProcessSteps) { CheckDisposed(); if (inputStream == null || !inputStream.CanRead) { throw new AssimpException("stream", "Can't read from the stream it's null or write-only"); } if (!TestIfExportIdIsValid(exportFormatId)) { return false; } IntPtr intPtr = IntPtr.Zero; PrepareImport(); try { intPtr = AssimpLibrary.Instance.ImportFileFromStream(inputStream, importProcessSteps, importFormatHint, m_propStore); if (intPtr == IntPtr.Zero) { throw new AssimpException("Error importing file: " + AssimpLibrary.Instance.GetErrorString()); } TransformScene(intPtr); if (importProcessSteps != PostProcessSteps.None) { intPtr = AssimpLibrary.Instance.ApplyPostProcessing(intPtr, importProcessSteps); } ReturnCode returnCode = AssimpLibrary.Instance.ExportScene(intPtr, exportFormatId, outputFilename, exportProcessSteps); return returnCode == ReturnCode.Success; } finally { CleanupImport(); if (intPtr != IntPtr.Zero) { AssimpLibrary.Instance.ReleaseImport(intPtr); } } } public ExportDataBlob ConvertFromStreamToBlob(Stream inputStream, string importFormatHint, string exportFormatId) { return ConvertFromStreamToBlob(inputStream, importFormatHint, PostProcessSteps.None, exportFormatId, PostProcessSteps.None); } public ExportDataBlob ConvertFromStreamToBlob(Stream inputStream, string importFormatHint, string exportFormatId, PostProcessSteps exportProcessSteps) { return ConvertFromStreamToBlob(inputStream, importFormatHint, PostProcessSteps.None, exportFormatId, exportProcessSteps); } public ExportDataBlob ConvertFromStreamToBlob(Stream inputStream, string importFormatHint, PostProcessSteps importProcessSteps, string exportFormatId, PostProcessSteps exportProcessSteps) { CheckDisposed(); if (inputStream == null || !inputStream.CanRead) { throw new AssimpException("stream", "Can't read from the stream it's null or write-only"); } if (!TestIfExportIdIsValid(exportFormatId)) { return null; } IntPtr intPtr = IntPtr.Zero; PrepareImport(); try { intPtr = AssimpLibrary.Instance.ImportFileFromStream(inputStream, importProcessSteps, importFormatHint, m_propStore); if (intPtr == IntPtr.Zero) { throw new AssimpException("Error importing file: " + AssimpLibrary.Instance.GetErrorString()); } TransformScene(intPtr); if (importProcessSteps != PostProcessSteps.None) { intPtr = AssimpLibrary.Instance.ApplyPostProcessing(intPtr, importProcessSteps); } return AssimpLibrary.Instance.ExportSceneToBlob(intPtr, exportFormatId, exportProcessSteps); } finally { CleanupImport(); if (intPtr != IntPtr.Zero) { AssimpLibrary.Instance.ReleaseImport(intPtr); } } } public void SetIOSystem(IOSystem ioSystem) { if (ioSystem == null || ioSystem.IsDisposed) { ioSystem = null; } m_ioSystem = ioSystem; } public void RemoveIOSystem() { m_ioSystem = null; } public ExportFormatDescription[] GetSupportedExportFormats() { QueryExportFormatsIfNecessary(); return (ExportFormatDescription[])m_exportFormats.Clone(); } public string[] GetSupportedImportFormats() { QueryImportFormatsIfNecessary(); return (string[])m_importFormats.Clone(); } public ImporterDescription[] GetImporterDescriptions() { QueryImporterDescriptionsIfNecessary(); return (ImporterDescription[])m_importerDescrs.Clone(); } public ImporterDescription GetImporterDescriptionFor(string fileExtension) { if (string.IsNullOrEmpty(fileExtension)) { return null; } QueryImporterDescriptionsIfNecessary(); if (fileExtension.StartsWith(".") && fileExtension.Length >= 2) { fileExtension = fileExtension.Substring(1); } ImporterDescription[] importerDescrs = m_importerDescrs; foreach (ImporterDescription importerDescription in importerDescrs) { string[] fileExtensions = importerDescription.FileExtensions; foreach (string b in fileExtensions) { if (string.Equals(fileExtension, b, StringComparison.Ordinal)) { return importerDescription; } } } return null; } public bool IsImportFormatSupported(string format) { return AssimpLibrary.Instance.IsExtensionSupported(format); } public bool IsExportFormatSupported(string format) { if (string.IsNullOrEmpty(format)) { return false; } QueryExportFormatsIfNecessary(); if (format.StartsWith(".") && format.Length >= 2) { format = format.Substring(1); } ExportFormatDescription[] exportFormats = m_exportFormats; foreach (ExportFormatDescription exportFormatDescription in exportFormats) { if (string.Equals(exportFormatDescription.FileExtension, format, StringComparison.Ordinal)) { return true; } } return false; } public void SetConfig(PropertyConfig config) { if (config != null) { string name = config.Name; m_configs[config.Name] = config; } } public void RemoveConfig(string configName) { if (!string.IsNullOrEmpty(configName) && m_configs.TryGetValue(configName, out var _)) { m_configs.Remove(configName); } } public void RemoveConfigs() { m_configs.Clear(); } public bool ContainsConfig(string configName) { if (string.IsNullOrEmpty(configName)) { return false; } return m_configs.ContainsKey(configName); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!m_isDisposed) { if (disposing && UsingCustomIOSystem) { m_ioSystem.Dispose(); } m_isDisposed = true; } } private void CheckDisposed() { if (m_isDisposed) { throw new ObjectDisposedException("Assimp Context has been disposed."); } } private void QueryExportFormatsIfNecessary() { if (m_exportFormats == null) { m_exportFormats = AssimpLibrary.Instance.GetExportFormatDescriptions(); } } private void QueryImportFormatsIfNecessary() { if (m_importFormats == null) { m_importFormats = AssimpLibrary.Instance.GetExtensionList(); } } private void QueryImporterDescriptionsIfNecessary() { if (m_importerDescrs == null) { m_importerDescrs = AssimpLibrary.Instance.GetImporterDescriptions(); } } private void BuildMatrix() { if (m_buildMatrix) { Matrix4x4 matrix4x = Matrix4x4.FromScaling(new Vector3D(m_scale, m_scale, m_scale)); Matrix4x4 matrix4x2 = Matrix4x4.FromRotationX(m_xAxisRotation * ((float)Math.PI / 180f)); Matrix4x4 matrix4x3 = Matrix4x4.FromRotationY(m_yAxisRotation * ((float)Math.PI / 180f)); Matrix4x4 matrix4x4 = Matrix4x4.FromRotationZ(m_zAxisRotation * ((float)Math.PI / 180f)); m_scaleRot = matrix4x * (matrix4x2 * matrix4x3 * matrix4x4); } m_buildMatrix = false; } private bool TransformScene(IntPtr scene) { BuildMatrix(); try { if (!m_scaleRot.IsIdentity) { AiScene aiScene = MemoryHelper.MarshalStructure(scene); if (aiScene.RootNode == IntPtr.Zero) { return false; } IntPtr intPtr = MemoryHelper.AddIntPtr(aiScene.RootNode, MemoryHelper.SizeOf()); Matrix4x4 matrix4x = MemoryHelper.Read(intPtr); MemoryHelper.Write(intPtr, matrix4x * m_scaleRot); return true; } } catch (Exception) { } return false; } private void CreateConfigs() { m_propStore = AssimpLibrary.Instance.CreatePropertyStore(); foreach (KeyValuePair config in m_configs) { config.Value.ApplyValue(m_propStore); } } private void ReleaseConfigs() { if (m_propStore != IntPtr.Zero) { AssimpLibrary.Instance.ReleasePropertyStore(m_propStore); } } private void PrepareImport() { CreateConfigs(); } private void CleanupImport() { ReleaseConfigs(); if (UsingCustomIOSystem) { m_ioSystem.CloseAllFiles(); } } private bool TestIfExportIdIsValid(string exportFormatId) { if (m_exportFormats == null) { m_exportFormats = AssimpLibrary.Instance.GetExportFormatDescriptions(); } ExportFormatDescription[] exportFormats = m_exportFormats; foreach (ExportFormatDescription exportFormatDescription in exportFormats) { if (exportFormatDescription.FormatId.Equals(exportFormatId, StringComparison.Ordinal)) { return true; } } IEnumerable attachedLogStreams = LogStream.GetAttachedLogStreams(); foreach (LogStream item in attachedLogStreams) { item.Log($"Info, Invalid export format: {exportFormatId}"); } return false; } } public class AssimpException : Exception { public AssimpException() { } public AssimpException(string msg) : base(msg) { } public AssimpException(string paramName, string msg) : base("Parameter: " + paramName + " Error: " + msg) { } public AssimpException(string msg, Exception innerException) : base(msg, innerException) { } } public sealed class Bone : IMarshalable { private string m_name; private List m_weights; private Matrix4x4 m_offsetMatrix; public string Name { get { return m_name; } set { m_name = value; } } public int VertexWeightCount => m_weights.Count; public bool HasVertexWeights => m_weights.Count > 0; public List VertexWeights => m_weights; public Matrix4x4 OffsetMatrix { get { return m_offsetMatrix; } set { m_offsetMatrix = value; } } bool IMarshalable.IsNativeBlittable => true; public Bone() { m_name = null; m_offsetMatrix = Matrix3x3.Identity; m_weights = new List(); } public Bone(string name, Matrix3x3 offsetMatrix, VertexWeight[] weights) { m_name = name; m_offsetMatrix = offsetMatrix; m_weights = new List(); if (weights != null) { m_weights.AddRange(weights); } } void IMarshalable.ToNative(IntPtr thisPtr, out AiBone nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.OffsetMatrix = m_offsetMatrix; nativeValue.NumWeights = (uint)m_weights.Count; nativeValue.Weights = IntPtr.Zero; if (nativeValue.NumWeights != 0) { nativeValue.Weights = MemoryHelper.ToNativeArray(m_weights.ToArray()); } } void IMarshalable.FromNative(in AiBone nativeValue) { m_name = AiString.GetString(in nativeValue.Name); m_offsetMatrix = nativeValue.OffsetMatrix; m_weights.Clear(); if (nativeValue.NumWeights != 0 && nativeValue.Weights != IntPtr.Zero) { m_weights.AddRange(MemoryHelper.FromNativeArray(nativeValue.Weights, (int)nativeValue.NumWeights)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiBone aiBone = MemoryHelper.Read(nativeValue); int num = MemoryHelper.Read(MemoryHelper.AddIntPtr(nativeValue, MemoryHelper.SizeOf())); IntPtr intPtr = MemoryHelper.AddIntPtr(nativeValue, MemoryHelper.SizeOf() + 4); if (aiBone.NumWeights != 0 && aiBone.Weights != IntPtr.Zero) { MemoryHelper.FreeMemory(aiBone.Weights); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public struct BoundingBox : IEquatable { public Vector3D Min; public Vector3D Max; public BoundingBox(Vector3D min, Vector3D max) { Min = min; Max = max; } public static bool operator ==(BoundingBox a, BoundingBox b) { return a.Min == b.Min && a.Max == b.Max; } public static bool operator !=(BoundingBox a, BoundingBox b) { return a.Min != b.Min || a.Max != b.Max; } public bool Equals(BoundingBox other) { return Min == other.Min && Max == other.Max; } public override bool Equals(object obj) { if (obj is BoundingBox) { return Equals((BoundingBox)obj); } return false; } public override int GetHashCode() { return Min.GetHashCode() + Max.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{Min:{0} Max:{1}}", new object[2] { Min.ToString(), Max.ToString() }); } } public sealed class Camera : IMarshalable { private string m_name; private Vector3D m_position; private Vector3D m_up; private Vector3D m_direction; private float m_fieldOfView; private float m_clipPlaneNear; private float m_clipPlaneFar; private float m_aspectRatio; public string Name { get { return m_name; } set { m_name = value; } } public Vector3D Position { get { return m_position; } set { m_position = value; } } public Vector3D Up { get { return m_up; } set { m_up = value; } } public Vector3D Direction { get { return m_direction; } set { m_direction = value; } } public float FieldOfview { get { return m_fieldOfView; } set { m_fieldOfView = value; } } public float ClipPlaneNear { get { return m_clipPlaneNear; } set { m_clipPlaneNear = value; } } public float ClipPlaneFar { get { return m_clipPlaneFar; } set { m_clipPlaneFar = value; } } public float AspectRatio { get { return m_aspectRatio; } set { m_aspectRatio = value; } } public Matrix4x4 ViewMatrix { get { Vector3D direction = m_direction; direction.Normalize(); Vector3D up = m_up; up.Normalize(); Vector3D a = Vector3D.Cross(m_up, m_direction); direction.Normalize(); Matrix4x4 result = default(Matrix4x4); result.A1 = a.X; result.A2 = a.Y; result.A3 = a.Z; result.A4 = 0f; result.B1 = up.X; result.B2 = up.Y; result.B3 = up.Z; result.B4 = 0f; result.C1 = direction.X; result.C2 = direction.Y; result.C3 = direction.Z; result.C4 = 0f; result.D1 = 0f - Vector3D.Dot(a, m_position); result.D2 = 0f - Vector3D.Dot(up, m_position); result.D3 = 0f - Vector3D.Dot(direction, m_position); result.D4 = 1f; return result; } } bool IMarshalable.IsNativeBlittable => true; public Camera() { m_name = string.Empty; } void IMarshalable.ToNative(IntPtr thisPtr, out AiCamera nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.Position = m_position; nativeValue.LookAt = m_direction; nativeValue.Up = m_up; nativeValue.HorizontalFOV = m_fieldOfView; nativeValue.ClipPlaneFar = m_clipPlaneFar; nativeValue.ClipPlaneNear = m_clipPlaneNear; nativeValue.Aspect = m_aspectRatio; } void IMarshalable.FromNative(in AiCamera nativeValue) { m_name = AiString.GetString(in nativeValue.Name); m_position = nativeValue.Position; m_direction = nativeValue.LookAt; m_up = nativeValue.Up; m_fieldOfView = nativeValue.HorizontalFOV; m_clipPlaneFar = nativeValue.ClipPlaneFar; m_clipPlaneNear = nativeValue.ClipPlaneNear; m_aspectRatio = nativeValue.Aspect; } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (nativeValue != IntPtr.Zero && freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } public struct Color3D { public float R; public float G; public float B; public float this[int index] { get { return index switch { 0 => R, 1 => G, 2 => B, _ => 0f, }; } set { switch (index) { case 0: R = value; break; case 1: G = value; break; case 2: B = value; break; } } } public Color3D(float r, float g, float b) { R = r; G = g; B = b; } public Color3D(float value) { R = value; G = value; B = value; } public bool IsBlack() { float num = 0.01f; return Math.Abs(R) < num && Math.Abs(G) < num && Math.Abs(B) < num; } public static Color3D operator +(Color3D a, Color3D b) { Color3D result = default(Color3D); result.R = a.R + b.R; result.G = a.G + b.G; result.B = a.B + b.B; return result; } public static Color3D operator +(Color3D color, float value) { Color3D result = default(Color3D); result.R = color.R + value; result.G = color.G + value; result.B = color.B + value; return result; } public static Color3D operator +(float value, Color3D color) { Color3D result = default(Color3D); result.R = color.R + value; result.G = color.G + value; result.B = color.B + value; return result; } public static Color3D operator -(Color3D a, Color3D b) { Color3D result = default(Color3D); result.R = a.R - b.R; result.G = a.G - b.G; result.B = a.B - b.B; return result; } public static Color3D operator -(Color3D color, float value) { Color3D result = default(Color3D); result.R = color.R - value; result.G = color.G - value; result.B = color.B - value; return result; } public static Color3D operator -(float value, Color3D color) { Color3D result = default(Color3D); result.R = value - color.R; result.G = value - color.G; result.B = value - color.B; return result; } public static Color3D operator *(Color3D a, Color3D b) { Color3D result = default(Color3D); result.R = a.R * b.R; result.G = a.G * b.G; result.B = a.B * b.B; return result; } public static Color3D operator *(Color3D value, float scale) { Color3D result = default(Color3D); result.R = value.R * scale; result.G = value.G * scale; result.B = value.B * scale; return result; } public static Color3D operator *(float scale, Color3D value) { Color3D result = default(Color3D); result.R = value.R * scale; result.G = value.G * scale; result.B = value.B * scale; return result; } public static Color3D operator /(Color3D a, Color3D b) { Color3D result = default(Color3D); result.R = a.R / b.R; result.G = a.G / b.G; result.B = a.B / b.B; return result; } public static Color3D operator /(Color3D color, float divisor) { float num = 1f / divisor; Color3D result = default(Color3D); result.R = color.R * num; result.G = color.G * num; result.B = color.B * num; return result; } public static bool operator ==(Color3D a, Color3D b) { return a.R == b.R && a.G == b.G && a.B == b.B; } public static bool operator !=(Color3D a, Color3D b) { return a.R != b.R || a.G != b.G || a.B != b.B; } public bool Equals(Color3D other) { return R == other.R && G == other.G && B == other.B; } public override bool Equals(object obj) { if (obj is Color3D) { return Equals((Color3D)obj); } return false; } public override int GetHashCode() { return R.GetHashCode() + G.GetHashCode() + B.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{R:{0} G:{1} B:{2}}}", new object[3] { R.ToString(currentCulture), G.ToString(currentCulture), B.ToString(currentCulture) }); } } public struct Color4D : IEquatable { public float R; public float G; public float B; public float A; public float this[int index] { get { return index switch { 0 => R, 1 => G, 2 => B, 3 => A, _ => 0f, }; } set { switch (index) { case 0: R = value; break; case 1: G = value; break; case 2: B = value; break; case 3: A = value; break; } } } public Color4D(float r, float g, float b, float a) { R = r; G = g; B = b; A = a; } public Color4D(float r, float g, float b) { R = r; G = g; B = b; A = 1f; } public Color4D(float value) { R = value; G = value; B = value; A = value; } public Color4D(Color3D rgb) { R = rgb.R; G = rgb.G; B = rgb.B; A = 1f; } public Color4D(Color3D rgb, float alpha) { R = rgb.R; G = rgb.G; B = rgb.B; A = alpha; } public bool IsBlack() { float num = 0.01f; return Math.Abs(R) < num && Math.Abs(G) < num && Math.Abs(B) < num; } public static Color4D operator +(Color4D a, Color4D b) { Color4D result = default(Color4D); result.R = a.R + b.R; result.G = a.G + b.G; result.B = a.B + b.B; result.A = a.A + b.A; return result; } public static Color4D operator +(Color4D color, float value) { Color4D result = default(Color4D); result.R = color.R + value; result.G = color.G + value; result.B = color.B + value; result.A = color.A + value; return result; } public static Color4D operator +(float value, Color4D color) { Color4D result = default(Color4D); result.R = color.R + value; result.G = color.G + value; result.B = color.B + value; result.A = color.A + value; return result; } public static Color4D operator -(Color4D a, Color4D b) { Color4D result = default(Color4D); result.R = a.R - b.R; result.G = a.G - b.G; result.B = a.B - b.B; result.A = a.A - b.A; return result; } public static Color4D operator -(Color4D color, float value) { Color4D result = default(Color4D); result.R = color.R - value; result.G = color.G - value; result.B = color.B - value; result.A = color.A - value; return result; } public static Color4D operator -(float value, Color4D color) { Color4D result = default(Color4D); result.R = value - color.R; result.G = value - color.G; result.B = value - color.B; result.A = value - color.A; return result; } public static Color4D operator *(Color4D a, Color4D b) { Color4D result = default(Color4D); result.R = a.R * b.R; result.G = a.G * b.G; result.B = a.B * b.B; result.A = a.A * b.A; return result; } public static Color4D operator *(Color4D value, float scale) { Color4D result = default(Color4D); result.R = value.R * scale; result.G = value.G * scale; result.B = value.B * scale; result.A = value.A * scale; return result; } public static Color4D operator *(float scale, Color4D value) { Color4D result = default(Color4D); result.R = value.R * scale; result.G = value.G * scale; result.B = value.B * scale; result.A = value.A * scale; return result; } public static Color4D operator /(Color4D a, Color4D b) { Color4D result = default(Color4D); result.R = a.R / b.R; result.G = a.G / b.G; result.B = a.B / b.B; result.A = a.A / b.A; return result; } public static Color4D operator /(Color4D color, float divisor) { float num = 1f / divisor; Color4D result = default(Color4D); result.R = color.R * num; result.G = color.G * num; result.B = color.B * num; result.A = color.A * num; return result; } public static bool operator ==(Color4D a, Color4D b) { return a.R == b.R && a.G == b.G && a.B == b.B && a.A == b.A; } public static bool operator !=(Color4D a, Color4D b) { return a.R != b.R || a.G != b.G || a.B != b.B || a.A != b.A; } public bool Equals(Color4D other) { return R == other.R && G == other.G && B == other.B && A == other.A; } public override bool Equals(object obj) { if (obj is Color4D) { return Equals((Color4D)obj); } return false; } public override int GetHashCode() { return R.GetHashCode() + G.GetHashCode() + B.GetHashCode() + A.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{R:{0} G:{1} B:{2} A:{3}}}", R.ToString(currentCulture), G.ToString(currentCulture), B.ToString(currentCulture), A.ToString(currentCulture)); } } public sealed class EmbeddedTexture : IMarshalable { private string m_filename; private bool m_isCompressed; private int m_width; private int m_height; private Texel[] m_nonCompressedData; private byte[] m_compressedData; private string m_compressedFormatHint; public string Filename { get { return m_filename; } set { m_filename = value; } } public bool IsCompressed => m_isCompressed; public int Width => m_width; public int Height => m_height; public bool HasNonCompressedData => m_nonCompressedData != null || m_nonCompressedData.Length != 0; public int NonCompressedDataSize => (m_nonCompressedData != null) ? m_nonCompressedData.Length : 0; public Texel[] NonCompressedData => m_nonCompressedData; public bool HasCompressedData => m_compressedData != null || m_compressedData.Length != 0; public int CompressedDataSize => (m_compressedData != null) ? m_compressedData.Length : 0; public byte[] CompressedData => m_compressedData; public string CompressedFormatHint => m_compressedFormatHint; bool IMarshalable.IsNativeBlittable => true; public EmbeddedTexture() { m_isCompressed = false; } public EmbeddedTexture(string compressedFormatHint, byte[] compressedData, string originalFileName = "") { m_filename = originalFileName; m_compressedFormatHint = compressedFormatHint; m_compressedData = compressedData; m_isCompressed = true; m_width = 0; m_height = 0; m_nonCompressedData = null; } public EmbeddedTexture(int width, int height, Texel[] uncompressedData, string originalFileName = "") { m_filename = originalFileName; m_width = width; m_height = height; m_nonCompressedData = uncompressedData; if (m_width * m_height != NonCompressedDataSize) { throw new ArgumentException("Texel data size does not match width * height."); } m_isCompressed = false; m_compressedFormatHint = null; m_compressedData = null; } void IMarshalable.ToNative(IntPtr thisPtr, out AiTexture nativeValue) { nativeValue.Filename = new AiString(m_filename); if (IsCompressed) { nativeValue.Width = (uint)CompressedDataSize; nativeValue.Height = 0u; nativeValue.Data = IntPtr.Zero; if (CompressedDataSize > 0) { nativeValue.Data = MemoryHelper.ToNativeArray(m_compressedData); } nativeValue.SetFormatHint(m_compressedFormatHint); } else { nativeValue.Width = (uint)m_width; nativeValue.Height = (uint)m_height; nativeValue.Data = IntPtr.Zero; if (NonCompressedDataSize > 0) { nativeValue.Data = MemoryHelper.ToNativeArray(m_nonCompressedData); } nativeValue.SetFormatHint(null); } } void IMarshalable.FromNative(in AiTexture nativeValue) { m_filename = AiString.GetString(in nativeValue.Filename); m_isCompressed = nativeValue.Height == 0; if (IsCompressed) { m_width = 0; m_height = 0; m_nonCompressedData = null; m_compressedData = null; if (nativeValue.Width != 0 && nativeValue.Data != IntPtr.Zero) { m_compressedData = MemoryHelper.FromNativeArray(nativeValue.Data, (int)nativeValue.Width); } m_compressedFormatHint = AiTexture.GetFormatHint(in nativeValue); return; } m_compressedData = null; m_compressedFormatHint = null; m_nonCompressedData = null; m_width = (int)nativeValue.Width; m_height = (int)nativeValue.Height; int num = m_width * m_height; if (num > 0 && nativeValue.Data != IntPtr.Zero) { m_nonCompressedData = MemoryHelper.FromNativeArray(nativeValue.Data, num); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiTexture aiTexture = MemoryHelper.Read(nativeValue); if (aiTexture.Width != 0 && aiTexture.Data != IntPtr.Zero) { MemoryHelper.FreeMemory(aiTexture.Data); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public enum MetaDataType { Bool, Int32, UInt64, Float, Double, String, Vector3D } [Flags] public enum PostProcessSteps : uint { None = 0u, CalculateTangentSpace = 1u, JoinIdenticalVertices = 2u, MakeLeftHanded = 4u, Triangulate = 8u, RemoveComponent = 0x10u, GenerateNormals = 0x20u, GenerateSmoothNormals = 0x40u, SplitLargeMeshes = 0x80u, PreTransformVertices = 0x100u, LimitBoneWeights = 0x200u, ValidateDataStructure = 0x400u, ImproveCacheLocality = 0x800u, RemoveRedundantMaterials = 0x1000u, FixInFacingNormals = 0x2000u, SortByPrimitiveType = 0x8000u, FindDegenerates = 0x10000u, FindInvalidData = 0x20000u, GenerateUVCoords = 0x40000u, TransformUVCoords = 0x80000u, FindInstances = 0x100000u, OptimizeMeshes = 0x200000u, OptimizeGraph = 0x400000u, FlipUVs = 0x800000u, FlipWindingOrder = 0x1000000u, SplitByBoneCount = 0x2000000u, Debone = 0x4000000u, GlobalScale = 0x8000000u, EmbedTextures = 0x10000000u, ForceGenerateNormals = 0x20000000u, DropNormals = 0x40000000u, GenerateBoundingBoxes = 0x80000000u } [Flags] public enum ExcludeComponent { None = 0, Normals = 2, TangentBasis = 4, Colors = 8, TexCoords = 0x10, Boneweights = 0x20, Animations = 0x40, Textures = 0x80, Lights = 0x100, Cameras = 0x200, Meshes = 0x400, Materials = 0x800 } [Flags] public enum PrimitiveType { Point = 1, Line = 2, Triangle = 4, Polygon = 8 } public enum AnimationBehaviour { Default, Constant, Linear, Repeat } public enum MeshMorphingMethod { None, VertexBlend, MorphNormalized, MorphRelative } public enum LightSourceType { Undefined, Directional, Point, Spot, Ambient, Area } public enum BlendMode { Default, Additive } public enum ShadingMode { None, Flat, Gouraud, Phong, Blinn, Toon, OrenNayar, Minnaert, CookTorrance, NoShading, Fresnel } [Flags] public enum TextureFlags { Invert = 1, UseAlpha = 2, IgnoreAlpha = 4 } public enum TextureWrapMode { Wrap, Clamp, Mirror, Decal } public enum TextureMapping { FromUV, Sphere, Cylinder, Box, Plane, Unknown } public enum TextureOperation { Multiply, Add, Subtract, Divide, SmoothAdd, SignedAdd } public enum TextureType { None, Diffuse, Specular, Ambient, Emissive, Height, Normals, Shininess, Opacity, Displacement, Lightmap, Reflection, BaseColor, NormalCamera, EmissionColor, Metalness, Roughness, AmbientOcclusion, Unknown } [Flags] public enum SceneFlags { None = 0, Incomplete = 1, Validated = 2, ValidationWarning = 4, NonVerboseFormat = 8, Terrain = 0x10, AllowShared = 0x20 } public enum ReturnCode { Success = 0, Failure = -1, OutOfMemory = -3 } public enum Origin { Set, Current, End } [Flags] public enum DefaultLogStream { File = 1, StdOut = 2, StdErr = 4, Debugger = 8 } public enum PropertyType { Float = 1, Double, String, Integer, Buffer } public enum CompileFlags { Shared = 1, STLport = 2, Debug = 4, NoBoost = 8, SingleThreaded = 0x10 } [Flags] public enum UVTransformFlags { Scaling = 1, Rotation = 2, Translation = 4 } public enum FileIOMode { Write, WriteBinary, WriteText, Read, ReadBinary, ReadText } [Flags] public enum ImporterFeatureFlags { SupportsText = 1, SupportsBinary = 2, SupportsCompressed = 4, LimitedSupport = 8, Experimental = 0x10 } public sealed class ExportDataBlob { private class BlobBinaryReader : BinaryReader { public BlobBinaryReader(Stream stream) : base(stream, Encoding.UTF8) { } protected override void Dispose(bool disposing) { base.Dispose(disposing: false); } } private string m_name; private byte[] m_data; private ExportDataBlob m_next; public string Name => m_name; public byte[] Data => m_data; public ExportDataBlob NextBlob => m_next; public bool HasData => m_data != null; internal ExportDataBlob(ref AiExportDataBlob dataBlob) { m_name = dataBlob.Name.GetString(); if (dataBlob.Size.ToUInt32() != 0 && dataBlob.Data != IntPtr.Zero) { m_data = MemoryHelper.FromNativeArray(dataBlob.Data, (int)dataBlob.Size.ToUInt32()); } m_next = null; if (dataBlob.NextBlob != IntPtr.Zero) { AiExportDataBlob dataBlob2 = MemoryHelper.MarshalStructure(dataBlob.NextBlob); m_next = new ExportDataBlob(ref dataBlob2); } } internal ExportDataBlob(string name, byte[] data) { m_name = name; m_data = data; m_next = null; } public void ToStream(Stream stream) { MemoryStream memoryStream = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(memoryStream); WriteBlob(this, writer); memoryStream.Position = 0L; memoryStream.WriteTo(stream); } public static ExportDataBlob FromStream(Stream stream) { if (stream == null || !stream.CanRead) { return null; } using BlobBinaryReader reader = new BlobBinaryReader(stream); return ReadBlob(reader); } private static void WriteBlob(ExportDataBlob blob, BinaryWriter writer) { if (blob != null && writer != null) { bool flag = blob.NextBlob != null; writer.Write(blob.Name); writer.Write(blob.Data.Length); writer.Write(blob.Data); writer.Write(flag); if (flag) { WriteBlob(blob.NextBlob, writer); } } } private static ExportDataBlob ReadBlob(BinaryReader reader) { if (reader == null) { return null; } string name = reader.ReadString(); int count = reader.ReadInt32(); byte[] data = reader.ReadBytes(count); bool flag = reader.ReadBoolean(); ExportDataBlob exportDataBlob = new ExportDataBlob(name, data); if (flag) { exportDataBlob.m_next = ReadBlob(reader); } return exportDataBlob; } } [DebuggerDisplay("{Description}")] public sealed class ExportFormatDescription { private string m_formatId; private string m_description; private string m_fileExtension; public string FormatId => m_formatId; public string Description => m_description; public string FileExtension => m_fileExtension; internal ExportFormatDescription(in AiExportFormatDesc formatDesc) { m_formatId = Marshal.PtrToStringAnsi(formatDesc.FormatId); m_description = Marshal.PtrToStringAnsi(formatDesc.Description); m_fileExtension = Marshal.PtrToStringAnsi(formatDesc.FileExtension); if (m_fileExtension == "dae") { m_formatId = "collada"; } } } public sealed class Face : IMarshalable { private List m_indices; public int IndexCount => m_indices.Count; public bool HasIndices => m_indices.Count > 0; public List Indices => m_indices; bool IMarshalable.IsNativeBlittable => true; public Face() { m_indices = new List(); } public Face(int[] indices) { m_indices = new List(); if (indices != null) { m_indices.AddRange(indices); } } void IMarshalable.ToNative(IntPtr thisPtr, out AiFace nativeValue) { nativeValue.NumIndices = (uint)IndexCount; nativeValue.Indices = IntPtr.Zero; if (nativeValue.NumIndices != 0) { nativeValue.Indices = MemoryHelper.ToNativeArray(m_indices.ToArray()); } } void IMarshalable.FromNative(in AiFace nativeValue) { m_indices.Clear(); if (nativeValue.NumIndices != 0 && nativeValue.Indices != IntPtr.Zero) { m_indices.AddRange(MemoryHelper.FromNativeArray(nativeValue.Indices, (int)nativeValue.NumIndices)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiFace aiFace = MemoryHelper.Read(nativeValue); if (aiFace.NumIndices != 0 && aiFace.Indices != IntPtr.Zero) { MemoryHelper.FreeMemory(aiFace.Indices); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public class FileIOSystem : IOSystem { private List m_searchDirectories; public FileIOSystem() : this((string[])null) { } public FileIOSystem(params string[] searchPaths) { m_searchDirectories = new List(); SetSearchDirectories(searchPaths); } public void SetSearchDirectories(params string[] searchPaths) { m_searchDirectories.Clear(); if (searchPaths == null || searchPaths.Length == 0) { return; } foreach (string text in searchPaths) { if (!string.IsNullOrEmpty(text) && Directory.Exists(text)) { m_searchDirectories.Add(new DirectoryInfo(text)); } } } public string[] GetSearchDirectories() { List list = new List(); foreach (DirectoryInfo searchDirectory in m_searchDirectories) { list.Add(searchDirectory.FullName); } return list.ToArray(); } public override IOStream OpenFile(string pathToFile, FileIOMode fileMode) { return new FileIOStream(this, pathToFile, fileMode); } public bool FindFile(string fileName, out string pathToFile) { pathToFile = null; if (string.IsNullOrEmpty(fileName) || m_searchDirectories.Count == 0) { return false; } foreach (DirectoryInfo searchDirectory in m_searchDirectories) { string text = Path.Combine(searchDirectory.FullName, fileName); if (File.Exists(text)) { pathToFile = text; return true; } } return false; } } internal class FileIOStream : IOStream { private FileIOSystem m_parent; private FileStream m_fileStream; public override bool IsValid => m_fileStream != null; public FileIOStream(FileIOSystem parent, string pathToFile, FileIOMode fileMode) : base(pathToFile, fileMode) { m_parent = parent; switch (fileMode) { case FileIOMode.Read: case FileIOMode.ReadBinary: case FileIOMode.ReadText: OpenRead(pathToFile, fileMode); break; case FileIOMode.Write: case FileIOMode.WriteBinary: case FileIOMode.WriteText: OpenWrite(pathToFile, fileMode); break; } } public override long Write(byte[] dataToWrite, long count) { if (dataToWrite == null) { throw new ArgumentOutOfRangeException("dataToWrite", "Data to write cannot be null."); } if (count < 0 || dataToWrite.Length < count) { throw new ArgumentOutOfRangeException("count", "Number of bytes to write is greater than data size."); } if (m_fileStream == null || !m_fileStream.CanWrite) { throw new IOException("Stream is not writable."); } if (m_fileStream.Position >= m_fileStream.Length) { return 0L; } m_fileStream.Write(dataToWrite, (int)m_fileStream.Position, (int)count); return count; } public override long Read(byte[] dataRead, long count) { if (dataRead == null) { throw new ArgumentOutOfRangeException("dataRead", "Array to store data in cannot be null."); } if (count < 0 || dataRead.Length < count) { throw new ArgumentOutOfRangeException("count", "Number of bytes to read is greater than data store size."); } if (m_fileStream == null || !m_fileStream.CanRead) { throw new IOException("Stream is not readable."); } if (m_fileStream.Position >= m_fileStream.Length) { return 0L; } m_fileStream.Read(dataRead, (int)m_fileStream.Position, (int)count); return count; } public override ReturnCode Seek(long offset, Origin seekOrigin) { if (m_fileStream == null || !m_fileStream.CanSeek) { throw new IOException("Stream does not support seeking."); } SeekOrigin origin = SeekOrigin.Begin; switch (seekOrigin) { case Origin.Set: origin = SeekOrigin.Begin; break; case Origin.Current: origin = SeekOrigin.Current; break; case Origin.End: origin = SeekOrigin.End; break; } m_fileStream.Seek(offset, origin); return ReturnCode.Success; } public override long GetPosition() { if (m_fileStream == null) { return -1L; } return m_fileStream.Position; } public override long GetFileSize() { if (m_fileStream == null) { return 0L; } return m_fileStream.Length; } public override void Flush() { if (m_fileStream != null) { m_fileStream.Flush(); } } protected override void Dispose(bool disposing) { if (!base.IsDisposed && disposing) { if (m_fileStream != null) { m_fileStream.Dispose(); } m_fileStream = null; base.Dispose(disposing); } } private void OpenRead(string pathToFile, FileIOMode fileMode) { string fileName = Path.GetFileName(pathToFile); if (m_parent.FindFile(fileName, out var pathToFile2)) { pathToFile = pathToFile2; } if (File.Exists(pathToFile)) { m_fileStream = File.OpenRead(pathToFile); } } private void OpenWrite(string pathToFile, FileIOMode fileMode) { m_fileStream = File.OpenWrite(pathToFile); } } [DebuggerDisplay("{Name}")] public sealed class ImporterDescription { private string m_name; private string m_author; private string m_maintainer; private string m_comments; private ImporterFeatureFlags m_featureFlags; private Version m_minVersion; private Version m_maxVersion; private string[] m_fileExtensions; public string Name => m_name; public string Author => m_author; public string Maintainer => m_maintainer; public string Comments => m_comments; public ImporterFeatureFlags FeatureFlags => m_featureFlags; public Version MinVersion => m_minVersion; public Version MaxVersion => m_maxVersion; public string[] FileExtensions => m_fileExtensions; internal ImporterDescription(in AiImporterDesc descr) { m_name = Marshal.PtrToStringAnsi(descr.Name); m_author = Marshal.PtrToStringAnsi(descr.Author); m_maintainer = Marshal.PtrToStringAnsi(descr.Maintainer); m_comments = Marshal.PtrToStringAnsi(descr.Comments); m_featureFlags = descr.Flags; m_minVersion = new Version((int)descr.MinMajor, (int)descr.MinMinor); m_maxVersion = new Version((int)descr.MaxMajor, (int)descr.MaxMajor); string text = Marshal.PtrToStringAnsi(descr.FileExtensions); if (string.IsNullOrEmpty(text)) { m_fileExtensions = new string[0]; return; } m_fileExtensions = text.Split(new char[1] { ' ' }); } } public interface IMarshalable where Managed : class, new() where Native : struct { bool IsNativeBlittable { get; } void ToNative(IntPtr thisPtr, out Native nativeValue); void FromNative(in Native nativeValue); } public interface INativeCustomMarshaler { int NativeDataSize { get; } void MarshalManagedToNative(object managedObj, IntPtr nativeData); object MarshalNativeToManaged(IntPtr nativeData); } public abstract class IOStream : IDisposable { private AiFileWriteProc m_writeProc; private AiFileReadProc m_readProc; private AiFileTellProc m_tellProc; private AiFileTellProc m_fileSizeProc; private AiFileSeek m_seekProc; private AiFileFlushProc m_flushProc; private IntPtr m_filePtr; private bool m_isDiposed; private string m_pathToFile; private FileIOMode m_fileMode; private byte[] m_byteBuffer; public bool IsDisposed => m_isDiposed; public string PathToFile => m_pathToFile; public FileIOMode FileMode => m_fileMode; public abstract bool IsValid { get; } internal IntPtr AiFile => m_filePtr; public IOStream(string pathToFile, FileIOMode fileMode) { m_pathToFile = pathToFile; m_fileMode = fileMode; m_writeProc = OnAiFileWriteProc; m_readProc = OnAiFileReadProc; m_tellProc = OnAiFileTellProc; m_fileSizeProc = OnAiFileSizeProc; m_seekProc = OnAiFileSeekProc; m_flushProc = OnAiFileFlushProc; AiFile aiFile = default(AiFile); aiFile.WriteProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_writeProc); aiFile.ReadProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_readProc); aiFile.TellProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_tellProc); aiFile.FileSizeProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_fileSizeProc); aiFile.SeekProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_seekProc); aiFile.FlushProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_flushProc); aiFile.UserData = IntPtr.Zero; m_filePtr = MemoryHelper.AllocateMemory(MemoryHelper.SizeOf()); Marshal.StructureToPtr((object)aiFile, m_filePtr, fDeleteOld: false); } ~IOStream() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!m_isDiposed) { if (m_filePtr != IntPtr.Zero) { MemoryHelper.FreeMemory(m_filePtr); m_filePtr = IntPtr.Zero; } if (disposing) { m_writeProc = null; m_readProc = null; m_tellProc = null; m_fileSizeProc = null; m_seekProc = null; m_flushProc = null; } m_isDiposed = true; } } public abstract long Write(byte[] dataToWrite, long count); public abstract long Read(byte[] dataRead, long count); public abstract ReturnCode Seek(long offset, Origin seekOrigin); public abstract long GetPosition(); public abstract long GetFileSize(); public abstract void Flush(); public virtual void Close() { Flush(); Dispose(); } private UIntPtr OnAiFileWriteProc(IntPtr file, IntPtr dataToWrite, UIntPtr sizeOfElemInBytes, UIntPtr numElements) { if (m_filePtr != file) { return UIntPtr.Zero; } long num = (long)sizeOfElemInBytes.ToUInt64(); long num2 = (long)numElements.ToUInt64(); long num3 = num * num2; if (num3 == 0) { return UIntPtr.Zero; } byte[] byteBuffer = GetByteBuffer(num, num2); MemoryHelper.Read(dataToWrite, byteBuffer, 0, (int)num3); long num4 = 0L; try { num4 = Write(byteBuffer, num3); } catch (Exception) { } return new UIntPtr((ulong)num4 / (ulong)num); } private UIntPtr OnAiFileReadProc(IntPtr file, IntPtr dataRead, UIntPtr sizeOfElemInBytes, UIntPtr numElements) { if (m_filePtr != file) { return UIntPtr.Zero; } long num = (long)sizeOfElemInBytes.ToUInt64(); long num2 = (long)numElements.ToUInt64(); long count = num * num2; byte[] byteBuffer = GetByteBuffer(num, num2); long num3 = 0L; try { num3 = Read(byteBuffer, count); if (num3 > 0) { MemoryHelper.Write(dataRead, byteBuffer, 0, (int)num3); } } catch (Exception) { } return new UIntPtr((ulong)num3 / (ulong)num); } private UIntPtr OnAiFileTellProc(IntPtr file) { if (m_filePtr != file) { return UIntPtr.Zero; } long value = 0L; try { value = GetPosition(); } catch (Exception) { } return new UIntPtr((ulong)value); } private UIntPtr OnAiFileSizeProc(IntPtr file) { if (m_filePtr != file) { return UIntPtr.Zero; } long value = 0L; try { value = GetFileSize(); } catch (Exception) { } return new UIntPtr((ulong)value); } private ReturnCode OnAiFileSeekProc(IntPtr file, UIntPtr offset, Origin seekOrigin) { if (m_filePtr != file) { return ReturnCode.Failure; } ReturnCode result = ReturnCode.Failure; try { result = Seek((long)offset.ToUInt64(), seekOrigin); } catch (Exception) { } return result; } private void OnAiFileFlushProc(IntPtr file) { if (m_filePtr != file) { return; } try { Flush(); } catch (Exception) { } } private byte[] GetByteBuffer(long sizeOfElemInBytes, long numElements) { if (m_byteBuffer == null || m_byteBuffer.Length < sizeOfElemInBytes * numElements) { m_byteBuffer = new byte[sizeOfElemInBytes * numElements]; } return m_byteBuffer; } } public abstract class IOSystem : IDisposable { private AiFileOpenProc m_openProc; private AiFileCloseProc m_closeProc; private IntPtr m_fileIOPtr; private bool m_isDisposed; private Dictionary m_openedFiles; public bool IsDisposed => m_isDisposed; public int OpenFileCount => m_openedFiles.Count; internal IntPtr AiFileIO => m_fileIOPtr; public IOSystem() { m_openProc = OnAiFileOpenProc; m_closeProc = OnAiFileCloseProc; AiFileIO aiFileIO = default(AiFileIO); aiFileIO.OpenProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_openProc); aiFileIO.CloseProc = Marshal.GetFunctionPointerForDelegate((Delegate)m_closeProc); aiFileIO.UserData = IntPtr.Zero; m_fileIOPtr = MemoryHelper.AllocateMemory(MemoryHelper.SizeOf()); Marshal.StructureToPtr((object)aiFileIO, m_fileIOPtr, fDeleteOld: false); m_openedFiles = new Dictionary(); } ~IOSystem() { Dispose(disposing: false); } public abstract IOStream OpenFile(string pathToFile, FileIOMode fileMode); public virtual void CloseFile(IOStream stream) { if (stream != null && m_openedFiles.ContainsKey(stream.AiFile)) { m_openedFiles.Remove(stream.AiFile); if (!stream.IsDisposed) { stream.Close(); } } } public virtual void CloseAllFiles() { foreach (KeyValuePair openedFile in m_openedFiles) { if (!openedFile.Value.IsDisposed) { openedFile.Value.Close(); } } m_openedFiles.Clear(); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!m_isDisposed) { if (m_fileIOPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(m_fileIOPtr); m_fileIOPtr = IntPtr.Zero; } if (disposing) { m_openProc = null; m_closeProc = null; CloseAllFiles(); } m_isDisposed = true; } } private IntPtr OnAiFileOpenProc(IntPtr fileIO, string pathToFile, string mode) { if (m_fileIOPtr != fileIO) { return IntPtr.Zero; } FileIOMode fileMode = ConvertFileMode(mode); IOStream iOStream = OpenFile(pathToFile, fileMode); IntPtr intPtr = IntPtr.Zero; if (iOStream != null) { if (iOStream.IsValid) { intPtr = iOStream.AiFile; m_openedFiles.Add(intPtr, iOStream); } else { iOStream.Dispose(); } } return intPtr; } private void OnAiFileCloseProc(IntPtr fileIO, IntPtr file) { if (!(m_fileIOPtr != fileIO) && m_openedFiles.TryGetValue(file, out var value)) { CloseFile(value); } } private FileIOMode ConvertFileMode(string mode) { FileIOMode result = FileIOMode.Read; switch (mode) { case "w": result = FileIOMode.Write; break; case "wb": result = FileIOMode.WriteBinary; break; case "wt": result = FileIOMode.WriteText; break; case "r": result = FileIOMode.Read; break; case "rb": result = FileIOMode.ReadBinary; break; case "rt": result = FileIOMode.ReadText; break; } return result; } } public sealed class Light : IMarshalable { private string m_name; private LightSourceType m_lightType; private float m_angleInnerCone; private float m_angleOuterCone; private float m_attConstant; private float m_attLinear; private float m_attQuadratic; private Vector3D m_position; private Vector3D m_direction; private Vector3D m_up; private Color3D m_diffuse; private Color3D m_specular; private Color3D m_ambient; private Vector2D m_areaSize; public string Name { get { return m_name; } set { m_name = value; } } public LightSourceType LightType { get { return m_lightType; } set { m_lightType = value; } } public float AngleInnerCone { get { return m_angleInnerCone; } set { m_angleInnerCone = value; } } public float AngleOuterCone { get { return m_angleOuterCone; } set { m_angleOuterCone = value; } } public float AttenuationConstant { get { return m_attConstant; } set { m_attConstant = value; } } public float AttenuationLinear { get { return m_attLinear; } set { m_attLinear = value; } } public float AttenuationQuadratic { get { return m_attQuadratic; } set { m_attQuadratic = value; } } public Vector3D Position { get { return m_position; } set { m_position = value; } } public Vector3D Direction { get { return m_direction; } set { m_direction = value; } } public Vector3D Up { get { return m_up; } set { m_up = value; } } public Color3D ColorDiffuse { get { return m_diffuse; } set { m_diffuse = value; } } public Color3D ColorSpecular { get { return m_specular; } set { m_specular = value; } } public Color3D ColorAmbient { get { return m_ambient; } set { m_ambient = value; } } public Vector2D AreaSize { get { return m_areaSize; } set { m_areaSize = value; } } bool IMarshalable.IsNativeBlittable => true; public Light() { m_lightType = LightSourceType.Undefined; m_attConstant = 0f; m_attLinear = 1f; m_attQuadratic = 0f; m_angleInnerCone = (float)Math.PI * 2f; m_angleOuterCone = (float)Math.PI * 2f; m_areaSize = new Vector2D(0f, 0f); } void IMarshalable.ToNative(IntPtr thisPtr, out AiLight nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.Type = m_lightType; nativeValue.AngleInnerCone = m_angleInnerCone; nativeValue.AngleOuterCone = m_angleOuterCone; nativeValue.AttenuationConstant = m_attConstant; nativeValue.AttenuationLinear = m_attLinear; nativeValue.AttenuationQuadratic = m_attQuadratic; nativeValue.ColorAmbient = m_ambient; nativeValue.ColorDiffuse = m_diffuse; nativeValue.ColorSpecular = m_specular; nativeValue.Direction = m_direction; nativeValue.Up = m_up; nativeValue.Position = m_position; nativeValue.AreaSize = m_areaSize; } void IMarshalable.FromNative(in AiLight nativeValue) { m_name = AiString.GetString(in nativeValue.Name); m_lightType = nativeValue.Type; m_angleInnerCone = nativeValue.AngleInnerCone; m_angleOuterCone = nativeValue.AngleOuterCone; m_attConstant = nativeValue.AttenuationConstant; m_attLinear = nativeValue.AttenuationLinear; m_attQuadratic = nativeValue.AttenuationQuadratic; m_position = nativeValue.Position; m_direction = nativeValue.Direction; m_up = nativeValue.Up; m_diffuse = nativeValue.ColorDiffuse; m_specular = nativeValue.ColorSpecular; m_ambient = nativeValue.ColorAmbient; m_areaSize = nativeValue.AreaSize; } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (nativeValue != IntPtr.Zero && freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } public delegate void LoggingCallback(string msg, string userData); [DebuggerDisplay("IsAttached = {IsAttached}")] public class LogStream : IDisposable { private static object s_sync; private static List s_activeLogstreams; private LoggingCallback m_logCallback; private AiLogStreamCallback m_assimpCallback; private IntPtr m_logstreamPtr; private string m_userData; private bool m_isDisposed; private bool m_isAttached; public static bool IsVerboseLoggingEnabled { get { return AssimpLibrary.Instance.GetVerboseLoggingEnabled(); } set { AssimpLibrary.Instance.EnableVerboseLogging(value); } } public string UserData { get { return m_userData; } set { m_userData = value; } } public bool IsDisposed => m_isDisposed; public bool IsAttached => m_isAttached; static LogStream() { s_sync = new object(); s_activeLogstreams = new List(); AssimpLibrary.Instance.LibraryFreed += AssimpLibraryFreed; } protected LogStream() : this("") { } protected LogStream(string userData) { Initialize(null, userData); } public LogStream(LoggingCallback callback) { Initialize(callback, string.Empty); } public LogStream(LoggingCallback callback, string userData) { Initialize(callback, userData); } ~LogStream() { Dispose(disposing: false); } public static void DetachAllLogstreams() { lock (s_sync) { foreach (LogStream s_activeLogstream in s_activeLogstreams) { s_activeLogstream.m_isAttached = false; AssimpLibrary.Instance.DetachLogStream(s_activeLogstream.m_logstreamPtr); s_activeLogstream.OnDetach(); } s_activeLogstreams.Clear(); } } public static IEnumerable GetAttachedLogStreams() { lock (s_sync) { return s_activeLogstreams.ToArray(); } } private static void AssimpLibraryFreed(object sender, EventArgs e) { DetachAllLogstreams(); } public void Attach() { if (m_isAttached) { return; } lock (s_sync) { s_activeLogstreams.Add(this); m_isAttached = true; AssimpLibrary.Instance.AttachLogStream(m_logstreamPtr); OnAttach(); } } public void Detach() { if (!m_isAttached) { return; } lock (s_sync) { s_activeLogstreams.Remove(this); m_isAttached = false; AssimpLibrary.Instance.DetachLogStream(m_logstreamPtr); OnDetach(); } } public void Log(string msg) { if (m_isAttached && !string.IsNullOrEmpty(msg)) { OnAiLogStreamCallback(msg, IntPtr.Zero); } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!m_isDisposed) { if (m_logstreamPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(m_logstreamPtr); m_logstreamPtr = IntPtr.Zero; } if (disposing) { m_assimpCallback = null; } m_isDisposed = true; } } protected virtual void LogMessage(string msg, string userData) { } protected virtual void OnAttach() { } protected virtual void OnDetach() { } private void OnAiLogStreamCallback(string msg, IntPtr userData) { if (m_logCallback != null) { m_logCallback(msg, m_userData); } else { LogMessage(msg, m_userData); } } private void Initialize(LoggingCallback callback, string userData) { if (userData == null) { userData = string.Empty; } m_assimpCallback = OnAiLogStreamCallback; m_logCallback = callback; m_userData = userData; AiLogStream aiLogStream = default(AiLogStream); aiLogStream.Callback = Marshal.GetFunctionPointerForDelegate((Delegate)m_assimpCallback); aiLogStream.UserData = IntPtr.Zero; m_logstreamPtr = MemoryHelper.AllocateMemory(MemoryHelper.SizeOf()); Marshal.StructureToPtr((object)aiLogStream, m_logstreamPtr, fDeleteOld: false); } } public sealed class ConsoleLogStream : LogStream { public ConsoleLogStream() { } public ConsoleLogStream(string userData) : base(userData) { } protected override void LogMessage(string msg, string userData) { if (string.IsNullOrEmpty(userData)) { Console.WriteLine(msg); } else { Console.WriteLine($"{userData}: {msg}"); } } } public sealed class Material : IMarshalable { public sealed class PBRMaterialProperties { private Material m_parent; public bool HasTextureBaseColor => m_parent.HasProperty("$tex.file", TextureType.BaseColor, 0); public TextureSlot TextureBaseColor { get { m_parent.GetMaterialTexture(TextureType.BaseColor, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.BaseColor) { m_parent.AddMaterialTexture(in value); } } } public bool HasTextureNormalCamera => m_parent.HasProperty("$tex.file", TextureType.NormalCamera, 0); public TextureSlot TextureNormalCamera { get { m_parent.GetMaterialTexture(TextureType.NormalCamera, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.NormalCamera) { m_parent.AddMaterialTexture(in value); } } } public bool HasTextureEmissionColor => m_parent.HasProperty("$tex.file", TextureType.EmissionColor, 0); public TextureSlot TextureEmissionColor { get { m_parent.GetMaterialTexture(TextureType.EmissionColor, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.EmissionColor) { m_parent.AddMaterialTexture(in value); } } } public bool HasTextureMetalness => m_parent.HasProperty("$tex.file", TextureType.Metalness, 0); public TextureSlot TextureMetalness { get { m_parent.GetMaterialTexture(TextureType.Metalness, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Metalness) { m_parent.AddMaterialTexture(in value); } } } public bool HasTextureRoughness => m_parent.HasProperty("$tex.file", TextureType.Roughness, 0); public TextureSlot TextureRoughness { get { m_parent.GetMaterialTexture(TextureType.Roughness, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Roughness) { m_parent.AddMaterialTexture(in value); } } } internal PBRMaterialProperties(Material parent) { m_parent = parent; } } public sealed class ShaderMaterialProperties { private Material m_parent; public bool HasShaderLanguageType => m_parent.HasProperty("?sh.lang,0,0"); public string ShaderLanguageType { get { return GetStringProperty("?sh.lang,0,0"); } set { SetStringProperty("?sh.lang,0,0", "?sh.lang", value); } } public bool HasVertexShader => m_parent.HasProperty("?sh.vs,0,0"); public string VertexShader { get { return GetStringProperty("?sh.vs,0,0"); } set { SetStringProperty("?sh.vs,0,0", "?sh.vs", value); } } public bool HasFragmentShader => m_parent.HasProperty("?sh.fs,0,0"); public string FragmentShader { get { return GetStringProperty("?sh.fs,0,0"); } set { SetStringProperty("?sh.fs,0,0", "?sh.fs", value); } } public bool HasGeometryShader => m_parent.HasProperty("?sh.gs,0,0"); public string GeometryShader { get { return GetStringProperty("?sh.gs,0,0"); } set { SetStringProperty("?sh.gs,0,0", "?sh.gs", value); } } public bool HasTesselationShader => m_parent.HasProperty("?sh.ts,0,0"); public string TesselationShader { get { return GetStringProperty("?sh.ts,0,0"); } set { SetStringProperty("?sh.ts,0,0", "?sh.ts", value); } } public bool HasPrimitiveShader => m_parent.HasProperty("?sh.ps,0,0"); public string PrimitiveShader { get { return GetStringProperty("?sh.ps,0,0"); } set { SetStringProperty("?sh.ps,0,0", "?sh.ps", value); } } public bool HasComputeShader => m_parent.HasProperty("?sh.cs,0,0"); public string ComputeShader { get { return GetStringProperty("?sh.cs,0,0"); } set { SetStringProperty("?sh.cs,0,0", "?sh.cs", value); } } internal ShaderMaterialProperties(Material parent) { m_parent = parent; } private string GetStringProperty(string fullName) { MaterialProperty property = m_parent.GetProperty(fullName); if (property != null) { return property.GetStringValue(); } return string.Empty; } private void SetStringProperty(string fullName, string baseName, string value) { if (string.IsNullOrEmpty(value)) { value = string.Empty; } MaterialProperty property = m_parent.GetProperty(fullName); if (property == null) { property = new MaterialProperty(baseName, value); m_parent.AddProperty(property); } else { property.SetStringValue(value); } } } private Dictionary m_properties; private PBRMaterialProperties m_pbrProperties; private ShaderMaterialProperties m_shaderProperties; public int PropertyCount => m_properties.Count; public bool HasName => HasProperty("?mat.name,0,0"); public string Name { get { MaterialProperty property = GetProperty("?mat.name,0,0"); if (property != null) { return property.GetStringValue(); } return string.Empty; } set { MaterialProperty property = GetProperty("?mat.name,0,0"); if (property == null) { property = new MaterialProperty("?mat.name", value); AddProperty(property); } else { property.SetStringValue(value); } } } public bool HasTwoSided => HasProperty("$mat.twosided,0,0"); public bool IsTwoSided { get { return GetProperty("$mat.twosided,0,0")?.GetBooleanValue() ?? false; } set { MaterialProperty property = GetProperty("$mat.twosided,0,0"); if (property == null) { property = new MaterialProperty("$mat.twosided", value); AddProperty(property); } else { property.SetBooleanValue(value); } } } public bool HasShadingMode => HasProperty("$mat.shadingm,0,0"); public ShadingMode ShadingMode { get { return (ShadingMode)(GetProperty("$mat.shadingm,0,0")?.GetIntegerValue() ?? 0); } set { MaterialProperty property = GetProperty("$mat.shadingm,0,0"); if (property == null) { property = new MaterialProperty("$mat.shadingm", (int)value); AddProperty(property); } else { property.SetIntegerValue((int)value); } } } public bool HasWireFrame => HasProperty("$mat.wireframe,0,0"); public bool IsWireFrameEnabled { get { return GetProperty("$mat.wireframe,0,0")?.GetBooleanValue() ?? false; } set { MaterialProperty property = GetProperty("$mat.wireframe,0,0"); if (property == null) { property = new MaterialProperty("$mat.wireframe", value); AddProperty(property); } else { property.SetBooleanValue(value); } } } public bool HasBlendMode => HasProperty("$mat.blend,0,0"); public BlendMode BlendMode { get { return (BlendMode)(GetProperty("$mat.blend,0,0")?.GetIntegerValue() ?? 0); } set { MaterialProperty property = GetProperty("$mat.blend,0,0"); if (property == null) { property = new MaterialProperty("$mat.blend", (int)value); AddProperty(property); } else { property.SetIntegerValue((int)value); } } } public bool HasOpacity => HasProperty("$mat.opacity,0,0"); public float Opacity { get { return GetProperty("$mat.opacity,0,0")?.GetFloatValue() ?? 1f; } set { MaterialProperty property = GetProperty("$mat.opacity,0,0"); if (property == null) { property = new MaterialProperty("$mat.opacity", value); AddProperty(property); } else { property.SetFloatValue(value); } } } public bool HasTransparencyFactor => HasProperty("$mat.transparencyfactor,0,0"); public float TransparencyFactor { get { return GetProperty("$mat.transparencyfactor,0,0")?.GetFloatValue() ?? 0f; } set { MaterialProperty property = GetProperty("$mat.transparencyfactor,0,0"); if (property == null) { property = new MaterialProperty("$mat.transparencyfactor", value); AddProperty(property); } else { property.SetFloatValue(value); } } } public bool HasBumpScaling => HasProperty("$mat.bumpscaling,0,0"); public float BumpScaling { get { return GetProperty("$mat.bumpscaling,0,0")?.GetFloatValue() ?? 0f; } set { MaterialProperty property = GetProperty("$mat.bumpscaling,0,0"); if (property == null) { property = new MaterialProperty("$mat.bumpscaling", value); AddProperty(property); } else { property.SetFloatValue(value); } } } public bool HasShininess => HasProperty("$mat.shininess,0,0"); public float Shininess { get { return GetProperty("$mat.shininess,0,0")?.GetFloatValue() ?? 0f; } set { MaterialProperty property = GetProperty("$mat.shininess,0,0"); if (property == null) { property = new MaterialProperty("$mat.shininess", value); AddProperty(property); } else { property.SetFloatValue(value); } } } public bool HasShininessStrength => HasProperty("$mat.shinpercent,0,0"); public float ShininessStrength { get { return GetProperty("$mat.shinpercent,0,0")?.GetFloatValue() ?? 1f; } set { MaterialProperty property = GetProperty("$mat.shinpercent,0,0"); if (property == null) { property = new MaterialProperty("$mat.shinpercent", value); AddProperty(property); } else { property.SetFloatValue(value); } } } public bool HasReflectivity => HasProperty("$mat.reflectivity,0,0"); public float Reflectivity { get { return GetProperty("$mat.reflectivity,0,0")?.GetFloatValue() ?? 0f; } set { MaterialProperty property = GetProperty("$mat.reflectivity,0,0"); if (property == null) { property = new MaterialProperty("$mat.reflectivity", value); AddProperty(property); } else { property.SetFloatValue(value); } } } public bool HasColorDiffuse => HasProperty("$clr.diffuse,0,0"); public Color4D ColorDiffuse { get { return GetProperty("$clr.diffuse,0,0")?.GetColor4DValue() ?? new Color4D(1f, 1f, 1f, 1f); } set { MaterialProperty property = GetProperty("$clr.diffuse,0,0"); if (property == null) { property = new MaterialProperty("$clr.diffuse", value); AddProperty(property); } else { property.SetColor4DValue(value); } } } public bool HasColorAmbient => HasProperty("$clr.ambient,0,0"); public Color4D ColorAmbient { get { return GetProperty("$clr.ambient,0,0")?.GetColor4DValue() ?? new Color4D(0.2f, 0.2f, 0.2f, 1f); } set { MaterialProperty property = GetProperty("$clr.ambient,0,0"); if (property == null) { property = new MaterialProperty("$clr.ambient", value); AddProperty(property); } else { property.SetColor4DValue(value); } } } public bool HasColorSpecular => HasProperty("$clr.specular,0,0"); public Color4D ColorSpecular { get { return GetProperty("$clr.specular,0,0")?.GetColor4DValue() ?? new Color4D(0f, 0f, 0f, 1f); } set { MaterialProperty property = GetProperty("$clr.specular,0,0"); if (property == null) { property = new MaterialProperty("$clr.specular", value); AddProperty(property); } else { property.SetColor4DValue(value); } } } public bool HasColorEmissive => HasProperty("$clr.emissive,0,0"); public Color4D ColorEmissive { get { return GetProperty("$clr.emissive,0,0")?.GetColor4DValue() ?? new Color4D(0f, 0f, 0f, 1f); } set { MaterialProperty property = GetProperty("$clr.emissive,0,0"); if (property == null) { property = new MaterialProperty("$clr.emissive", value); AddProperty(property); } else { property.SetColor4DValue(value); } } } public bool HasColorTransparent => HasProperty("$clr.transparent,0,0"); public Color4D ColorTransparent { get { return GetProperty("$clr.transparent,0,0")?.GetColor4DValue() ?? new Color4D(0f, 0f, 0f, 1f); } set { MaterialProperty property = GetProperty("$clr.transparent,0,0"); if (property == null) { property = new MaterialProperty("$clr.transparent", value); AddProperty(property); } else { property.SetColor4DValue(value); } } } public bool HasColorReflective => HasProperty("$clr.reflective,0,0"); public Color4D ColorReflective { get { return GetProperty("$clr.reflective,0,0")?.GetColor4DValue() ?? new Color4D(0f, 0f, 0f, 1f); } set { MaterialProperty property = GetProperty("$clr.reflective,0,0"); if (property == null) { property = new MaterialProperty("$clr.reflective", value); AddProperty(property); } else { property.SetColor4DValue(value); } } } public bool HasTextureDiffuse => HasProperty("$tex.file", TextureType.Diffuse, 0); public TextureSlot TextureDiffuse { get { GetMaterialTexture(TextureType.Diffuse, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Diffuse) { AddMaterialTexture(in value); } } } public bool HasTextureSpecular => HasProperty("$tex.file", TextureType.Specular, 0); public TextureSlot TextureSpecular { get { GetMaterialTexture(TextureType.Specular, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Specular) { AddMaterialTexture(in value); } } } public bool HasTextureAmbient => HasProperty("$tex.file", TextureType.Ambient, 0); public TextureSlot TextureAmbient { get { GetMaterialTexture(TextureType.Ambient, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Ambient) { AddMaterialTexture(in value); } } } public bool HasTextureEmissive => HasProperty("$tex.file", TextureType.Emissive, 0); public TextureSlot TextureEmissive { get { GetMaterialTexture(TextureType.Emissive, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Emissive) { AddMaterialTexture(in value); } } } public bool HasTextureHeight => HasProperty("$tex.file", TextureType.Height, 0); public TextureSlot TextureHeight { get { GetMaterialTexture(TextureType.Height, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Height) { AddMaterialTexture(in value); } } } public bool HasTextureNormal => HasProperty("$tex.file", TextureType.Normals, 0); public TextureSlot TextureNormal { get { GetMaterialTexture(TextureType.Normals, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Normals) { AddMaterialTexture(in value); } } } public bool HasTextureOpacity => HasProperty("$tex.file", TextureType.Opacity, 0); public TextureSlot TextureOpacity { get { GetMaterialTexture(TextureType.Opacity, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Opacity) { AddMaterialTexture(in value); } } } public bool HasTextureDisplacement => HasProperty("$tex.file", TextureType.Displacement, 0); public TextureSlot TextureDisplacement { get { GetMaterialTexture(TextureType.Displacement, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Displacement) { AddMaterialTexture(in value); } } } public bool HasTextureLightMap => HasProperty("$tex.file", TextureType.Lightmap, 0); public TextureSlot TextureLightMap { get { GetMaterialTexture(TextureType.Lightmap, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Lightmap) { AddMaterialTexture(in value); } } } public bool HasTextureAmbientOcclusion => HasProperty("$tex.file", TextureType.AmbientOcclusion, 0); public TextureSlot TextureAmbientOcclusion { get { GetMaterialTexture(TextureType.AmbientOcclusion, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.AmbientOcclusion) { AddMaterialTexture(in value); } } } public bool HasTextureReflection => HasProperty("$tex.file", TextureType.Reflection, 0); public TextureSlot TextureReflection { get { GetMaterialTexture(TextureType.Reflection, 0, out var texture); return texture; } set { if (value.TextureIndex == 0 && value.TextureType == TextureType.Reflection) { AddMaterialTexture(in value); } } } public bool IsPBRMaterial { get { PBRMaterialProperties pbrProperties = m_pbrProperties; return pbrProperties.HasTextureBaseColor || pbrProperties.HasTextureMetalness || pbrProperties.HasTextureRoughness || pbrProperties.HasTextureNormalCamera || pbrProperties.HasTextureEmissionColor; } } public PBRMaterialProperties PBR => m_pbrProperties; public bool HasShaders { get { ShaderMaterialProperties shaderProperties = m_shaderProperties; return shaderProperties.HasShaderLanguageType && (shaderProperties.HasVertexShader || shaderProperties.HasFragmentShader || shaderProperties.HasGeometryShader || shaderProperties.HasTesselationShader || shaderProperties.HasPrimitiveShader || shaderProperties.HasComputeShader); } } public ShaderMaterialProperties Shaders => m_shaderProperties; bool IMarshalable.IsNativeBlittable => true; public Material() { m_properties = new Dictionary(); m_pbrProperties = new PBRMaterialProperties(this); m_shaderProperties = new ShaderMaterialProperties(this); } public static string CreateFullyQualifiedName(string baseName, TextureType texType, int texIndex) { if (string.IsNullOrEmpty(baseName)) { return string.Empty; } return $"{baseName},{(int)texType},{texIndex}"; } public MaterialProperty GetNonTextureProperty(string baseName) { if (string.IsNullOrEmpty(baseName)) { return null; } string fullyQualifiedName = CreateFullyQualifiedName(baseName, TextureType.None, 0); return GetProperty(fullyQualifiedName); } public MaterialProperty GetProperty(string baseName, TextureType texType, int texIndex) { if (string.IsNullOrEmpty(baseName)) { return null; } string fullyQualifiedName = CreateFullyQualifiedName(baseName, texType, texIndex); return GetProperty(fullyQualifiedName); } public MaterialProperty GetProperty(string fullyQualifiedName) { if (string.IsNullOrEmpty(fullyQualifiedName)) { return null; } if (!m_properties.TryGetValue(fullyQualifiedName, out var value)) { return null; } return value; } public bool HasNonTextureProperty(string baseName) { if (string.IsNullOrEmpty(baseName)) { return false; } string fullyQualifiedName = CreateFullyQualifiedName(baseName, TextureType.None, 0); return HasProperty(fullyQualifiedName); } public bool HasProperty(string baseName, TextureType texType, int texIndex) { if (string.IsNullOrEmpty(baseName)) { return false; } string fullyQualifiedName = CreateFullyQualifiedName(baseName, texType, texIndex); return HasProperty(fullyQualifiedName); } public bool HasProperty(string fullyQualifiedName) { if (string.IsNullOrEmpty(fullyQualifiedName)) { return false; } return m_properties.ContainsKey(fullyQualifiedName); } public bool AddProperty(MaterialProperty matProp) { if (matProp == null || string.IsNullOrEmpty(matProp.FullyQualifiedName)) { return false; } if (m_properties.ContainsKey(matProp.FullyQualifiedName)) { return false; } m_properties.Add(matProp.FullyQualifiedName, matProp); return true; } public bool RemoveNonTextureProperty(string baseName) { if (string.IsNullOrEmpty(baseName)) { return false; } return RemoveProperty(CreateFullyQualifiedName(baseName, TextureType.None, 0)); } public bool RemoveProperty(string baseName, TextureType texType, int texIndex) { if (string.IsNullOrEmpty(baseName)) { return false; } return RemoveProperty(CreateFullyQualifiedName(baseName, texType, texIndex)); } public bool RemoveProperty(string fullyQualifiedName) { if (string.IsNullOrEmpty(fullyQualifiedName)) { return false; } return m_properties.Remove(fullyQualifiedName); } public void Clear() { m_properties.Clear(); } public MaterialProperty[] GetAllProperties() { MaterialProperty[] array = new MaterialProperty[m_properties.Values.Count]; m_properties.Values.CopyTo(array, 0); return array; } public int GetMaterialTextureCount(TextureType texType) { int num = 0; foreach (KeyValuePair property in m_properties) { MaterialProperty value = property.Value; if (value.Name.StartsWith("$tex.file") && value.TextureType == texType) { num++; } } return num; } public bool AddMaterialTexture(in TextureSlot texture) { return AddMaterialTexture(in texture, onlySetFilePath: false); } public bool AddMaterialTexture(in TextureSlot texture, bool onlySetFilePath) { if (string.IsNullOrEmpty(texture.FilePath)) { return false; } TextureType textureType = texture.TextureType; int textureIndex = texture.TextureIndex; string fullyQualifiedName = CreateFullyQualifiedName("$tex.file", textureType, textureIndex); MaterialProperty property = GetProperty(fullyQualifiedName); if (property == null) { AddProperty(new MaterialProperty("$tex.file", texture.FilePath, textureType, textureIndex)); } else { property.SetStringValue(texture.FilePath); } if (onlySetFilePath) { return true; } string fullyQualifiedName2 = CreateFullyQualifiedName("$tex.mapping", textureType, textureIndex); string fullyQualifiedName3 = CreateFullyQualifiedName("$tex.uvwsrc", textureType, textureIndex); string fullyQualifiedName4 = CreateFullyQualifiedName("$tex.blend", textureType, textureIndex); string fullyQualifiedName5 = CreateFullyQualifiedName("$tex.op", textureType, textureIndex); string fullyQualifiedName6 = CreateFullyQualifiedName("$tex.mapmodeu", textureType, textureIndex); string fullyQualifiedName7 = CreateFullyQualifiedName("$tex.mapmodev", textureType, textureIndex); string fullyQualifiedName8 = CreateFullyQualifiedName("$tex.flags", textureType, textureIndex); MaterialProperty property2 = GetProperty(fullyQualifiedName2); MaterialProperty property3 = GetProperty(fullyQualifiedName3); MaterialProperty property4 = GetProperty(fullyQualifiedName4); MaterialProperty property5 = GetProperty(fullyQualifiedName5); MaterialProperty property6 = GetProperty(fullyQualifiedName6); MaterialProperty property7 = GetProperty(fullyQualifiedName7); MaterialProperty property8 = GetProperty(fullyQualifiedName8); if (property2 == null) { AddProperty(new MaterialProperty("$tex.mapping", (int)texture.Mapping)); } else { property2.SetIntegerValue((int)texture.Mapping); } if (property3 == null) { AddProperty(new MaterialProperty("$tex.mapping", (int)texture.Mapping)); } else { property3.SetIntegerValue(texture.UVIndex); } if (property4 == null) { AddProperty(new MaterialProperty("$tex.blend", texture.BlendFactor)); } else { property4.SetFloatValue(texture.BlendFactor); } if (property5 == null) { AddProperty(new MaterialProperty("$tex.op", (int)texture.Operation)); } else { property5.SetIntegerValue((int)texture.Operation); } if (property6 == null) { AddProperty(new MaterialProperty("$tex.mapmodeu", (int)texture.WrapModeU)); } else { property6.SetIntegerValue((int)texture.WrapModeU); } if (property7 == null) { AddProperty(new MaterialProperty("$tex.mapmodev", (int)texture.WrapModeV)); } else { property7.SetIntegerValue((int)texture.WrapModeV); } if (property8 == null) { AddProperty(new MaterialProperty("$tex.flags", texture.Flags)); } else { property8.SetIntegerValue(texture.Flags); } return true; } public bool RemoveMaterialTexture(in TextureSlot texture) { if (string.IsNullOrEmpty(texture.FilePath)) { return false; } TextureType textureType = texture.TextureType; int textureIndex = texture.TextureIndex; string fullyQualifiedName = CreateFullyQualifiedName("$tex.file", textureType, textureIndex); string fullyQualifiedName2 = CreateFullyQualifiedName("$tex.mapping", textureType, textureIndex); string fullyQualifiedName3 = CreateFullyQualifiedName("$tex.uvwsrc", textureType, textureIndex); string fullyQualifiedName4 = CreateFullyQualifiedName("$tex.blend", textureType, textureIndex); string fullyQualifiedName5 = CreateFullyQualifiedName("$tex.op", textureType, textureIndex); string fullyQualifiedName6 = CreateFullyQualifiedName("$tex.mapmodeu", textureType, textureIndex); string fullyQualifiedName7 = CreateFullyQualifiedName("$tex.mapmodev", textureType, textureIndex); string fullyQualifiedName8 = CreateFullyQualifiedName("$tex.flags", textureType, textureIndex); RemoveProperty(fullyQualifiedName); RemoveProperty(fullyQualifiedName2); RemoveProperty(fullyQualifiedName3); RemoveProperty(fullyQualifiedName4); RemoveProperty(fullyQualifiedName5); RemoveProperty(fullyQualifiedName6); RemoveProperty(fullyQualifiedName7); RemoveProperty(fullyQualifiedName8); return true; } public bool GetMaterialTexture(TextureType texType, int texIndex, out TextureSlot texture) { texture = default(TextureSlot); string fullyQualifiedName = CreateFullyQualifiedName("$tex.file", texType, texIndex); MaterialProperty property = GetProperty(fullyQualifiedName); if (property == null) { return false; } string fullyQualifiedName2 = CreateFullyQualifiedName("$tex.mapping", texType, texIndex); string fullyQualifiedName3 = CreateFullyQualifiedName("$tex.uvwsrc", texType, texIndex); string fullyQualifiedName4 = CreateFullyQualifiedName("$tex.blend", texType, texIndex); string fullyQualifiedName5 = CreateFullyQualifiedName("$tex.op", texType, texIndex); string fullyQualifiedName6 = CreateFullyQualifiedName("$tex.mapmodeu", texType, texIndex); string fullyQualifiedName7 = CreateFullyQualifiedName("$tex.mapmodev", texType, texIndex); string fullyQualifiedName8 = CreateFullyQualifiedName("$tex.flags", texType, texIndex); MaterialProperty property2 = GetProperty(fullyQualifiedName2); MaterialProperty property3 = GetProperty(fullyQualifiedName3); MaterialProperty property4 = GetProperty(fullyQualifiedName4); MaterialProperty property5 = GetProperty(fullyQualifiedName5); MaterialProperty property6 = GetProperty(fullyQualifiedName6); MaterialProperty property7 = GetProperty(fullyQualifiedName7); MaterialProperty property8 = GetProperty(fullyQualifiedName8); texture.FilePath = property.GetStringValue(); texture.TextureType = texType; texture.TextureIndex = texIndex; texture.Mapping = (TextureMapping)(property2?.GetIntegerValue() ?? 0); texture.UVIndex = property3?.GetIntegerValue() ?? 0; texture.BlendFactor = property4?.GetFloatValue() ?? 0f; texture.Operation = (TextureOperation)(property5?.GetIntegerValue() ?? 0); texture.WrapModeU = (TextureWrapMode)(property6?.GetIntegerValue() ?? 0); texture.WrapModeV = (TextureWrapMode)(property7?.GetIntegerValue() ?? 0); texture.Flags = property8?.GetIntegerValue() ?? 0; return true; } public TextureSlot[] GetMaterialTextures(TextureType type) { int materialTextureCount = GetMaterialTextureCount(type); if (materialTextureCount == 0) { return new TextureSlot[0]; } TextureSlot[] array = new TextureSlot[materialTextureCount]; for (int i = 0; i < materialTextureCount; i++) { GetMaterialTexture(type, i, out var texture); array[i] = texture; } return array; } public TextureSlot[] GetAllMaterialTextures() { List list = new List(); TextureType[] array = Enum.GetValues(typeof(TextureType)) as TextureType[]; TextureType[] array2 = array; foreach (TextureType type in array2) { list.AddRange(GetMaterialTextures(type)); } return list.ToArray(); } void IMarshalable.ToNative(IntPtr thisPtr, out AiMaterial nativeValue) { nativeValue.NumAllocated = (nativeValue.NumProperties = (uint)m_properties.Count); nativeValue.Properties = IntPtr.Zero; if (m_properties.Count > 0) { MaterialProperty[] array = new MaterialProperty[m_properties.Values.Count]; m_properties.Values.CopyTo(array, 0); nativeValue.Properties = MemoryHelper.ToNativeArray(array, arrayOfPointers: true); } } void IMarshalable.FromNative(in AiMaterial nativeValue) { Clear(); if (nativeValue.NumProperties != 0 && nativeValue.Properties != IntPtr.Zero) { MaterialProperty[] array = MemoryHelper.FromNativeArray(nativeValue.Properties, (int)nativeValue.NumProperties, arrayOfPointers: true); MaterialProperty[] array2 = array; foreach (MaterialProperty matProp in array2) { AddProperty(matProp); } } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiMaterial aiMaterial = MemoryHelper.Read(nativeValue); if (aiMaterial.NumAllocated != 0 && aiMaterial.Properties != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiMaterial.Properties, (int)aiMaterial.NumProperties, MaterialProperty.FreeNative, arrayOfPointers: true); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public sealed class MaterialProperty : IMarshalable { private string m_name; private PropertyType m_type; private byte[] m_rawValue; private TextureType m_texType; private int m_texIndex; private string m_fullyQualifiedName; private bool m_fullQualifiedNameNeedsUpdate = true; public string Name { get { return m_name; } set { m_name = value; m_fullQualifiedNameNeedsUpdate = true; AssertIsBaseName(); } } public PropertyType PropertyType { get { return m_type; } set { m_type = value; } } public int ByteCount => (m_rawValue != null) ? m_rawValue.Length : 0; public bool HasRawData => m_rawValue != null; public byte[] RawData => m_rawValue; public TextureType TextureType { get { return m_texType; } set { m_texType = value; m_fullQualifiedNameNeedsUpdate = true; } } public int TextureIndex { get { return m_texIndex; } set { m_texIndex = value; m_fullQualifiedNameNeedsUpdate = true; } } public string FullyQualifiedName { get { if (m_fullQualifiedNameNeedsUpdate) { m_fullyQualifiedName = Material.CreateFullyQualifiedName(m_name, m_texType, m_texIndex); m_fullQualifiedNameNeedsUpdate = false; } return m_fullyQualifiedName; } } bool IMarshalable.IsNativeBlittable => true; public MaterialProperty() { m_name = string.Empty; m_type = PropertyType.Buffer; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; } public MaterialProperty(string baseName, byte[] buffer) { m_name = baseName; m_type = PropertyType.Buffer; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = buffer; AssertIsBaseName(); } public MaterialProperty(string baseName, float value) { m_name = baseName; m_type = PropertyType.Float; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetFloatValue(value); AssertIsBaseName(); } public MaterialProperty(string baseName, int value) { m_name = baseName; m_type = PropertyType.Integer; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetIntegerValue(value); AssertIsBaseName(); } public MaterialProperty(string baseName, bool value) { m_name = baseName; m_type = PropertyType.Integer; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetBooleanValue(value); AssertIsBaseName(); } public MaterialProperty(string baseName, string value) { m_name = baseName; m_type = PropertyType.String; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetStringValue(value); AssertIsBaseName(); } public MaterialProperty(string baseName, string value, TextureType texType, int textureIndex) { m_name = baseName; m_type = PropertyType.String; m_texIndex = textureIndex; m_texType = texType; m_rawValue = null; SetStringValue(value); AssertIsBaseName(); } public MaterialProperty(string baseName, float[] values) { m_name = baseName; m_type = PropertyType.Float; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetFloatArrayValue(values); AssertIsBaseName(); } public MaterialProperty(string baseName, int[] values) { m_name = baseName; m_type = PropertyType.Integer; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetIntegerArrayValue(values); AssertIsBaseName(); } public MaterialProperty(string baseName, Color3D value) { m_name = baseName; m_type = PropertyType.Float; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetColor3DValue(value); AssertIsBaseName(); } public MaterialProperty(string baseName, Color4D value) { m_name = baseName; m_type = PropertyType.Float; m_texIndex = 0; m_texType = TextureType.None; m_rawValue = null; SetColor4DValue(value); AssertIsBaseName(); } public float GetFloatValue() { if (m_type == PropertyType.Float || m_type == PropertyType.Integer) { return GetValueAs(); } return 0f; } public bool SetFloatValue(float value) { if (m_type != PropertyType.Float && m_type != PropertyType.Integer) { return false; } return SetValueAs(value); } public double GetDoubleValue() { if (m_type == PropertyType.Double) { return GetValueAs(); } return 0.0; } public bool SetDoubleValue(double value) { if (m_type != PropertyType.Double) { return false; } return SetValueAs(value); } public int GetIntegerValue() { if (m_type == PropertyType.Float || m_type == PropertyType.Integer) { return GetValueAs(); } return 0; } public bool SetIntegerValue(int value) { if (m_type != PropertyType.Float && m_type != PropertyType.Integer) { return false; } return SetValueAs(value); } public string GetStringValue() { if (m_type != PropertyType.String) { return null; } return GetMaterialString(m_rawValue); } public bool SetStringValue(string value) { if (m_type != PropertyType.String) { return false; } m_rawValue = SetMaterialString(value, m_rawValue); return true; } public float[] GetFloatArrayValue(int count) { if (m_type == PropertyType.Float || m_type == PropertyType.Integer) { return GetValueArrayAs(count); } return null; } public float[] GetFloatArrayValue() { if (m_type == PropertyType.Float || m_type == PropertyType.Integer) { int count = ByteCount / 4; return GetValueArrayAs(count); } return null; } public bool SetFloatArrayValue(float[] values) { if (m_type != PropertyType.Float && m_type != PropertyType.Integer) { return false; } return SetValueArrayAs(values); } public double[] GetDoubleArrayValue() { if (m_type == PropertyType.Double) { int count = ByteCount / 8; return GetValueArrayAs(count); } return null; } public bool SetDoubleArrayValue(double[] values) { if (m_type != PropertyType.Double) { return false; } return SetValueArrayAs(values); } public int[] GetIntegerArrayValue(int count) { if (m_type == PropertyType.Float || m_type == PropertyType.Integer) { return GetValueArrayAs(count); } return null; } public int[] GetIntegerArrayValue() { if (m_type == PropertyType.Float || m_type == PropertyType.Integer) { int count = ByteCount / 4; return GetValueArrayAs(count); } return null; } public bool SetIntegerArrayValue(int[] values) { if (m_type != PropertyType.Float && m_type != PropertyType.Integer) { return false; } return SetValueArrayAs(values); } public bool GetBooleanValue() { return (GetIntegerValue() != 0) ? true : false; } public bool SetBooleanValue(bool value) { return SetIntegerValue(value ? 1 : 0); } public Color3D GetColor3DValue() { if (m_type != PropertyType.Float) { return default(Color3D); } return GetValueAs(); } public bool SetColor3DValue(Color3D value) { if (m_type != PropertyType.Float) { return false; } return SetValueAs(value); } public unsafe Color4D GetColor4DValue() { if (m_type != PropertyType.Float || m_rawValue == null) { return default(Color4D); } fixed (byte* rawValue = m_rawValue) { if (m_rawValue.Length >= MemoryHelper.SizeOf()) { return MemoryHelper.Read(new IntPtr(rawValue)); } if (m_rawValue.Length >= MemoryHelper.SizeOf()) { return new Color4D(MemoryHelper.Read(new IntPtr(rawValue)), 1f); } } return default(Color4D); } public bool SetColor4DValue(Color4D value) { if (m_type != PropertyType.Float) { return false; } return SetValueAs(value); } private unsafe T[] GetValueArrayAs(int count) where T : struct { int num = MemoryHelper.SizeOf(); if (m_rawValue != null && m_rawValue.Length >= num * count) { T[] array = new T[count]; fixed (byte* rawValue = m_rawValue) { MemoryHelper.Read(new IntPtr(rawValue), array, 0, count); } return array; } return null; } private unsafe T GetValueAs() where T : struct { int num = MemoryHelper.SizeOf(); if (m_rawValue != null && m_rawValue.Length >= num) { fixed (byte* rawValue = m_rawValue) { return MemoryHelper.Read(new IntPtr(rawValue)); } } return default(T); } private unsafe bool SetValueArrayAs(T[] data) where T : struct { if (data == null || data.Length == 0) { return false; } int num = MemoryHelper.SizeOf(data); if (m_rawValue == null || m_rawValue.Length != num) { m_rawValue = new byte[num]; } fixed (byte* rawValue = m_rawValue) { MemoryHelper.Write(new IntPtr(rawValue), data, 0, data.Length); } return true; } private unsafe bool SetValueAs(T value) where T : struct { int num = MemoryHelper.SizeOf(); if (m_rawValue == null || m_rawValue.Length != num) { m_rawValue = new byte[num]; } fixed (byte* rawValue = m_rawValue) { MemoryHelper.Write(new IntPtr(rawValue), in value); } return true; } private unsafe static string GetMaterialString(byte[] matPropData) { if (matPropData == null) { return string.Empty; } fixed (byte* value = &matPropData[0]) { AiString aiString = default(AiString); aiString.Length = (uint)MemoryHelper.Read(new IntPtr(value)); MemoryHelper.CopyMemory(new IntPtr(aiString.Data), MemoryHelper.AddIntPtr(new IntPtr(value), 4), (int)(aiString.Length + 1)); return aiString.GetString(); } } private unsafe static byte[] SetMaterialString(string value, byte[] existing) { if (string.IsNullOrEmpty(value)) { return null; } int data = Encoding.UTF8.GetByteCount(value); if (data < 0) { return null; } int num = data + 1 + 4; byte[] array = existing; if (existing == null || existing.Length != num) { array = new byte[num]; } fixed (byte* ptr = &array[0]) { MemoryHelper.Write(new IntPtr(ptr), in data); byte[] bytes = Encoding.UTF8.GetBytes(value); MemoryHelper.Write(new IntPtr(ptr + 4), bytes, 0, bytes.Length); } return array; } [Conditional("DEBUG")] private void AssertIsBaseName() { if (m_name != null) { Debug.Assert(!m_name.Contains(",")); } } void IMarshalable.ToNative(IntPtr thisPtr, out AiMaterialProperty nativeValue) { nativeValue.Key = new AiString(m_name); nativeValue.Type = m_type; nativeValue.Index = (uint)m_texIndex; nativeValue.Semantic = m_texType; nativeValue.Data = IntPtr.Zero; nativeValue.DataLength = 0u; if (m_rawValue != null) { nativeValue.DataLength = (uint)m_rawValue.Length; nativeValue.Data = MemoryHelper.ToNativeArray(m_rawValue); } } void IMarshalable.FromNative(in AiMaterialProperty nativeValue) { m_name = AiString.GetString(in nativeValue.Key); m_type = nativeValue.Type; m_texIndex = (int)nativeValue.Index; m_texType = nativeValue.Semantic; m_rawValue = null; if (nativeValue.DataLength != 0 && nativeValue.Data != IntPtr.Zero) { m_rawValue = MemoryHelper.FromNativeArray(nativeValue.Data, (int)nativeValue.DataLength); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiMaterialProperty aiMaterialProperty = MemoryHelper.Read(nativeValue); if (aiMaterialProperty.DataLength != 0 && aiMaterialProperty.Data != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMaterialProperty.Data); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public struct Matrix3x3 : IEquatable { public float A1; public float A2; public float A3; public float B1; public float B2; public float B3; public float C1; public float C2; public float C3; private static Matrix3x3 _identity = new Matrix3x3(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f); public static Matrix3x3 Identity => _identity; public bool IsIdentity { get { float num = 0.01f; return A2 <= num && A2 >= 0f - num && A3 <= num && A3 >= 0f - num && B1 <= num && B1 >= 0f - num && B3 <= num && B3 >= 0f - num && C1 <= num && C1 >= 0f - num && C2 <= num && C2 >= 0f - num && A1 <= 1f + num && A1 >= 1f - num && B2 <= 1f + num && B2 >= 1f - num && C3 <= 1f + num && C3 >= 1f - num; } } public float this[int i, int j] { get { return i switch { 1 => j switch { 1 => A1, 2 => A2, 3 => A3, _ => 0f, }, 2 => j switch { 1 => B1, 2 => B2, 3 => B3, _ => 0f, }, 3 => j switch { 1 => C1, 2 => C2, 3 => C3, _ => 0f, }, _ => 0f, }; } set { switch (i) { case 1: switch (j) { case 1: A1 = value; break; case 2: A2 = value; break; case 3: A3 = value; break; } break; case 2: switch (j) { case 1: B1 = value; break; case 2: B2 = value; break; case 3: B3 = value; break; } break; case 3: switch (j) { case 1: C1 = value; break; case 2: C2 = value; break; case 3: C3 = value; break; } break; } } } public Matrix3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) { A1 = a1; A2 = a2; A3 = a3; B1 = b1; B2 = b2; B3 = b3; C1 = c1; C2 = c2; C3 = c3; } public Matrix3x3(Matrix4x4 rotMatrix) { A1 = rotMatrix.A1; A2 = rotMatrix.A2; A3 = rotMatrix.A3; B1 = rotMatrix.B1; B2 = rotMatrix.B2; B3 = rotMatrix.B3; C1 = rotMatrix.C1; C2 = rotMatrix.C2; C3 = rotMatrix.C3; } public void Transpose() { Matrix3x3 matrix3x = new Matrix3x3(this); A2 = matrix3x.B1; A3 = matrix3x.C1; B1 = matrix3x.A2; B3 = matrix3x.C2; C1 = matrix3x.A3; C2 = matrix3x.B3; } public void Inverse() { float num = Determinant(); if (num == 0f) { A1 = float.NaN; A2 = float.NaN; A3 = float.NaN; B1 = float.NaN; B2 = float.NaN; B3 = float.NaN; C1 = float.NaN; C2 = float.NaN; C3 = float.NaN; } float num2 = 1f / num; float a = num2 * (B2 * C3 - B3 * C2); float a2 = (0f - num2) * (A2 * C3 - A3 * C2); float a3 = num2 * (A2 * B3 - A3 * B2); float b = (0f - num2) * (B1 * C3 - B3 * C1); float b2 = num2 * (A1 * C3 - A3 * C1); float b3 = (0f - num2) * (A1 * B3 - A3 * B1); float c = num2 * (B1 * C2 - B2 * C1); float c2 = (0f - num2) * (A1 * C2 - A2 * C1); float c3 = num2 * (A1 * B2 - A2 * B1); A1 = a; A2 = a2; A3 = a3; B1 = b; B2 = b2; B3 = b3; C1 = c; C2 = c2; C3 = c3; } public float Determinant() { return A1 * B2 * C3 - A1 * B3 * C2 + A2 * B3 * C1 - A2 * B1 * C3 + A3 * B1 * C2 - A3 * B2 * C1; } public static Matrix3x3 FromEulerAnglesXYZ(float x, float y, float z) { float num = (float)Math.Cos(x); float num2 = (float)Math.Sin(x); float num3 = (float)Math.Cos(y); float num4 = (float)Math.Sin(y); float num5 = (float)Math.Cos(z); float num6 = (float)Math.Sin(z); float num7 = num2 * num4; float num8 = num * num4; Matrix3x3 result = default(Matrix3x3); result.A1 = num3 * num5; result.A2 = num3 * num6; result.A3 = 0f - num4; result.B1 = num7 * num5 - num * num6; result.B2 = num7 * num6 + num * num5; result.B3 = num2 * num3; result.C1 = num8 * num5 + num2 * num6; result.C2 = num8 * num6 - num2 * num5; result.C3 = num * num3; return result; } public static Matrix3x3 FromEulerAnglesXYZ(Vector3D angles) { return FromEulerAnglesXYZ(angles.X, angles.Y, angles.Z); } public static Matrix3x3 FromRotationX(float radians) { Matrix3x3 identity = Identity; identity.B2 = (identity.C3 = (float)Math.Cos(radians)); identity.C2 = (float)Math.Sin(radians); identity.B3 = 0f - identity.C2; return identity; } public static Matrix3x3 FromRotationY(float radians) { Matrix3x3 identity = Identity; identity.A1 = (identity.C3 = (float)Math.Cos(radians)); identity.A3 = (float)Math.Sin(radians); identity.C1 = 0f - identity.A3; return identity; } public static Matrix3x3 FromRotationZ(float radians) { Matrix3x3 identity = Identity; identity.A1 = (identity.B2 = (float)Math.Cos(radians)); identity.B1 = (float)Math.Sin(radians); identity.A2 = 0f - identity.B1; return identity; } public static Matrix3x3 FromAngleAxis(float radians, Vector3D axis) { float x = axis.X; float y = axis.Y; float z = axis.Z; float num = (float)Math.Sin(radians); float num2 = (float)Math.Cos(radians); float num3 = x * x; float num4 = y * y; float num5 = z * z; float num6 = x * y; float num7 = x * z; float num8 = y * z; Matrix3x3 result = default(Matrix3x3); result.A1 = num3 + num2 * (1f - num3); result.B1 = num6 - num2 * num6 + num * z; result.C1 = num7 - num2 * num7 - num * y; result.A2 = num6 - num2 * num6 - num * z; result.B2 = num4 + num2 * (1f - num4); result.C2 = num8 - num2 * num8 + num * x; result.A3 = num7 - num2 * num7 + num * y; result.B3 = num8 - num2 * num8 - num * x; result.C3 = num5 + num2 * (1f - num5); return result; } public static Matrix3x3 FromScaling(Vector3D scaling) { Matrix3x3 identity = Identity; identity.A1 = scaling.X; identity.B2 = scaling.Y; identity.C3 = scaling.Z; return identity; } public static Matrix3x3 FromToMatrix(Vector3D from, Vector3D to) { float num = Vector3D.Dot(from, to); float num2 = ((num < 0f) ? (0f - num) : num); Matrix3x3 identity = Identity; if (num2 > 0.99999f) { Vector3D vector3D = default(Vector3D); vector3D.X = ((from.X > 0f) ? from.X : (0f - from.X)); vector3D.Y = ((from.Y > 0f) ? from.Y : (0f - from.Y)); vector3D.Z = ((from.Z > 0f) ? from.Z : (0f - from.Z)); if (vector3D.X < vector3D.Y) { if (vector3D.X < vector3D.Z) { vector3D.X = 1f; vector3D.Y = 0f; vector3D.Z = 0f; } else { vector3D.X = 0f; vector3D.Y = 0f; vector3D.Z = 1f; } } else if (vector3D.Y < vector3D.Z) { vector3D.X = 0f; vector3D.Y = 1f; vector3D.Z = 0f; } else { vector3D.X = 0f; vector3D.Y = 0f; vector3D.Z = 1f; } Vector3D vector3D2 = default(Vector3D); vector3D2.X = vector3D.X - from.X; vector3D2.Y = vector3D.Y - from.Y; vector3D2.Z = vector3D.Z - from.Z; Vector3D vector3D3 = default(Vector3D); vector3D3.X = vector3D.X - to.X; vector3D3.Y = vector3D.Y - to.Y; vector3D3.Z = vector3D.Z - to.Z; float num3 = 2f / Vector3D.Dot(vector3D2, vector3D2); float num4 = 2f / Vector3D.Dot(vector3D3, vector3D3); float num5 = num3 * num4 * Vector3D.Dot(vector3D2, vector3D3); for (int i = 1; i < 4; i++) { for (int j = 1; j < 4; j++) { identity[i, j] = (0f - num3) * vector3D2[i - 1] * vector3D2[j - 1] - num4 * vector3D3[i - 1] * vector3D3[j - 1] + num5 * vector3D3[i - 1] * vector3D2[j - 1]; } identity[i, i] += 1f; } } else { Vector3D vector3D4 = Vector3D.Cross(from, to); float num6 = 1f / (1f + num); float num7 = num6 * vector3D4.X; float num8 = num6 * vector3D4.Z; float num9 = num7 * vector3D4.Y; float num10 = num7 * vector3D4.Z; float num11 = num8 * vector3D4.Y; identity.A1 = num + num7 * vector3D4.X; identity.A2 = num9 - vector3D4.Z; identity.A3 = num10 + vector3D4.Y; identity.B1 = num9 + vector3D4.Z; identity.B2 = num + num6 * vector3D4.Y * vector3D4.Y; identity.B3 = num11 - vector3D4.X; identity.C1 = num10 - vector3D4.Y; identity.C2 = num11 + vector3D4.X; identity.C3 = num + num8 * vector3D4.Z; } return identity; } public static bool operator ==(Matrix3x3 a, Matrix3x3 b) { return a.A1 == b.A1 && a.A2 == b.A2 && a.A3 == b.A3 && a.B1 == b.B1 && a.B2 == b.B2 && a.B3 == b.B3 && a.C1 == b.C1 && a.C2 == b.C2 && a.C3 == b.C3; } public static bool operator !=(Matrix3x3 a, Matrix3x3 b) { return a.A1 != b.A1 || a.A2 != b.A2 || a.A3 != b.A3 || a.B1 != b.B1 || a.B2 != b.B2 || a.B3 != b.B3 || a.C1 != b.C1 || a.C2 != b.C2 || a.C3 != b.C3; } public static Matrix3x3 operator *(Matrix3x3 a, Matrix3x3 b) { return new Matrix3x3(a.A1 * b.A1 + a.B1 * b.A2 + a.C1 * b.A3, a.A2 * b.A1 + a.B2 * b.A2 + a.C2 * b.A3, a.A3 * b.A1 + a.B3 * b.A2 + a.C3 * b.A3, a.A1 * b.B1 + a.B1 * b.B2 + a.C1 * b.B3, a.A2 * b.B1 + a.B2 * b.B2 + a.C2 * b.B3, a.A3 * b.B1 + a.B3 * b.B2 + a.C3 * b.B3, a.A1 * b.C1 + a.B1 * b.C2 + a.C1 * b.C3, a.A2 * b.C1 + a.B2 * b.C2 + a.C2 * b.C3, a.A3 * b.C1 + a.B3 * b.C2 + a.C3 * b.C3); } public static implicit operator Matrix3x3(Matrix4x4 mat) { Matrix3x3 result = default(Matrix3x3); result.A1 = mat.A1; result.A2 = mat.A2; result.A3 = mat.A3; result.B1 = mat.B1; result.B2 = mat.B2; result.B3 = mat.B3; result.C1 = mat.C1; result.C2 = mat.C2; result.C3 = mat.C3; return result; } public bool Equals(Matrix3x3 other) { return A1 == other.A1 && A2 == other.A2 && A3 == other.A3 && B1 == other.B1 && B2 == other.B2 && B3 == other.B3 && C1 == other.C1 && C2 == other.C2 && C3 == other.C3; } public override bool Equals(object obj) { if (obj is Matrix3x3) { return Equals((Matrix3x3)obj); } return false; } public override int GetHashCode() { return A1.GetHashCode() + A2.GetHashCode() + A3.GetHashCode() + B1.GetHashCode() + B2.GetHashCode() + B3.GetHashCode() + C1.GetHashCode() + C2.GetHashCode() + C3.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; object[] args = new object[9] { A1.ToString(currentCulture), A2.ToString(currentCulture), A3.ToString(currentCulture), B1.ToString(currentCulture), B2.ToString(currentCulture), B3.ToString(currentCulture), C1.ToString(currentCulture), C2.ToString(currentCulture), C3.ToString(currentCulture) }; return string.Format(currentCulture, "{{[A1:{0} A2:{1} A3:{2}] [B1:{3} B2:{4} B3:{5}] [C1:{6} C2:{7} C3:{8}]}}", args); } } public struct Matrix4x4 : IEquatable { public float A1; public float A2; public float A3; public float A4; public float B1; public float B2; public float B3; public float B4; public float C1; public float C2; public float C3; public float C4; public float D1; public float D2; public float D3; public float D4; private static Matrix4x4 _identity = new Matrix4x4(1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f); public static Matrix4x4 Identity => _identity; public bool IsIdentity { get { float num = 0.01f; return A2 <= num && A2 >= 0f - num && A3 <= num && A3 >= 0f - num && A4 <= num && A4 >= 0f - num && B1 <= num && B1 >= 0f - num && B3 <= num && B3 >= 0f - num && B4 <= num && B4 >= 0f - num && C1 <= num && C1 >= 0f - num && C2 <= num && C2 >= 0f - num && C4 <= num && C4 >= 0f - num && D1 <= num && D1 >= 0f - num && D2 <= num && D2 >= 0f - num && D3 <= num && D3 >= 0f - num && A1 <= 1f + num && A1 >= 1f - num && B2 <= 1f + num && B2 >= 1f - num && C3 <= 1f + num && C3 >= 1f - num && D4 <= 1f + num && D4 >= 1f - num; } } public float this[int i, int j] { get { return i switch { 1 => j switch { 1 => A1, 2 => A2, 3 => A3, 4 => A4, _ => 0f, }, 2 => j switch { 1 => B1, 2 => B2, 3 => B3, 4 => B4, _ => 0f, }, 3 => j switch { 1 => C1, 2 => C2, 3 => C3, 4 => C4, _ => 0f, }, 4 => j switch { 1 => D1, 2 => D2, 3 => D3, 4 => D4, _ => 0f, }, _ => 0f, }; } set { switch (i) { case 1: switch (j) { case 1: A1 = value; break; case 2: A2 = value; break; case 3: A3 = value; break; case 4: A4 = value; break; } break; case 2: switch (j) { case 1: B1 = value; break; case 2: B2 = value; break; case 3: B3 = value; break; case 4: B4 = value; break; } break; case 3: switch (j) { case 1: C1 = value; break; case 2: C2 = value; break; case 3: C3 = value; break; case 4: C4 = value; break; } break; case 4: switch (j) { case 1: D1 = value; break; case 2: D2 = value; break; case 3: D3 = value; break; case 4: D4 = value; break; } break; } } } public Matrix4x4(float a1, float a2, float a3, float a4, float b1, float b2, float b3, float b4, float c1, float c2, float c3, float c4, float d1, float d2, float d3, float d4) { A1 = a1; A2 = a2; A3 = a3; A4 = a4; B1 = b1; B2 = b2; B3 = b3; B4 = b4; C1 = c1; C2 = c2; C3 = c3; C4 = c4; D1 = d1; D2 = d2; D3 = d3; D4 = d4; } public Matrix4x4(Matrix3x3 rotMatrix) { A1 = rotMatrix.A1; A2 = rotMatrix.A2; A3 = rotMatrix.A3; A4 = 0f; B1 = rotMatrix.B1; B2 = rotMatrix.B2; B3 = rotMatrix.B3; B4 = 0f; C1 = rotMatrix.C1; C2 = rotMatrix.C2; C3 = rotMatrix.C3; C4 = 0f; D1 = 0f; D2 = 0f; D3 = 0f; D4 = 1f; } public void Transpose() { Matrix4x4 matrix4x = new Matrix4x4(this); matrix4x.A4 = A4; matrix4x.B4 = B4; matrix4x.C4 = C4; matrix4x.D1 = D1; matrix4x.D2 = D2; matrix4x.D3 = D3; A2 = matrix4x.B1; A3 = matrix4x.C1; A4 = matrix4x.D1; B1 = matrix4x.A2; B3 = matrix4x.C2; B4 = matrix4x.D2; C1 = matrix4x.A3; C2 = matrix4x.B3; C4 = matrix4x.D3; D1 = matrix4x.A4; D2 = matrix4x.B4; D3 = matrix4x.C4; } public void Inverse() { float num = Determinant(); if (num == 0f) { A1 = float.NaN; A2 = float.NaN; A3 = float.NaN; A4 = float.NaN; B1 = float.NaN; B2 = float.NaN; B3 = float.NaN; B4 = float.NaN; C1 = float.NaN; C2 = float.NaN; C3 = float.NaN; C4 = float.NaN; D1 = float.NaN; D2 = float.NaN; D3 = float.NaN; D4 = float.NaN; } float num2 = 1f / num; float a = num2 * (B2 * (C3 * D4 - C4 * D3) + B3 * (C4 * D2 - C2 * D4) + B4 * (C2 * D3 - C3 * D2)); float a2 = (0f - num2) * (A2 * (C3 * D4 - C4 * D3) + A3 * (C4 * D2 - C2 * D4) + A4 * (C2 * D3 - C3 * D2)); float a3 = num2 * (A2 * (B3 * D4 - B4 * D3) + A3 * (B4 * D2 - B2 * D4) + A4 * (B2 * D3 - B3 * D2)); float a4 = (0f - num2) * (A2 * (B3 * C4 - B4 * C3) + A3 * (B4 * C2 - B2 * C4) + A4 * (B2 * C3 - B3 * C2)); float b = (0f - num2) * (B1 * (C3 * D4 - C4 * D3) + B3 * (C4 * D1 - C1 * D4) + B4 * (C1 * D3 - C3 * D1)); float b2 = num2 * (A1 * (C3 * D4 - C4 * D3) + A3 * (C4 * D1 - C1 * D4) + A4 * (C1 * D3 - C3 * D1)); float b3 = (0f - num2) * (A1 * (B3 * D4 - B4 * D3) + A3 * (B4 * D1 - B1 * D4) + A4 * (B1 * D3 - B3 * D1)); float b4 = num2 * (A1 * (B3 * C4 - B4 * C3) + A3 * (B4 * C1 - B1 * C4) + A4 * (B1 * C3 - B3 * C1)); float c = num2 * (B1 * (C2 * D4 - C4 * D2) + B2 * (C4 * D1 - C1 * D4) + B4 * (C1 * D2 - C2 * D1)); float c2 = (0f - num2) * (A1 * (C2 * D4 - C4 * D2) + A2 * (C4 * D1 - C1 * D4) + A4 * (C1 * D2 - C2 * D1)); float c3 = num2 * (A1 * (B2 * D4 - B4 * D2) + A2 * (B4 * D1 - B1 * D4) + A4 * (B1 * D2 - B2 * D1)); float c4 = (0f - num2) * (A1 * (B2 * C4 - B4 * C2) + A2 * (B4 * C1 - B1 * C4) + A4 * (B1 * C2 - B2 * C1)); float d = (0f - num2) * (B1 * (C2 * D3 - C3 * D2) + B2 * (C3 * D1 - C1 * D3) + B3 * (C1 * D2 - C2 * D1)); float d2 = num2 * (A1 * (C2 * D3 - C3 * D2) + A2 * (C3 * D1 - C1 * D3) + A3 * (C1 * D2 - C2 * D1)); float d3 = (0f - num2) * (A1 * (B2 * D3 - B3 * D2) + A2 * (B3 * D1 - B1 * D3) + A3 * (B1 * D2 - B2 * D1)); float d4 = num2 * (A1 * (B2 * C3 - B3 * C2) + A2 * (B3 * C1 - B1 * C3) + A3 * (B1 * C2 - B2 * C1)); A1 = a; A2 = a2; A3 = a3; A4 = a4; B1 = b; B2 = b2; B3 = b3; B4 = b4; C1 = c; C2 = c2; C3 = c3; C4 = c4; D1 = d; D2 = d2; D3 = d3; D4 = d4; } public float Determinant() { return A1 * B2 * C3 * D4 - A1 * B2 * C4 * D3 + A1 * B3 * C4 * D2 - A1 * B3 * C2 * D4 + A1 * B4 * C2 * D3 - A1 * B4 * C3 * D2 - A2 * B3 * C4 * D1 + A2 * B3 * C1 * D4 - A2 * B4 * C1 * D3 + A2 * B4 * C3 * D1 - A2 * B1 * C3 * D4 + A2 * B1 * C4 * D3 + A3 * B4 * C1 * D2 - A3 * B4 * C2 * D1 + A3 * B1 * C2 * D4 - A3 * B1 * C4 * D2 + A3 * B2 * C4 * D1 - A3 * B2 * C1 * D4 - A4 * B1 * C2 * D3 + A4 * B1 * C3 * D2 - A4 * B2 * C3 * D1 + A4 * B2 * C1 * D3 - A4 * B3 * C1 * D2 + A4 * B3 * C2 * D1; } public void Decompose(out Vector3D scaling, out Quaternion rotation, out Vector3D translation) { translation.X = A4; translation.Y = B4; translation.Z = C4; Vector3D vector3D = new Vector3D(A1, A2, A3); Vector3D vector3D2 = new Vector3D(B1, B2, B3); Vector3D vector3D3 = new Vector3D(C1, C2, C3); scaling.X = vector3D.Length(); scaling.Y = vector3D2.Length(); scaling.Z = vector3D3.Length(); if (Determinant() < 0f) { scaling.X = 0f - scaling.X; scaling.Y = 0f - scaling.Y; scaling.Z = 0f - scaling.Z; } if (scaling.X != 0f) { vector3D /= scaling.X; } if (scaling.Y != 0f) { vector3D2 /= scaling.Y; } if (scaling.Z != 0f) { vector3D3 /= scaling.Z; } Matrix3x3 matrix = new Matrix3x3(vector3D.X, vector3D.Y, vector3D.Z, vector3D2.X, vector3D2.Y, vector3D2.Z, vector3D3.X, vector3D3.Y, vector3D3.Z); rotation = new Quaternion(matrix); } public void DecomposeNoScaling(out Quaternion rotation, out Vector3D translation) { translation.X = A4; translation.Y = B4; translation.Z = C4; rotation = new Quaternion(new Matrix3x3(this)); } public static Matrix4x4 FromEulerAnglesXYZ(float x, float y, float z) { float num = (float)Math.Cos(x); float num2 = (float)Math.Sin(x); float num3 = (float)Math.Cos(y); float num4 = (float)Math.Sin(y); float num5 = (float)Math.Cos(z); float num6 = (float)Math.Sin(z); float num7 = num2 * num4; float num8 = num * num4; Matrix4x4 result = default(Matrix4x4); result.A1 = num3 * num5; result.A2 = num3 * num6; result.A3 = 0f - num4; result.A4 = 0f; result.B1 = num7 * num5 - num * num6; result.B2 = num7 * num6 + num * num5; result.B3 = num2 * num3; result.B4 = 0f; result.C1 = num8 * num5 + num2 * num6; result.C2 = num8 * num6 - num2 * num5; result.C3 = num * num3; result.C4 = 0f; result.D1 = 0f; result.D2 = 0f; result.D3 = 0f; result.D4 = 1f; return result; } public static Matrix4x4 FromEulerAnglesXYZ(Vector3D angles) { return FromEulerAnglesXYZ(angles.X, angles.Y, angles.Z); } public static Matrix4x4 FromRotationX(float radians) { Matrix4x4 identity = Identity; identity.B2 = (identity.C3 = (float)Math.Cos(radians)); identity.C2 = (float)Math.Sin(radians); identity.B3 = 0f - identity.C2; return identity; } public static Matrix4x4 FromRotationY(float radians) { Matrix4x4 identity = Identity; identity.A1 = (identity.C3 = (float)Math.Cos(radians)); identity.A3 = (float)Math.Sin(radians); identity.C1 = 0f - identity.A3; return identity; } public static Matrix4x4 FromRotationZ(float radians) { Matrix4x4 identity = Identity; identity.A1 = (identity.B2 = (float)Math.Cos(radians)); identity.B1 = (float)Math.Sin(radians); identity.A2 = 0f - identity.B1; return identity; } public static Matrix4x4 FromAngleAxis(float radians, Vector3D axis) { float x = axis.X; float y = axis.Y; float z = axis.Z; float num = (float)Math.Sin(radians); float num2 = (float)Math.Cos(radians); float num3 = x * x; float num4 = y * y; float num5 = z * z; float num6 = x * y; float num7 = x * z; float num8 = y * z; Matrix4x4 result = default(Matrix4x4); result.A1 = num3 + num2 * (1f - num3); result.B1 = num6 - num2 * num6 + num * z; result.C1 = num7 - num2 * num7 - num * y; result.D1 = 0f; result.A2 = num6 - num2 * num6 - num * z; result.B2 = num4 + num2 * (1f - num4); result.C2 = num8 - num2 * num8 + num * x; result.D2 = 0f; result.A3 = num7 - num2 * num7 + num * y; result.B3 = num8 - num2 * num8 - num * x; result.C3 = num5 + num2 * (1f - num5); result.D3 = 0f; result.A4 = 0f; result.B4 = 0f; result.C4 = 0f; result.D4 = 1f; return result; } public static Matrix4x4 FromTranslation(Vector3D translation) { Matrix4x4 identity = Identity; identity.A4 = translation.X; identity.B4 = translation.Y; identity.C4 = translation.Z; return identity; } public static Matrix4x4 FromScaling(Vector3D scaling) { Matrix4x4 identity = Identity; identity.A1 = scaling.X; identity.B2 = scaling.Y; identity.C3 = scaling.Z; return identity; } public static Matrix4x4 FromToMatrix(Vector3D from, Vector3D to) { Matrix3x3 rotMatrix = Matrix3x3.FromToMatrix(from, to); return new Matrix4x4(rotMatrix); } public static bool operator ==(Matrix4x4 a, Matrix4x4 b) { return a.A1 == b.A1 && a.A2 == b.A2 && a.A3 == b.A3 && a.A4 == b.A4 && a.B1 == b.B1 && a.B2 == b.B2 && a.B3 == b.B3 && a.B4 == b.B4 && a.C1 == b.C1 && a.C2 == b.C2 && a.C3 == b.C3 && a.C4 == b.C4 && a.D1 == b.D1 && a.D2 == b.D2 && a.D3 == b.D3 && a.D4 == b.D4; } public static bool operator !=(Matrix4x4 a, Matrix4x4 b) { return a.A1 != b.A1 || a.A2 != b.A2 || a.A3 != b.A3 || a.A4 != b.A4 || a.B1 != b.B1 || a.B2 != b.B2 || a.B3 != b.B3 || a.B4 != b.B4 || a.C1 != b.C1 || a.C2 != b.C2 || a.C3 != b.C3 || a.C4 != b.C4 || a.D1 != b.D1 || a.D2 != b.D2 || a.D3 != b.D3 || a.D4 != b.D4; } public static Matrix4x4 operator *(Matrix4x4 a, Matrix4x4 b) { return new Matrix4x4(a.A1 * b.A1 + a.B1 * b.A2 + a.C1 * b.A3 + a.D1 * b.A4, a.A2 * b.A1 + a.B2 * b.A2 + a.C2 * b.A3 + a.D2 * b.A4, a.A3 * b.A1 + a.B3 * b.A2 + a.C3 * b.A3 + a.D3 * b.A4, a.A4 * b.A1 + a.B4 * b.A2 + a.C4 * b.A3 + a.D4 * b.A4, a.A1 * b.B1 + a.B1 * b.B2 + a.C1 * b.B3 + a.D1 * b.B4, a.A2 * b.B1 + a.B2 * b.B2 + a.C2 * b.B3 + a.D2 * b.B4, a.A3 * b.B1 + a.B3 * b.B2 + a.C3 * b.B3 + a.D3 * b.B4, a.A4 * b.B1 + a.B4 * b.B2 + a.C4 * b.B3 + a.D4 * b.B4, a.A1 * b.C1 + a.B1 * b.C2 + a.C1 * b.C3 + a.D1 * b.C4, a.A2 * b.C1 + a.B2 * b.C2 + a.C2 * b.C3 + a.D2 * b.C4, a.A3 * b.C1 + a.B3 * b.C2 + a.C3 * b.C3 + a.D3 * b.C4, a.A4 * b.C1 + a.B4 * b.C2 + a.C4 * b.C3 + a.D4 * b.C4, a.A1 * b.D1 + a.B1 * b.D2 + a.C1 * b.D3 + a.D1 * b.D4, a.A2 * b.D1 + a.B2 * b.D2 + a.C2 * b.D3 + a.D2 * b.D4, a.A3 * b.D1 + a.B3 * b.D2 + a.C3 * b.D3 + a.D3 * b.D4, a.A4 * b.D1 + a.B4 * b.D2 + a.C4 * b.D3 + a.D4 * b.D4); } public static implicit operator Matrix4x4(Matrix3x3 mat) { Matrix4x4 result = default(Matrix4x4); result.A1 = mat.A1; result.A2 = mat.A2; result.A3 = mat.A3; result.A4 = 0f; result.B1 = mat.B1; result.B2 = mat.B2; result.B3 = mat.B3; result.B4 = 0f; result.C1 = mat.C1; result.C2 = mat.C2; result.C3 = mat.C3; result.C4 = 0f; result.D1 = 0f; result.D2 = 0f; result.D3 = 0f; result.D4 = 1f; return result; } public bool Equals(Matrix4x4 other) { return A1 == other.A1 && A2 == other.A2 && A3 == other.A3 && A4 == other.A4 && B1 == other.B1 && B2 == other.B2 && B3 == other.B3 && B4 == other.B4 && C1 == other.C1 && C2 == other.C2 && C3 == other.C3 && C4 == other.C4 && D1 == other.D1 && D2 == other.D2 && D3 == other.D3 && D4 == other.D4; } public override bool Equals(object obj) { if (obj is Matrix4x4) { return Equals((Matrix4x4)obj); } return false; } public override int GetHashCode() { return A1.GetHashCode() + A2.GetHashCode() + A3.GetHashCode() + A4.GetHashCode() + B1.GetHashCode() + B2.GetHashCode() + B3.GetHashCode() + B4.GetHashCode() + C1.GetHashCode() + C2.GetHashCode() + C3.GetHashCode() + C4.GetHashCode() + D1.GetHashCode() + D2.GetHashCode() + D3.GetHashCode() + D4.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; object[] args = new object[16] { A1.ToString(currentCulture), A2.ToString(currentCulture), A3.ToString(currentCulture), A4.ToString(currentCulture), B1.ToString(currentCulture), B2.ToString(currentCulture), B3.ToString(currentCulture), B4.ToString(currentCulture), C1.ToString(currentCulture), C2.ToString(currentCulture), C3.ToString(currentCulture), C4.ToString(currentCulture), D1.ToString(currentCulture), D2.ToString(currentCulture), D3.ToString(currentCulture), D4.ToString(currentCulture) }; return string.Format(currentCulture, "{{[A1:{0} A2:{1} A3:{2} A4:{3}] [B1:{4} B2:{5} B3:{6} B4:{7}] [C1:{8} C2:{9} C3:{10} C4:{11}] [D1:{12} D2:{13} D3:{14} D4:{15}]}}", args); } } public delegate void FreeNativeDelegate(IntPtr nativeValue, bool freeNative); public static class MemoryHelper { private static Dictionary s_customMarshalers = new Dictionary(); private static Dictionary s_pinnedObjects = new Dictionary(); public static IntPtr ToNativeArray(Managed[] managedArray) where Managed : class, IMarshalable, new() where Native : struct { return ToNativeArray(managedArray, arrayOfPointers: false); } public static IntPtr ToNativeArray(Managed[] managedArray, bool arrayOfPointers) where Managed : class, IMarshalable, new() where Native : struct { if (managedArray == null || managedArray.Length == 0) { return IntPtr.Zero; } bool flag = IsNativeBlittable(managedArray); int num = (flag ? SizeOf() : MarshalSizeOf()); int num2 = (arrayOfPointers ? IntPtr.Size : num); IntPtr intPtr = (arrayOfPointers ? AllocateMemory(managedArray.Length * IntPtr.Size) : AllocateMemory(managedArray.Length * num)); for (int i = 0; i < managedArray.Length; i++) { IntPtr intPtr2 = AddIntPtr(intPtr, num2 * i); Managed val = managedArray[i]; Native nativeValue = default(Native); if (arrayOfPointers) { IntPtr data = IntPtr.Zero; if (val != null) { data = AllocateMemory(num); val.ToNative(data, out nativeValue); if (flag) { Write(data, in nativeValue); } else { MarshalPointer(in nativeValue, data); } } Write(intPtr2, in data); } else { if (managedArray != null) { val.ToNative(IntPtr.Zero, out nativeValue); } if (flag) { Write(intPtr2, in nativeValue); } else { MarshalPointer(in nativeValue, intPtr2); } } } return intPtr; } public static Managed[] FromNativeArray(IntPtr nativeArray, int length) where Managed : class, IMarshalable, new() where Native : struct { return FromNativeArray(nativeArray, length, arrayOfPointers: false); } public static Managed[] FromNativeArray(IntPtr nativeArray, int length, bool arrayOfPointers) where Managed : class, IMarshalable, new() where Native : struct { if (nativeArray == IntPtr.Zero || length == 0) { return new Managed[0]; } int num = (arrayOfPointers ? IntPtr.Size : MarshalSizeOf()); Type typeFromHandle = typeof(Native); Managed[] array = new Managed[length]; for (int i = 0; i < length; i++) { IntPtr intPtr = AddIntPtr(nativeArray, num * i); if (arrayOfPointers) { intPtr = Read(intPtr); } Managed val = new Managed(); Native value; if (val.IsNativeBlittable) { value = Read(intPtr); } else { MarshalStructure(intPtr, out value); } val.FromNative(in value); array[i] = val; } return array; } public static IntPtr ToNativeArray(T[] managedArray) where T : struct { if (managedArray == null || managedArray.Length == 0) { return IntPtr.Zero; } IntPtr intPtr = AllocateMemory(SizeOf() * managedArray.Length); Write(intPtr, managedArray, 0, managedArray.Length); return intPtr; } public static T[] FromNativeArray(IntPtr nativeArray, int length) where T : struct { if (nativeArray == IntPtr.Zero || length == 0) { return new T[0]; } T[] array = new T[length]; Read(nativeArray, array, 0, length); return array; } public static void FreeNativeArray(IntPtr nativeArray, int length, FreeNativeDelegate action) where T : struct { FreeNativeArray(nativeArray, length, action, arrayOfPointers: false); } public static void FreeNativeArray(IntPtr nativeArray, int length, FreeNativeDelegate action, bool arrayOfPointers) where T : struct { if (nativeArray == IntPtr.Zero || length == 0 || action == null) { return; } int num = (arrayOfPointers ? IntPtr.Size : MarshalSizeOf()); for (int i = 0; i < length; i++) { IntPtr intPtr = AddIntPtr(nativeArray, num * i); if (arrayOfPointers) { intPtr = Read(intPtr); } action(intPtr, arrayOfPointers); } FreeMemory(nativeArray); } public static IntPtr ToNativePointer(Managed managedValue) where Managed : class, IMarshalable, new() where Native : struct { if (managedValue == null) { return IntPtr.Zero; } int sizeInBytes = (managedValue.IsNativeBlittable ? SizeOf() : MarshalSizeOf()); IntPtr intPtr = AllocateMemory(sizeInBytes); managedValue.ToNative(intPtr, out var nativeValue); if (managedValue.IsNativeBlittable) { Write(intPtr, in nativeValue); } else { MarshalPointer(in nativeValue, intPtr); } return intPtr; } public static Managed FromNativePointer(IntPtr ptr) where Managed : class, IMarshalable, new() where Native : struct { if (ptr == IntPtr.Zero) { return null; } Managed val = new Managed(); Native value; if (val.IsNativeBlittable) { Read(ptr, out value); } else { MarshalStructure(ptr, out value); } val.FromNative(in value); return val; } public static void MarshalStructure(IntPtr ptr, out T value) where T : struct { if (ptr == IntPtr.Zero) { value = default(T); } Type typeFromHandle = typeof(T); if (HasNativeCustomMarshaler(typeFromHandle, out var marshaler)) { value = (T)marshaler.MarshalNativeToManaged(ptr); } else { value = (T)Marshal.PtrToStructure(ptr, typeFromHandle); } } public static T MarshalStructure(IntPtr ptr) where T : struct { if (ptr == IntPtr.Zero) { return default(T); } Type typeFromHandle = typeof(T); if (HasNativeCustomMarshaler(typeFromHandle, out var marshaler)) { return (T)marshaler.MarshalNativeToManaged(ptr); } return (T)Marshal.PtrToStructure(ptr, typeFromHandle); } public static void MarshalPointer(in T value, IntPtr ptr) where T : struct { if (!(ptr == IntPtr.Zero)) { if (HasNativeCustomMarshaler(typeof(T), out var marshaler)) { marshaler.MarshalManagedToNative(value, ptr); } else { Marshal.StructureToPtr((object)value, ptr, fDeleteOld: true); } } } public static int MarshalSizeOf() where T : struct { Type typeFromHandle = typeof(T); if (HasNativeCustomMarshaler(typeFromHandle, out var marshaler)) { return marshaler.NativeDataSize; } return Marshal.SizeOf(typeFromHandle); } public static int MarshalSizeOf(T[] array) where T : struct { return (array != null) ? (array.Length * MarshalSizeOf()) : 0; } public static IntPtr PinObject(object obj) { lock (s_pinnedObjects) { if (!s_pinnedObjects.TryGetValue(obj, out var value)) { value = GCHandle.Alloc(obj, GCHandleType.Pinned); s_pinnedObjects.Add(obj, value); } return value.AddrOfPinnedObject(); } } public static void UnpinObject(object obj) { lock (s_pinnedObjects) { if (s_pinnedObjects.TryGetValue(obj, out var value)) { value.Free(); s_pinnedObjects.Remove(obj); } } } public static void DisposeCollection(ICollection collection) where T : IDisposable { if (collection == null) { return; } if (collection is IList list) { for (int i = 0; i < list.Count; i++) { ((IDisposable)list[i])?.Dispose(); } return; } foreach (T item in collection) { ((IDisposable)item)?.Dispose(); } } public static T CastToEnum(V value) where V : unmanaged where T : Enum { return (T)value; } public unsafe static IntPtr AllocateMemory(int sizeInBytes, int alignment = 16) { int num = alignment - 1; IntPtr intPtr = Marshal.AllocHGlobal(sizeInBytes + num + IntPtr.Size); long num2 = (long)((byte*)(void*)intPtr + sizeof(void*) + num) & (long)(~num); *(IntPtr*)((nint)num2 + (nint)(-1) * (nint)sizeof(IntPtr)) = intPtr; return new IntPtr(num2); } public static IntPtr AllocateClearedMemory(int sizeInBytes, byte clearValue = 0, int alignment = 16) { IntPtr intPtr = AllocateMemory(sizeInBytes, alignment); ClearMemory(intPtr, clearValue, sizeInBytes); return intPtr; } public unsafe static void FreeMemory(IntPtr memoryPtr) { if (!(memoryPtr == IntPtr.Zero)) { Marshal.FreeHGlobal(((IntPtr*)(void*)memoryPtr)[-1]); } } public static bool IsMemoryAligned(IntPtr memoryPtr, int alignment = 16) { int num = alignment - 1; return (memoryPtr.ToInt64() & num) == 0; } public static void Swap(ref T left, ref T right) { T val = left; left = right; right = val; } public static int ComputeFNVModifiedHashCode(byte[] data) { if (data == null || data.Length == 0) { return 0; } uint num = 16777619u; uint num2 = 2166136261u; for (int i = 0; i < data.Length; i++) { num2 = (num2 ^ data[i]) * num; } num2 += num2 << 13; num2 ^= num2 >> 7; num2 += num2 << 3; num2 ^= num2 >> 17; return (int)(num2 + (num2 << 5)); } public static byte[] ReadStreamFully(Stream stream, int initialLength) { if (initialLength < 1) { initialLength = 32768; } byte[] array = new byte[initialLength]; int num = 0; int num2; while ((num2 = stream.Read(array, num, array.Length - num)) > 0) { num += num2; if (num == array.Length) { int num3 = stream.ReadByte(); if (num3 == -1) { return array; } byte[] array2 = new byte[array.Length * 2]; Array.Copy(array, array2, array.Length); array2[num] = (byte)num3; array = array2; num++; } } byte[] array3 = new byte[num]; Array.Copy(array, array3, num); return array3; } public static bool Compare(byte[] firstData, byte[] secondData) { if (firstData == secondData) { return true; } if (firstData == null || secondData == null) { return false; } if (firstData.Length != secondData.Length) { return false; } for (int i = 0; i < firstData.Length; i++) { if (firstData[i] != secondData[i]) { return false; } } return true; } public unsafe static void ClearMemory(IntPtr memoryPtr, byte clearValue, int sizeInBytesToClear) { // IL initblk instruction Unsafe.InitBlockUnaligned((void*)memoryPtr, clearValue, sizeInBytesToClear); } public static int SizeOf() where T : struct { return Unsafe.SizeOf(); } public unsafe static IntPtr AsPointer(ref T src) where T : struct { return (nint)Unsafe.AsPointer(ref src); } public unsafe static IntPtr AsPointerReadonly(in T src) where T : struct { return (nint)Unsafe.AsPointer(ref src); } public unsafe static ref T AsRef(IntPtr pSrc) where T : struct { return ref *(T*)pSrc; } public static ref TTo As(ref TFrom src) where TFrom : struct where TTo : struct { return ref Unsafe.As(ref src); } public static ref readonly TTo AsReadonly(in TFrom src) where TFrom : struct where TTo : struct { return ref Unsafe.As(ref src); } public static int SizeOf(T[] array) where T : struct { return (array != null) ? (array.Length * Unsafe.SizeOf()) : 0; } public static IntPtr AddIntPtr(IntPtr ptr, int offset) { return new IntPtr(ptr.ToInt64() + offset); } public unsafe static void CopyMemory(IntPtr pDest, IntPtr pSrc, int sizeInBytesToCopy) { // IL cpblk instruction Unsafe.CopyBlockUnaligned((void*)pDest, (void*)pSrc, sizeInBytesToCopy); } public static int Count(IEnumerable source) { if (source == null) { throw new ArgumentNullException("source"); } if (source is ICollection { Count: var count }) { return count; } if (!(source is ICollection { Count: var count2 })) { int num = 0; using (IEnumerator enumerator = source.GetEnumerator()) { while (enumerator.MoveNext()) { num++; } } return num; } return count2; } public unsafe static byte[] ToByteArray(T[] source) where T : struct { if (source == null || source.Length == 0) { return null; } byte[] array = new byte[Unsafe.SizeOf() * source.Length]; fixed (byte* ptr = array) { void* ptr2 = ptr; Write((IntPtr)ptr2, source, 0, source.Length); } return array; } public unsafe static T[] FromByteArray(byte[] source) where T : struct { if (source == null || source.Length == 0) { return null; } T[] array = new T[(int)Math.Floor((double)source.Length / (double)Unsafe.SizeOf())]; fixed (byte* ptr = source) { void* ptr2 = ptr; Read((IntPtr)ptr2, array, 0, array.Length); } return array; } public unsafe static void CopyBytes(byte[] srcArray, int srcStartIndex, T[] destArray, int destStartIndex, int count) where T : struct { if (srcArray == null || srcArray.Length == 0 || destArray == null || destArray.Length == 0) { return; } int num = Unsafe.SizeOf() * count; if (srcStartIndex >= 0 && srcStartIndex + num <= srcArray.Length && destStartIndex >= 0 && destStartIndex + count <= destArray.Length) { fixed (byte* ptr = &srcArray[srcStartIndex]) { void* ptr2 = ptr; Read((IntPtr)ptr2, destArray, destStartIndex, count); } } } public unsafe static void CopyBytes(T[] srcArray, int srcStartIndex, byte[] destArray, int destStartIndex, int count) where T : struct { if (srcArray == null || srcArray.Length == 0 || destArray == null || destArray.Length == 0) { return; } int num = Unsafe.SizeOf() * count; if (srcStartIndex >= 0 && srcStartIndex + count <= srcArray.Length && destStartIndex >= 0 && destStartIndex + num <= destArray.Length) { fixed (byte* ptr = &destArray[destStartIndex]) { void* ptr2 = ptr; Write((IntPtr)ptr2, srcArray, srcStartIndex, count); } } } public unsafe static void Read(IntPtr pSrc, T[] data, int startIndexInArray, int count) where T : struct { fixed (T* ptr = &data[startIndexInArray]) { // IL cpblk instruction Unsafe.CopyBlockUnaligned(ptr, pSrc, Unsafe.SizeOf() * count); } } public unsafe static T Read(IntPtr pSrc) where T : struct { return Unsafe.Read((void*)pSrc); } public unsafe static void Read(IntPtr pSrc, out T value) where T : struct { value = Unsafe.Read((void*)pSrc); } public unsafe static void Write(IntPtr pDest, T[] data, int startIndexInArray, int count) where T : struct { fixed (T* ptr = &data[startIndexInArray]) { // IL cpblk instruction Unsafe.CopyBlockUnaligned(pDest, ptr, Unsafe.SizeOf() * count); } } public unsafe static void Write(IntPtr pDest, in T data) where T : struct { Unsafe.Write((void*)pDest, data); } private static bool IsNativeBlittable(Managed managedValue) where Managed : class, IMarshalable, new() where Native : struct { return managedValue?.IsNativeBlittable ?? false; } private static bool IsNativeBlittable(Managed[] managedArray) where Managed : class, IMarshalable, new() where Native : struct { if (managedArray == null || managedArray.Length == 0) { return false; } foreach (Managed val in managedArray) { if (val != null) { return val.IsNativeBlittable; } } return false; } private static bool HasNativeCustomMarshaler(Type type, out INativeCustomMarshaler marshaler) { marshaler = null; if (type == null) { return false; } lock (s_customMarshalers) { if (!s_customMarshalers.TryGetValue(type, out marshaler)) { object[] customAttributes = PlatformHelper.GetCustomAttributes(type, typeof(NativeCustomMarshalerAttribute), inherit: false); if (customAttributes.Length != 0) { marshaler = (customAttributes[0] as NativeCustomMarshalerAttribute).Marshaler; } s_customMarshalers.Add(type, marshaler); } } return marshaler != null; } } public sealed class Mesh : IMarshalable { private string m_name; private PrimitiveType m_primitiveType; private int m_materialIndex; private List m_vertices; private List m_normals; private List m_tangents; private List m_bitangents; private List m_faces; private List[] m_colors; private List[] m_texCoords; private int[] m_texComponentCount; private List m_bones; private List m_meshAttachments; private MeshMorphingMethod m_morphMethod; private BoundingBox m_boundingBox; public string Name { get { return m_name; } set { m_name = value; } } public PrimitiveType PrimitiveType { get { return m_primitiveType; } set { m_primitiveType = value; } } public int MaterialIndex { get { return m_materialIndex; } set { m_materialIndex = value; } } public int VertexCount => m_vertices.Count; public bool HasVertices => m_vertices.Count > 0; public List Vertices => m_vertices; public bool HasNormals => m_normals.Count > 0; public List Normals => m_normals; public bool HasTangentBasis => m_tangents.Count > 0 && m_bitangents.Count > 0; public List Tangents => m_tangents; public List BiTangents => m_bitangents; public int FaceCount => m_faces.Count; public bool HasFaces => m_faces.Count > 0; public List Faces => m_faces; public int VertexColorChannelCount { get { int num = 0; for (int i = 0; i < m_colors.Length; i++) { if (HasVertexColors(i)) { num++; } } return num; } } public int TextureCoordinateChannelCount { get { int num = 0; for (int i = 0; i < m_texCoords.Length; i++) { if (HasTextureCoords(i)) { num++; } } return num; } } public List[] VertexColorChannels => m_colors; public List[] TextureCoordinateChannels => m_texCoords; public int[] UVComponentCount => m_texComponentCount; public int BoneCount => m_bones.Count; public bool HasBones => m_bones.Count > 0; public List Bones => m_bones; public int MeshAnimationAttachmentCount => m_meshAttachments.Count; public bool HasMeshAnimationAttachments => m_meshAttachments.Count > 0; public List MeshAnimationAttachments => m_meshAttachments; public MeshMorphingMethod MorphMethod { get { return m_morphMethod; } set { m_morphMethod = value; } } public BoundingBox BoundingBox { get { return m_boundingBox; } set { m_boundingBox = value; } } bool IMarshalable.IsNativeBlittable => true; public Mesh() : this(string.Empty, PrimitiveType.Triangle) { } public Mesh(string name) : this(name, PrimitiveType.Triangle) { } public Mesh(PrimitiveType primType) : this(string.Empty, primType) { } public Mesh(string name, PrimitiveType primType) { m_name = name; m_primitiveType = primType; m_materialIndex = 0; m_morphMethod = MeshMorphingMethod.None; m_vertices = new List(); m_normals = new List(); m_tangents = new List(); m_bitangents = new List(); m_colors = new List[8]; for (int i = 0; i < m_colors.Length; i++) { m_colors[i] = new List(); } m_texCoords = new List[8]; for (int j = 0; j < m_texCoords.Length; j++) { m_texCoords[j] = new List(); } m_texComponentCount = new int[8]; m_bones = new List(); m_faces = new List(); m_meshAttachments = new List(); } public bool HasVertexColors(int channelIndex) { if (channelIndex >= m_colors.Length || channelIndex < 0) { return false; } List list = m_colors[channelIndex]; if (list != null) { return list.Count > 0; } return false; } public bool HasTextureCoords(int channelIndex) { if (channelIndex >= m_texCoords.Length || channelIndex < 0) { return false; } List list = m_texCoords[channelIndex]; if (list != null) { return list.Count > 0; } return false; } public bool SetIndices(int[] indices, int indicesPerFace) { if (indices == null || indices.Length == 0 || indices.Length % indicesPerFace != 0) { return false; } m_faces.Clear(); int num = indices.Length / indicesPerFace; int num2 = 0; for (int i = 0; i < num; i++) { Face face = new Face(); for (int j = 0; j < indicesPerFace; j++) { face.Indices.Add(indices[num2]); num2++; } m_faces.Add(face); } return true; } public int[] GetIndices() { if (HasFaces) { List list = new List(); foreach (Face face in m_faces) { if (face.IndexCount > 0 && face.Indices != null) { list.AddRange(face.Indices); } } return list.ToArray(); } return null; } public uint[] GetUnsignedIndices() { if (HasFaces) { List list = new List(); foreach (Face face in m_faces) { if (face.IndexCount <= 0 || face.Indices == null) { continue; } foreach (int index in face.Indices) { list.Add((uint)index); } } return list.ToArray(); } return null; } public short[] GetShortIndices() { if (HasFaces) { List list = new List(); foreach (Face face in m_faces) { if (face.IndexCount <= 0 || face.Indices == null) { continue; } foreach (int index in face.Indices) { list.Add((short)index); } } return list.ToArray(); } return null; } private void ClearBuffers() { m_vertices.Clear(); m_normals.Clear(); m_tangents.Clear(); m_bitangents.Clear(); for (int i = 0; i < m_colors.Length; i++) { List list = m_colors[i]; if (list == null) { m_colors[i] = new List(); } else { list.Clear(); } } for (int j = 0; j < m_texCoords.Length; j++) { List list2 = m_texCoords[j]; if (list2 == null) { m_texCoords[j] = new List(); } else { list2.Clear(); } } for (int k = 0; k < m_texComponentCount.Length; k++) { m_texComponentCount[k] = 0; } m_bones.Clear(); m_faces.Clear(); m_meshAttachments.Clear(); } private Vector3D[] CopyTo(List list, Vector3D[] copy) { list.CopyTo(copy); return copy; } void IMarshalable.ToNative(IntPtr thisPtr, out AiMesh nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.Vertices = IntPtr.Zero; nativeValue.Normals = IntPtr.Zero; nativeValue.Tangents = IntPtr.Zero; nativeValue.BiTangents = IntPtr.Zero; nativeValue.AnimMeshes = IntPtr.Zero; nativeValue.Bones = IntPtr.Zero; nativeValue.Faces = IntPtr.Zero; nativeValue.Colors = default(AiMeshColorArray); nativeValue.TextureCoords = default(AiMeshTextureCoordinateArray); nativeValue.NumUVComponents = default(AiMeshUVComponentArray); nativeValue.PrimitiveTypes = m_primitiveType; nativeValue.MaterialIndex = (uint)m_materialIndex; nativeValue.NumVertices = (uint)VertexCount; nativeValue.NumBones = (uint)BoneCount; nativeValue.NumFaces = (uint)FaceCount; nativeValue.NumAnimMeshes = (uint)MeshAnimationAttachmentCount; nativeValue.MorphMethod = m_morphMethod; nativeValue.AABB = m_boundingBox; if (nativeValue.NumVertices != 0) { Vector3D[] copy = new Vector3D[nativeValue.NumVertices]; nativeValue.Vertices = MemoryHelper.ToNativeArray(CopyTo(m_vertices, copy)); if (HasNormals) { nativeValue.Normals = MemoryHelper.ToNativeArray(CopyTo(m_normals, copy)); } if (HasTangentBasis) { nativeValue.Tangents = MemoryHelper.ToNativeArray(CopyTo(m_tangents, copy)); nativeValue.BiTangents = MemoryHelper.ToNativeArray(CopyTo(m_bitangents, copy)); } for (int i = 0; i < m_colors.Length; i++) { List list = m_colors[i]; if (list == null || list.Count == 0) { nativeValue.Colors[i] = IntPtr.Zero; } else { nativeValue.Colors[i] = MemoryHelper.ToNativeArray(list.ToArray()); } } for (int j = 0; j < m_texCoords.Length; j++) { List list2 = m_texCoords[j]; if (list2 == null || list2.Count == 0) { nativeValue.TextureCoords[j] = IntPtr.Zero; } else { nativeValue.TextureCoords[j] = MemoryHelper.ToNativeArray(CopyTo(list2, copy)); } } for (int k = 0; k < m_texComponentCount.Length; k++) { nativeValue.NumUVComponents[k] = (uint)m_texComponentCount[k]; } } if (nativeValue.NumFaces != 0) { nativeValue.Faces = MemoryHelper.ToNativeArray(m_faces.ToArray()); } if (nativeValue.NumBones != 0) { nativeValue.Bones = MemoryHelper.ToNativeArray(m_bones.ToArray(), arrayOfPointers: true); } if (nativeValue.NumAnimMeshes != 0) { nativeValue.AnimMeshes = MemoryHelper.ToNativeArray(m_meshAttachments.ToArray()); } } void IMarshalable.FromNative(in AiMesh nativeValue) { ClearBuffers(); int numVertices = (int)nativeValue.NumVertices; m_name = AiString.GetString(in nativeValue.Name); m_materialIndex = (int)nativeValue.MaterialIndex; m_morphMethod = nativeValue.MorphMethod; m_boundingBox = nativeValue.AABB; if (numVertices > 0) { if (nativeValue.Vertices != IntPtr.Zero) { m_vertices.AddRange(MemoryHelper.FromNativeArray(nativeValue.Vertices, numVertices)); } if (nativeValue.Normals != IntPtr.Zero) { m_normals.AddRange(MemoryHelper.FromNativeArray(nativeValue.Normals, numVertices)); } if (nativeValue.Tangents != IntPtr.Zero) { m_tangents.AddRange(MemoryHelper.FromNativeArray(nativeValue.Tangents, numVertices)); } if (nativeValue.BiTangents != IntPtr.Zero) { m_bitangents.AddRange(MemoryHelper.FromNativeArray(nativeValue.BiTangents, numVertices)); } for (int i = 0; i < nativeValue.Colors.Length; i++) { IntPtr intPtr = nativeValue.Colors[i]; if (intPtr != IntPtr.Zero) { m_colors[i].AddRange(MemoryHelper.FromNativeArray(intPtr, numVertices)); } } for (int j = 0; j < nativeValue.TextureCoords.Length; j++) { IntPtr intPtr2 = nativeValue.TextureCoords[j]; if (intPtr2 != IntPtr.Zero) { m_texCoords[j].AddRange(MemoryHelper.FromNativeArray(intPtr2, numVertices)); } } for (int k = 0; k < nativeValue.NumUVComponents.Length; k++) { m_texComponentCount[k] = (int)nativeValue.NumUVComponents[k]; } } if (nativeValue.NumFaces != 0 && nativeValue.Faces != IntPtr.Zero) { m_faces.AddRange(MemoryHelper.FromNativeArray(nativeValue.Faces, (int)nativeValue.NumFaces)); } if (nativeValue.NumBones != 0 && nativeValue.Bones != IntPtr.Zero) { m_bones.AddRange(MemoryHelper.FromNativeArray(nativeValue.Bones, (int)nativeValue.NumBones, arrayOfPointers: true)); } if (nativeValue.NumAnimMeshes != 0 && nativeValue.AnimMeshes != IntPtr.Zero) { m_meshAttachments.AddRange(MemoryHelper.FromNativeArray(nativeValue.AnimMeshes, (int)nativeValue.NumAnimMeshes, arrayOfPointers: true)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (nativeValue == IntPtr.Zero) { return; } AiMesh aiMesh = MemoryHelper.Read(nativeValue); if (aiMesh.NumVertices != 0) { if (aiMesh.Vertices != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMesh.Vertices); } if (aiMesh.Normals != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMesh.Normals); } if (aiMesh.Tangents != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMesh.Tangents); } if (aiMesh.BiTangents != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMesh.BiTangents); } for (int i = 0; i < aiMesh.Colors.Length; i++) { IntPtr intPtr = aiMesh.Colors[i]; if (intPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr); } } for (int j = 0; j < aiMesh.TextureCoords.Length; j++) { IntPtr intPtr2 = aiMesh.TextureCoords[j]; if (intPtr2 != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr2); } } } if (aiMesh.NumFaces != 0 && aiMesh.Faces != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiMesh.Faces, (int)aiMesh.NumFaces, Face.FreeNative); } if (aiMesh.NumBones != 0 && aiMesh.Bones != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiMesh.Bones, (int)aiMesh.NumBones, Bone.FreeNative, arrayOfPointers: true); } if (aiMesh.NumAnimMeshes != 0 && aiMesh.AnimMeshes != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiMesh.AnimMeshes, (int)aiMesh.NumAnimMeshes, MeshAnimationAttachment.FreeNative, arrayOfPointers: true); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } public sealed class MeshAnimationAttachment : IMarshalable { private string m_name; private List m_vertices; private List m_normals; private List m_tangents; private List m_bitangents; private List[] m_colors; private List[] m_texCoords; private float m_weight; public string Name { get { return m_name; } set { m_name = value; } } public int VertexCount => m_vertices.Count; public bool HasVertices => m_vertices.Count > 0; public List Vertices => m_vertices; public bool HasNormals => m_normals.Count > 0; public List Normals => m_normals; public bool HasTangentBasis => m_tangents.Count > 0 && m_bitangents.Count > 0; public List Tangents => m_tangents; public List BiTangents => m_bitangents; public int VertexColorChannelCount { get { int num = 0; for (int i = 0; i < m_colors.Length; i++) { if (HasVertexColors(i)) { num++; } } return num; } } public int TextureCoordinateChannelCount { get { int num = 0; for (int i = 0; i < m_texCoords.Length; i++) { if (HasTextureCoords(i)) { num++; } } return num; } } public List[] VertexColorChannels => m_colors; public List[] TextureCoordinateChannels => m_texCoords; public float Weight { get { return m_weight; } set { m_weight = value; } } bool IMarshalable.IsNativeBlittable => true; public MeshAnimationAttachment() { m_vertices = new List(); m_normals = new List(); m_tangents = new List(); m_bitangents = new List(); m_weight = 0f; m_colors = new List[8]; for (int i = 0; i < m_colors.Length; i++) { m_colors[i] = new List(); } m_texCoords = new List[8]; for (int j = 0; j < m_texCoords.Length; j++) { m_texCoords[j] = new List(); } } public bool HasVertexColors(int channelIndex) { if (channelIndex >= m_colors.Length || channelIndex < 0) { return false; } List list = m_colors[channelIndex]; if (list != null) { return list.Count > 0; } return false; } public bool HasTextureCoords(int channelIndex) { if (channelIndex >= m_texCoords.Length || channelIndex < 0) { return false; } List list = m_texCoords[channelIndex]; if (list != null) { return list.Count > 0; } return false; } private void ClearBuffers() { m_vertices.Clear(); m_normals.Clear(); m_tangents.Clear(); m_bitangents.Clear(); for (int i = 0; i < m_colors.Length; i++) { List list = m_colors[i]; if (list == null) { m_colors[i] = new List(); } else { list.Clear(); } } for (int j = 0; j < m_texCoords.Length; j++) { List list2 = m_texCoords[j]; if (list2 == null) { m_texCoords[j] = new List(); } else { list2.Clear(); } } } private Vector3D[] CopyTo(List list, Vector3D[] copy) { list.CopyTo(copy); return copy; } void IMarshalable.ToNative(IntPtr thisPtr, out AiAnimMesh nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.Vertices = IntPtr.Zero; nativeValue.Normals = IntPtr.Zero; nativeValue.Tangents = IntPtr.Zero; nativeValue.BiTangents = IntPtr.Zero; nativeValue.Colors = default(AiMeshColorArray); nativeValue.TextureCoords = default(AiMeshTextureCoordinateArray); nativeValue.NumVertices = (uint)VertexCount; nativeValue.Weight = m_weight; if (VertexCount <= 0) { return; } Vector3D[] copy = new Vector3D[VertexCount]; nativeValue.Vertices = MemoryHelper.ToNativeArray(CopyTo(m_vertices, copy)); if (HasNormals) { nativeValue.Normals = MemoryHelper.ToNativeArray(CopyTo(m_normals, copy)); } if (HasTangentBasis) { nativeValue.Tangents = MemoryHelper.ToNativeArray(CopyTo(m_tangents, copy)); nativeValue.BiTangents = MemoryHelper.ToNativeArray(CopyTo(m_bitangents, copy)); } for (int i = 0; i < m_colors.Length; i++) { List list = m_colors[i]; if (list == null || list.Count == 0) { nativeValue.Colors[i] = IntPtr.Zero; } else { nativeValue.Colors[i] = MemoryHelper.ToNativeArray(list.ToArray()); } } for (int j = 0; j < m_texCoords.Length; j++) { List list2 = m_texCoords[j]; if (list2 == null || list2.Count == 0) { nativeValue.TextureCoords[j] = IntPtr.Zero; } else { nativeValue.TextureCoords[j] = MemoryHelper.ToNativeArray(CopyTo(list2, copy)); } } } void IMarshalable.FromNative(in AiAnimMesh nativeValue) { ClearBuffers(); m_name = AiString.GetString(in nativeValue.Name); int numVertices = (int)nativeValue.NumVertices; m_weight = nativeValue.Weight; if (numVertices <= 0) { return; } if (nativeValue.Vertices != IntPtr.Zero) { m_vertices.AddRange(MemoryHelper.FromNativeArray(nativeValue.Vertices, numVertices)); } if (nativeValue.Normals != IntPtr.Zero) { m_normals.AddRange(MemoryHelper.FromNativeArray(nativeValue.Normals, numVertices)); } if (nativeValue.Tangents != IntPtr.Zero) { m_tangents.AddRange(MemoryHelper.FromNativeArray(nativeValue.Tangents, numVertices)); } if (nativeValue.BiTangents != IntPtr.Zero) { m_bitangents.AddRange(MemoryHelper.FromNativeArray(nativeValue.BiTangents, numVertices)); } for (int i = 0; i < nativeValue.Colors.Length; i++) { IntPtr intPtr = nativeValue.Colors[i]; if (intPtr != IntPtr.Zero) { m_colors[i].AddRange(MemoryHelper.FromNativeArray(intPtr, numVertices)); } } for (int j = 0; j < nativeValue.TextureCoords.Length; j++) { IntPtr intPtr2 = nativeValue.TextureCoords[j]; if (intPtr2 != IntPtr.Zero) { m_texCoords[j].AddRange(MemoryHelper.FromNativeArray(intPtr2, numVertices)); } } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (nativeValue == IntPtr.Zero) { return; } AiAnimMesh aiAnimMesh = MemoryHelper.Read(nativeValue); if (aiAnimMesh.NumVertices != 0) { if (aiAnimMesh.Vertices != IntPtr.Zero) { MemoryHelper.FreeMemory(aiAnimMesh.Vertices); } if (aiAnimMesh.Normals != IntPtr.Zero) { MemoryHelper.FreeMemory(aiAnimMesh.Normals); } if (aiAnimMesh.Tangents != IntPtr.Zero) { MemoryHelper.FreeMemory(aiAnimMesh.Tangents); } if (aiAnimMesh.BiTangents != IntPtr.Zero) { MemoryHelper.FreeMemory(aiAnimMesh.BiTangents); } for (int i = 0; i < aiAnimMesh.Colors.Length; i++) { IntPtr intPtr = aiAnimMesh.Colors[i]; if (intPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr); } } for (int j = 0; j < aiAnimMesh.TextureCoords.Length; j++) { IntPtr intPtr2 = aiAnimMesh.TextureCoords[j]; if (intPtr2 != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr2); } } } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } public sealed class MeshAnimationChannel : IMarshalable { private string m_name; private List m_meshKeys; public string MeshName { get { return m_name; } set { m_name = value; } } public int MeshKeyCount => m_meshKeys.Count; public bool HasMeshKeys => m_meshKeys.Count > 0; public List MeshKeys => m_meshKeys; bool IMarshalable.IsNativeBlittable => true; public MeshAnimationChannel() { m_name = string.Empty; m_meshKeys = new List(); } void IMarshalable.ToNative(IntPtr thisPtr, out AiMeshAnim nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.NumKeys = (uint)MeshKeyCount; nativeValue.Keys = IntPtr.Zero; if (nativeValue.NumKeys != 0) { nativeValue.Keys = MemoryHelper.ToNativeArray(m_meshKeys.ToArray()); } } void IMarshalable.FromNative(in AiMeshAnim nativeValue) { m_name = AiString.GetString(in nativeValue.Name); m_meshKeys.Clear(); if (nativeValue.NumKeys != 0 && nativeValue.Keys != IntPtr.Zero) { m_meshKeys.AddRange(MemoryHelper.FromNativeArray(nativeValue.Keys, (int)nativeValue.NumKeys)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiMeshAnim aiMeshAnim = MemoryHelper.Read(nativeValue); if (aiMeshAnim.NumKeys != 0 && aiMeshAnim.Keys != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMeshAnim.Keys); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public struct MeshKey : IEquatable { public double Time; public int Value; public MeshKey(double time, int index) { Time = time; Value = index; } public static bool operator ==(MeshKey a, MeshKey b) { return a.Value == b.Value; } public static bool operator !=(MeshKey a, MeshKey b) { return a.Value != b.Value; } public static bool operator <(MeshKey a, MeshKey b) { return a.Time < b.Time; } public static bool operator >(MeshKey a, MeshKey b) { return a.Time > b.Time; } public override bool Equals(object obj) { if (obj is MeshKey) { return Equals((MeshKey)obj); } return false; } public bool Equals(MeshKey key) { return Value == key.Value; } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{Time:{0} Index:{1}}}", new object[2] { Time.ToString(currentCulture), Value.ToString(currentCulture) }); } } public sealed class MeshMorphAnimationChannel : IMarshalable { private string m_name; private List m_meshMorphkeys; public string Name { get { return m_name; } set { m_name = value; } } public int MeshMorphKeyCount => m_meshMorphkeys.Count; public bool HasMeshMorphKeys => m_meshMorphkeys.Count > 0; public List MeshMorphKeys => m_meshMorphkeys; bool IMarshalable.IsNativeBlittable => true; public MeshMorphAnimationChannel() { m_name = string.Empty; m_meshMorphkeys = new List(); } void IMarshalable.FromNative(in AiMeshMorphAnim nativeValue) { m_name = AiString.GetString(in nativeValue.Name); m_meshMorphkeys.Clear(); if (nativeValue.NumKeys != 0 && nativeValue.Keys != IntPtr.Zero) { m_meshMorphkeys.AddRange(MemoryHelper.FromNativeArray(nativeValue.Keys, (int)nativeValue.NumKeys, arrayOfPointers: false)); } } void IMarshalable.ToNative(IntPtr thisPtr, out AiMeshMorphAnim nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.NumKeys = (uint)MeshMorphKeyCount; nativeValue.Keys = IntPtr.Zero; if (nativeValue.NumKeys != 0) { nativeValue.Keys = MemoryHelper.ToNativeArray(m_meshMorphkeys.ToArray(), arrayOfPointers: false); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiMeshMorphAnim aiMeshMorphAnim = MemoryHelper.Read(nativeValue); if (aiMeshMorphAnim.NumKeys != 0 && aiMeshMorphAnim.Keys != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiMeshMorphAnim.Keys, (int)aiMeshMorphAnim.NumKeys, MeshMorphKey.FreeNative, arrayOfPointers: false); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public sealed class MeshMorphKey : IMarshalable { private double m_time; private List m_values; private List m_weights; public double Time { get { return m_time; } set { m_time = value; } } public List Values => m_values; public List Weights => m_weights; bool IMarshalable.IsNativeBlittable => true; public MeshMorphKey() { m_time = 0.0; m_values = new List(); m_weights = new List(); } void IMarshalable.ToNative(IntPtr thisPtr, out AiMeshMorphKey nativeValue) { nativeValue.Time = m_time; nativeValue.NumValuesAndWeights = (uint)m_weights.Count; nativeValue.Values = IntPtr.Zero; nativeValue.Weights = IntPtr.Zero; Debug.Assert(m_weights.Count == m_values.Count); if (m_weights.Count == m_values.Count) { if (m_weights.Count > 0) { nativeValue.Values = MemoryHelper.ToNativeArray(m_values.ToArray()); nativeValue.Weights = MemoryHelper.ToNativeArray(m_weights.ToArray()); } } else { nativeValue.NumValuesAndWeights = 0u; } } void IMarshalable.FromNative(in AiMeshMorphKey nativeValue) { m_time = nativeValue.Time; m_values.Clear(); m_weights.Clear(); if (nativeValue.NumValuesAndWeights != 0) { if (nativeValue.Values != IntPtr.Zero) { m_values.AddRange(MemoryHelper.FromNativeArray(nativeValue.Values, (int)nativeValue.NumValuesAndWeights)); } if (nativeValue.Weights != IntPtr.Zero) { m_weights.AddRange(MemoryHelper.FromNativeArray(nativeValue.Weights, (int)nativeValue.NumValuesAndWeights)); } } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (nativeValue == IntPtr.Zero) { return; } AiMeshMorphKey aiMeshMorphKey = MemoryHelper.Read(nativeValue); if (aiMeshMorphKey.NumValuesAndWeights != 0) { if (aiMeshMorphKey.Values != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMeshMorphKey.Values); } if (aiMeshMorphKey.Weights != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMeshMorphKey.Weights); } } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } public sealed class Metadata : Dictionary, IMarshalable { public struct Entry : IEquatable { private MetaDataType m_dataType; private object m_data; public MetaDataType DataType => m_dataType; public object Data => m_data; public Entry(MetaDataType dataType, object data) { m_dataType = dataType; m_data = data; } public static bool operator ==(Entry a, Entry b) { return a.Equals(b); } public static bool operator !=(Entry a, Entry b) { return !a.Equals(b); } public T? DataAs() where T : struct { Type type = null; switch (m_dataType) { case MetaDataType.Bool: type = typeof(bool); break; case MetaDataType.Float: type = typeof(float); break; case MetaDataType.Double: type = typeof(double); break; case MetaDataType.Int32: type = typeof(int); break; case MetaDataType.String: type = typeof(string); break; case MetaDataType.UInt64: type = typeof(ulong); break; case MetaDataType.Vector3D: type = typeof(Vector3D); break; } if (type == typeof(T)) { return (T)m_data; } return null; } public override bool Equals(object obj) { if (obj is Entry) { return Equals((Entry)obj); } return false; } public bool Equals(Entry other) { if (other.DataType != DataType) { return false; } return object.Equals(other.Data, Data); } public override int GetHashCode() { int num = 17; num = num * 31 + m_data.GetHashCode(); return num * 31 + ((m_data != null) ? m_data.GetHashCode() : 0); } public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "DataType: {0}, Data: {1}", new object[2] { m_dataType.ToString(), (m_data == null) ? "null" : m_data.ToString() }); } } bool IMarshalable.IsNativeBlittable => true; void IMarshalable.ToNative(IntPtr thisPtr, out AiMetadata nativeValue) { nativeValue = default(AiMetadata); nativeValue.NumProperties = (uint)base.Count; AiString[] array = new AiString[base.Count]; AiMetadataEntry[] array2 = new AiMetadataEntry[base.Count]; int num = 0; using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { KeyValuePair current = enumerator.Current; AiMetadataEntry aiMetadataEntry = new AiMetadataEntry { DataType = current.Value.DataType }; switch (current.Value.DataType) { case MetaDataType.Bool: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(1); bool data7 = (bool)current.Value.Data; MemoryHelper.Write(aiMetadataEntry.Data, in data7); break; } case MetaDataType.Float: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(4); float data6 = (float)current.Value.Data; MemoryHelper.Write(aiMetadataEntry.Data, in data6); break; } case MetaDataType.Double: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(8); double data5 = (double)current.Value.Data; MemoryHelper.Write(aiMetadataEntry.Data, in data5); break; } case MetaDataType.Int32: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(4); int data4 = (int)current.Value.Data; MemoryHelper.Write(aiMetadataEntry.Data, in data4); break; } case MetaDataType.String: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(MemoryHelper.SizeOf()); AiString data3 = new AiString(current.Value.Data as string); MemoryHelper.Write(aiMetadataEntry.Data, in data3); break; } case MetaDataType.UInt64: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(8); ulong data2 = (ulong)current.Value.Data; MemoryHelper.Write(aiMetadataEntry.Data, in data2); break; } case MetaDataType.Vector3D: { aiMetadataEntry.Data = MemoryHelper.AllocateMemory(MemoryHelper.SizeOf()); Vector3D data = (Vector3D)current.Value.Data; MemoryHelper.Write(aiMetadataEntry.Data, in data); break; } } array[num] = new AiString(current.Key); array2[num] = aiMetadataEntry; num++; } } nativeValue.keys = MemoryHelper.ToNativeArray(array); nativeValue.Values = MemoryHelper.ToNativeArray(array2); } void IMarshalable.FromNative(in AiMetadata nativeValue) { Clear(); if (nativeValue.NumProperties == 0 || nativeValue.keys == IntPtr.Zero || nativeValue.Values == IntPtr.Zero) { return; } AiString[] array = MemoryHelper.FromNativeArray(nativeValue.keys, (int)nativeValue.NumProperties); AiMetadataEntry[] array2 = MemoryHelper.FromNativeArray(nativeValue.Values, (int)nativeValue.NumProperties); for (int i = 0; i < nativeValue.NumProperties; i++) { string text = array[i].GetString(); AiMetadataEntry aiMetadataEntry = array2[i]; if (!string.IsNullOrEmpty(text) && !(aiMetadataEntry.Data == IntPtr.Zero)) { object obj = null; switch (aiMetadataEntry.DataType) { case MetaDataType.Bool: obj = MemoryHelper.Read(aiMetadataEntry.Data); break; case MetaDataType.Float: obj = MemoryHelper.Read(aiMetadataEntry.Data); break; case MetaDataType.Double: obj = MemoryHelper.Read(aiMetadataEntry.Data); break; case MetaDataType.Int32: obj = MemoryHelper.Read(aiMetadataEntry.Data); break; case MetaDataType.String: obj = MemoryHelper.Read(aiMetadataEntry.Data).GetString(); break; case MetaDataType.UInt64: obj = MemoryHelper.Read(aiMetadataEntry.Data); break; case MetaDataType.Vector3D: obj = MemoryHelper.Read(aiMetadataEntry.Data); break; } if (obj != null) { Add(text, new Entry(aiMetadataEntry.DataType, obj)); } } } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (nativeValue == IntPtr.Zero) { return; } AiMetadata aiMetadata = MemoryHelper.MarshalStructure(nativeValue); if (aiMetadata.keys != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMetadata.keys); } if (aiMetadata.Values != IntPtr.Zero) { AiMetadataEntry[] array = MemoryHelper.FromNativeArray(aiMetadata.Values, (int)aiMetadata.NumProperties); AiMetadataEntry[] array2 = array; for (int i = 0; i < array2.Length; i++) { AiMetadataEntry aiMetadataEntry = array2[i]; if (aiMetadataEntry.Data != IntPtr.Zero) { MemoryHelper.FreeMemory(aiMetadataEntry.Data); } } MemoryHelper.FreeMemory(aiMetadata.Values); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } [AttributeUsage(AttributeTargets.Struct)] public sealed class NativeCustomMarshalerAttribute : Attribute { private INativeCustomMarshaler m_marshaler; public INativeCustomMarshaler Marshaler => m_marshaler; public NativeCustomMarshalerAttribute(Type type) { if (type == null) { throw new NullReferenceException("type"); } if (!PlatformHelper.IsAssignable(typeof(INativeCustomMarshaler), type)) { throw new ArgumentException($"{type.FullName} does not implement INativeCustomMarshaler."); } m_marshaler = Activator.CreateInstance(type) as INativeCustomMarshaler; } } public sealed class Node : IMarshalable { private string m_name; private Matrix4x4 m_transform; private Node m_parent; private NodeCollection m_children; private List m_meshes; private Metadata m_metaData; public string Name { get { return m_name; } set { m_name = value; } } public Matrix4x4 Transform { get { return m_transform; } set { m_transform = value; } } public Node Parent => m_parent; public int ChildCount => m_children.Count; public bool HasChildren => m_children.Count > 0; public NodeCollection Children => m_children; public int MeshCount => m_meshes.Count; public bool HasMeshes => m_meshes.Count > 0; public List MeshIndices => m_meshes; public Metadata Metadata => m_metaData; bool IMarshalable.IsNativeBlittable => true; public Node() { m_name = string.Empty; m_transform = Matrix4x4.Identity; m_parent = null; m_children = new NodeCollection(this); m_meshes = new List(); m_metaData = new Metadata(); } public Node(string name) : this() { m_name = name; } public Node(string name, Node parent) : this() { m_name = name; m_parent = parent; } internal void SetParent(Node parent) { m_parent = parent; } public Node FindNode(string name) { if (name.Equals(m_name)) { return this; } if (HasChildren) { foreach (Node child in m_children) { Node node = child.FindNode(name); if (node != null) { return node; } } } return null; } private IntPtr ToNativeRecursive(IntPtr parentPtr, Node node) { if (node == null) { return IntPtr.Zero; } int sizeInBytes = MemoryHelper.SizeOf(); IntPtr intPtr = MemoryHelper.AllocateMemory(sizeInBytes); AiNode data = default(AiNode); data.Name = new AiString(node.m_name); data.Transformation = node.m_transform; data.Parent = parentPtr; data.NumMeshes = (uint)node.m_meshes.Count; data.Meshes = MemoryHelper.ToNativeArray(node.m_meshes.ToArray()); data.MetaData = IntPtr.Zero; if (m_metaData.Count > 0) { data.MetaData = MemoryHelper.ToNativePointer(m_metaData); } data.NumChildren = (uint)node.m_children.Count; int numChildren = (int)data.NumChildren; int size = IntPtr.Size; IntPtr intPtr2 = IntPtr.Zero; if (numChildren > 0) { intPtr2 = MemoryHelper.AllocateMemory(numChildren * IntPtr.Size); for (int i = 0; i < numChildren; i++) { IntPtr pDest = MemoryHelper.AddIntPtr(intPtr2, size * i); Node node2 = node.m_children[i]; IntPtr data2 = IntPtr.Zero; if (node2 != null) { data2 = ToNativeRecursive(intPtr, node2); } MemoryHelper.Write(pDest, in data2); } } data.Children = intPtr2; MemoryHelper.Write(intPtr, in data); return intPtr; } void IMarshalable.ToNative(IntPtr thisPtr, out AiNode nativeValue) { nativeValue.Name = new AiString(m_name); nativeValue.Transformation = m_transform; nativeValue.Parent = IntPtr.Zero; nativeValue.NumMeshes = (uint)m_meshes.Count; nativeValue.Meshes = IntPtr.Zero; nativeValue.MetaData = IntPtr.Zero; if (m_metaData.Count > 0) { nativeValue.MetaData = MemoryHelper.ToNativePointer(m_metaData); } if (nativeValue.NumMeshes != 0) { nativeValue.Meshes = MemoryHelper.ToNativeArray(m_meshes.ToArray()); } nativeValue.NumChildren = (uint)m_children.Count; int numChildren = (int)nativeValue.NumChildren; int size = IntPtr.Size; IntPtr intPtr = IntPtr.Zero; if (numChildren > 0) { intPtr = MemoryHelper.AllocateMemory(numChildren * IntPtr.Size); for (int i = 0; i < numChildren; i++) { IntPtr pDest = MemoryHelper.AddIntPtr(intPtr, size * i); Node node = m_children[i]; IntPtr data = IntPtr.Zero; if (node != null) { data = ToNativeRecursive(thisPtr, node); } MemoryHelper.Write(pDest, in data); } } nativeValue.Children = intPtr; } void IMarshalable.FromNative(in AiNode nativeValue) { m_name = AiString.GetString(in nativeValue.Name); m_transform = nativeValue.Transformation; m_parent = null; m_children.Clear(); m_meshes.Clear(); m_metaData.Clear(); if (nativeValue.MetaData != IntPtr.Zero) { Metadata metadata = MemoryHelper.FromNativePointer(nativeValue.MetaData); foreach (KeyValuePair item in metadata) { m_metaData.Add(item.Key, item.Value); } } if (nativeValue.NumMeshes != 0 && nativeValue.Meshes != IntPtr.Zero) { m_meshes.AddRange(MemoryHelper.FromNativeArray(nativeValue.Meshes, (int)nativeValue.NumMeshes)); } if (nativeValue.NumChildren != 0 && nativeValue.Children != IntPtr.Zero) { m_children.AddRange(MemoryHelper.FromNativeArray(nativeValue.Children, (int)nativeValue.NumChildren, arrayOfPointers: true)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiNode aiNode = MemoryHelper.Read(nativeValue); if (aiNode.NumMeshes != 0 && aiNode.Meshes != IntPtr.Zero) { MemoryHelper.FreeMemory(aiNode.Meshes); } if (aiNode.NumChildren != 0 && aiNode.Children != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiNode.Children, (int)aiNode.NumChildren, FreeNative, arrayOfPointers: true); } if (aiNode.MetaData != IntPtr.Zero) { Metadata.FreeNative(aiNode.MetaData, freeNative: true); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public sealed class NodeAnimationChannel : IMarshalable { private string m_nodeName; private List m_positionKeys; private List m_rotationKeys; private List m_scalingKeys; private AnimationBehaviour m_preState; private AnimationBehaviour m_postState; public string NodeName { get { return m_nodeName; } set { m_nodeName = value; } } public int PositionKeyCount => m_positionKeys.Count; public bool HasPositionKeys => m_positionKeys.Count > 0; public List PositionKeys => m_positionKeys; public int RotationKeyCount => m_rotationKeys.Count; public bool HasRotationKeys => m_rotationKeys.Count > 0; public List RotationKeys => m_rotationKeys; public int ScalingKeyCount => m_scalingKeys.Count; public bool HasScalingKeys => m_scalingKeys.Count > 0; public List ScalingKeys => m_scalingKeys; public AnimationBehaviour PreState { get { return m_preState; } set { m_preState = value; } } public AnimationBehaviour PostState { get { return m_postState; } set { m_postState = value; } } bool IMarshalable.IsNativeBlittable => true; public NodeAnimationChannel() { m_nodeName = string.Empty; m_preState = AnimationBehaviour.Default; m_postState = AnimationBehaviour.Default; m_positionKeys = new List(); m_rotationKeys = new List(); m_scalingKeys = new List(); } void IMarshalable.ToNative(IntPtr thisPtr, out AiNodeAnim nativeValue) { nativeValue.NodeName = new AiString(m_nodeName); nativeValue.Prestate = m_preState; nativeValue.PostState = m_postState; nativeValue.NumPositionKeys = (uint)m_positionKeys.Count; nativeValue.PositionKeys = IntPtr.Zero; if (nativeValue.NumPositionKeys != 0) { nativeValue.PositionKeys = MemoryHelper.ToNativeArray(m_positionKeys.ToArray()); } nativeValue.NumRotationKeys = (uint)m_rotationKeys.Count; nativeValue.RotationKeys = IntPtr.Zero; if (nativeValue.NumRotationKeys != 0) { nativeValue.RotationKeys = MemoryHelper.ToNativeArray(m_rotationKeys.ToArray()); } nativeValue.NumScalingKeys = (uint)m_scalingKeys.Count; nativeValue.ScalingKeys = IntPtr.Zero; if (nativeValue.NumScalingKeys != 0) { nativeValue.ScalingKeys = MemoryHelper.ToNativeArray(m_scalingKeys.ToArray()); } } void IMarshalable.FromNative(in AiNodeAnim nativeValue) { m_nodeName = nativeValue.NodeName.GetString(); m_preState = nativeValue.Prestate; m_postState = nativeValue.PostState; m_positionKeys.Clear(); m_rotationKeys.Clear(); m_scalingKeys.Clear(); if (nativeValue.NumPositionKeys != 0 && nativeValue.PositionKeys != IntPtr.Zero) { m_positionKeys.AddRange(MemoryHelper.FromNativeArray(nativeValue.PositionKeys, (int)nativeValue.NumPositionKeys)); } if (nativeValue.NumRotationKeys != 0 && nativeValue.RotationKeys != IntPtr.Zero) { m_rotationKeys.AddRange(MemoryHelper.FromNativeArray(nativeValue.RotationKeys, (int)nativeValue.NumRotationKeys)); } if (nativeValue.NumScalingKeys != 0 && nativeValue.ScalingKeys != IntPtr.Zero) { m_scalingKeys.AddRange(MemoryHelper.FromNativeArray(nativeValue.ScalingKeys, (int)nativeValue.NumScalingKeys)); } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiNodeAnim aiNodeAnim = MemoryHelper.Read(nativeValue); if (aiNodeAnim.NumPositionKeys != 0 && aiNodeAnim.PositionKeys != IntPtr.Zero) { MemoryHelper.FreeMemory(aiNodeAnim.PositionKeys); } if (aiNodeAnim.NumRotationKeys != 0 && aiNodeAnim.RotationKeys != IntPtr.Zero) { MemoryHelper.FreeMemory(aiNodeAnim.RotationKeys); } if (aiNodeAnim.NumScalingKeys != 0 && aiNodeAnim.ScalingKeys != IntPtr.Zero) { MemoryHelper.FreeMemory(aiNodeAnim.ScalingKeys); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public sealed class NodeCollection : IList, ICollection, IEnumerable, IEnumerable { private Node m_parent; private List m_children; public int Count => m_children.Count; public Node this[int index] { get { if (index < 0 || index > Count) { return null; } return m_children[index]; } set { if (index >= 0 && index <= Count && value != null) { m_children[index] = value; value.SetParent(m_parent); } } } public bool IsReadOnly => false; internal NodeCollection(Node parent) { m_parent = parent; m_children = new List(); } public void Add(Node item) { if (item != null) { m_children.Add(item); item.SetParent(m_parent); } } public void AddRange(Node[] items) { if (items == null || items.Length == 0) { return; } foreach (Node node in items) { if (node != null) { m_children.Add(node); node.SetParent(m_parent); } } } public void Clear() { foreach (Node child in m_children) { child.SetParent(null); } m_children.Clear(); } public bool Contains(Node item) { return m_children.Contains(item); } public void CopyTo(Node[] array, int arrayIndex) { m_children.CopyTo(array, arrayIndex); } public int IndexOf(Node item) { return m_children.IndexOf(item); } public void Insert(int index, Node item) { if (index >= 0 && index <= Count && item != null) { m_children.Insert(index, item); item.SetParent(m_parent); } } public void RemoveAt(int index) { Node node = this[index]; if (node == null) { node.SetParent(null); m_children.RemoveAt(index); } } public bool Remove(Node item) { if (item != null && m_children.Remove(item)) { item.SetParent(null); return true; } return false; } public Node[] ToArray() { return m_children.ToArray(); } public IEnumerator GetEnumerator() { return m_children.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return m_children.GetEnumerator(); } } public struct Plane { public float A; public float B; public float C; public float D; public Plane(float a, float b, float c, float d) { A = a; B = b; C = c; D = d; } } public static class PostProcessPreset { public static PostProcessSteps ConvertToLeftHanded => PostProcessSteps.MakeLeftHanded | PostProcessSteps.FlipUVs | PostProcessSteps.FlipWindingOrder; public static PostProcessSteps TargetRealTimeFast => PostProcessSteps.CalculateTangentSpace | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.Triangulate | PostProcessSteps.GenerateNormals | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.GenerateUVCoords; public static PostProcessSteps TargetRealTimeQuality => PostProcessSteps.CalculateTangentSpace | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.Triangulate | PostProcessSteps.GenerateSmoothNormals | PostProcessSteps.SplitLargeMeshes | PostProcessSteps.LimitBoneWeights | PostProcessSteps.RemoveRedundantMaterials | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.FindDegenerates | PostProcessSteps.FindInvalidData | PostProcessSteps.GenerateUVCoords; public static PostProcessSteps TargetRealTimeMaximumQuality => TargetRealTimeQuality | PostProcessSteps.FindInstances | PostProcessSteps.ValidateDataStructure | PostProcessSteps.OptimizeMeshes; } public struct Quaternion : IEquatable { public float W; public float X; public float Y; public float Z; public Quaternion(float w, float x, float y, float z) { W = w; X = x; Y = y; Z = z; } public Quaternion(Matrix3x3 matrix) { float num = matrix.A1 + matrix.B2 + matrix.C3; if (num > 0f) { float num2 = (float)Math.Sqrt(num + 1f) * 2f; W = 0.25f * num2; X = (matrix.C2 - matrix.B3) / num2; Y = (matrix.A3 - matrix.C1) / num2; Z = (matrix.B1 - matrix.A2) / num2; } else if (matrix.A1 > matrix.B2 && matrix.A1 > matrix.C3) { float num3 = (float)Math.Sqrt(1.0 + (double)matrix.A1 - (double)matrix.B2 - (double)matrix.C3) * 2f; W = (matrix.C2 - matrix.B3) / num3; X = 0.25f * num3; Y = (matrix.A2 + matrix.B1) / num3; Z = (matrix.A3 + matrix.C1) / num3; } else if (matrix.B2 > matrix.C3) { float num4 = (float)Math.Sqrt(1f + matrix.B2 - matrix.A1 - matrix.C3) * 2f; W = (matrix.A3 - matrix.C1) / num4; X = (matrix.A2 + matrix.B1) / num4; Y = 0.25f * num4; Z = (matrix.B3 + matrix.C2) / num4; } else { float num5 = (float)Math.Sqrt(1f + matrix.C3 - matrix.A1 - matrix.B2) * 2f; W = (matrix.B1 - matrix.A2) / num5; X = (matrix.A3 + matrix.C1) / num5; Y = (matrix.B3 + matrix.C2) / num5; Z = 0.25f * num5; } Normalize(); } public Quaternion(float pitch, float yaw, float roll) { float num = (float)Math.Sin(pitch * 0.5f); float num2 = (float)Math.Cos(pitch * 0.5f); float num3 = (float)Math.Sin(yaw * 0.5f); float num4 = (float)Math.Cos(yaw * 0.5f); float num5 = (float)Math.Sin(roll * 0.5f); float num6 = (float)Math.Cos(roll * 0.5f); float num7 = num2 * num4; float num8 = num * num3; X = num5 * num7 - num6 * num8; Y = num6 * num * num4 + num5 * num2 * num3; Z = num6 * num2 * num3 - num5 * num * num4; W = num6 * num7 + num5 * num8; } public Quaternion(Vector3D axis, float angle) { axis.Normalize(); float num = angle * 0.5f; float num2 = (float)Math.Sin(num); float w = (float)Math.Cos(num); X = axis.X * num2; Y = axis.Y * num2; Z = axis.Z * num2; W = w; } public void Normalize() { float num = X * X + Y * Y + Z * Z + W * W; if (num != 0f) { X /= num; Y /= num; Z /= num; W /= num; } } public void Conjugate() { X = 0f - X; Y = 0f - Y; Z = 0f - Z; } public Matrix3x3 GetMatrix() { float num = X * X; float num2 = Y * Y; float num3 = Z * Z; float num4 = X * Y; float num5 = Z * W; float num6 = Z * X; float num7 = Y * W; float num8 = Y * Z; float num9 = X * W; Matrix3x3 result = default(Matrix3x3); result.A1 = 1f - 2f * (num2 + num3); result.B1 = 2f * (num4 + num5); result.C1 = 2f * (num6 - num7); result.A2 = 2f * (num4 - num5); result.B2 = 1f - 2f * (num3 + num); result.C2 = 2f * (num8 + num9); result.A3 = 2f * (num6 + num7); result.B3 = 2f * (num8 - num9); result.C3 = 1f - 2f * (num2 + num); return result; } public static Quaternion Slerp(Quaternion start, Quaternion end, float factor) { float num = start.X * end.X + start.Y * end.Y + start.Z * end.Z + start.W * end.W; if (num < 0f) { num = 0f - num; end.X = 0f - end.X; end.Y = 0f - end.Y; end.Z = 0f - end.Z; end.W = 0f - end.W; } float num4; float num5; if (1f - num > 0.0001f) { float num2 = (float)Math.Acos(num); float num3 = (float)Math.Sin(num2); num4 = (float)Math.Sin((1f - factor) * num2) / num3; num5 = (float)Math.Sin(factor * num2) / num3; } else { num4 = 1f - factor; num5 = factor; } Quaternion result = default(Quaternion); result.X = num4 * start.X + num5 * end.X; result.Y = num4 * start.Y + num5 * end.Y; result.Z = num4 * start.Z + num5 * end.Z; result.W = num4 * start.W + num5 * end.W; return result; } public static Vector3D Rotate(Vector3D pt, Quaternion quat) { float num = quat.X + quat.X; float num2 = quat.Y + quat.Y; float num3 = quat.Z + quat.Z; float num4 = quat.W * num; float num5 = quat.W * num2; float num6 = quat.W * num3; float num7 = quat.X * num; float num8 = quat.X * num2; float num9 = quat.X * num3; float num10 = quat.Y * num2; float num11 = quat.Y * num3; float num12 = quat.Z * num3; float x = pt.X * (1f - num10 - num12) + pt.Y * (num8 - num6) + pt.Z * (num9 + num5); float y = pt.X * (num8 + num6) + pt.Y * (1f - num7 - num12) + pt.Z * (num11 - num4); float z = pt.X * (num9 - num5) + pt.Y * (num11 + num4) + pt.Z * (1f - num7 - num10); Vector3D result = default(Vector3D); result.X = x; result.Y = y; result.Z = z; return result; } public static Quaternion operator *(Quaternion a, Quaternion b) { Quaternion result = default(Quaternion); result.W = a.W * b.W - a.X * b.X - a.Y * b.Y - a.Z * b.Z; result.X = a.W * b.X + a.X * b.W + a.Y * b.Z - a.Z * b.Y; result.Y = a.W * b.Y + a.Y * b.W + a.Z * b.X - a.X * b.Z; result.Z = a.W * b.Z + a.Z * b.W + a.X * b.Y - a.Y * b.X; return result; } public static bool operator ==(Quaternion a, Quaternion b) { return a.W == b.W && a.X == b.X && a.Y == b.Y && a.Z == b.Z; } public static bool operator !=(Quaternion a, Quaternion b) { return a.W != b.W || a.X != b.X || a.Y != b.Y || a.Z != b.Z; } public bool Equals(Quaternion other) { return W == other.W && X == other.X && Y == other.Y && Z == other.Z; } public override bool Equals(object obj) { if (obj is Quaternion) { return Equals((Quaternion)obj); } return false; } public override int GetHashCode() { return W.GetHashCode() + X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{W:{0} X:{1} Y:{2} Z:{3}}}", W.ToString(currentCulture), X.ToString(currentCulture), Y.ToString(currentCulture), Z.ToString(currentCulture)); } } public struct QuaternionKey : IEquatable { public double Time; public Quaternion Value; public QuaternionKey(double time, Quaternion rot) { Time = time; Value = rot; } public static bool operator ==(QuaternionKey a, QuaternionKey b) { return a.Value == b.Value; } public static bool operator !=(QuaternionKey a, QuaternionKey b) { return a.Value != b.Value; } public static bool operator <(QuaternionKey a, QuaternionKey b) { return a.Time < b.Time; } public static bool operator >(QuaternionKey a, QuaternionKey b) { return a.Time > b.Time; } public override bool Equals(object obj) { if (obj is QuaternionKey) { return Equals((QuaternionKey)obj); } return false; } public bool Equals(QuaternionKey key) { return Value == key.Value; } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{Time:{0} Rotation:{1}}}", new object[2] { Time.ToString(currentCulture), Value.ToString() }); } } public struct Ray { public Vector3D Position; public Vector3D Direction; public Ray(Vector3D pos, Vector3D dir) { Position = pos; Direction = dir; } } public sealed class Scene : IMarshalable { private SceneFlags m_flags; private Node m_rootNode; private List m_meshes; private List m_lights; private List m_cameras; private List m_textures; private List m_animations; private List m_materials; private Metadata m_metadata; public SceneFlags SceneFlags { get { return m_flags; } set { m_flags = value; } } public Node RootNode { get { return m_rootNode; } set { m_rootNode = value; } } public bool HasMeshes => m_meshes.Count > 0; public int MeshCount => m_meshes.Count; public List Meshes => m_meshes; public bool HasLights => m_lights.Count > 0; public int LightCount => m_lights.Count; public List Lights => m_lights; public bool HasCameras => m_cameras.Count > 0; public int CameraCount => m_cameras.Count; public List Cameras => m_cameras; public bool HasTextures => m_textures.Count > 0; public int TextureCount => m_textures.Count; public List Textures => m_textures; public bool HasAnimations => m_animations.Count > 0; public int AnimationCount => m_animations.Count; public List Animations => m_animations; public bool HasMaterials => m_materials.Count > 0; public int MaterialCount => m_materials.Count; public List Materials => m_materials; public Metadata Metadata => m_metadata; bool IMarshalable.IsNativeBlittable => true; public Scene() { m_flags = SceneFlags.None; m_rootNode = null; m_meshes = new List(); m_lights = new List(); m_cameras = new List(); m_textures = new List(); m_animations = new List(); m_materials = new List(); m_metadata = new Metadata(); } public void Clear() { m_rootNode = null; m_meshes.Clear(); m_lights.Clear(); m_cameras.Clear(); m_textures.Clear(); m_animations.Clear(); m_materials.Clear(); m_metadata.Clear(); } public EmbeddedTexture GetEmbeddedTexture(string fileName) { if (string.IsNullOrEmpty(fileName)) { return null; } if (fileName.StartsWith("*")) { string s = fileName.Substring(1); if (!int.TryParse(s, out var result) || result < 0 || result >= m_textures.Count) { return null; } return m_textures[result]; } string fileName2 = Path.GetFileName(fileName); foreach (EmbeddedTexture texture in m_textures) { if (texture != null) { string fileName3 = Path.GetFileName(texture.Filename); if (string.Equals(fileName2, fileName3, StringComparison.Ordinal)) { return texture; } } } return null; } public static IntPtr ToUnmanagedScene(Scene scene) { if (scene == null) { return IntPtr.Zero; } return MemoryHelper.ToNativePointer(scene); } public static Scene FromUnmanagedScene(IntPtr scenePtr) { if (scenePtr == IntPtr.Zero) { return null; } return MemoryHelper.FromNativePointer(scenePtr); } public static void FreeUnmanagedScene(IntPtr scenePtr) { if (!(scenePtr == IntPtr.Zero)) { FreeNative(scenePtr, freeNative: true); } } void IMarshalable.ToNative(IntPtr thisPtr, out AiScene nativeValue) { nativeValue.Flags = m_flags; nativeValue.Materials = IntPtr.Zero; nativeValue.RootNode = IntPtr.Zero; nativeValue.Meshes = IntPtr.Zero; nativeValue.Lights = IntPtr.Zero; nativeValue.Cameras = IntPtr.Zero; nativeValue.Textures = IntPtr.Zero; nativeValue.Animations = IntPtr.Zero; nativeValue.Metadata = IntPtr.Zero; nativeValue.PrivateData = IntPtr.Zero; nativeValue.NumMaterials = (uint)MaterialCount; nativeValue.NumMeshes = (uint)MeshCount; nativeValue.NumLights = (uint)LightCount; nativeValue.NumCameras = (uint)CameraCount; nativeValue.NumTextures = (uint)TextureCount; nativeValue.NumAnimations = (uint)AnimationCount; if (nativeValue.NumMaterials != 0) { nativeValue.Materials = MemoryHelper.ToNativeArray(m_materials.ToArray(), arrayOfPointers: true); } if (m_rootNode != null) { nativeValue.RootNode = MemoryHelper.ToNativePointer(m_rootNode); } if (nativeValue.NumMeshes != 0) { nativeValue.Meshes = MemoryHelper.ToNativeArray(m_meshes.ToArray(), arrayOfPointers: true); } if (nativeValue.NumLights != 0) { nativeValue.Lights = MemoryHelper.ToNativeArray(m_lights.ToArray(), arrayOfPointers: true); } if (nativeValue.NumCameras != 0) { nativeValue.Cameras = MemoryHelper.ToNativeArray(m_cameras.ToArray(), arrayOfPointers: true); } if (nativeValue.NumTextures != 0) { nativeValue.Textures = MemoryHelper.ToNativeArray(m_textures.ToArray(), arrayOfPointers: true); } if (nativeValue.NumAnimations != 0) { nativeValue.Animations = MemoryHelper.ToNativeArray(m_animations.ToArray(), arrayOfPointers: true); } if (m_metadata.Count > 0) { nativeValue.Metadata = MemoryHelper.ToNativePointer(m_metadata); } } void IMarshalable.FromNative(in AiScene nativeValue) { Clear(); m_flags = nativeValue.Flags; if (nativeValue.NumMaterials != 0 && nativeValue.Materials != IntPtr.Zero) { m_materials.AddRange(MemoryHelper.FromNativeArray(nativeValue.Materials, (int)nativeValue.NumMaterials, arrayOfPointers: true)); } if (nativeValue.RootNode != IntPtr.Zero) { m_rootNode = MemoryHelper.FromNativePointer(nativeValue.RootNode); } if (nativeValue.NumMeshes != 0 && nativeValue.Meshes != IntPtr.Zero) { m_meshes.AddRange(MemoryHelper.FromNativeArray(nativeValue.Meshes, (int)nativeValue.NumMeshes, arrayOfPointers: true)); } if (nativeValue.NumLights != 0 && nativeValue.Lights != IntPtr.Zero) { m_lights.AddRange(MemoryHelper.FromNativeArray(nativeValue.Lights, (int)nativeValue.NumLights, arrayOfPointers: true)); } if (nativeValue.NumCameras != 0 && nativeValue.Cameras != IntPtr.Zero) { m_cameras.AddRange(MemoryHelper.FromNativeArray(nativeValue.Cameras, (int)nativeValue.NumCameras, arrayOfPointers: true)); } if (nativeValue.NumTextures != 0 && nativeValue.Textures != IntPtr.Zero) { m_textures.AddRange(MemoryHelper.FromNativeArray(nativeValue.Textures, (int)nativeValue.NumTextures, arrayOfPointers: true)); } if (nativeValue.NumAnimations != 0 && nativeValue.Animations != IntPtr.Zero) { m_animations.AddRange(MemoryHelper.FromNativeArray(nativeValue.Animations, (int)nativeValue.NumAnimations, arrayOfPointers: true)); } if (nativeValue.Metadata != IntPtr.Zero) { m_metadata = MemoryHelper.FromNativePointer(nativeValue.Metadata); if (m_metadata == null) { m_metadata = new Metadata(); } } } public static void FreeNative(IntPtr nativeValue, bool freeNative) { if (!(nativeValue == IntPtr.Zero)) { AiScene aiScene = MemoryHelper.Read(nativeValue); if (aiScene.NumMaterials != 0 && aiScene.Materials != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiScene.Materials, (int)aiScene.NumMaterials, Material.FreeNative, arrayOfPointers: true); } if (aiScene.RootNode != IntPtr.Zero) { Node.FreeNative(aiScene.RootNode, freeNative: true); } if (aiScene.NumMeshes != 0 && aiScene.Meshes != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiScene.Meshes, (int)aiScene.NumMeshes, Mesh.FreeNative, arrayOfPointers: true); } if (aiScene.NumLights != 0 && aiScene.Lights != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiScene.Lights, (int)aiScene.NumLights, Light.FreeNative, arrayOfPointers: true); } if (aiScene.NumCameras != 0 && aiScene.Cameras != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiScene.Cameras, (int)aiScene.NumCameras, Camera.FreeNative, arrayOfPointers: true); } if (aiScene.NumTextures != 0 && aiScene.Textures != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiScene.Textures, (int)aiScene.NumTextures, EmbeddedTexture.FreeNative, arrayOfPointers: true); } if (aiScene.NumAnimations != 0 && aiScene.Animations != IntPtr.Zero) { MemoryHelper.FreeNativeArray(aiScene.Animations, (int)aiScene.NumAnimations, Animation.FreeNative, arrayOfPointers: true); } if (aiScene.Metadata != IntPtr.Zero) { Metadata.FreeNative(aiScene.Metadata, freeNative: true); } if (freeNative) { MemoryHelper.FreeMemory(nativeValue); } } } } public struct Texel : IEquatable { public byte B; public byte G; public byte R; public byte A; public Texel(byte b, byte g, byte r, byte a) { B = b; G = g; R = r; A = a; } public static bool operator ==(Texel a, Texel b) { return a.B == b.B && a.G == b.G && a.R == b.R && a.A == b.A; } public static bool operator !=(Texel a, Texel b) { return a.B != b.B && a.G != b.G && a.R != b.R && a.A != b.A; } public static implicit operator Color4D(Texel texel) { return new Color4D((float)(int)texel.R / 255f, (float)(int)texel.G / 255f, (float)(int)texel.B / 255f, (float)(int)texel.A / 255f); } public override bool Equals(object obj) { if (obj is Texel) { return Equals((Texel)obj); } return false; } public bool Equals(Texel other) { return B == other.B && G == other.G && R == other.R && A == other.A; } public override int GetHashCode() { return B.GetHashCode() + G.GetHashCode() + R.GetHashCode() + A.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{B:{0} G:{1} R:{2} A:{3}}}", B.ToString(currentCulture), G.ToString(currentCulture), R.ToString(currentCulture), A.ToString(currentCulture)); } } public struct TextureSlot { public string FilePath; public TextureType TextureType; public int TextureIndex; public TextureMapping Mapping; public int UVIndex; public float BlendFactor; public TextureOperation Operation; public TextureWrapMode WrapModeU; public TextureWrapMode WrapModeV; public int Flags; public TextureSlot(string filePath, TextureType typeSemantic, int texIndex, TextureMapping mapping, int uvIndex, float blendFactor, TextureOperation texOp, TextureWrapMode wrapModeU, TextureWrapMode wrapModeV, int flags) { FilePath = ((filePath == null) ? string.Empty : filePath); TextureType = typeSemantic; TextureIndex = texIndex; Mapping = mapping; UVIndex = uvIndex; BlendFactor = blendFactor; Operation = texOp; WrapModeU = wrapModeU; WrapModeV = wrapModeV; Flags = flags; } } public struct UVTransform { public Vector2D Translation; public Vector2D Scaling; public float Rotation; } public struct Vector2D : IEquatable { public float X; public float Y; public float this[int index] { get { return index switch { 0 => X, 1 => Y, _ => 0f, }; } set { switch (index) { case 0: X = value; break; case 1: Y = value; break; } } } public Vector2D(float x, float y) { X = x; Y = y; } public Vector2D(float value) { X = value; Y = value; } public void Set(float x, float y) { X = x; Y = y; } public float Length() { return (float)Math.Sqrt(LengthSquared()); } public float LengthSquared() { return X * X + Y * Y; } public void Normalize() { float num = 1f / (float)Math.Sqrt(X * X + Y * Y); X *= num; Y *= num; } public void Negate() { X = 0f - X; Y = 0f - Y; } public static Vector2D operator +(Vector2D a, Vector2D b) { Vector2D result = default(Vector2D); result.X = a.X + b.X; result.Y = a.Y + b.Y; return result; } public static Vector2D operator -(Vector2D a, Vector2D b) { Vector2D result = default(Vector2D); result.X = a.X - b.X; result.Y = a.Y - b.Y; return result; } public static Vector2D operator *(Vector2D a, Vector2D b) { Vector2D result = default(Vector2D); result.X = a.X * b.X; result.Y = a.Y * b.Y; return result; } public static Vector2D operator *(Vector2D value, float scale) { Vector2D result = default(Vector2D); result.X = value.X * scale; result.Y = value.Y * scale; return result; } public static Vector2D operator *(float scale, Vector2D value) { Vector2D result = default(Vector2D); result.X = value.X * scale; result.Y = value.Y * scale; return result; } public static Vector2D operator /(Vector2D a, Vector2D b) { Vector2D result = default(Vector2D); result.X = a.X / b.X; result.Y = a.Y / b.Y; return result; } public static Vector2D operator /(Vector2D value, float divisor) { float num = 1f / divisor; Vector2D result = default(Vector2D); result.X = value.X * num; result.Y = value.Y * num; return result; } public static Vector2D operator -(Vector2D value) { Vector2D result = default(Vector2D); result.X = 0f - value.X; result.Y = 0f - value.Y; return result; } public static bool operator ==(Vector2D a, Vector2D b) { return a.X == b.X && a.Y == b.Y; } public static bool operator !=(Vector2D a, Vector2D b) { return a.X != b.X || a.Y != b.Y; } public bool Equals(Vector2D other) { return X == other.X && Y == other.Y; } public override bool Equals(object obj) { if (obj is Vector2D) { return Equals((Vector2D)obj); } return false; } public override int GetHashCode() { return X.GetHashCode() + Y.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{X:{0} Y:{1}}}", new object[2] { X.ToString(currentCulture), Y.ToString(currentCulture) }); } } public struct Vector3D : IEquatable { public float X; public float Y; public float Z; public float this[int index] { get { return index switch { 0 => X, 1 => Y, 2 => Z, _ => 0f, }; } set { switch (index) { case 0: X = value; break; case 1: Y = value; break; case 2: Z = value; break; } } } public Vector3D(float x, float y, float z) { X = x; Y = y; Z = z; } public Vector3D(Vector2D value, float z) { X = value.X; Y = value.Y; Z = z; } public Vector3D(float value) { X = value; Y = value; Z = value; } public void Set(float x, float y, float z) { X = x; Y = y; Z = z; } public float Length() { return (float)Math.Sqrt(LengthSquared()); } public float LengthSquared() { return X * X + Y * Y + Z * Z; } public void Normalize() { float num = 1f / (float)Math.Sqrt(X * X + Y * Y + Z * Z); X *= num; Y *= num; Z *= num; } public void Negate() { X = 0f - X; Y = 0f - Y; Z = 0f - Z; } public static Vector3D Cross(Vector3D a, Vector3D b) { Vector3D result = default(Vector3D); result.X = a.Y * b.Z - a.Z * b.Y; result.Y = a.Z * b.X - a.X * b.Z; result.Z = a.X * b.Y - a.Y * b.X; return result; } public static float Dot(Vector3D a, Vector3D b) { return a.X * b.X + a.Y * b.Y + a.Z * b.Z; } public static Vector3D operator +(Vector3D a, Vector3D b) { Vector3D result = default(Vector3D); result.X = a.X + b.X; result.Y = a.Y + b.Y; result.Z = a.Z + b.Z; return result; } public static Vector3D operator -(Vector3D a, Vector3D b) { Vector3D result = default(Vector3D); result.X = a.X - b.X; result.Y = a.Y - b.Y; result.Z = a.Z - b.Z; return result; } public static Vector3D operator *(Vector3D a, Vector3D b) { Vector3D result = default(Vector3D); result.X = a.X * b.X; result.Y = a.Y * b.Y; result.Z = a.Z * b.Z; return result; } public static Vector3D operator *(Vector3D value, float scale) { Vector3D result = default(Vector3D); result.X = value.X * scale; result.Y = value.Y * scale; result.Z = value.Z * scale; return result; } public static Vector3D operator *(float scale, Vector3D value) { Vector3D result = default(Vector3D); result.X = value.X * scale; result.Y = value.Y * scale; result.Z = value.Z * scale; return result; } public static Vector3D operator *(Matrix3x3 matrix, Vector3D vector) { Vector3D result = default(Vector3D); result.X = vector.X * matrix.A1 + vector.Y * matrix.A2 + vector.Z * matrix.A3; result.Y = vector.X * matrix.B1 + vector.Y * matrix.B2 + vector.Z * matrix.B3; result.Z = vector.X * matrix.C1 + vector.Y * matrix.C2 + vector.Z * matrix.C3; return result; } public static Vector3D operator *(Matrix4x4 matrix, Vector3D vector) { Vector3D result = default(Vector3D); result.X = vector.X * matrix.A1 + vector.Y * matrix.A2 + vector.Z * matrix.A3 + matrix.A4; result.Y = vector.X * matrix.B1 + vector.Y * matrix.B2 + vector.Z * matrix.B3 + matrix.B4; result.Z = vector.X * matrix.C1 + vector.Y * matrix.C2 + vector.Z * matrix.C3 + matrix.C4; return result; } public static Vector3D operator /(Vector3D a, Vector3D b) { Vector3D result = default(Vector3D); result.X = a.X / b.X; result.Y = a.Y / b.Y; result.Z = a.Z / b.Z; return result; } public static Vector3D operator /(Vector3D value, float divisor) { float num = 1f / divisor; Vector3D result = default(Vector3D); result.X = value.X * num; result.Y = value.Y * num; result.Z = value.Z * num; return result; } public static Vector3D operator -(Vector3D value) { Vector3D result = default(Vector3D); result.X = 0f - value.X; result.Y = 0f - value.Y; result.Z = 0f - value.Z; return result; } public static bool operator ==(Vector3D a, Vector3D b) { return a.X == b.X && a.Y == b.Y && a.Z == b.Z; } public static bool operator !=(Vector3D a, Vector3D b) { return a.X != b.X || a.Y != b.Y || a.Z != b.Z; } public bool Equals(Vector3D other) { return X == other.X && Y == other.Y && Z == other.Z; } public override bool Equals(object obj) { if (obj is Vector3D) { return Equals((Vector3D)obj); } return false; } public override int GetHashCode() { return X.GetHashCode() + Y.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{X:{0} Y:{1} Z:{2}}}", new object[3] { X.ToString(currentCulture), Y.ToString(currentCulture), Z.ToString(currentCulture) }); } } public struct VectorKey : IEquatable { public double Time; public Vector3D Value; public VectorKey(double time, Vector3D vector) { Time = time; Value = vector; } public static bool operator ==(VectorKey a, VectorKey b) { return a.Value == b.Value; } public static bool operator !=(VectorKey a, VectorKey b) { return a.Value != b.Value; } public static bool operator <(VectorKey a, VectorKey b) { return a.Time < b.Time; } public static bool operator >(VectorKey a, VectorKey b) { return a.Time > b.Time; } public override bool Equals(object obj) { if (obj is VectorKey) { return Equals((VectorKey)obj); } return false; } public bool Equals(VectorKey key) { return Value == key.Value; } public override int GetHashCode() { return Value.GetHashCode(); } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{Time:{0} Vector:{1}}}", new object[2] { Time.ToString(currentCulture), Value.ToString() }); } } public struct VertexWeight { public int VertexID; public float Weight; public VertexWeight(int vertID, float weight) { VertexID = vertID; Weight = weight; } public override string ToString() { CultureInfo currentCulture = CultureInfo.CurrentCulture; return string.Format(currentCulture, "{{VertexID:{0} Weight:{1}}}", new object[2] { VertexID.ToString(currentCulture), Weight.ToString(currentCulture) }); } } } namespace Assimp.Unmanaged { public static class AiConfigs { public const string AI_CONFIG_GLOB_MEASURE_TIME = "GLOB_MEASURE_TIME"; public const string AI_CONFIG_GLOB_MULTITHREADING = "GLOB_MULTITHREADING"; public const string AI_CONFIG_IMPORT_NO_SKELETON_MESHES = "IMPORT_NO_SKELETON_MESHES"; public const string AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE = "PP_CT_MAX_SMOOTHING_ANGLE"; public const string AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE = "PP_GSN_MAX_SMOOTHING_ANGLE"; public const string AI_CONFIG_IMPORT_MDL_COLORMAP = "IMPORT_MDL_COLORMAP"; public const string AI_CONFIG_PP_RRM_EXCLUDE_LIST = "PP_RRM_EXCLUDE_LIST"; public const string AI_CONFIG_PP_PTV_KEEP_HIERARCHY = "PP_PTV_KEEP_HIERARCHY"; public const string AI_CONFIG_PP_PTV_NORMALIZE = "PP_PTV_NORMALIZE"; public const string AI_CONFIG_PP_FD_REMOVE = "PP_FD_REMOVE"; public const string AI_CONFIG_PP_FD_CHECKAREA = "PP_FD_CHECKAREA"; public const string AI_CONFIG_PP_OG_EXCLUDE_LIST = "PP_OG_EXCLUDE_LIST"; public const string AI_CONFIG_PP_SLM_TRIANGLE_LIMIT = "PP_SLM_TRIANGLE_LIMIT"; public const string AI_CONFIG_PP_SLM_VERTEX_LIMIT = "PP_SLM_VERTEX_LIMIT"; public const string AI_CONFIG_PP_LBW_MAX_WEIGHTS = "PP_LBW_MAX_WEIGHTS"; public const string AI_CONFIG_PP_ICL_PTCACHE_SIZE = "PP_ICL_PTCACHE_SIZE"; public const string AI_CONFIG_PP_RVC_FLAGS = "PP_RVC_FLAGS"; public const string AI_CONFIG_PP_SBP_REMOVE = "PP_SBP_REMOVE"; public const string AI_CONFIG_PP_FID_ANIM_ACCURACY = "PP_FID_ANIM_ACCURACY"; public const string AI_CONFIG_PP_FID_IGNORE_TEXTURECOORDS = "PP_FID_IGNORE_TEXTURECOORDS"; public const string AI_CONFIG_PP_TUV_EVALUATE = "PP_TUV_EVALUATE"; public const string AI_CONFIG_FAVOUR_SPEED = "FAVOUR_SPEED"; public const string AI_CONFIG_PP_SBBC_MAX_BONES = "PP_SBBC_MAX_BONES"; public const string AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX = "PP_CT_TEXTURE_CHANNEL_INDEX"; public const string AI_CONFIG_PP_DB_THRESHOLD = "PP_DB_THRESHOLD"; public const string AI_CONFIG_PP_DB_ALL_OR_NONE = "PP_DB_ALL_OR_NONE"; public const string AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION = "PP_PTV_ADD_ROOT_TRANSFORMATION"; public const string AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION = "PP_PTV_ROOT_TRANSFORMATION"; public const string AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY = "GLOBAL_SCALE_FACTOR"; public const string AI_CONFIG_APP_SCALE_KEY = "APP_SCALE_FACTOR"; public const string AI_CONFIG_IMPORT_GLOBAL_KEYFRAME = "IMPORT_GLOBAL_KEYFRAME"; public const string AI_CONFIG_IMPORT_MD3_KEYFRAME = "IMPORT_MD3_KEYFRAME"; public const string AI_CONFIG_IMPORT_MD2_KEYFRAME = "IMPORT_MD3_KEYFRAME"; public const string AI_CONFIG_IMPORT_MDL_KEYFRAME = "IMPORT_MDL_KEYFRAME"; public const string AI_CONFIG_IMPORT_MDC_KEYFRAME = "IMPORT_MDC_KEYFRAME"; public const string AI_CONFIG_IMPORT_SMD_KEYFRAME = "IMPORT_SMD_KEYFRAME"; public const string AI_CONFIG_IMPORT_UNREAL_KEYFRAME = "IMPORT_UNREAL_KEYFRAME"; public const string AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL = "IMPORT_AC_SEPARATE_BFCULL"; public const string AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION = "IMPORT_AC_EVAL_SUBDIVISION"; public const string AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS = "UNREAL_HANDLE_FLAGS"; public const string AI_CONFIG_IMPORT_TER_MAKE_UVS = "IMPORT_TER_MAKE_UVS"; public const string AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS = "IMPORT_ASE_RECONSTRUCT_NORMALS"; public const string AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART = "IMPORT_MD3_HANDLE_MULTIPART"; public const string AI_CONFIG_IMPORT_MD3_SKIN_NAME = "IMPORT_MD3_SKIN_NAME"; public const string AI_CONFIG_IMPORT_MD3_SHADER_SRC = "IMPORT_MD3_SHADER_SRC"; public const string AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY = "IMPORT_LWO_ONE_LAYER_ONLY"; public const string AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD = "IMPORT_MD5_NO_ANIM_AUTOLOAD"; public const string AI_CONFIG_IMPORT_LWS_ANIM_START = "IMPORT_LWS_ANIM_START"; public const string AI_CONFIG_IMPORT_LWS_ANIM_END = "IMPORT_LWS_ANIM_END"; public const string AI_CONFIG_IMPORT_IRR_ANIM_FPS = "IMPORT_IRR_ANIM_FPS"; public const string AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE = "IMPORT_OGRE_MATERIAL_FILE"; public const string AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME = "IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME"; public const string AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS = "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS"; public const string AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION = "IMPORT_IFC_CUSTOM_TRIANGULATION"; public const string AI_CONFIG_IMPORT_IFC_SMOOTHING_ANGLE = "IMPORT_IFC_SMOOTHING_ANGLE"; public const string AI_CONFIG_IMPORT_IFC_CYLINDRICAL_TESSELLATION = "IMPORT_IFC_CYLINDRICAL_TESSELLATION"; public const string AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION = "IMPORT_COLLADA_IGNORE_UP_DIRECTION"; public const string AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES = "IMPORT_COLLADA_USE_COLLADA_NAMES"; public const string AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS = "IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS"; public const string AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS = "IMPORT_FBX_READ_ALL_MATERIALS"; public const string AI_CONFIG_IMPORT_FBX_READ_MATERIALS = "IMPORT_FBX_READ_MATERIALS"; public const string AI_CONFIG_IMPORT_FBX_READ_TEXTURES = "IMPORT_FBX_READ_TEXTURES"; public const string AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING = "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"; public const string AI_CONFIG_IMPORT_FBX_READ_CAMERAS = "IMPORT_FBX_READ_CAMERAS"; public const string AI_CONFIG_IMPORT_FBX_READ_LIGHTS = "IMPORT_FBX_READ_LIGHTS"; public const string AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS = "IMPORT_FBX_READ_ANIMATIONS"; public const string AI_CONFIG_IMPORT_FBX_STRICT_MODE = "IMPORT_FBX_STRICT_MODE"; public const string AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS = "IMPORT_FBX_PRESERVE_PIVOTS"; public const string AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES = "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES"; public const string AI_CONFIG_FBX_CONVERT_TO_M = "AI_CONFIG_FBX_CONVERT_TO_M"; public const string AI_CONFIG_IMPORT_SMD_LOAD_ANIMATION_LIST = "IMPORT_SMD_LOAD_ANIMATION_LIST"; public const string AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES = "AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES"; public const string AI_CONFIG_EXPORT_XFILE_64BIT = "EXPORT_XFILE_64BIT"; public const string AI_CONFIG_EXPORT_POINT_CLOUDS = "EXPORT_POINT_CLOUDS"; } public static class AiDefines { public const int AI_SLM_DEFAULT_MAX_TRIANGLES = 1000000; public const int AI_SLM_DEFAULT_MAX_VERTICES = 1000000; public const int AI_LBW_MAX_WEIGHTS = 4; public const int PP_ICL_PTCACHE_SIZE = 12; public const int AI_UVTRAFO_ALL = 7; public const int AI_MAX_FACE_INDICES = 32767; public const int AI_MAX_BONE_WEIGHTS = int.MaxValue; public const int AI_MAX_VERTICES = int.MaxValue; public const int AI_MAX_FACES = int.MaxValue; public const int AI_MAX_NUMBER_OF_COLOR_SETS = 8; public const int AI_MAX_NUMBER_OF_TEXTURECOORDS = 8; public const int AI_SBBC_DEFAULT_MAX_BONES = 60; public const float AI_DEBONE_THRESHOLD = 1f; public const int MAX_LENGTH = 1024; public const string AI_DEFAULT_MATERIAL_NAME = "DefaultMaterial"; public const string AI_DEFAULT_TEXTURED_MATERIAL_NAME = "TexturedDefaultMaterial"; } public static class AiMatKeys { public const string NAME_BASE = "?mat.name"; public const string NAME = "?mat.name,0,0"; public const string TWOSIDED_BASE = "$mat.twosided"; public const string TWOSIDED = "$mat.twosided,0,0"; public const string SHADING_MODEL_BASE = "$mat.shadingm"; public const string SHADING_MODEL = "$mat.shadingm,0,0"; public const string ENABLE_WIREFRAME_BASE = "$mat.wireframe"; public const string ENABLE_WIREFRAME = "$mat.wireframe,0,0"; public const string BLEND_FUNC_BASE = "$mat.blend"; public const string BLEND_FUNC = "$mat.blend,0,0"; public const string OPACITY_BASE = "$mat.opacity"; public const string OPACITY = "$mat.opacity,0,0"; public const string TRANSPARENCYFACTOR_BASE = "$mat.transparencyfactor"; public const string TRANSPARENCYFACTOR = "$mat.transparencyfactor,0,0"; public const string BUMPSCALING_BASE = "$mat.bumpscaling"; public const string BUMPSCALING = "$mat.bumpscaling,0,0"; public const string SHININESS_BASE = "$mat.shininess"; public const string SHININESS = "$mat.shininess,0,0"; public const string REFLECTIVITY_BASE = "$mat.reflectivity"; public const string REFLECTIVITY = "$mat.reflectivity,0,0"; public const string SHININESS_STRENGTH_BASE = "$mat.shinpercent"; public const string SHININESS_STRENGTH = "$mat.shinpercent,0,0"; public const string REFRACTI_BASE = "$mat.refracti"; public const string REFRACTI = "$mat.refracti,0,0"; public const string COLOR_DIFFUSE_BASE = "$clr.diffuse"; public const string COLOR_DIFFUSE = "$clr.diffuse,0,0"; public const string COLOR_AMBIENT_BASE = "$clr.ambient"; public const string COLOR_AMBIENT = "$clr.ambient,0,0"; public const string COLOR_SPECULAR_BASE = "$clr.specular"; public const string COLOR_SPECULAR = "$clr.specular,0,0"; public const string COLOR_EMISSIVE_BASE = "$clr.emissive"; public const string COLOR_EMISSIVE = "$clr.emissive,0,0"; public const string COLOR_TRANSPARENT_BASE = "$clr.transparent"; public const string COLOR_TRANSPARENT = "$clr.transparent,0,0"; public const string COLOR_REFLECTIVE_BASE = "$clr.reflective"; public const string COLOR_REFLECTIVE = "$clr.reflective,0,0"; public const string GLOBAL_BACKGROUND_IMAGE_BASE = "?bg.global"; public const string GLOBAL_BACKGROUND_IMAGE = "?bg.global,0,0"; public const string TEXTURE_BASE = "$tex.file"; public const string UVWSRC_BASE = "$tex.uvwsrc"; public const string TEXOP_BASE = "$tex.op"; public const string MAPPING_BASE = "$tex.mapping"; public const string TEXBLEND_BASE = "$tex.blend"; public const string MAPPINGMODE_U_BASE = "$tex.mapmodeu"; public const string MAPPINGMODE_V_BASE = "$tex.mapmodev"; public const string TEXMAP_AXIS_BASE = "$tex.mapaxis"; public const string UVTRANSFORM_BASE = "$tex.uvtrafo"; public const string TEXFLAGS_BASE = "$tex.flags"; public const string GLOBAL_SHADERLANG_BASE = "?sh.lang"; public const string GLOBAL_SHADERLANG = "?sh.lang,0,0"; public const string SHADER_VERTEX_BASE = "?sh.vs"; public const string SHADER_VERTEX = "?sh.vs,0,0"; public const string SHADER_FRAGMENT_BASE = "?sh.fs"; public const string SHADER_FRAGMENT = "?sh.fs,0,0"; public const string SHADER_GEO_BASE = "?sh.gs"; public const string SHADER_GEO = "?sh.gs,0,0"; public const string SHADER_TESSELATION_BASE = "?sh.ts"; public const string SHADER_TESSELATION = "?sh.ts,0,0"; public const string SHADER_PRIMITIVE_BASE = "?sh.ps"; public const string SHADER_PRIMITIVE = "?sh.ps,0,0"; public const string SHADER_COMPUTE_BASE = "?sh.cs"; public const string SHADER_COMPUTE = "?sh.cs,0,0"; public static string GetFullTextureName(string baseName, TextureType texType, int texIndex) { return $"{baseName},{(int)texType},{texIndex}"; } public static string GetBaseName(string fullyQualifiedName) { if (string.IsNullOrEmpty(fullyQualifiedName)) { return string.Empty; } string[] array = fullyQualifiedName.Split(new char[1] { ',' }); if (array != null && array.Length == 3) { return array[0]; } return string.Empty; } } public sealed class AssimpLibrary : UnmanagedLibrary { internal static class FunctionNames { public const string aiImportFile = "aiImportFile"; public const string aiImportFileEx = "aiImportFileEx"; public const string aiImportFileExWithProperties = "aiImportFileExWithProperties"; public const string aiImportFileFromMemory = "aiImportFileFromMemory"; public const string aiImportFileFromMemoryWithProperties = "aiImportFileFromMemoryWithProperties"; public const string aiReleaseImport = "aiReleaseImport"; public const string aiApplyPostProcessing = "aiApplyPostProcessing"; public const string aiGetExportFormatCount = "aiGetExportFormatCount"; public const string aiGetExportFormatDescription = "aiGetExportFormatDescription"; public const string aiReleaseExportFormatDescription = "aiReleaseExportFormatDescription"; public const string aiExportSceneToBlob = "aiExportSceneToBlob"; public const string aiReleaseExportBlob = "aiReleaseExportBlob"; public const string aiExportScene = "aiExportScene"; public const string aiExportSceneEx = "aiExportSceneEx"; public const string aiCopyScene = "aiCopyScene"; public const string aiAttachLogStream = "aiAttachLogStream"; public const string aiEnableVerboseLogging = "aiEnableVerboseLogging"; public const string aiDetachLogStream = "aiDetachLogStream"; public const string aiDetachAllLogStreams = "aiDetachAllLogStreams"; public const string aiCreatePropertyStore = "aiCreatePropertyStore"; public const string aiReleasePropertyStore = "aiReleasePropertyStore"; public const string aiSetImportPropertyInteger = "aiSetImportPropertyInteger"; public const string aiSetImportPropertyFloat = "aiSetImportPropertyFloat"; public const string aiSetImportPropertyString = "aiSetImportPropertyString"; public const string aiSetImportPropertyMatrix = "aiSetImportPropertyMatrix"; public const string aiGetMaterialColor = "aiGetMaterialColor"; public const string aiGetMaterialFloatArray = "aiGetMaterialFloatArray"; public const string aiGetMaterialIntegerArray = "aiGetMaterialIntegerArray"; public const string aiGetMaterialProperty = "aiGetMaterialProperty"; public const string aiGetMaterialString = "aiGetMaterialString"; public const string aiGetMaterialTextureCount = "aiGetMaterialTextureCount"; public const string aiGetMaterialTexture = "aiGetMaterialTexture"; public const string aiGetErrorString = "aiGetErrorString"; public const string aiIsExtensionSupported = "aiIsExtensionSupported"; public const string aiGetExtensionList = "aiGetExtensionList"; public const string aiGetImportFormatCount = "aiGetImportFormatCount"; public const string aiGetImportFormatDescription = "aiGetImportFormatDescription"; public const string aiGetMemoryRequirements = "aiGetMemoryRequirements"; public const string aiCreateQuaternionFromMatrix = "aiCreateQuaternionFromMatrix"; public const string aiDecomposeMatrix = "aiDecomposeMatrix"; public const string aiTransposeMatrix4 = "aiTransposeMatrix4"; public const string aiTransposeMatrix3 = "aiTransposeMatrix3"; public const string aiTransformVecByMatrix3 = "aiTransformVecByMatrix3"; public const string aiTransformVecByMatrix4 = "aiTransformVecByMatrix4"; public const string aiMultiplyMatrix4 = "aiMultiplyMatrix4"; public const string aiMultiplyMatrix3 = "aiMultiplyMatrix3"; public const string aiIdentityMatrix3 = "aiIdentityMatrix3"; public const string aiIdentityMatrix4 = "aiIdentityMatrix4"; public const string aiGetLegalString = "aiGetLegalString"; public const string aiGetVersionMinor = "aiGetVersionMinor"; public const string aiGetVersionMajor = "aiGetVersionMajor"; public const string aiGetVersionRevision = "aiGetVersionRevision"; public const string aiGetCompileFlags = "aiGetCompileFlags"; public const string aiGetBranchName = "aiGetBranchName"; } internal static class Functions { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiImportFile")] public delegate IntPtr aiImportFile([In][MarshalAs(UnmanagedType.LPStr)] string file, uint flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiImportFileEx")] public delegate IntPtr aiImportFileEx([In][MarshalAs(UnmanagedType.LPStr)] string file, uint flags, IntPtr fileIO); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiImportFileExWithProperties")] public delegate IntPtr aiImportFileExWithProperties([In][MarshalAs(UnmanagedType.LPStr)] string file, uint flag, IntPtr fileIO, IntPtr propStore); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiImportFileFromMemory")] public delegate IntPtr aiImportFileFromMemory(byte[] buffer, uint bufferLength, uint flags, [In][MarshalAs(UnmanagedType.LPStr)] string formatHint); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiImportFileFromMemoryWithProperties")] public delegate IntPtr aiImportFileFromMemoryWithProperties(byte[] buffer, uint bufferLength, uint flags, [In][MarshalAs(UnmanagedType.LPStr)] string formatHint, IntPtr propStore); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiReleaseImport")] public delegate void aiReleaseImport(IntPtr scene); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiApplyPostProcessing")] public delegate IntPtr aiApplyPostProcessing(IntPtr scene, uint Flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetExportFormatCount")] public delegate UIntPtr aiGetExportFormatCount(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetExportFormatDescription")] public delegate IntPtr aiGetExportFormatDescription(UIntPtr index); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiReleaseExportFormatDescription")] public delegate void aiReleaseExportFormatDescription(IntPtr desc); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiExportSceneToBlob")] public delegate IntPtr aiExportSceneToBlob(IntPtr scene, [In][MarshalAs(UnmanagedType.LPStr)] string formatId, uint preProcessing); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiReleaseExportBlob")] public delegate void aiReleaseExportBlob(IntPtr blobData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiExportScene")] public delegate ReturnCode aiExportScene(IntPtr scene, [In][MarshalAs(UnmanagedType.LPStr)] string formatId, [In][MarshalAs(UnmanagedType.LPStr)] string fileName, uint preProcessing); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiExportSceneEx")] public delegate ReturnCode aiExportSceneEx(IntPtr scene, [In][MarshalAs(UnmanagedType.LPStr)] string formatId, [In][MarshalAs(UnmanagedType.LPStr)] string fileName, IntPtr fileIO, uint preProcessing); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiCopyScene")] public delegate void aiCopyScene(IntPtr sceneIn, out IntPtr sceneOut); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiAttachLogStream")] public delegate void aiAttachLogStream(IntPtr logStreamPtr); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiEnableVerboseLogging")] public delegate void aiEnableVerboseLogging([In][MarshalAs(UnmanagedType.Bool)] bool enable); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiDetachLogStream")] public delegate ReturnCode aiDetachLogStream(IntPtr logStreamPtr); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiDetachAllLogStreams")] public delegate void aiDetachAllLogStreams(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiCreatePropertyStore")] public delegate IntPtr aiCreatePropertyStore(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiReleasePropertyStore")] public delegate void aiReleasePropertyStore(IntPtr propertyStore); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiSetImportPropertyInteger")] public delegate void aiSetImportPropertyInteger(IntPtr propertyStore, [In][MarshalAs(UnmanagedType.LPStr)] string name, int value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiSetImportPropertyFloat")] public delegate void aiSetImportPropertyFloat(IntPtr propertyStore, [In][MarshalAs(UnmanagedType.LPStr)] string name, float value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiSetImportPropertyString")] public delegate void aiSetImportPropertyString(IntPtr propertyStore, [In][MarshalAs(UnmanagedType.LPStr)] string name, ref AiString value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiSetImportPropertyMatrix")] public delegate void aiSetImportPropertyMatrix(IntPtr propertyStore, [In][MarshalAs(UnmanagedType.LPStr)] string name, ref Matrix4x4 value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialColor")] public delegate ReturnCode aiGetMaterialColor(ref AiMaterial mat, [In][MarshalAs(UnmanagedType.LPStr)] string key, uint texType, uint texIndex, IntPtr colorOut); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialFloatArray")] public delegate ReturnCode aiGetMaterialFloatArray(ref AiMaterial mat, [In][MarshalAs(UnmanagedType.LPStr)] string key, uint texType, uint texIndex, IntPtr ptrOut, ref uint valueCount); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialIntegerArray")] public delegate ReturnCode aiGetMaterialIntegerArray(ref AiMaterial mat, [In][MarshalAs(UnmanagedType.LPStr)] string key, uint texType, uint texIndex, IntPtr ptrOut, ref uint valueCount); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialProperty")] public delegate ReturnCode aiGetMaterialProperty(ref AiMaterial mat, [In][MarshalAs(UnmanagedType.LPStr)] string key, uint texType, uint texIndex, out IntPtr propertyOut); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialString")] public delegate ReturnCode aiGetMaterialString(ref AiMaterial mat, [In][MarshalAs(UnmanagedType.LPStr)] string key, uint texType, uint texIndex, out AiString str); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialTexture")] public delegate ReturnCode aiGetMaterialTexture(ref AiMaterial mat, TextureType type, uint index, out AiString path, out TextureMapping mapping, out uint uvIndex, out float blendFactor, out TextureOperation textureOp, [In][Out] TextureWrapMode[] wrapModes, out uint flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMaterialTextureCount")] public delegate uint aiGetMaterialTextureCount(ref AiMaterial mat, TextureType type); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiCreateQuaternionFromMatrix")] public delegate void aiCreateQuaternionFromMatrix(out Quaternion quat, ref Matrix3x3 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiDecomposeMatrix")] public delegate void aiDecomposeMatrix(ref Matrix4x4 mat, out Vector3D scaling, out Quaternion rotation, out Vector3D position); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiTransposeMatrix4")] public delegate void aiTransposeMatrix4(ref Matrix4x4 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiTransposeMatrix3")] public delegate void aiTransposeMatrix3(ref Matrix3x3 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiTransformVecByMatrix3")] public delegate void aiTransformVecByMatrix3(ref Vector3D vec, ref Matrix3x3 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiTransformVecByMatrix4")] public delegate void aiTransformVecByMatrix4(ref Vector3D vec, ref Matrix4x4 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiMultiplyMatrix4")] public delegate void aiMultiplyMatrix4(ref Matrix4x4 dst, ref Matrix4x4 src); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiMultiplyMatrix3")] public delegate void aiMultiplyMatrix3(ref Matrix3x3 dst, ref Matrix3x3 src); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiIdentityMatrix3")] public delegate void aiIdentityMatrix3(out Matrix3x3 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiIdentityMatrix4")] public delegate void aiIdentityMatrix4(out Matrix4x4 mat); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetErrorString")] public delegate IntPtr aiGetErrorString(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetExtensionList")] public delegate void aiGetExtensionList(ref AiString extensionsOut); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetMemoryRequirements")] public delegate void aiGetMemoryRequirements(IntPtr scene, ref AiMemoryInfo memoryInfo); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiIsExtensionSupported")] [return: MarshalAs(UnmanagedType.Bool)] public delegate bool aiIsExtensionSupported([In][MarshalAs(UnmanagedType.LPStr)] string extension); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetImportFormatCount")] public delegate UIntPtr aiGetImportFormatCount(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetImportFormatDescription")] public delegate IntPtr aiGetImportFormatDescription(UIntPtr index); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetLegalString")] public delegate IntPtr aiGetLegalString(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetVersionMinor")] public delegate uint aiGetVersionMinor(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetVersionMajor")] public delegate uint aiGetVersionMajor(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetVersionRevision")] public delegate uint aiGetVersionRevision(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetBranchName")] public delegate IntPtr aiGetBranchName(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionName("aiGetCompileFlags")] public delegate uint aiGetCompileFlags(); } private static readonly object s_sync = new object(); private const string DefaultLibName = "assimp"; private static AssimpLibrary s_instance; private bool m_enableVerboseLogging = false; public static AssimpLibrary Instance { get { lock (s_sync) { if (s_instance == null) { s_instance = CreateInstance(); } return s_instance; } } } public bool IsMultithreadingSupported => (GetCompileFlags() & CompileFlags.SingleThreaded) != CompileFlags.SingleThreaded; private AssimpLibrary(string defaultLibName, Type[] unmanagedFunctionDelegateTypes) : base(defaultLibName, unmanagedFunctionDelegateTypes) { } private static AssimpLibrary CreateInstance() { return new AssimpLibrary("assimp", PlatformHelper.GetNestedTypes(typeof(Functions))); } public IntPtr ImportFile(string file, PostProcessSteps flags, IntPtr propStore) { return ImportFile(file, flags, IntPtr.Zero, propStore); } public IntPtr ImportFile(string file, PostProcessSteps flags, IntPtr fileIO, IntPtr propStore) { LoadIfNotLoaded(); Functions.aiImportFileExWithProperties function = GetFunction("aiImportFileExWithProperties"); return function(file, (uint)flags, fileIO, propStore); } public IntPtr ImportFileFromStream(Stream stream, PostProcessSteps flags, string formatHint, IntPtr propStore) { LoadIfNotLoaded(); Functions.aiImportFileFromMemoryWithProperties function = GetFunction("aiImportFileFromMemoryWithProperties"); byte[] array = MemoryHelper.ReadStreamFully(stream, 0); return function(array, (uint)array.Length, (uint)flags, formatHint, propStore); } public void ReleaseImport(IntPtr scene) { LoadIfNotLoaded(); if (!(scene == IntPtr.Zero)) { Functions.aiReleaseImport function = GetFunction("aiReleaseImport"); function(scene); } } public IntPtr ApplyPostProcessing(IntPtr scene, PostProcessSteps flags) { LoadIfNotLoaded(); if (scene == IntPtr.Zero) { return IntPtr.Zero; } Functions.aiApplyPostProcessing function = GetFunction("aiApplyPostProcessing"); return function(scene, (uint)flags); } public ExportFormatDescription[] GetExportFormatDescriptions() { LoadIfNotLoaded(); int num = (int)GetFunction("aiGetExportFormatCount")().ToUInt32(); if (num == 0) { return new ExportFormatDescription[0]; } ExportFormatDescription[] array = new ExportFormatDescription[num]; Functions.aiGetExportFormatDescription function = GetFunction("aiGetExportFormatDescription"); Functions.aiReleaseExportFormatDescription function2 = GetFunction("aiReleaseExportFormatDescription"); for (int i = 0; i < num; i++) { IntPtr intPtr = function(new UIntPtr((uint)i)); if (intPtr != IntPtr.Zero) { array[i] = new ExportFormatDescription(MemoryHelper.Read(intPtr)); function2(intPtr); } } return array; } public ExportDataBlob ExportSceneToBlob(IntPtr scene, string formatId, PostProcessSteps preProcessing) { LoadIfNotLoaded(); if (string.IsNullOrEmpty(formatId) || scene == IntPtr.Zero) { return null; } Functions.aiExportSceneToBlob function = GetFunction("aiExportSceneToBlob"); Functions.aiReleaseExportBlob function2 = GetFunction("aiReleaseExportBlob"); IntPtr intPtr = function(scene, formatId, (uint)preProcessing); if (intPtr == IntPtr.Zero) { return null; } AiExportDataBlob dataBlob = MemoryHelper.Read(intPtr); ExportDataBlob result = new ExportDataBlob(ref dataBlob); function2(intPtr); return result; } public ReturnCode ExportScene(IntPtr scene, string formatId, string fileName, PostProcessSteps preProcessing) { return ExportScene(scene, formatId, fileName, IntPtr.Zero, preProcessing); } public ReturnCode ExportScene(IntPtr scene, string formatId, string fileName, IntPtr fileIO, PostProcessSteps preProcessing) { LoadIfNotLoaded(); if (string.IsNullOrEmpty(formatId) || scene == IntPtr.Zero) { return ReturnCode.Failure; } Functions.aiExportSceneEx function = GetFunction("aiExportSceneEx"); return function(scene, formatId, fileName, fileIO, (uint)preProcessing); } public IntPtr CopyScene(IntPtr sceneToCopy) { if (sceneToCopy == IntPtr.Zero) { return IntPtr.Zero; } Functions.aiCopyScene function = GetFunction("aiCopyScene"); function(sceneToCopy, out var sceneOut); return sceneOut; } public void AttachLogStream(IntPtr logStreamPtr) { LoadIfNotLoaded(); Functions.aiAttachLogStream function = GetFunction("aiAttachLogStream"); function(logStreamPtr); } public void EnableVerboseLogging(bool enable) { LoadIfNotLoaded(); Functions.aiEnableVerboseLogging function = GetFunction("aiEnableVerboseLogging"); function(enable); m_enableVerboseLogging = enable; } public bool GetVerboseLoggingEnabled() { return m_enableVerboseLogging; } public ReturnCode DetachLogStream(IntPtr logStreamPtr) { LoadIfNotLoaded(); Functions.aiDetachLogStream function = GetFunction("aiDetachLogStream"); return function(logStreamPtr); } public void DetachAllLogStreams() { LoadIfNotLoaded(); Functions.aiDetachAllLogStreams function = GetFunction("aiDetachAllLogStreams"); function(); } public IntPtr CreatePropertyStore() { LoadIfNotLoaded(); Functions.aiCreatePropertyStore function = GetFunction("aiCreatePropertyStore"); return function(); } public void ReleasePropertyStore(IntPtr propertyStore) { LoadIfNotLoaded(); if (!(propertyStore == IntPtr.Zero)) { Functions.aiReleasePropertyStore function = GetFunction("aiReleasePropertyStore"); function(propertyStore); } } public void SetImportPropertyInteger(IntPtr propertyStore, string name, int value) { LoadIfNotLoaded(); if (!(propertyStore == IntPtr.Zero) && !string.IsNullOrEmpty(name)) { Functions.aiSetImportPropertyInteger function = GetFunction("aiSetImportPropertyInteger"); function(propertyStore, name, value); } } public void SetImportPropertyFloat(IntPtr propertyStore, string name, float value) { LoadIfNotLoaded(); if (!(propertyStore == IntPtr.Zero) && !string.IsNullOrEmpty(name)) { Functions.aiSetImportPropertyFloat function = GetFunction("aiSetImportPropertyFloat"); function(propertyStore, name, value); } } public void SetImportPropertyString(IntPtr propertyStore, string name, string value) { LoadIfNotLoaded(); if (!(propertyStore == IntPtr.Zero) && !string.IsNullOrEmpty(name)) { Functions.aiSetImportPropertyString function = GetFunction("aiSetImportPropertyString"); AiString value2 = default(AiString); if (value2.SetString(value)) { function(propertyStore, name, ref value2); } } } public void SetImportPropertyMatrix(IntPtr propertyStore, string name, Matrix4x4 value) { LoadIfNotLoaded(); if (!(propertyStore == IntPtr.Zero) && !string.IsNullOrEmpty(name)) { Functions.aiSetImportPropertyMatrix function = GetFunction("aiSetImportPropertyMatrix"); function(propertyStore, name, ref value); } } public Color4D GetMaterialColor(ref AiMaterial mat, string key, TextureType texType, uint texIndex) { LoadIfNotLoaded(); Functions.aiGetMaterialColor function = GetFunction("aiGetMaterialColor"); IntPtr intPtr = IntPtr.Zero; try { intPtr = MemoryHelper.AllocateMemory(MemoryHelper.SizeOf()); ReturnCode returnCode = function(ref mat, key, (uint)texType, texIndex, intPtr); Color4D result = default(Color4D); if (returnCode == ReturnCode.Success && intPtr != IntPtr.Zero) { return MemoryHelper.Read(intPtr); } return result; } finally { if (intPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr); } } } public float[] GetMaterialFloatArray(ref AiMaterial mat, string key, TextureType texType, uint texIndex, uint floatCount) { LoadIfNotLoaded(); Functions.aiGetMaterialFloatArray function = GetFunction("aiGetMaterialFloatArray"); IntPtr intPtr = IntPtr.Zero; try { intPtr = MemoryHelper.AllocateMemory(IntPtr.Size); ReturnCode returnCode = function(ref mat, key, (uint)texType, texIndex, intPtr, ref floatCount); float[] array = null; if (returnCode == ReturnCode.Success && floatCount != 0) { array = new float[floatCount]; MemoryHelper.Read(intPtr, array, 0, (int)floatCount); } return array; } finally { if (intPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr); } } } public int[] GetMaterialIntegerArray(ref AiMaterial mat, string key, TextureType texType, uint texIndex, uint intCount) { LoadIfNotLoaded(); Functions.aiGetMaterialIntegerArray function = GetFunction("aiGetMaterialIntegerArray"); IntPtr intPtr = IntPtr.Zero; try { intPtr = MemoryHelper.AllocateMemory(IntPtr.Size); ReturnCode returnCode = function(ref mat, key, (uint)texType, texIndex, intPtr, ref intCount); int[] array = null; if (returnCode == ReturnCode.Success && intCount != 0) { array = new int[intCount]; MemoryHelper.Read(intPtr, array, 0, (int)intCount); } return array; } finally { if (intPtr != IntPtr.Zero) { MemoryHelper.FreeMemory(intPtr); } } } public AiMaterialProperty GetMaterialProperty(ref AiMaterial mat, string key, TextureType texType, uint texIndex) { LoadIfNotLoaded(); Functions.aiGetMaterialProperty function = GetFunction("aiGetMaterialProperty"); IntPtr propertyOut; ReturnCode returnCode = function(ref mat, key, (uint)texType, texIndex, out propertyOut); AiMaterialProperty result = default(AiMaterialProperty); if (returnCode == ReturnCode.Success && propertyOut != IntPtr.Zero) { return MemoryHelper.Read(propertyOut); } return result; } public string GetMaterialString(ref AiMaterial mat, string key, TextureType texType, uint texIndex) { LoadIfNotLoaded(); Functions.aiGetMaterialString function = GetFunction("aiGetMaterialString"); if (function(ref mat, key, (uint)texType, texIndex, out var str) == ReturnCode.Success) { return str.GetString(); } return string.Empty; } public uint GetMaterialTextureCount(ref AiMaterial mat, TextureType type) { LoadIfNotLoaded(); Functions.aiGetMaterialTextureCount function = GetFunction("aiGetMaterialTextureCount"); return function(ref mat, type); } public string GetMaterialTextureFilePath(ref AiMaterial mat, TextureType type, uint index) { LoadIfNotLoaded(); Functions.aiGetMaterialTexture function = GetFunction("aiGetMaterialTexture"); TextureWrapMode[] wrapModes = new TextureWrapMode[2]; if (function(ref mat, type, index, out var path, out var _, out var _, out var _, out var _, wrapModes, out var _) == ReturnCode.Success) { return path.GetString(); } return string.Empty; } public TextureSlot GetMaterialTexture(ref AiMaterial mat, TextureType type, uint index) { LoadIfNotLoaded(); Functions.aiGetMaterialTexture function = GetFunction("aiGetMaterialTexture"); TextureWrapMode[] array = new TextureWrapMode[2]; AiString path; TextureMapping mapping; uint uvIndex; float blendFactor; TextureOperation textureOp; uint flags; ReturnCode returnCode = function(ref mat, type, index, out path, out mapping, out uvIndex, out blendFactor, out textureOp, array, out flags); return new TextureSlot(path.GetString(), type, (int)index, mapping, (int)uvIndex, blendFactor, textureOp, array[0], array[1], (int)flags); } public string GetErrorString() { LoadIfNotLoaded(); Functions.aiGetErrorString function = GetFunction("aiGetErrorString"); IntPtr intPtr = function(); if (intPtr == IntPtr.Zero) { return string.Empty; } return Marshal.PtrToStringAnsi(intPtr); } public bool IsExtensionSupported(string extension) { LoadIfNotLoaded(); Functions.aiIsExtensionSupported function = GetFunction("aiIsExtensionSupported"); return function(extension); } public string[] GetExtensionList() { LoadIfNotLoaded(); Functions.aiGetExtensionList function = GetFunction("aiGetExtensionList"); AiString extensionsOut = default(AiString); function(ref extensionsOut); return extensionsOut.GetString().Split(new string[2] { "*", ";*" }, StringSplitOptions.RemoveEmptyEntries); } public ImporterDescription[] GetImporterDescriptions() { LoadIfNotLoaded(); Functions.aiGetImportFormatCount function = GetFunction("aiGetImportFormatCount"); Functions.aiGetImportFormatDescription function2 = GetFunction("aiGetImportFormatDescription"); int num = (int)function().ToUInt32(); ImporterDescription[] array = new ImporterDescription[num]; for (int i = 0; i < num; i++) { IntPtr intPtr = function2(new UIntPtr((uint)i)); if (intPtr != IntPtr.Zero) { array[i] = new ImporterDescription(in MemoryHelper.AsRef(intPtr)); } } return array; } public AiMemoryInfo GetMemoryRequirements(IntPtr scene) { LoadIfNotLoaded(); Functions.aiGetMemoryRequirements function = GetFunction("aiGetMemoryRequirements"); AiMemoryInfo memoryInfo = default(AiMemoryInfo); if (scene != IntPtr.Zero) { function(scene, ref memoryInfo); } return memoryInfo; } public void CreateQuaternionFromMatrix(out Quaternion quat, ref Matrix3x3 mat) { LoadIfNotLoaded(); Functions.aiCreateQuaternionFromMatrix function = GetFunction("aiCreateQuaternionFromMatrix"); function(out quat, ref mat); } public void DecomposeMatrix(ref Matrix4x4 mat, out Vector3D scaling, out Quaternion rotation, out Vector3D position) { LoadIfNotLoaded(); Functions.aiDecomposeMatrix function = GetFunction("aiDecomposeMatrix"); function(ref mat, out scaling, out rotation, out position); } public void TransposeMatrix4(ref Matrix4x4 mat) { LoadIfNotLoaded(); Functions.aiTransposeMatrix4 function = GetFunction("aiTransposeMatrix4"); function(ref mat); } public void TransposeMatrix3(ref Matrix3x3 mat) { LoadIfNotLoaded(); Functions.aiTransposeMatrix3 function = GetFunction("aiTransposeMatrix3"); function(ref mat); } public void TransformVecByMatrix3(ref Vector3D vec, ref Matrix3x3 mat) { LoadIfNotLoaded(); Functions.aiTransformVecByMatrix3 function = GetFunction("aiTransformVecByMatrix3"); function(ref vec, ref mat); } public void TransformVecByMatrix4(ref Vector3D vec, ref Matrix4x4 mat) { LoadIfNotLoaded(); Functions.aiTransformVecByMatrix4 function = GetFunction("aiTransformVecByMatrix4"); function(ref vec, ref mat); } public void MultiplyMatrix4(ref Matrix4x4 dst, ref Matrix4x4 src) { LoadIfNotLoaded(); Functions.aiMultiplyMatrix4 function = GetFunction("aiMultiplyMatrix4"); function(ref dst, ref src); } public void MultiplyMatrix3(ref Matrix3x3 dst, ref Matrix3x3 src) { LoadIfNotLoaded(); Functions.aiMultiplyMatrix3 function = GetFunction("aiMultiplyMatrix3"); function(ref dst, ref src); } public void IdentityMatrix3(out Matrix3x3 mat) { LoadIfNotLoaded(); Functions.aiIdentityMatrix3 function = GetFunction("aiIdentityMatrix3"); function(out mat); } public void IdentityMatrix4(out Matrix4x4 mat) { LoadIfNotLoaded(); Functions.aiIdentityMatrix4 function = GetFunction("aiIdentityMatrix4"); function(out mat); } public string GetLegalString() { LoadIfNotLoaded(); Functions.aiGetLegalString function = GetFunction("aiGetLegalString"); IntPtr intPtr = function(); if (intPtr == IntPtr.Zero) { return string.Empty; } return Marshal.PtrToStringAnsi(intPtr); } public uint GetVersionMinor() { LoadIfNotLoaded(); Functions.aiGetVersionMinor function = GetFunction("aiGetVersionMinor"); return function(); } public uint GetVersionMajor() { LoadIfNotLoaded(); Functions.aiGetVersionMajor function = GetFunction("aiGetVersionMajor"); return function(); } public uint GetVersionRevision() { LoadIfNotLoaded(); Functions.aiGetVersionRevision function = GetFunction("aiGetVersionRevision"); return function(); } public string GetBranchName() { LoadIfNotLoaded(); Functions.aiGetBranchName function = GetFunction("aiGetBranchName"); IntPtr intPtr = function(); if (intPtr == IntPtr.Zero) { return string.Empty; } return Marshal.PtrToStringAnsi(intPtr); } public string GetVersion() { uint versionMajor = GetVersionMajor(); uint versionMinor = GetVersionMinor(); uint versionRevision = GetVersionRevision(); return $"{versionMajor.ToString()}.{versionMinor.ToString()}.{versionRevision.ToString()}"; } public Version GetVersionAsVersion() { return new Version((int)GetVersionMajor(), (int)GetVersionMinor(), 0, (int)GetVersionRevision()); } public CompileFlags GetCompileFlags() { LoadIfNotLoaded(); Functions.aiGetCompileFlags function = GetFunction("aiGetCompileFlags"); return (CompileFlags)function(); } } internal enum OSPlatform { Windows, Linux, OSX } internal static class RuntimeInformation { private static OSPlatform s_platform; static RuntimeInformation() { switch (Environment.OSVersion.Platform) { case PlatformID.Unix: if (Directory.Exists("/Applications") && Directory.Exists("/System") && Directory.Exists("/Users") && Directory.Exists("/Volumes")) { s_platform = OSPlatform.OSX; } else { s_platform = OSPlatform.Linux; } break; case PlatformID.MacOSX: s_platform = OSPlatform.OSX; break; default: s_platform = OSPlatform.Windows; break; } } public static bool IsOSPlatform(OSPlatform osPlat) { return s_platform == osPlat; } } internal class PlatformHelper { public static string GetInformationalVersion() { object[] customAttributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), inherit: false); if (customAttributes == null || customAttributes.Length == 0) { return null; } return (customAttributes[0] is AssemblyInformationalVersionAttribute assemblyInformationalVersionAttribute) ? assemblyInformationalVersionAttribute.InformationalVersion : null; } public static string GetAssemblyName() { return Assembly.GetExecutingAssembly().GetName().Name; } public static string GetAppBaseDirectory() { return AppDomain.CurrentDomain.BaseDirectory; } public static bool IsAssignable(Type baseType, Type subType) { if (baseType == null || subType == null) { return false; } return baseType.IsAssignableFrom(subType); } public static Type[] GetNestedTypes(Type type) { if (type == null) { return new Type[0]; } return type.GetNestedTypes(); } public static object[] GetCustomAttributes(Type type, Type attributeType, bool inherit) { if (type == null || attributeType == null) { return new object[0]; } return type.GetCustomAttributes(attributeType, inherit); } public static Delegate GetDelegateForFunctionPointer(IntPtr procAddress, Type delegateType) { if (procAddress == IntPtr.Zero || delegateType == null) { return null; } return Marshal.GetDelegateForFunctionPointer(procAddress, delegateType); } public static IntPtr GetFunctionPointerForDelegate(Delegate func) { if ((object)func == null) { return IntPtr.Zero; } return Marshal.GetFunctionPointerForDelegate(func); } } public enum Platform { Windows, Linux, Mac } [AttributeUsage(AttributeTargets.Delegate)] public class UnmanagedFunctionNameAttribute : Attribute { private string m_unmanagedFunctionName; public string UnmanagedFunctionName => m_unmanagedFunctionName; public UnmanagedFunctionNameAttribute(string unmanagedFunctionName) { m_unmanagedFunctionName = unmanagedFunctionName; } } public abstract class UnmanagedLibrary { internal abstract class UnmanagedLibraryImplementation : IDisposable { private string m_defaultLibName; private Type[] m_unmanagedFunctionDelegateTypes; private Dictionary m_nameToUnmanagedFunction; private IntPtr m_libraryHandle; private bool m_isDisposed; private bool m_throwOnLoadFailure; public bool IsLibraryLoaded => m_libraryHandle != IntPtr.Zero; public bool IsDisposed => m_isDisposed; public string DefaultLibraryName => m_defaultLibName; public bool ThrowOnLoadFailure { get { return m_throwOnLoadFailure; } set { m_throwOnLoadFailure = value; } } public abstract string DllExtension { get; } public virtual string DllPrefix => string.Empty; public UnmanagedLibraryImplementation(string defaultLibName, Type[] unmanagedFunctionDelegateTypes) { m_defaultLibName = DllPrefix + Path.ChangeExtension(defaultLibName, DllExtension); m_unmanagedFunctionDelegateTypes = unmanagedFunctionDelegateTypes; m_nameToUnmanagedFunction = new Dictionary(); m_isDisposed = false; m_libraryHandle = IntPtr.Zero; m_throwOnLoadFailure = true; } ~UnmanagedLibraryImplementation() { Dispose(isDisposing: false); } public T GetFunction(string functionName) where T : class { if (string.IsNullOrEmpty(functionName)) { return null; } if (!m_nameToUnmanagedFunction.TryGetValue(functionName, out var value)) { return null; } object obj = value; return (T)obj; } public bool LoadLibrary(string path) { FreeLibrary(clearFunctions: true); m_libraryHandle = NativeLoadLibrary(path); if (m_libraryHandle != IntPtr.Zero) { LoadFunctions(); } return m_libraryHandle != IntPtr.Zero; } public bool FreeLibrary() { return FreeLibrary(clearFunctions: true); } private bool FreeLibrary(bool clearFunctions) { if (m_libraryHandle != IntPtr.Zero) { NativeFreeLibrary(m_libraryHandle); m_libraryHandle = IntPtr.Zero; if (clearFunctions) { m_nameToUnmanagedFunction.Clear(); } return true; } return false; } private void LoadFunctions() { Type[] unmanagedFunctionDelegateTypes = m_unmanagedFunctionDelegateTypes; foreach (Type type in unmanagedFunctionDelegateTypes) { string unmanagedName = GetUnmanagedName(type); if (string.IsNullOrEmpty(unmanagedName)) { Debug.Assert(condition: false, $"No UnmanagedFunctionNameAttribute on {type.AssemblyQualifiedName} type."); continue; } IntPtr intPtr = NativeGetProcAddress(m_libraryHandle, unmanagedName); Delegate value; if (intPtr == IntPtr.Zero) { Debug.Assert(condition: false, $"No unmanaged function found for {type.AssemblyQualifiedName} type."); } else if (!m_nameToUnmanagedFunction.TryGetValue(unmanagedName, out value)) { value = PlatformHelper.GetDelegateForFunctionPointer(intPtr, type); m_nameToUnmanagedFunction.Add(unmanagedName, value); } } } private string GetUnmanagedName(Type funcType) { object[] customAttributes = PlatformHelper.GetCustomAttributes(funcType, typeof(UnmanagedFunctionNameAttribute), inherit: false); object[] array = customAttributes; foreach (object obj in array) { if (obj is UnmanagedFunctionNameAttribute) { return (obj as UnmanagedFunctionNameAttribute).UnmanagedFunctionName; } } return null; } protected abstract IntPtr NativeLoadLibrary(string path); protected abstract void NativeFreeLibrary(IntPtr handle); protected abstract IntPtr NativeGetProcAddress(IntPtr handle, string functionName); public void Dispose() { Dispose(isDisposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool isDisposing) { if (!m_isDisposed) { FreeLibrary(isDisposing); m_isDisposed = true; } } } internal sealed class UnmanagedWindowsLibraryImplementation : UnmanagedLibraryImplementation { public override string DllExtension => ".dll"; public UnmanagedWindowsLibraryImplementation(string defaultLibName, Type[] unmanagedFunctionDelegateTypes) : base(defaultLibName, unmanagedFunctionDelegateTypes) { } protected override IntPtr NativeLoadLibrary(string path) { IntPtr intPtr = WinLoadLibrary(path); if (intPtr == IntPtr.Zero && base.ThrowOnLoadFailure) { Exception ex = null; try { int hRForLastWin32Error = GetHRForLastWin32Error(); ex = Marshal.GetExceptionForHR(hRForLastWin32Error); } catch (Exception) { } if (ex != null) { throw new AssimpException($"Error loading unmanaged library from path: {path}\n\n{ex.Message}", ex); } throw new AssimpException($"Error loading unmanaged library from path: {path}"); } return intPtr; } protected override IntPtr NativeGetProcAddress(IntPtr handle, string functionName) { return GetProcAddress(handle, functionName); } protected override void NativeFreeLibrary(IntPtr handle) { FreeLibrary(handle); } private int GetHRForLastWin32Error() { int lastWin32Error = Marshal.GetLastWin32Error(); if ((lastWin32Error & 0x80000000u) == 2147483648u) { return lastWin32Error; } return (lastWin32Error & 0xFFFF) | -2147024896; } [DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Ansi, EntryPoint = "LoadLibrary", SetLastError = true)] private static extern IntPtr WinLoadLibrary(string fileName); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FreeLibrary(IntPtr hModule); [DllImport("kernel32.dll")] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); } internal sealed class UnmanagedLinuxLibraryImplementation : UnmanagedLibraryImplementation { private const int RTLD_NOW = 2; public override string DllExtension => ".so"; public override string DllPrefix => "lib"; public UnmanagedLinuxLibraryImplementation(string defaultLibName, Type[] unmanagedFunctionDelegateTypes) : base(defaultLibName, unmanagedFunctionDelegateTypes) { } protected override IntPtr NativeLoadLibrary(string path) { IntPtr intPtr = dlopen(path, 2); if (intPtr == IntPtr.Zero && base.ThrowOnLoadFailure) { IntPtr ptr = dlerror(); string text = Marshal.PtrToStringAnsi(ptr); if (!string.IsNullOrEmpty(text)) { throw new AssimpException($"Error loading unmanaged library from path: {path}\n\n{text}"); } throw new AssimpException($"Error loading unmanaged library from path: {path}"); } return intPtr; } protected override IntPtr NativeGetProcAddress(IntPtr handle, string functionName) { return dlsym(handle, functionName); } protected override void NativeFreeLibrary(IntPtr handle) { dlclose(handle); } [DllImport("libdl.so")] private static extern IntPtr dlopen(string fileName, int flags); [DllImport("libdl.so")] private static extern IntPtr dlsym(IntPtr handle, string functionName); [DllImport("libdl.so")] private static extern int dlclose(IntPtr handle); [DllImport("libdl.so")] private static extern IntPtr dlerror(); } internal sealed class UnmanagedMacLibraryImplementation : UnmanagedLibraryImplementation { private const int RTLD_NOW = 2; public override string DllExtension => ".dylib"; public override string DllPrefix => "lib"; public UnmanagedMacLibraryImplementation(string defaultLibName, Type[] unmanagedFunctionDelegateTypes) : base(defaultLibName, unmanagedFunctionDelegateTypes) { } protected override IntPtr NativeLoadLibrary(string path) { IntPtr intPtr = dlopen(path, 2); if (intPtr == IntPtr.Zero && base.ThrowOnLoadFailure) { IntPtr ptr = dlerror(); string text = Marshal.PtrToStringAnsi(ptr); if (!string.IsNullOrEmpty(text)) { throw new AssimpException($"Error loading unmanaged library from path: {path}\n\n{text}"); } throw new AssimpException($"Error loading unmanaged library from path: {path}"); } return intPtr; } protected override IntPtr NativeGetProcAddress(IntPtr handle, string functionName) { return dlsym(handle, functionName); } protected override void NativeFreeLibrary(IntPtr handle) { dlclose(handle); } [DllImport("libSystem.B.dylib")] private static extern IntPtr dlopen(string fileName, int flags); [DllImport("libSystem.B.dylib")] private static extern IntPtr dlsym(IntPtr handle, string functionName); [DllImport("libSystem.B.dylib")] private static extern int dlclose(IntPtr handle); [DllImport("libSystem.B.dylib")] private static extern IntPtr dlerror(); } private static object s_defaultLoadSync = new object(); private UnmanagedLibraryImplementation m_impl; private UnmanagedLibraryResolver m_resolver; private string m_libraryPath = string.Empty; private volatile bool m_checkNeedsLoading = true; public bool IsLibraryLoaded => m_impl.IsLibraryLoaded; public string DefaultLibraryName => m_impl.DefaultLibraryName; public string LibraryPath => m_libraryPath; public UnmanagedLibraryResolver Resolver => m_resolver; public bool ThrowOnLoadFailure { get { return m_impl.ThrowOnLoadFailure; } set { m_impl.ThrowOnLoadFailure = value; } } public static bool Is64Bit => IntPtr.Size == 8; public event EventHandler LibraryLoaded; public event EventHandler LibraryFreed; protected UnmanagedLibrary(string defaultName, Type[] unmanagedFunctionDelegateTypes) { CreateRuntimeImplementation(defaultName, unmanagedFunctionDelegateTypes); } public static Platform GetPlatform() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return Platform.Windows; } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { return Platform.Linux; } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return Platform.Mac; } throw new InvalidOperationException("Cannot determine OS-specific implementation."); } public bool LoadLibrary() { string libPath = m_resolver.ResolveLibraryPath(DefaultLibraryName); return LoadLibrary(libPath); } public bool LoadLibrary(string lib32Path, string lib64Path) { return LoadLibrary(Is64Bit ? lib64Path : lib32Path); } public bool LoadLibrary(string libPath) { if (IsLibraryLoaded) { Debug.Assert(condition: false, "Library already loaded"); return true; } if (!string.IsNullOrEmpty(libPath) && !Path.HasExtension(libPath)) { libPath = Path.ChangeExtension(libPath, m_impl.DllExtension); } if (m_impl.LoadLibrary(libPath)) { m_libraryPath = libPath; OnLibraryLoaded(); return true; } return false; } public bool FreeLibrary() { if (IsLibraryLoaded) { OnLibraryFreed(); m_impl.FreeLibrary(); m_libraryPath = string.Empty; m_checkNeedsLoading = true; return true; } return false; } public T GetFunction(string funcName) where T : class { return m_impl.GetFunction(funcName); } protected void LoadIfNotLoaded() { if (!m_checkNeedsLoading) { return; } lock (s_defaultLoadSync) { if (!IsLibraryLoaded) { LoadLibrary(); } m_checkNeedsLoading = false; } } protected virtual void OnLibraryLoaded() { this.LibraryLoaded?.Invoke(this, EventArgs.Empty); } protected virtual void OnLibraryFreed() { this.LibraryFreed?.Invoke(this, EventArgs.Empty); } private void CreateRuntimeImplementation(string defaultLibName, Type[] unmanagedFunctionDelegateTypes) { Platform platform = GetPlatform(); m_resolver = new UnmanagedLibraryResolver(platform); switch (platform) { case Platform.Windows: m_impl = new UnmanagedWindowsLibraryImplementation(defaultLibName, unmanagedFunctionDelegateTypes); break; case Platform.Linux: m_impl = new UnmanagedLinuxLibraryImplementation(defaultLibName, unmanagedFunctionDelegateTypes); break; case Platform.Mac: m_impl = new UnmanagedMacLibraryImplementation(defaultLibName, unmanagedFunctionDelegateTypes); break; default: throw new PlatformNotSupportedException(); } } } public sealed class UnmanagedLibraryResolver { private string[] m_32BitLibNames; private string[] m_64BitLibNames; private string[] m_32BitProbingPaths; private string[] m_64BitProbingPaths; private string m_override32BitName; private string m_override64BitName; private Platform m_platform; public Platform Platform => m_platform; internal UnmanagedLibraryResolver(Platform platformHint) { m_platform = platformHint; } public void SetFallbackLibraryNames32(params string[] fallbackLibNames) { m_32BitLibNames = fallbackLibNames; } public void SetFallbackLibraryNames64(params string[] fallbackLibNames) { m_64BitLibNames = fallbackLibNames; } public void SetFallbackLibraryNames(params string[] fallbackLibNames) { m_32BitLibNames = fallbackLibNames; m_64BitLibNames = fallbackLibNames; } public void SetProbingPaths32(params string[] probingPaths) { m_32BitProbingPaths = probingPaths; } public void SetProbingPaths64(params string[] probingPaths) { m_64BitProbingPaths = probingPaths; } public void SetProbingPaths(params string[] probingPaths) { m_32BitProbingPaths = probingPaths; m_64BitProbingPaths = probingPaths; } public void SetOverrideLibraryName32(string overrideName) { m_override32BitName = overrideName; } public void SetOverrideLibraryName64(string overrideName) { m_override64BitName = overrideName; } public void SetOverrideLibraryName(string overrideName) { m_override32BitName = overrideName; m_override64BitName = overrideName; } public string ResolveLibraryPath(string libName) { bool is64Bit = UnmanagedLibrary.Is64Bit; string text = (is64Bit ? m_override64BitName : m_override32BitName); if (!string.IsNullOrEmpty(text)) { libName = text; } if (string.IsNullOrEmpty(libName)) { return libName; } string[] fallbackNames = (is64Bit ? m_64BitLibNames : m_32BitLibNames); string[] probingPaths = (is64Bit ? m_64BitProbingPaths : m_32BitProbingPaths); string runtimeIdentifier = GetRuntimeIdentifier(); return ResolveLibraryPathInternal(libName, runtimeIdentifier, fallbackNames, probingPaths); } private string ResolveLibraryPathInternal(string libName, string rid, string[] fallbackNames, string[] probingPaths) { if (probingPaths != null) { foreach (string basePath in probingPaths) { string text = TryGetExistingFile(basePath, libName, fallbackNames); if (!string.IsNullOrEmpty(text)) { return text; } } } string text2 = Path.Combine(PlatformHelper.GetAppBaseDirectory(), Path.Combine("runtimes", Path.Combine(rid, "native"))); if (Directory.Exists(text2)) { string text3 = TryGetExistingFile(text2, libName, fallbackNames); if (!string.IsNullOrEmpty(text3)) { return text3; } } string text4 = TryGetExistingFile(PlatformHelper.GetAppBaseDirectory(), libName, fallbackNames); if (!string.IsNullOrEmpty(text4)) { return text4; } string packageRuntimeFolder = GetPackageRuntimeFolder(GetNugetPackagePath(), rid); string text5 = TryGetExistingFile(packageRuntimeFolder, libName, fallbackNames); if (!string.IsNullOrEmpty(text5)) { return text5; } return libName; } private string GetPackageRuntimeFolder(string packagePath, string rid) { if (string.IsNullOrEmpty(packagePath)) { return null; } return Path.Combine(packagePath, Path.Combine("runtimes", Path.Combine(rid, "native"))); } private string GetNugetPackagePath() { string text = PlatformHelper.GetAssemblyName().ToLowerInvariant(); string informationalVersion = PlatformHelper.GetInformationalVersion(); if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(informationalVersion)) { return null; } string path = Path.Combine(text, informationalVersion); string environmentVariable = Environment.GetEnvironmentVariable("NUGET_PACKAGES"); if (!string.IsNullOrEmpty(environmentVariable) && Directory.Exists(environmentVariable)) { return Path.Combine(environmentVariable, path); } environmentVariable = ((m_platform != Platform.Windows) ? Environment.GetEnvironmentVariable("HOME") : Environment.GetEnvironmentVariable("USERPROFILE")); if (!string.IsNullOrEmpty(environmentVariable)) { return Path.Combine(environmentVariable, Path.Combine(Path.Combine(".nuget", "packages"), path)); } return null; } private string TryGetExistingFile(string basePath, string libName, string[] fallbackNames) { if (string.IsNullOrEmpty(basePath) || !Directory.Exists(basePath)) { return null; } string text = Path.Combine(basePath, libName); if (File.Exists(text)) { return text; } if (fallbackNames != null) { foreach (string path in fallbackNames) { text = Path.Combine(basePath, path); if (File.Exists(text)) { return text; } } } return null; } private string GetRuntimeIdentifier() { return $"{GetRIDOS()}-{GetRIDArch()}"; } private string GetRIDOS() { return m_platform switch { Platform.Windows => "win", Platform.Mac => "osx", Platform.Linux => "linux", _ => "unknown", }; } private string GetRIDArch() { return UnmanagedLibrary.Is64Bit ? "x64" : "x86"; } } public struct AiScene { public SceneFlags Flags; public IntPtr RootNode; public uint NumMeshes; public IntPtr Meshes; public uint NumMaterials; public IntPtr Materials; public uint NumAnimations; public IntPtr Animations; public uint NumTextures; public IntPtr Textures; public uint NumLights; public IntPtr Lights; public uint NumCameras; public IntPtr Cameras; public IntPtr Metadata; public IntPtr PrivateData; } public struct AiNode { public AiString Name; public Matrix4x4 Transformation; public IntPtr Parent; public uint NumChildren; public IntPtr Children; public uint NumMeshes; public IntPtr Meshes; public IntPtr MetaData; } public struct AiMetadataEntry { public MetaDataType DataType; public IntPtr Data; } public struct AiMetadata { public uint NumProperties; public IntPtr keys; public IntPtr Values; } public struct AiMesh { public PrimitiveType PrimitiveTypes; public uint NumVertices; public uint NumFaces; public IntPtr Vertices; public IntPtr Normals; public IntPtr Tangents; public IntPtr BiTangents; public AiMeshColorArray Colors; public AiMeshTextureCoordinateArray TextureCoords; public AiMeshUVComponentArray NumUVComponents; public IntPtr Faces; public uint NumBones; public IntPtr Bones; public uint MaterialIndex; public AiString Name; public uint NumAnimMeshes; public IntPtr AnimMeshes; public MeshMorphingMethod MorphMethod; public BoundingBox AABB; } public struct AiTexture { private static readonly char[] s_nullFormat = new char[9]; public uint Width; public uint Height; public unsafe fixed sbyte FormatHint[9]; public IntPtr Data; public AiString Filename; public unsafe void SetFormatHint(string formatHint) { int num = s_nullFormat.Length; char[] array = (string.IsNullOrEmpty(formatHint) ? s_nullFormat : formatHint.ToLowerInvariant().ToCharArray()); int num2 = array.Length; fixed (sbyte* formatHint2 = FormatHint) { for (int i = 0; i < num; i++) { formatHint2[i] = (sbyte)((num2 > i) ? array[i] : '\0'); } } } public string GetFormatHint() { return GetFormatHint(in this); } public unsafe static string GetFormatHint(in AiTexture aiTex) { fixed (sbyte* formatHint = aiTex.FormatHint) { return new string(formatHint); } } } public struct AiFace { public uint NumIndices; public IntPtr Indices; } public struct AiBone { public AiString Name; public uint NumWeights; public IntPtr Weights; public Matrix4x4 OffsetMatrix; } public struct AiMaterialProperty { public AiString Key; public TextureType Semantic; public uint Index; public uint DataLength; public PropertyType Type; public IntPtr Data; } public struct AiMaterial { public IntPtr Properties; public uint NumProperties; public uint NumAllocated; } public struct AiNodeAnim { public AiString NodeName; public uint NumPositionKeys; public IntPtr PositionKeys; public uint NumRotationKeys; public IntPtr RotationKeys; public uint NumScalingKeys; public IntPtr ScalingKeys; public AnimationBehaviour Prestate; public AnimationBehaviour PostState; } public struct AiMeshAnim { public AiString Name; public uint NumKeys; public IntPtr Keys; } public struct AiMeshMorphKey { public double Time; public IntPtr Values; public IntPtr Weights; public uint NumValuesAndWeights; } public struct AiMeshMorphAnim { public AiString Name; public uint NumKeys; public IntPtr Keys; } public struct AiAnimation { public AiString Name; public double Duration; public double TicksPerSecond; public uint NumChannels; public IntPtr Channels; public uint NumMeshChannels; public IntPtr MeshChannels; public uint NumMeshMorphChannels; public IntPtr MeshMorphChannels; } public struct AiLight { public AiString Name; public LightSourceType Type; public Vector3D Position; public Vector3D Direction; public Vector3D Up; public float AttenuationConstant; public float AttenuationLinear; public float AttenuationQuadratic; public Color3D ColorDiffuse; public Color3D ColorSpecular; public Color3D ColorAmbient; public float AngleInnerCone; public float AngleOuterCone; public Vector2D AreaSize; } public struct AiCamera { public AiString Name; public Vector3D Position; public Vector3D Up; public Vector3D LookAt; public float HorizontalFOV; public float ClipPlaneNear; public float ClipPlaneFar; public float Aspect; } public struct AiString { public uint Length; public unsafe fixed byte Data[1024]; public AiString(string data) { Length = 0u; SetString(data); } public unsafe static string GetString(in AiString aiStr) { int length = (int)aiStr.Length; if (length > 0) { byte[] array = new byte[length]; fixed (byte* data = aiStr.Data) { MemoryHelper.Read(new IntPtr(data), array, 0, length); } return Encoding.UTF8.GetString(array, 0, length); } return string.Empty; } public string GetString() { return GetString(in this); } public unsafe bool SetString(string data) { if (string.IsNullOrEmpty(data)) { Length = 0u; fixed (byte* data2 = Data) { MemoryHelper.ClearMemory(new IntPtr(data2), 0, 1024); } return true; } if (Encoding.UTF8.GetByteCount(data) <= 1024) { byte[] bytes = Encoding.UTF8.GetBytes(data); if (bytes.Length != 0) { fixed (byte* data3 = Data) { MemoryHelper.Write(new IntPtr(data3), bytes, 0, bytes.Length); } } Length = (uint)bytes.Length; return true; } return false; } public override string ToString() { return GetString(); } } public struct AiLogStream { public IntPtr Callback; public IntPtr UserData; } public struct AiMemoryInfo { public uint Textures; public uint Materials; public uint Meshes; public uint Nodes; public uint Animations; public uint Cameras; public uint Lights; public uint Total; } public struct AiAnimMesh { public AiString Name; public IntPtr Vertices; public IntPtr Normals; public IntPtr Tangents; public IntPtr BiTangents; public AiMeshColorArray Colors; public AiMeshTextureCoordinateArray TextureCoords; public uint NumVertices; public float Weight; } public struct AiImporterDesc { public IntPtr Name; public IntPtr Author; public IntPtr Maintainer; public IntPtr Comments; public ImporterFeatureFlags Flags; public uint MinMajor; public uint MinMinor; public uint MaxMajor; public uint MaxMinor; public IntPtr FileExtensions; } public struct AiExportFormatDesc { public IntPtr FormatId; public IntPtr Description; public IntPtr FileExtension; } public struct AiExportDataBlob { public UIntPtr Size; public IntPtr Data; public AiString Name; public IntPtr NextBlob; } public struct AiFileIO { public IntPtr OpenProc; public IntPtr CloseProc; public IntPtr UserData; } public struct AiFile { public IntPtr ReadProc; public IntPtr WriteProc; public IntPtr TellProc; public IntPtr FileSizeProc; public IntPtr SeekProc; public IntPtr FlushProc; public IntPtr UserData; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void AiLogStreamCallback([In][MarshalAs(UnmanagedType.LPStr)] string msg, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate UIntPtr AiFileWriteProc(IntPtr file, IntPtr dataToWrite, UIntPtr sizeOfElemInBytes, UIntPtr numElements); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate UIntPtr AiFileReadProc(IntPtr file, IntPtr dataToRead, UIntPtr sizeOfElemInBytes, UIntPtr numElements); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate UIntPtr AiFileTellProc(IntPtr file); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void AiFileFlushProc(IntPtr file); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate ReturnCode AiFileSeek(IntPtr file, UIntPtr offset, Origin seekOrigin); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr AiFileOpenProc(IntPtr fileIO, [In][MarshalAs(UnmanagedType.LPStr)] string pathToFile, [In][MarshalAs(UnmanagedType.LPStr)] string mode); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void AiFileCloseProc(IntPtr fileIO, IntPtr file); public struct AiMeshColorArray { private IntPtr m_ptr0; private IntPtr m_ptr1; private IntPtr m_ptr2; private IntPtr m_ptr3; private IntPtr m_ptr4; private IntPtr m_ptr5; private IntPtr m_ptr6; private IntPtr m_ptr7; public int Length => 8; public IntPtr this[int index] { get { return index switch { 0 => m_ptr0, 1 => m_ptr1, 2 => m_ptr2, 3 => m_ptr3, 4 => m_ptr4, 5 => m_ptr5, 6 => m_ptr6, 7 => m_ptr7, _ => IntPtr.Zero, }; } set { switch (index) { case 0: m_ptr0 = value; break; case 1: m_ptr1 = value; break; case 2: m_ptr2 = value; break; case 3: m_ptr3 = value; break; case 4: m_ptr4 = value; break; case 5: m_ptr5 = value; break; case 6: m_ptr6 = value; break; case 7: m_ptr7 = value; break; } } } } public struct AiMeshTextureCoordinateArray { private IntPtr m_ptr0; private IntPtr m_ptr1; private IntPtr m_ptr2; private IntPtr m_ptr3; private IntPtr m_ptr4; private IntPtr m_ptr5; private IntPtr m_ptr6; private IntPtr m_ptr7; public int Length => 8; public IntPtr this[int index] { get { return index switch { 0 => m_ptr0, 1 => m_ptr1, 2 => m_ptr2, 3 => m_ptr3, 4 => m_ptr4, 5 => m_ptr5, 6 => m_ptr6, 7 => m_ptr7, _ => IntPtr.Zero, }; } set { switch (index) { case 0: m_ptr0 = value; break; case 1: m_ptr1 = value; break; case 2: m_ptr2 = value; break; case 3: m_ptr3 = value; break; case 4: m_ptr4 = value; break; case 5: m_ptr5 = value; break; case 6: m_ptr6 = value; break; case 7: m_ptr7 = value; break; } } } } public struct AiMeshUVComponentArray { private uint m_uvw0; private uint m_uvw1; private uint m_uvw2; private uint m_uvw3; private uint m_uvw4; private uint m_uvw5; private uint m_uvw6; private uint m_uvw7; public int Length => 8; public uint this[int index] { get { return index switch { 0 => m_uvw0, 1 => m_uvw1, 2 => m_uvw2, 3 => m_uvw3, 4 => m_uvw4, 5 => m_uvw5, 6 => m_uvw6, 7 => m_uvw7, _ => 0u, }; } set { switch (index) { case 0: m_uvw0 = value; break; case 1: m_uvw1 = value; break; case 2: m_uvw2 = value; break; case 3: m_uvw3 = value; break; case 4: m_uvw4 = value; break; case 5: m_uvw5 = value; break; case 6: m_uvw6 = value; break; case 7: m_uvw7 = value; break; } } } } } namespace Assimp.Configs { public abstract class PropertyConfig { private string m_name; public string Name => m_name; protected PropertyConfig(string name) { m_name = name; } public abstract void SetDefaultValue(); internal void ApplyValue(IntPtr propStore) { OnApplyValue(propStore); } protected abstract void OnApplyValue(IntPtr propStore); } public class IntegerPropertyConfig : PropertyConfig { private int m_value; private int m_defaultValue; public int Value { get { return m_value; } set { m_value = value; } } public int DefaultValue => m_defaultValue; public IntegerPropertyConfig(string name, int value) : this(name, value, 0) { } public IntegerPropertyConfig(string name, int value, int defaultValue) : base(name) { m_value = value; m_defaultValue = defaultValue; } public override void SetDefaultValue() { m_value = m_defaultValue; } protected override void OnApplyValue(IntPtr propStore) { if (propStore != IntPtr.Zero) { AssimpLibrary.Instance.SetImportPropertyInteger(propStore, base.Name, m_value); } } } public class FloatPropertyConfig : PropertyConfig { private float m_value; private float m_defaultValue; public float Value { get { return m_value; } set { m_value = value; } } public float DefaultValue => m_defaultValue; public FloatPropertyConfig(string name, float value) : this(name, value, 0f) { } public FloatPropertyConfig(string name, float value, float defaultValue) : base(name) { m_value = value; m_defaultValue = defaultValue; } public override void SetDefaultValue() { m_value = m_defaultValue; } protected override void OnApplyValue(IntPtr propStore) { if (propStore != IntPtr.Zero) { AssimpLibrary.Instance.SetImportPropertyFloat(propStore, base.Name, m_value); } } } public class MatrixPropertyConfig : PropertyConfig { private Matrix4x4 m_value; private Matrix4x4 m_defaultValue; public Matrix4x4 Value { get { return m_value; } set { m_value = value; } } public Matrix4x4 DefaultValue => m_defaultValue; public MatrixPropertyConfig(string name, Matrix4x4 value) : this(name, value, Matrix4x4.Identity) { } public MatrixPropertyConfig(string name, Matrix4x4 value, Matrix4x4 defaultValue) : base(name) { m_value = value; m_defaultValue = defaultValue; } public override void SetDefaultValue() { m_value = m_defaultValue; } protected override void OnApplyValue(IntPtr propStore) { if (propStore != IntPtr.Zero) { AssimpLibrary.Instance.SetImportPropertyMatrix(propStore, base.Name, m_value); } } } public class BooleanPropertyConfig : PropertyConfig { private bool m_value; private bool m_defaultValue; public bool Value { get { return m_value; } set { m_value = value; } } public bool DefaultValue => m_defaultValue; public BooleanPropertyConfig(string name, bool value) : this(name, value, defaultValue: false) { } public BooleanPropertyConfig(string name, bool value, bool defaultValue) : base(name) { m_value = value; m_defaultValue = defaultValue; } public override void SetDefaultValue() { m_value = m_defaultValue; } protected override void OnApplyValue(IntPtr propStore) { if (propStore != IntPtr.Zero) { int value = (m_value ? 1 : 0); AssimpLibrary.Instance.SetImportPropertyInteger(propStore, base.Name, value); } } } public class StringPropertyConfig : PropertyConfig { private string m_value; private string m_defaultValue; public string Value { get { return m_value; } set { m_value = value; } } public string DefaultValue => m_defaultValue; public StringPropertyConfig(string name, string value) : this(name, value, string.Empty) { } public StringPropertyConfig(string name, string value, string defaultValue) : base(name) { m_value = value; m_defaultValue = defaultValue; } public override void SetDefaultValue() { m_value = m_defaultValue; } protected override void OnApplyValue(IntPtr propStore) { if (propStore != IntPtr.Zero) { AssimpLibrary.Instance.SetImportPropertyString(propStore, base.Name, m_value); } } protected static string ProcessNames(string[] names) { if (names == null || names.Length == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (string value in names) { if (!string.IsNullOrEmpty(value)) { stringBuilder.Append(value); stringBuilder.Append(' '); } } return stringBuilder.ToString(); } } public sealed class MeasureTimeConfig : BooleanPropertyConfig { public static string MeasureTimeConfigName => "GLOB_MEASURE_TIME"; public MeasureTimeConfig(bool measureTime) : base(MeasureTimeConfigName, measureTime, defaultValue: false) { } } public sealed class MultithreadingConfig : IntegerPropertyConfig { public static string MultithreadingConfigName => "GLOB_MULTITHREADING"; public MultithreadingConfig(int value) : base(MultithreadingConfigName, value, -1) { } } public sealed class NoSkeletonMeshesConfig : BooleanPropertyConfig { public static string NoSkeletonMeshesConfigName => "IMPORT_NO_SKELETON_MESHES"; public NoSkeletonMeshesConfig(bool disableDummySkeletonMeshes) : base(NoSkeletonMeshesConfigName, disableDummySkeletonMeshes, defaultValue: false) { } } public sealed class TangentSmoothingAngleConfig : FloatPropertyConfig { public static string TangentSmoothingAngleConfigName => "PP_CT_MAX_SMOOTHING_ANGLE"; public TangentSmoothingAngleConfig(float angle) : base(TangentSmoothingAngleConfigName, Math.Min(angle, 175f), 45f) { } } public sealed class NormalSmoothingAngleConfig : FloatPropertyConfig { public static string NormalSmoothingAngleConfigName => "PP_GSN_MAX_SMOOTHING_ANGLE"; public NormalSmoothingAngleConfig(float angle) : base(NormalSmoothingAngleConfigName, Math.Min(angle, 175f), 175f) { } } public sealed class MDLColorMapConfig : StringPropertyConfig { public static string MDLColorMapConfigName => "IMPORT_MDL_COLORMAP"; public MDLColorMapConfig(string fileName) : base(MDLColorMapConfigName, string.IsNullOrEmpty(fileName) ? "colormap.lmp" : fileName, "colormap.lmp") { } } public sealed class MaterialExcludeListConfig : StringPropertyConfig { public static string MaterialExcludeListConfigName => "PP_RRM_EXCLUDE_LIST"; public MaterialExcludeListConfig(string[] materialNames) : base(MaterialExcludeListConfigName, StringPropertyConfig.ProcessNames(materialNames), string.Empty) { } } public sealed class KeepSceneHierarchyConfig : BooleanPropertyConfig { public static string KeepSceneHierarchyConfigName => "PP_PTV_KEEP_HIERARCHY"; public KeepSceneHierarchyConfig(bool keepHierarchy) : base(KeepSceneHierarchyConfigName, keepHierarchy, defaultValue: false) { } } public sealed class NormalizeVertexComponentsConfig : BooleanPropertyConfig { public static string NormalizeVertexComponentsConfigName => "PP_PTV_NORMALIZE"; public NormalizeVertexComponentsConfig(bool normalizeVertexComponents) : base(NormalizeVertexComponentsConfigName, normalizeVertexComponents, defaultValue: false) { } } public sealed class RemoveDegeneratePrimitivesConfig : BooleanPropertyConfig { public static string RemoveDegeneratePrimitivesConfigName => "PP_FD_REMOVE"; public RemoveDegeneratePrimitivesConfig(bool removeDegenerates) : base(RemoveDegeneratePrimitivesConfigName, removeDegenerates, defaultValue: false) { } } public sealed class RemoveDegeneratePrimitivesCheckAreaConfig : BooleanPropertyConfig { public static string RemoveDegeneratePrimitivesCheckAreaConfigName => "PP_FD_CHECKAREA"; public RemoveDegeneratePrimitivesCheckAreaConfig(bool checkArea) : base(RemoveDegeneratePrimitivesCheckAreaConfigName, checkArea, defaultValue: false) { } } public sealed class NodeExcludeListConfig : StringPropertyConfig { public static string NodeExcludeListConfigName => "PP_OG_EXCLUDE_LIST"; public NodeExcludeListConfig(params string[] nodeNames) : base(NodeExcludeListConfigName, StringPropertyConfig.ProcessNames(nodeNames), string.Empty) { } } public sealed class MeshTriangleLimitConfig : IntegerPropertyConfig { public static string MeshTriangleLimitConfigName => "PP_SLM_TRIANGLE_LIMIT"; public static int MeshTriangleLimitConfigDefaultValue => 1000000; public MeshTriangleLimitConfig(int maxTriangleLimit) : base(MeshTriangleLimitConfigName, maxTriangleLimit, MeshTriangleLimitConfigDefaultValue) { } } public sealed class MeshVertexLimitConfig : IntegerPropertyConfig { public static string MeshVertexLimitConfigName => "PP_SLM_VERTEX_LIMIT"; public static int MeshVertexLimitConfigDefaultValue => 1000000; public MeshVertexLimitConfig(int maxVertexLimit) : base(MeshVertexLimitConfigName, maxVertexLimit, MeshVertexLimitConfigDefaultValue) { } } public sealed class VertexBoneWeightLimitConfig : IntegerPropertyConfig { public static string VertexBoneWeightLimitConfigName => "PP_LBW_MAX_WEIGHTS"; public static int VertexBoneWeightLimitConfigDefaultValue => 4; public VertexBoneWeightLimitConfig(int maxBoneWeights) : base(VertexBoneWeightLimitConfigName, maxBoneWeights, VertexBoneWeightLimitConfigDefaultValue) { } } public sealed class VertexCacheSizeConfig : IntegerPropertyConfig { public static string VertexCacheSizeConfigName => "PP_ICL_PTCACHE_SIZE"; public static int VertexCacheSizeConfigDefaultValue => 12; public VertexCacheSizeConfig(int vertexCacheSize) : base(VertexCacheSizeConfigName, vertexCacheSize, VertexCacheSizeConfigDefaultValue) { } } public sealed class RemoveComponentConfig : IntegerPropertyConfig { public static string RemoveComponentConfigName => "PP_RVC_FLAGS"; public RemoveComponentConfig(ExcludeComponent componentsToExclude) : base(RemoveComponentConfigName, (int)componentsToExclude, 0) { } } public sealed class SortByPrimitiveTypeConfig : IntegerPropertyConfig { public static string SortByPrimitiveTypeConfigName => "PP_SBP_REMOVE"; public SortByPrimitiveTypeConfig(PrimitiveType typesToRemove) : base(SortByPrimitiveTypeConfigName, (int)typesToRemove, 0) { } } public sealed class AnimationAccuracyConfig : FloatPropertyConfig { public static string AnimationAccuracyConfigName => "PP_FID_ANIM_ACCURACY"; public AnimationAccuracyConfig(float episilon) : base(AnimationAccuracyConfigName, episilon, 0f) { } } public sealed class IgnoreTextureCoordinatesConfig : BooleanPropertyConfig { public static string IgnoreTextureCoordinatesConfigName => "PP_FID_IGNORE_TEXTURECOORDS"; public IgnoreTextureCoordinatesConfig(bool ignoreTexCoords) : base(IgnoreTextureCoordinatesConfigName, ignoreTexCoords, defaultValue: false) { } } public sealed class TransformUVConfig : IntegerPropertyConfig { public static string TransformUVConfigName => "PP_TUV_EVALUATE"; public TransformUVConfig(UVTransformFlags transformFlags) : base(TransformUVConfigName, (int)transformFlags, 7) { } } public sealed class FavorSpeedConfig : BooleanPropertyConfig { public static string FavorSpeedConfigName => "FAVOUR_SPEED"; public FavorSpeedConfig(bool favorSpeed) : base(FavorSpeedConfigName, favorSpeed, defaultValue: false) { } } public sealed class MaxBoneCountConfig : IntegerPropertyConfig { public static string MaxBoneCountConfigName => "PP_SBBC_MAX_BONES"; public MaxBoneCountConfig(int maxBones) : base(MaxBoneCountConfigName, maxBones, 60) { } } public sealed class TangentTextureChannelIndexConfig : IntegerPropertyConfig { public static string TangentTextureChannelIndexConfigName => "PP_CT_TEXTURE_CHANNEL_INDEX"; public TangentTextureChannelIndexConfig(int textureChannelIndex) : base(TangentTextureChannelIndexConfigName, textureChannelIndex, 0) { } } public sealed class DeboneThresholdConfig : FloatPropertyConfig { public static string DeboneThresholdConfigName => "PP_DB_THRESHOLD"; public DeboneThresholdConfig(float threshold) : base(DeboneThresholdConfigName, threshold, 1f) { } } public sealed class DeboneAllOrNoneConfig : BooleanPropertyConfig { public static string DeboneAllOrNoneConfigName => "PP_DB_ALL_OR_NONE"; public DeboneAllOrNoneConfig(bool allOrNone) : base(DeboneAllOrNoneConfigName, allOrNone, defaultValue: false) { } } public sealed class RootTransformationConfig : MatrixPropertyConfig { public static string RootTransformationConfigName => "PP_PTV_ROOT_TRANSFORMATION"; public RootTransformationConfig(Matrix4x4 rootTransform) : base(RootTransformationConfigName, rootTransform, Matrix4x4.Identity) { } protected override void OnApplyValue(IntPtr propStore) { if (propStore != IntPtr.Zero) { AssimpLibrary.Instance.SetImportPropertyInteger(propStore, "PP_PTV_ADD_ROOT_TRANSFORMATION", 1); AssimpLibrary.Instance.SetImportPropertyMatrix(propStore, RootTransformationConfigName, base.Value); } } } public sealed class GlobalScaleConfig : FloatPropertyConfig { public static string GlobalScaleConfigName => "GLOBAL_SCALE_FACTOR"; public GlobalScaleConfig(float globalScale) : base(GlobalScaleConfigName, globalScale, 1f) { } } public sealed class AppScaleConfig : FloatPropertyConfig { public static string AppScaleConfigName => "APP_SCALE_FACTOR"; public AppScaleConfig(float appScale) : base(AppScaleConfigName, appScale, 1f) { } } public sealed class GlobalKeyFrameImportConfig : IntegerPropertyConfig { public static string GlobalKeyFrameImportConfigName => "IMPORT_GLOBAL_KEYFRAME"; public GlobalKeyFrameImportConfig(int keyFrame) : base(GlobalKeyFrameImportConfigName, keyFrame, 0) { } } public sealed class MD3KeyFrameImportConfig : IntegerPropertyConfig { public static string MD3KeyFrameImportConfigName => "IMPORT_MD3_KEYFRAME"; public MD3KeyFrameImportConfig(int keyFrame) : base(MD3KeyFrameImportConfigName, keyFrame, 0) { } } public sealed class MD2KeyFrameImportConfig : IntegerPropertyConfig { public static string MD2KeyFrameImportConfigName => "IMPORT_MD3_KEYFRAME"; public MD2KeyFrameImportConfig(int keyFrame) : base(MD2KeyFrameImportConfigName, keyFrame, 0) { } } public sealed class MDLKeyFrameImportConfig : IntegerPropertyConfig { public static string MDLKeyFrameImportConfigName => "IMPORT_MDL_KEYFRAME"; public MDLKeyFrameImportConfig(int keyFrame) : base(MDLKeyFrameImportConfigName, keyFrame, 0) { } } public sealed class SMDKeyFrameImportConfig : IntegerPropertyConfig { public static string SMDKeyFrameImportConfigName => "IMPORT_SMD_KEYFRAME"; public SMDKeyFrameImportConfig(int keyFrame) : base(SMDKeyFrameImportConfigName, keyFrame, 0) { } } public sealed class UnrealKeyFrameImportConfig : IntegerPropertyConfig { public static string UnrealKeyFrameImportConfigName => "IMPORT_UNREAL_KEYFRAME"; public UnrealKeyFrameImportConfig(int keyFrame) : base(UnrealKeyFrameImportConfigName, keyFrame, 0) { } } public sealed class ACSeparateBackfaceCullConfig : BooleanPropertyConfig { public static string ACSeparateBackfaceCullConfigName => "IMPORT_AC_SEPARATE_BFCULL"; public ACSeparateBackfaceCullConfig(bool separateBackfaces) : base(ACSeparateBackfaceCullConfigName, separateBackfaces, defaultValue: true) { } } public sealed class ACEvaluateSubdivisionConfig : BooleanPropertyConfig { public static string ACEvaluateSubdivisionConfigName => "IMPORT_AC_EVAL_SUBDIVISION"; public ACEvaluateSubdivisionConfig(bool evaluateSubdivision) : base(ACEvaluateSubdivisionConfigName, evaluateSubdivision, defaultValue: true) { } } public sealed class UnrealHandleFlagsConfig : BooleanPropertyConfig { public static string UnrealHandleFlagsConfigName => "UNREAL_HANDLE_FLAGS"; public UnrealHandleFlagsConfig(bool handleFlags) : base(UnrealHandleFlagsConfigName, handleFlags, defaultValue: true) { } } public sealed class TerragenComputeTexCoordsConfig : BooleanPropertyConfig { public static string TerragenComputeTexCoordsConfigName => "IMPORT_TER_MAKE_UVS"; public TerragenComputeTexCoordsConfig(bool computeTexCoords) : base(TerragenComputeTexCoordsConfigName, computeTexCoords, defaultValue: false) { } } public sealed class ASEReconstructNormalsConfig : BooleanPropertyConfig { public static string ASEReconstructNormalsConfigName => "IMPORT_ASE_RECONSTRUCT_NORMALS"; public ASEReconstructNormalsConfig(bool reconstructNormals) : base(ASEReconstructNormalsConfigName, reconstructNormals, defaultValue: true) { } } public sealed class MD3HandleMultiPartConfig : BooleanPropertyConfig { public static string MD3HandleMultiPartConfigName => "IMPORT_MD3_HANDLE_MULTIPART"; public MD3HandleMultiPartConfig(bool handleMultiParts) : base(MD3HandleMultiPartConfigName, handleMultiParts, defaultValue: true) { } } public sealed class MD3SkinNameConfig : StringPropertyConfig { public static string MD3SkinNameConfigName => "IMPORT_MD3_SKIN_NAME"; public MD3SkinNameConfig(string skinName) : base(MD3SkinNameConfigName, skinName, "default") { } } public sealed class MD3ShaderSourceConfig : StringPropertyConfig { public static string MD3ShaderSourceConfigName => "IMPORT_MD3_SHADER_SRC"; public MD3ShaderSourceConfig(string shaderFile) : base(MD3ShaderSourceConfigName, shaderFile, string.Empty) { } } public sealed class LWOImportOneLayerConfig : BooleanPropertyConfig { public static string LWOImportOneLayerConfigName => "IMPORT_LWO_ONE_LAYER_ONLY"; public LWOImportOneLayerConfig(bool importOneLayerOnly) : base(LWOImportOneLayerConfigName, importOneLayerOnly, defaultValue: false) { } } public sealed class MD5NoAnimationAutoLoadConfig : BooleanPropertyConfig { public static string MD5NoAnimationAutoLoadConfigName => "IMPORT_MD5_NO_ANIM_AUTOLOAD"; public MD5NoAnimationAutoLoadConfig(bool noAutoLoadAnim) : base(MD5NoAnimationAutoLoadConfigName, noAutoLoadAnim, defaultValue: false) { } } public sealed class LWSAnimationStartConfig : IntegerPropertyConfig { public static string LWSAnimationStartConfigName => "IMPORT_LWS_ANIM_START"; public LWSAnimationStartConfig(int animStart) : base(LWSAnimationStartConfigName, animStart, -1) { } } public sealed class LWSAnimationEndConfig : IntegerPropertyConfig { public static string LWSAnimationEndConfigName => "IMPORT_LWS_ANIM_END"; public LWSAnimationEndConfig(int animEnd) : base(LWSAnimationEndConfigName, animEnd, -1) { } } public sealed class IRRAnimationFrameRateConfig : IntegerPropertyConfig { public static string IRRAnimationFrameRateConfigName => "IMPORT_IRR_ANIM_FPS"; public IRRAnimationFrameRateConfig(int frameRate) : base(IRRAnimationFrameRateConfigName, frameRate, 100) { } } public sealed class OgreMaterialFileConfig : StringPropertyConfig { public static string OgreMaterialFileConfigName => "IMPORT_OGRE_MATERIAL_FILE"; public OgreMaterialFileConfig(string materialFileName) : base(OgreMaterialFileConfigName, materialFileName, "Scene.Material") { } } public sealed class OgreTextureTypeFromFilenameConfig : BooleanPropertyConfig { public static string OgreTextureTypeFromFilenameConfigName => "IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME"; public OgreTextureTypeFromFilenameConfig(bool fileNameDefinesTextureUsage) : base(OgreTextureTypeFromFilenameConfigName, fileNameDefinesTextureUsage, defaultValue: false) { } } public sealed class IFCSkipSpaceRepresentationsConfig : BooleanPropertyConfig { public static string IFCSkipSpaceRepresentationsConfigName => "IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS"; public IFCSkipSpaceRepresentationsConfig(bool skipSpaceRepresentations) : base(IFCSkipSpaceRepresentationsConfigName, skipSpaceRepresentations, defaultValue: true) { } } public sealed class IFCUseCustomTriangulationConfig : BooleanPropertyConfig { public static string IFCUseCustomTriangulationConfigName => "IMPORT_IFC_CUSTOM_TRIANGULATION"; public IFCUseCustomTriangulationConfig(bool useCustomTriangulation) : base(IFCUseCustomTriangulationConfigName, useCustomTriangulation, defaultValue: true) { } } public sealed class IFCSmoothingAngleConfig : FloatPropertyConfig { public static string IFCSmoothingAngleConfigName => "IMPORT_IFC_SMOOTHING_ANGLE"; public IFCSmoothingAngleConfig(float angle) : base(IFCSmoothingAngleConfigName, angle, 10f) { } } public sealed class IFCCylindricalTessellationConfig : IntegerPropertyConfig { public static string IFCCylindricalTessellationConfigName => "IMPORT_IFC_CYLINDRICAL_TESSELLATION"; public IFCCylindricalTessellationConfig(int tessellation) : base(IFCCylindricalTessellationConfigName, tessellation, 32) { } } public sealed class ColladaIgnoreUpDirectionConfig : BooleanPropertyConfig { public static string ColladaIgnoreUpDirectionConfigName => "IMPORT_COLLADA_IGNORE_UP_DIRECTION"; public ColladaIgnoreUpDirectionConfig(bool ignoreUpDirection) : base(ColladaIgnoreUpDirectionConfigName, ignoreUpDirection, defaultValue: false) { } } public sealed class ColladaUseColladaNamesConfig : BooleanPropertyConfig { public static string ColladaUseColladaNamesConfigName => "IMPORT_COLLADA_USE_COLLADA_NAMES"; public ColladaUseColladaNamesConfig(bool useColladaNames) : base(ColladaUseColladaNamesConfigName, useColladaNames, defaultValue: false) { } } public sealed class FBXImportAllGeometryLayersConfig : BooleanPropertyConfig { public static string FBXImportAllGeometryLayersConfigName => "IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS"; public FBXImportAllGeometryLayersConfig(bool importAllGeometryLayers) : base(FBXImportAllGeometryLayersConfigName, importAllGeometryLayers, defaultValue: true) { } } public sealed class FBXImportAllMaterialsConfig : BooleanPropertyConfig { public static string FBXImportAllMaterialsConfigName => "IMPORT_FBX_READ_ALL_MATERIALS"; public FBXImportAllMaterialsConfig(bool importAllMaterials) : base(FBXImportAllMaterialsConfigName, importAllMaterials, defaultValue: false) { } } public sealed class FBXImportMaterialsConfig : BooleanPropertyConfig { public static string FBXImportMaterialsConfigName => "IMPORT_FBX_READ_MATERIALS"; public FBXImportMaterialsConfig(bool importMaterials) : base(FBXImportMaterialsConfigName, importMaterials, defaultValue: true) { } } public sealed class FBXImportEmbeddedTexturesConfig : BooleanPropertyConfig { public static string FBXImportEmbeddedTexturesConfigName => "IMPORT_FBX_READ_TEXTURES"; public FBXImportEmbeddedTexturesConfig(bool importTextures) : base(FBXImportEmbeddedTexturesConfigName, importTextures, defaultValue: true) { } } public sealed class FBXImportEmbeddedTexturesLegacyNamingConfig : BooleanPropertyConfig { public static string FBXImportEmbeddedTexturesLegacyNamingConfigName => "AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING"; public FBXImportEmbeddedTexturesLegacyNamingConfig(bool searchEmbeddedTextures) : base(FBXImportEmbeddedTexturesLegacyNamingConfigName, searchEmbeddedTextures, defaultValue: false) { } } public sealed class FBXImportCamerasConfig : BooleanPropertyConfig { public static string FBXImportCamerasConfigName => "IMPORT_FBX_READ_CAMERAS"; public FBXImportCamerasConfig(bool importCameras) : base(FBXImportCamerasConfigName, importCameras, defaultValue: true) { } } public sealed class FBXImportLightsConfig : BooleanPropertyConfig { public static string FBXImportLightsConfigName => "IMPORT_FBX_READ_LIGHTS"; public FBXImportLightsConfig(bool importLights) : base(FBXImportLightsConfigName, importLights, defaultValue: true) { } } public sealed class FBXImportAnimationsConfig : BooleanPropertyConfig { public static string FBXImportAnimationsConfigName => "IMPORT_FBX_READ_ANIMATIONS"; public FBXImportAnimationsConfig(bool importAnimations) : base(FBXImportAnimationsConfigName, importAnimations, defaultValue: true) { } } public sealed class FBXStrictModeConfig : BooleanPropertyConfig { public static string FBXStrictModeConfigName => "IMPORT_FBX_STRICT_MODE"; public FBXStrictModeConfig(bool useStrictMode) : base(FBXStrictModeConfigName, useStrictMode, defaultValue: false) { } } public sealed class FBXPreservePivotsConfig : BooleanPropertyConfig { public static string FBXPreservePivotsConfigName => "IMPORT_FBX_PRESERVE_PIVOTS"; public FBXPreservePivotsConfig(bool preservePivots) : base(FBXPreservePivotsConfigName, preservePivots, defaultValue: true) { } } public sealed class FBXOptimizeEmptyAnimationCurvesConfig : BooleanPropertyConfig { public static string FBXOptimizeEmptyAnimationCurvesConfigName => "IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES"; public FBXOptimizeEmptyAnimationCurvesConfig(bool optimizeEmptyAnimations) : base(FBXOptimizeEmptyAnimationCurvesConfigName, optimizeEmptyAnimations, defaultValue: true) { } } public sealed class FBXConvertToMetersConfig : BooleanPropertyConfig { public static string FBXConvertToMetersConfigName => "AI_CONFIG_FBX_CONVERT_TO_M"; public FBXConvertToMetersConfig(bool convertToMeters) : base(FBXConvertToMetersConfigName, convertToMeters, defaultValue: false) { } } public sealed class SmdLoadAnimationListConfig : BooleanPropertyConfig { public static string SmdLoadAnimationListConfigName => "IMPORT_SMD_LOAD_ANIMATION_LIST"; public SmdLoadAnimationListConfig(bool loadAnimList) : base(SmdLoadAnimationListConfigName, loadAnimList, defaultValue: true) { } } public sealed class RemoveEmptyBonesConfig : BooleanPropertyConfig { public static string RemoveEmptyBonesConfigName => "AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES"; public RemoveEmptyBonesConfig(bool removeEmptyBones) : base(RemoveEmptyBonesConfigName, removeEmptyBones, defaultValue: true) { } } public sealed class XFileUseDoublesConfig : BooleanPropertyConfig { public static string XFileUseDoublesConfigName => "EXPORT_XFILE_64BIT"; public XFileUseDoublesConfig(bool useDoubles) : base(XFileUseDoublesConfigName, useDoubles, defaultValue: false) { } } public sealed class ExportPointCloudsConfig : BooleanPropertyConfig { public static string ExportPointCloudsConfigName => "EXPORT_POINT_CLOUDS"; public ExportPointCloudsConfig(bool exportPointCloud) : base(ExportPointCloudsConfigName, exportPointCloud, defaultValue: false) { } } }