using System; using System.Numerics; using System.Text; using Auspex.Vfx.Native; namespace Auspex.Vfx.Internal; public sealed class VfxInstance : IDisposable { internal string? path; internal Vector3 position; internal Vector3 size; internal float rotation; internal Vector4 color; internal unsafe VfxData* data; internal bool libOwned; public unsafe bool IsValid { get { if (data != null) { return data->Instance != null; } return false; } } public unsafe static VfxInstance Create(string path, Vector3 position, Vector3 size, float rotation, Vector4? color = null) { VfxData* ptr = CreateVfxInternal(path, position, size, rotation, color ?? Vector4.One); return new VfxInstance(path, ptr, position, size, rotation, color ?? Vector4.One) { libOwned = true }; } public unsafe static VfxInstance Wrap(VfxData* data, Vector3 position, Vector3 size, float rotation, Vector4? color = null) { return new VfxInstance(null, data, position, size, rotation, color ?? Vector4.One); } private unsafe VfxInstance(string? path, VfxData* data, Vector3 position, Vector3 size, float rotation, Vector4 color) { if (data == null) { throw new ArgumentNullException("data"); } this.path = path; this.data = data; this.position = position; this.size = size; this.rotation = rotation; this.color = color; } public unsafe void Dispose() { if (libOwned && IsValid) { VfxNativeBridge.DestroyVfx(data); } data = null; GC.SuppressFinalize(this); } private unsafe static VfxData* CreateVfxInternal(string path, Vector3 position, Vector3 size, float rotation, Vector4 color) { byte[] bytes = Encoding.UTF8.GetBytes(path + "\0"); VfxInitData vfxInitData = default(VfxInitData); VfxNativeBridge.VfxInitDataCtor(&vfxInitData); fixed (byte* ptr = bytes) { VfxData* intPtr = VfxNativeBridge.CreateVfx(ptr, &vfxInitData, 2, 0, position.X, position.Y, position.Z, size.X, size.Y, size.Z, rotation, 1f, -1); intPtr->Instance->Color = color; return intPtr; } } public void UpdatePosition(Vector3 position) { UpdateTransform(position, size, rotation); } public void UpdateScale(Vector3 scale) { UpdateTransform(position, scale, rotation); } public void UpdateRotation(float rotation) { UpdateTransform(position, size, rotation); } public unsafe void UpdateTransform(Vector3 position, Vector3 size, float rotation) { MaybeRefreshVfxResourceInstance(); if (!IsValid || (this.position == position && this.rotation == rotation && this.size == size)) { return; } this.position = position; this.size = size; this.rotation = rotation; using PinnedAlignedMatrix pinnedAlignedMatrix = new PinnedAlignedMatrix(); Matrix4x4* get = pinnedAlignedMatrix.Get; get->M11 = size.X; get->M12 = 0f; get->M13 = 0f; get->M14 = 0f; get->M21 = 0f; get->M22 = size.Y; get->M23 = 0f; get->M24 = 0f; get->M31 = 0f; get->M32 = 0f; get->M33 = size.Z; get->M34 = 0f; VfxNativeBridge.RotateMatrix(get, rotation); get->M41 = position.X; get->M42 = position.Y; get->M43 = position.Z; get->M44 = 1f; VfxNativeBridge.UpdateVfxTransform(data->Instance, get); } public unsafe void UpdateColor(Vector4 color) { if (IsValid && !(this.color == color)) { this.color = color; VfxNativeBridge.UpdateVfxColor(data->Instance, color.X, color.Y, color.Z, color.W); } } private unsafe void MaybeRefreshVfxResourceInstance() { if (!IsValid && libOwned && path != null) { data = CreateVfxInternal(path, position, size, rotation, color); } } }