reworked mesh decimation yes
This commit is contained in:
144
LightlessSync/ThirdParty/Nanomesh/Base/BoneWeight.cs
vendored
Normal file
144
LightlessSync/ThirdParty/Nanomesh/Base/BoneWeight.cs
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct BoneWeight : IEquatable<BoneWeight>, IInterpolable<BoneWeight>
|
||||
{
|
||||
public readonly int index0;
|
||||
public readonly int index1;
|
||||
public readonly int index2;
|
||||
public readonly int index3;
|
||||
public readonly float weight0;
|
||||
public readonly float weight1;
|
||||
public readonly float weight2;
|
||||
public readonly float weight3;
|
||||
|
||||
public int GetIndex(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0: return index0;
|
||||
case 1: return index1;
|
||||
case 2: return index2;
|
||||
case 3: return index3;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetWeight(int i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 0: return weight0;
|
||||
case 1: return weight1;
|
||||
case 2: return weight2;
|
||||
case 3: return weight3;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public BoneWeight(int index0, int index1, int index2, int index3, float weight0, float weight1, float weight2, float weight3)
|
||||
{
|
||||
this.index0 = index0;
|
||||
this.index1 = index1;
|
||||
this.index2 = index2;
|
||||
this.index3 = index3;
|
||||
this.weight0 = weight0;
|
||||
this.weight1 = weight1;
|
||||
this.weight2 = weight2;
|
||||
this.weight3 = weight3;
|
||||
}
|
||||
|
||||
public bool Equals(BoneWeight other)
|
||||
{
|
||||
return index0 == other.index0
|
||||
&& index1 == other.index1
|
||||
&& index2 == other.index2
|
||||
&& index3 == other.index3
|
||||
&& weight0 == other.weight0
|
||||
&& weight1 == other.weight1
|
||||
&& weight2 == other.weight2
|
||||
&& weight3 == other.weight3;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 31 + index0;
|
||||
hash = hash * 31 + index1;
|
||||
hash = hash * 31 + index2;
|
||||
hash = hash * 31 + index3;
|
||||
hash = hash * 31 + weight0.GetHashCode();
|
||||
hash = hash * 31 + weight1.GetHashCode();
|
||||
hash = hash * 31 + weight2.GetHashCode();
|
||||
hash = hash * 31 + weight3.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe BoneWeight Interpolate(BoneWeight other, double ratio)
|
||||
{
|
||||
BoneWeight boneWeightA = this;
|
||||
BoneWeight boneWeightB = other;
|
||||
|
||||
Dictionary<int, float> newBoneWeight = new Dictionary<int, float>();
|
||||
|
||||
// Map weights and indices
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
newBoneWeight.TryAdd(boneWeightA.GetIndex(i), 0);
|
||||
newBoneWeight.TryAdd(boneWeightB.GetIndex(i), 0);
|
||||
newBoneWeight[boneWeightA.GetIndex(i)] += (float)((1 - ratio) * boneWeightA.GetWeight(i));
|
||||
newBoneWeight[boneWeightB.GetIndex(i)] += (float)(ratio * boneWeightB.GetWeight(i));
|
||||
}
|
||||
|
||||
int* newIndices = stackalloc int[4];
|
||||
float* newWeights = stackalloc float[4];
|
||||
|
||||
// Order from biggest to smallest weight, and drop bones above 4th
|
||||
float totalWeight = 0;
|
||||
int k = 0;
|
||||
foreach (KeyValuePair<int, float> boneWeightN in newBoneWeight.OrderByDescending(x => x.Value))
|
||||
{
|
||||
newIndices[k] = boneWeightN.Key;
|
||||
newWeights[k] = boneWeightN.Value;
|
||||
totalWeight += boneWeightN.Value;
|
||||
if (k == 3)
|
||||
break;
|
||||
k++;
|
||||
}
|
||||
|
||||
var sumA = boneWeightA.weight0 + boneWeightA.weight1 + boneWeightA.weight2 + boneWeightA.weight3;
|
||||
var sumB = boneWeightB.weight0 + boneWeightB.weight1 + boneWeightB.weight2 + boneWeightB.weight3;
|
||||
var targetSum = (float)((1d - ratio) * sumA + ratio * sumB);
|
||||
|
||||
// Normalize and re-scale to preserve original weight sum.
|
||||
if (totalWeight > 0f)
|
||||
{
|
||||
var scale = targetSum / totalWeight;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
newWeights[j] *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
return new BoneWeight(
|
||||
newIndices[0], newIndices[1], newIndices[2], newIndices[3],
|
||||
newWeights[0], newWeights[1], newWeights[2], newWeights[3]);
|
||||
|
||||
//return new BoneWeight(
|
||||
// ratio < 0.5f ? index0 : other.index0,
|
||||
// ratio < 0.5f ? index1 : other.index1,
|
||||
// ratio < 0.5f ? index2 : other.index2,
|
||||
// ratio < 0.5f ? index3 : other.index3,
|
||||
// (float)(ratio * weight0 + (1 - ratio) * other.weight0),
|
||||
// (float)(ratio * weight1 + (1 - ratio) * other.weight1),
|
||||
// (float)(ratio * weight2 + (1 - ratio) * other.weight2),
|
||||
// (float)(ratio * weight3 + (1 - ratio) * other.weight3));
|
||||
}
|
||||
}
|
||||
}
|
||||
110
LightlessSync/ThirdParty/Nanomesh/Base/Color32.cs
vendored
Normal file
110
LightlessSync/ThirdParty/Nanomesh/Base/Color32.cs
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public readonly struct Color32 : IEquatable<Color32>, IInterpolable<Color32>
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
internal readonly int rgba;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public readonly byte r;
|
||||
|
||||
[FieldOffset(1)]
|
||||
public readonly byte g;
|
||||
|
||||
[FieldOffset(2)]
|
||||
public readonly byte b;
|
||||
|
||||
[FieldOffset(3)]
|
||||
public readonly byte a;
|
||||
|
||||
public Color32(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
rgba = 0;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color32(float r, float g, float b, float a)
|
||||
{
|
||||
rgba = 0;
|
||||
this.r = (byte)MathF.Round(r);
|
||||
this.g = (byte)MathF.Round(g);
|
||||
this.b = (byte)MathF.Round(b);
|
||||
this.a = (byte)MathF.Round(a);
|
||||
}
|
||||
|
||||
public Color32(double r, double g, double b, double a)
|
||||
{
|
||||
rgba = 0;
|
||||
this.r = (byte)Math.Round(r);
|
||||
this.g = (byte)Math.Round(g);
|
||||
this.b = (byte)Math.Round(b);
|
||||
this.a = (byte)Math.Round(a);
|
||||
}
|
||||
|
||||
public bool Equals(Color32 other)
|
||||
{
|
||||
return other.rgba == rgba;
|
||||
}
|
||||
|
||||
public Color32 Interpolate(Color32 other, double ratio)
|
||||
{
|
||||
return ratio * this + (1 - ratio) * other;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds two colors.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator +(Color32 a, Color32 b) { return new Color32(a.r + b.r, a.g + b.g, a.b + b.b, a.a + b.a); }
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one color from another.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator -(Color32 a, Color32 b) { return new Color32(1f * a.r - b.r, a.g - b.g, a.b - b.b, a.a - b.a); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies one color by another.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator *(Color32 a, Color32 b) { return new Color32(1f * a.r * b.r, 1f * a.g * b.g, 1f * a.b * b.b, 1f * a.a * b.a); }
|
||||
|
||||
/// <summary>
|
||||
/// Divides one color over another.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator /(Color32 a, Color32 b) { return new Color32(1f * a.r / b.r, 1f * a.g / b.g, 1f * a.b / b.b, 1f * a.a / b.a); }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a color by a number.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="d"></param>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator *(Color32 a, float d) { return new Color32(d * a.r, d * a.g, d * a.b, d * a.a); }
|
||||
|
||||
public static Color32 operator *(Color32 a, double d) { return new Color32(d * a.r, d * a.g, d * a.b, d * a.a); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a color by a number.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator *(float d, Color32 a) { return new Color32(d * a.r, d * a.g, d * a.b, d * a.a); }
|
||||
|
||||
public static Color32 operator *(double d, Color32 a) { return new Color32(d * a.r, d * a.g, d * a.b, d * a.a); }
|
||||
|
||||
/// <summary>
|
||||
/// Divides a color by a number.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Color32 operator /(Color32 a, float d) { return new Color32(1f * a.r / d, 1f * a.g / d, 1f * a.b / d, 1f * a.a / d); }
|
||||
}
|
||||
}
|
||||
347
LightlessSync/ThirdParty/Nanomesh/Base/FfxivVertexAttribute.cs
vendored
Normal file
347
LightlessSync/ThirdParty/Nanomesh/Base/FfxivVertexAttribute.cs
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
[Flags]
|
||||
public enum FfxivAttributeFlags : uint
|
||||
{
|
||||
None = 0,
|
||||
Normal = 1u << 0,
|
||||
Tangent1 = 1u << 1,
|
||||
Tangent2 = 1u << 2,
|
||||
Color = 1u << 3,
|
||||
BoneWeights = 1u << 4,
|
||||
PositionW = 1u << 5,
|
||||
NormalW = 1u << 6,
|
||||
Uv0 = 1u << 7,
|
||||
Uv1 = 1u << 8,
|
||||
Uv2 = 1u << 9,
|
||||
Uv3 = 1u << 10,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly struct FfxivVertexAttribute : IEquatable<FfxivVertexAttribute>, IInterpolable<FfxivVertexAttribute>
|
||||
{
|
||||
public readonly Vector3F normal;
|
||||
public readonly Vector4F tangent1;
|
||||
public readonly Vector4F tangent2;
|
||||
public readonly Vector2F uv0;
|
||||
public readonly Vector2F uv1;
|
||||
public readonly Vector2F uv2;
|
||||
public readonly Vector2F uv3;
|
||||
public readonly Vector4F color;
|
||||
public readonly BoneWeight boneWeight;
|
||||
public readonly float positionW;
|
||||
public readonly float normalW;
|
||||
public readonly FfxivAttributeFlags flags;
|
||||
|
||||
public FfxivVertexAttribute(
|
||||
FfxivAttributeFlags flags,
|
||||
Vector3F normal,
|
||||
Vector4F tangent1,
|
||||
Vector4F tangent2,
|
||||
Vector2F uv0,
|
||||
Vector2F uv1,
|
||||
Vector2F uv2,
|
||||
Vector2F uv3,
|
||||
Vector4F color,
|
||||
BoneWeight boneWeight,
|
||||
float positionW,
|
||||
float normalW)
|
||||
{
|
||||
this.flags = flags;
|
||||
this.normal = normal;
|
||||
this.tangent1 = tangent1;
|
||||
this.tangent2 = tangent2;
|
||||
this.uv0 = uv0;
|
||||
this.uv1 = uv1;
|
||||
this.uv2 = uv2;
|
||||
this.uv3 = uv3;
|
||||
this.color = color;
|
||||
this.boneWeight = boneWeight;
|
||||
this.positionW = positionW;
|
||||
this.normalW = normalW;
|
||||
}
|
||||
|
||||
public FfxivVertexAttribute Interpolate(FfxivVertexAttribute other, double ratio)
|
||||
{
|
||||
var t = (float)ratio;
|
||||
var inv = 1f - t;
|
||||
var combinedFlags = flags | other.flags;
|
||||
|
||||
var normal = (combinedFlags & FfxivAttributeFlags.Normal) != 0
|
||||
? NormalizeVector3(new Vector3F(
|
||||
(this.normal.x * inv) + (other.normal.x * t),
|
||||
(this.normal.y * inv) + (other.normal.y * t),
|
||||
(this.normal.z * inv) + (other.normal.z * t)))
|
||||
: default;
|
||||
|
||||
var tangent1 = (combinedFlags & FfxivAttributeFlags.Tangent1) != 0
|
||||
? BlendTangent(this.tangent1, other.tangent1, t)
|
||||
: default;
|
||||
|
||||
var tangent2 = (combinedFlags & FfxivAttributeFlags.Tangent2) != 0
|
||||
? BlendTangent(this.tangent2, other.tangent2, t)
|
||||
: default;
|
||||
|
||||
var uv0 = (combinedFlags & FfxivAttributeFlags.Uv0) != 0
|
||||
? Vector2F.LerpUnclamped(this.uv0, other.uv0, t)
|
||||
: default;
|
||||
|
||||
var uv1 = (combinedFlags & FfxivAttributeFlags.Uv1) != 0
|
||||
? Vector2F.LerpUnclamped(this.uv1, other.uv1, t)
|
||||
: default;
|
||||
|
||||
var uv2 = (combinedFlags & FfxivAttributeFlags.Uv2) != 0
|
||||
? Vector2F.LerpUnclamped(this.uv2, other.uv2, t)
|
||||
: default;
|
||||
|
||||
var uv3 = (combinedFlags & FfxivAttributeFlags.Uv3) != 0
|
||||
? Vector2F.LerpUnclamped(this.uv3, other.uv3, t)
|
||||
: default;
|
||||
|
||||
var color = (combinedFlags & FfxivAttributeFlags.Color) != 0
|
||||
? new Vector4F(
|
||||
(this.color.x * inv) + (other.color.x * t),
|
||||
(this.color.y * inv) + (other.color.y * t),
|
||||
(this.color.z * inv) + (other.color.z * t),
|
||||
(this.color.w * inv) + (other.color.w * t))
|
||||
: default;
|
||||
|
||||
var boneWeight = (combinedFlags & FfxivAttributeFlags.BoneWeights) != 0
|
||||
? BlendBoneWeights(this.boneWeight, other.boneWeight, t)
|
||||
: default;
|
||||
|
||||
var positionW = (combinedFlags & FfxivAttributeFlags.PositionW) != 0
|
||||
? (this.positionW * inv) + (other.positionW * t)
|
||||
: 0f;
|
||||
|
||||
var normalW = (combinedFlags & FfxivAttributeFlags.NormalW) != 0
|
||||
? (this.normalW * inv) + (other.normalW * t)
|
||||
: 0f;
|
||||
|
||||
return new FfxivVertexAttribute(
|
||||
combinedFlags,
|
||||
normal,
|
||||
tangent1,
|
||||
tangent2,
|
||||
uv0,
|
||||
uv1,
|
||||
uv2,
|
||||
uv3,
|
||||
color,
|
||||
boneWeight,
|
||||
positionW,
|
||||
normalW);
|
||||
}
|
||||
|
||||
public bool Equals(FfxivVertexAttribute other)
|
||||
{
|
||||
if (flags != other.flags)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Normal) != 0 && !normal.Equals(other.normal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Tangent1) != 0 && !tangent1.Equals(other.tangent1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Tangent2) != 0 && !tangent2.Equals(other.tangent2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Uv0) != 0 && !uv0.Equals(other.uv0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Uv1) != 0 && !uv1.Equals(other.uv1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Uv2) != 0 && !uv2.Equals(other.uv2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Uv3) != 0 && !uv3.Equals(other.uv3))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.Color) != 0 && !color.Equals(other.color))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.BoneWeights) != 0 && !boneWeight.Equals(other.boneWeight))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.PositionW) != 0 && positionW != other.positionW)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & FfxivAttributeFlags.NormalW) != 0 && normalW != other.normalW)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is FfxivVertexAttribute other && Equals(other);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = new HashCode();
|
||||
hash.Add(normal);
|
||||
hash.Add(tangent1);
|
||||
hash.Add(tangent2);
|
||||
hash.Add(uv0);
|
||||
hash.Add(uv1);
|
||||
hash.Add(uv2);
|
||||
hash.Add(uv3);
|
||||
hash.Add(color);
|
||||
hash.Add(boneWeight);
|
||||
hash.Add(positionW);
|
||||
hash.Add(normalW);
|
||||
hash.Add(flags);
|
||||
return hash.ToHashCode();
|
||||
}
|
||||
|
||||
private static Vector3F NormalizeVector3(in Vector3F value)
|
||||
{
|
||||
var length = Vector3F.Magnitude(value);
|
||||
return length > 0f ? value / length : value;
|
||||
}
|
||||
|
||||
private static Vector4F BlendTangent(in Vector4F a, in Vector4F b, float t)
|
||||
{
|
||||
var inv = 1f - t;
|
||||
var blended = new Vector3F(
|
||||
(a.x * inv) + (b.x * t),
|
||||
(a.y * inv) + (b.y * t),
|
||||
(a.z * inv) + (b.z * t));
|
||||
blended = NormalizeVector3(blended);
|
||||
|
||||
var w = t >= 0.5f ? b.w : a.w;
|
||||
if (w != 0f)
|
||||
{
|
||||
w = w >= 0f ? 1f : -1f;
|
||||
}
|
||||
|
||||
return new Vector4F(blended.x, blended.y, blended.z, w);
|
||||
}
|
||||
|
||||
private static BoneWeight BlendBoneWeights(in BoneWeight a, in BoneWeight b, float ratio)
|
||||
{
|
||||
Span<int> indices = stackalloc int[8];
|
||||
Span<float> weights = stackalloc float[8];
|
||||
var count = 0;
|
||||
|
||||
static void AddWeight(Span<int> indices, Span<float> weights, ref int count, int index, float weight)
|
||||
{
|
||||
if (weight <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (indices[i] == index)
|
||||
{
|
||||
weights[i] += weight;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (count < indices.Length)
|
||||
{
|
||||
indices[count] = index;
|
||||
weights[count] = weight;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
var inv = 1f - ratio;
|
||||
var sumA = a.weight0 + a.weight1 + a.weight2 + a.weight3;
|
||||
var sumB = b.weight0 + b.weight1 + b.weight2 + b.weight3;
|
||||
var targetSum = (sumA * inv) + (sumB * ratio);
|
||||
AddWeight(indices, weights, ref count, a.index0, a.weight0 * inv);
|
||||
AddWeight(indices, weights, ref count, a.index1, a.weight1 * inv);
|
||||
AddWeight(indices, weights, ref count, a.index2, a.weight2 * inv);
|
||||
AddWeight(indices, weights, ref count, a.index3, a.weight3 * inv);
|
||||
AddWeight(indices, weights, ref count, b.index0, b.weight0 * ratio);
|
||||
AddWeight(indices, weights, ref count, b.index1, b.weight1 * ratio);
|
||||
AddWeight(indices, weights, ref count, b.index2, b.weight2 * ratio);
|
||||
AddWeight(indices, weights, ref count, b.index3, b.weight3 * ratio);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
Span<int> topIndices = stackalloc int[4];
|
||||
Span<float> topWeights = stackalloc float[4];
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
topIndices[i] = -1;
|
||||
topWeights[i] = 0f;
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var weight = weights[i];
|
||||
var index = indices[i];
|
||||
for (var slot = 0; slot < 4; slot++)
|
||||
{
|
||||
if (weight > topWeights[slot])
|
||||
{
|
||||
for (var shift = 3; shift > slot; shift--)
|
||||
{
|
||||
topWeights[shift] = topWeights[shift - 1];
|
||||
topIndices[shift] = topIndices[shift - 1];
|
||||
}
|
||||
|
||||
topWeights[slot] = weight;
|
||||
topIndices[slot] = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sum = topWeights[0] + topWeights[1] + topWeights[2] + topWeights[3];
|
||||
if (sum > 0f)
|
||||
{
|
||||
var scale = targetSum > 0f ? targetSum / sum : 0f;
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
topWeights[i] *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
return new BoneWeight(
|
||||
topIndices[0] < 0 ? 0 : topIndices[0],
|
||||
topIndices[1] < 0 ? 0 : topIndices[1],
|
||||
topIndices[2] < 0 ? 0 : topIndices[2],
|
||||
topIndices[3] < 0 ? 0 : topIndices[3],
|
||||
topWeights[0],
|
||||
topWeights[1],
|
||||
topWeights[2],
|
||||
topWeights[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
LightlessSync/ThirdParty/Nanomesh/Base/IInterpolable.cs
vendored
Normal file
7
LightlessSync/ThirdParty/Nanomesh/Base/IInterpolable.cs
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Nanomesh
|
||||
{
|
||||
public interface IInterpolable<T>
|
||||
{
|
||||
T Interpolate(T other, double ratio);
|
||||
}
|
||||
}
|
||||
356
LightlessSync/ThirdParty/Nanomesh/Base/MathF.cs
vendored
Normal file
356
LightlessSync/ThirdParty/Nanomesh/Base/MathF.cs
vendored
Normal file
@@ -0,0 +1,356 @@
|
||||
using System;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public static partial class MathF
|
||||
{
|
||||
// Returns the sine of angle /f/ in radians.
|
||||
public static float Sin(float f) { return (float)Math.Sin(f); }
|
||||
|
||||
// Returns the cosine of angle /f/ in radians.
|
||||
public static float Cos(float f) { return (float)Math.Cos(f); }
|
||||
|
||||
// Returns the tangent of angle /f/ in radians.
|
||||
public static float Tan(float f) { return (float)Math.Tan(f); }
|
||||
|
||||
// Returns the arc-sine of /f/ - the angle in radians whose sine is /f/.
|
||||
public static float Asin(float f) { return (float)Math.Asin(f); }
|
||||
|
||||
// Returns the arc-cosine of /f/ - the angle in radians whose cosine is /f/.
|
||||
public static float Acos(float f) { return (float)Math.Acos(f); }
|
||||
|
||||
// Returns the arc-tangent of /f/ - the angle in radians whose tangent is /f/.
|
||||
public static float Atan(float f) { return (float)Math.Atan(f); }
|
||||
|
||||
// Returns the angle in radians whose ::ref::Tan is @@y/x@@.
|
||||
public static float Atan2(float y, float x) { return (float)Math.Atan2(y, x); }
|
||||
|
||||
// Returns square root of /f/.
|
||||
public static float Sqrt(float f) { return (float)Math.Sqrt(f); }
|
||||
|
||||
// Returns the absolute value of /f/.
|
||||
public static float Abs(float f) { return (float)Math.Abs(f); }
|
||||
|
||||
// Returns the absolute value of /value/.
|
||||
public static int Abs(int value) { return Math.Abs(value); }
|
||||
|
||||
/// *listonly*
|
||||
public static float Min(float a, float b) { return a < b ? a : b; }
|
||||
// Returns the smallest of two or more values.
|
||||
public static float Min(params float[] values)
|
||||
{
|
||||
int len = values.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] < m)
|
||||
{
|
||||
m = values[i];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// *listonly*
|
||||
public static int Min(int a, int b) { return a < b ? a : b; }
|
||||
// Returns the smallest of two or more values.
|
||||
public static int Min(params int[] values)
|
||||
{
|
||||
int len = values.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] < m)
|
||||
{
|
||||
m = values[i];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// *listonly*
|
||||
public static float Max(float a, float b) { return a > b ? a : b; }
|
||||
// Returns largest of two or more values.
|
||||
public static float Max(params float[] values)
|
||||
{
|
||||
int len = values.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] > m)
|
||||
{
|
||||
m = values[i];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/// *listonly*
|
||||
public static int Max(int a, int b) { return a > b ? a : b; }
|
||||
// Returns the largest of two or more values.
|
||||
public static int Max(params int[] values)
|
||||
{
|
||||
int len = values.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m = values[0];
|
||||
for (int i = 1; i < len; i++)
|
||||
{
|
||||
if (values[i] > m)
|
||||
{
|
||||
m = values[i];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
// Returns /f/ raised to power /p/.
|
||||
public static float Pow(float f, float p) { return (float)Math.Pow(f, p); }
|
||||
|
||||
// Returns e raised to the specified power.
|
||||
public static float Exp(float power) { return (float)Math.Exp(power); }
|
||||
|
||||
// Returns the logarithm of a specified number in a specified base.
|
||||
public static float Log(float f, float p) { return (float)Math.Log(f, p); }
|
||||
|
||||
// Returns the natural (base e) logarithm of a specified number.
|
||||
public static float Log(float f) { return (float)Math.Log(f); }
|
||||
|
||||
// Returns the base 10 logarithm of a specified number.
|
||||
public static float Log10(float f) { return (float)Math.Log10(f); }
|
||||
|
||||
// Returns the smallest integer greater to or equal to /f/.
|
||||
public static float Ceil(float f) { return (float)Math.Ceiling(f); }
|
||||
|
||||
// Returns the largest integer smaller to or equal to /f/.
|
||||
public static float Floor(float f) { return (float)Math.Floor(f); }
|
||||
|
||||
// Returns /f/ rounded to the nearest integer.
|
||||
public static float Round(float f) { return (float)Math.Round(f); }
|
||||
|
||||
// Returns the smallest integer greater to or equal to /f/.
|
||||
public static int CeilToInt(float f) { return (int)Math.Ceiling(f); }
|
||||
|
||||
// Returns the largest integer smaller to or equal to /f/.
|
||||
public static int FloorToInt(float f) { return (int)Math.Floor(f); }
|
||||
|
||||
// Returns /f/ rounded to the nearest integer.
|
||||
public static int RoundToInt(float f) { return (int)Math.Round(f); }
|
||||
|
||||
// Returns the sign of /f/.
|
||||
public static float Sign(float f) { return f >= 0F ? 1F : -1F; }
|
||||
|
||||
// The infamous ''3.14159265358979...'' value (RO).
|
||||
public const float PI = (float)Math.PI;
|
||||
|
||||
// A representation of positive infinity (RO).
|
||||
public const float Infinity = float.PositiveInfinity;
|
||||
|
||||
// A representation of negative infinity (RO).
|
||||
public const float NegativeInfinity = float.NegativeInfinity;
|
||||
|
||||
// Degrees-to-radians conversion constant (RO).
|
||||
public const float Deg2Rad = PI * 2F / 360F;
|
||||
|
||||
// Radians-to-degrees conversion constant (RO).
|
||||
public const float Rad2Deg = 1F / Deg2Rad;
|
||||
|
||||
// Clamps a value between a minimum float and maximum float value.
|
||||
public static double Clamp(double value, double min, double max)
|
||||
{
|
||||
if (value < min)
|
||||
{
|
||||
value = min;
|
||||
}
|
||||
else if (value > max)
|
||||
{
|
||||
value = max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Clamps a value between a minimum float and maximum float value.
|
||||
public static float Clamp(float value, float min, float max)
|
||||
{
|
||||
if (value < min)
|
||||
{
|
||||
value = min;
|
||||
}
|
||||
else if (value > max)
|
||||
{
|
||||
value = max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Clamps value between min and max and returns value.
|
||||
// Set the position of the transform to be that of the time
|
||||
// but never less than 1 or more than 3
|
||||
//
|
||||
public static int Clamp(int value, int min, int max)
|
||||
{
|
||||
if (value < min)
|
||||
{
|
||||
value = min;
|
||||
}
|
||||
else if (value > max)
|
||||
{
|
||||
value = max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Clamps value between 0 and 1 and returns value
|
||||
public static float Clamp01(float value)
|
||||
{
|
||||
if (value < 0F)
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
else if (value > 1F)
|
||||
{
|
||||
return 1F;
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolates between /a/ and /b/ by /t/. /t/ is clamped between 0 and 1.
|
||||
public static float Lerp(float a, float b, float t)
|
||||
{
|
||||
return a + (b - a) * Clamp01(t);
|
||||
}
|
||||
|
||||
// Interpolates between /a/ and /b/ by /t/ without clamping the interpolant.
|
||||
public static float LerpUnclamped(float a, float b, float t)
|
||||
{
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
// Same as ::ref::Lerp but makes sure the values interpolate correctly when they wrap around 360 degrees.
|
||||
public static float LerpAngle(float a, float b, float t)
|
||||
{
|
||||
float delta = Repeat((b - a), 360);
|
||||
if (delta > 180)
|
||||
{
|
||||
delta -= 360;
|
||||
}
|
||||
|
||||
return a + delta * Clamp01(t);
|
||||
}
|
||||
|
||||
// Moves a value /current/ towards /target/.
|
||||
public static float MoveTowards(float current, float target, float maxDelta)
|
||||
{
|
||||
if (MathF.Abs(target - current) <= maxDelta)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
return current + MathF.Sign(target - current) * maxDelta;
|
||||
}
|
||||
|
||||
// Same as ::ref::MoveTowards but makes sure the values interpolate correctly when they wrap around 360 degrees.
|
||||
public static float MoveTowardsAngle(float current, float target, float maxDelta)
|
||||
{
|
||||
float deltaAngle = DeltaAngle(current, target);
|
||||
if (-maxDelta < deltaAngle && deltaAngle < maxDelta)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
target = current + deltaAngle;
|
||||
return MoveTowards(current, target, maxDelta);
|
||||
}
|
||||
|
||||
// Interpolates between /min/ and /max/ with smoothing at the limits.
|
||||
public static float SmoothStep(float from, float to, float t)
|
||||
{
|
||||
t = MathF.Clamp01(t);
|
||||
t = -2.0F * t * t * t + 3.0F * t * t;
|
||||
return to * t + from * (1F - t);
|
||||
}
|
||||
|
||||
//*undocumented
|
||||
public static float Gamma(float value, float absmax, float gamma)
|
||||
{
|
||||
bool negative = value < 0F;
|
||||
float absval = Abs(value);
|
||||
if (absval > absmax)
|
||||
{
|
||||
return negative ? -absval : absval;
|
||||
}
|
||||
|
||||
float result = Pow(absval / absmax, gamma) * absmax;
|
||||
return negative ? -result : result;
|
||||
}
|
||||
|
||||
// Loops the value t, so that it is never larger than length and never smaller than 0.
|
||||
public static float Repeat(float t, float length)
|
||||
{
|
||||
return Clamp(t - MathF.Floor(t / length) * length, 0.0f, length);
|
||||
}
|
||||
|
||||
// PingPongs the value t, so that it is never larger than length and never smaller than 0.
|
||||
public static float PingPong(float t, float length)
|
||||
{
|
||||
t = Repeat(t, length * 2F);
|
||||
return length - MathF.Abs(t - length);
|
||||
}
|
||||
|
||||
// Calculates the ::ref::Lerp parameter between of two values.
|
||||
public static float InverseLerp(float a, float b, float value)
|
||||
{
|
||||
if (a != b)
|
||||
{
|
||||
return Clamp01((value - a) / (b - a));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculates the shortest difference between two given angles.
|
||||
public static float DeltaAngle(float current, float target)
|
||||
{
|
||||
float delta = MathF.Repeat((target - current), 360.0F);
|
||||
if (delta > 180.0F)
|
||||
{
|
||||
delta -= 360.0F;
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
internal static long RandomToLong(System.Random r)
|
||||
{
|
||||
byte[] buffer = new byte[8];
|
||||
r.NextBytes(buffer);
|
||||
return (long)(System.BitConverter.ToUInt64(buffer, 0) & long.MaxValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
114
LightlessSync/ThirdParty/Nanomesh/Base/MathUtils.cs
vendored
Normal file
114
LightlessSync/ThirdParty/Nanomesh/Base/MathUtils.cs
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public static class MathUtils
|
||||
{
|
||||
public const float EpsilonFloat = 1e-15f;
|
||||
public const double EpsilonDouble = 1e-40f;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float DivideSafe(float numerator, float denominator)
|
||||
{
|
||||
return (denominator > -EpsilonFloat && denominator < EpsilonFloat) ? 0f : numerator / denominator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double DivideSafe(double numerator, double denominator)
|
||||
{
|
||||
return (denominator > -EpsilonDouble && denominator < EpsilonDouble) ? 0d : numerator / denominator;
|
||||
}
|
||||
|
||||
public static void SelectMin<T>(double e1, double e2, double e3, in T v1, in T v2, in T v3, out double e, out T v)
|
||||
{
|
||||
if (e1 < e2)
|
||||
{
|
||||
if (e1 < e3)
|
||||
{
|
||||
e = e1;
|
||||
v = v1;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = e3;
|
||||
v = v3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e2 < e3)
|
||||
{
|
||||
e = e2;
|
||||
v = v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = e3;
|
||||
v = v3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SelectMin<T>(double e1, double e2, double e3, double e4, in T v1, in T v2, in T v3, in T v4, out double e, out T v)
|
||||
{
|
||||
if (e1 < e2)
|
||||
{
|
||||
if (e1 < e3)
|
||||
{
|
||||
if (e1 < e4)
|
||||
{
|
||||
e = e1;
|
||||
v = v1;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = e4;
|
||||
v = v4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e3 < e4)
|
||||
{
|
||||
e = e3;
|
||||
v = v3;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = e4;
|
||||
v = v4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e2 < e3)
|
||||
{
|
||||
if (e2 < e4)
|
||||
{
|
||||
e = e2;
|
||||
v = v2;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = e4;
|
||||
v = v4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e3 < e4)
|
||||
{
|
||||
e = e3;
|
||||
v = v3;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = e4;
|
||||
v = v4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
LightlessSync/ThirdParty/Nanomesh/Base/Profiling.cs
vendored
Normal file
50
LightlessSync/ThirdParty/Nanomesh/Base/Profiling.cs
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public static class Profiling
|
||||
{
|
||||
private static readonly Dictionary<string, Stopwatch> stopwatches = new Dictionary<string, Stopwatch>();
|
||||
|
||||
public static void Start(string key)
|
||||
{
|
||||
if (!stopwatches.ContainsKey(key))
|
||||
{
|
||||
stopwatches.Add(key, Stopwatch.StartNew());
|
||||
}
|
||||
else
|
||||
{
|
||||
stopwatches[key] = Stopwatch.StartNew();
|
||||
}
|
||||
}
|
||||
|
||||
public static string End(string key)
|
||||
{
|
||||
TimeSpan time = EndTimer(key);
|
||||
return $"{key} done in {time.ToString("mm':'ss':'fff")}";
|
||||
}
|
||||
|
||||
private static TimeSpan EndTimer(string key)
|
||||
{
|
||||
if (!stopwatches.ContainsKey(key))
|
||||
{
|
||||
return TimeSpan.MinValue;
|
||||
}
|
||||
|
||||
Stopwatch sw = stopwatches[key];
|
||||
sw.Stop();
|
||||
stopwatches.Remove(key);
|
||||
return sw.Elapsed;
|
||||
}
|
||||
|
||||
public static TimeSpan Time(Action toTime)
|
||||
{
|
||||
Stopwatch timer = Stopwatch.StartNew();
|
||||
toTime();
|
||||
timer.Stop();
|
||||
return timer.Elapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
632
LightlessSync/ThirdParty/Nanomesh/Base/Quaternion.cs
vendored
Normal file
632
LightlessSync/ThirdParty/Nanomesh/Base/Quaternion.cs
vendored
Normal file
@@ -0,0 +1,632 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public partial struct Quaternion : IEquatable<Quaternion>
|
||||
{
|
||||
private const double radToDeg = 180.0 / Math.PI;
|
||||
private const double degToRad = Math.PI / 180.0;
|
||||
|
||||
public const double kEpsilon = 1E-20; // should probably be used in the 0 tests in LookRotation or Slerp
|
||||
|
||||
public Vector3 xyz
|
||||
{
|
||||
set
|
||||
{
|
||||
x = value.x;
|
||||
y = value.y;
|
||||
z = value.z;
|
||||
}
|
||||
get => new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
public double x;
|
||||
|
||||
public double y;
|
||||
|
||||
public double z;
|
||||
|
||||
public double w;
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
case 3:
|
||||
return w;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Quaternion index: " + index + ", can use only 0,1,2,3");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
x = value;
|
||||
break;
|
||||
case 1:
|
||||
y = value;
|
||||
break;
|
||||
case 2:
|
||||
z = value;
|
||||
break;
|
||||
case 3:
|
||||
w = value;
|
||||
break;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Quaternion index: " + index + ", can use only 0,1,2,3");
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// <para>The identity rotation (RO).</para>
|
||||
/// </summary>
|
||||
public static Quaternion identity => new Quaternion(0, 0, 0, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length (magnitude) of the quaternion.
|
||||
/// </summary>
|
||||
/// <seealso cref="LengthSquared"/>
|
||||
public double Length => (double)System.Math.Sqrt(x * x + y * y + z * z + w * w);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the square of the quaternion length (magnitude).
|
||||
/// </summary>
|
||||
public double LengthSquared => x * x + y * y + z * z + w * w;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Constructs new Quaternion with given x,y,z,w components.</para>
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <param name="z"></param>
|
||||
/// <param name="w"></param>
|
||||
public Quaternion(double x, double y, double z, double w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new Quaternion from vector and w components
|
||||
/// </summary>
|
||||
/// <param name="v">The vector part</param>
|
||||
/// <param name="w">The w part</param>
|
||||
public Quaternion(Vector3 v, double w)
|
||||
{
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Set x, y, z and w components of an existing Quaternion.</para>
|
||||
/// </summary>
|
||||
/// <param name="new_x"></param>
|
||||
/// <param name="new_y"></param>
|
||||
/// <param name="new_z"></param>
|
||||
/// <param name="new_w"></param>
|
||||
public void Set(double new_x, double new_y, double new_z, double new_w)
|
||||
{
|
||||
x = new_x;
|
||||
y = new_y;
|
||||
z = new_z;
|
||||
w = new_w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scales the Quaternion to unit length.
|
||||
/// </summary>
|
||||
public static Quaternion Normalize(Quaternion q)
|
||||
{
|
||||
double mag = Math.Sqrt(Dot(q, q));
|
||||
|
||||
if (mag < kEpsilon)
|
||||
{
|
||||
return Quaternion.identity;
|
||||
}
|
||||
|
||||
return new Quaternion(q.x / mag, q.y / mag, q.z / mag, q.w / mag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale the given quaternion to unit length
|
||||
/// </summary>
|
||||
/// <param name="q">The quaternion to normalize</param>
|
||||
/// <param name="result">The normalized quaternion</param>
|
||||
public void Normalize()
|
||||
{
|
||||
this = Normalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>The dot product between two rotations.</para>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
public static double Dot(Quaternion a, Quaternion b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a rotation which rotates /angle/ degrees around /axis/.</para>
|
||||
/// </summary>
|
||||
/// <param name="angle"></param>
|
||||
/// <param name="axis"></param>
|
||||
public static Quaternion AngleAxis(double angle, Vector3 axis)
|
||||
{
|
||||
return Quaternion.AngleAxis(angle, ref axis);
|
||||
}
|
||||
|
||||
private static Quaternion AngleAxis(double degress, ref Vector3 axis)
|
||||
{
|
||||
if (axis.LengthSquared == 0.0)
|
||||
{
|
||||
return identity;
|
||||
}
|
||||
|
||||
Quaternion result = identity;
|
||||
double radians = degress * degToRad;
|
||||
radians *= 0.5;
|
||||
axis = axis.Normalized;
|
||||
axis = axis * Math.Sin(radians);
|
||||
result.x = axis.x;
|
||||
result.y = axis.y;
|
||||
result.z = axis.z;
|
||||
result.w = Math.Cos(radians);
|
||||
|
||||
return Normalize(result);
|
||||
}
|
||||
|
||||
public void ToAngleAxis(out double angle, out Vector3 axis)
|
||||
{
|
||||
Quaternion.ToAxisAngleRad(this, out axis, out angle);
|
||||
angle *= radToDeg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a rotation which rotates from /fromDirection/ to /toDirection/.</para>
|
||||
/// </summary>
|
||||
/// <param name="fromDirection"></param>
|
||||
/// <param name="toDirection"></param>
|
||||
public static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection)
|
||||
{
|
||||
return RotateTowards(LookRotation(fromDirection), LookRotation(toDirection), double.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a rotation which rotates from /fromDirection/ to /toDirection/.</para>
|
||||
/// </summary>
|
||||
/// <param name="fromDirection"></param>
|
||||
/// <param name="toDirection"></param>
|
||||
public void SetFromToRotation(Vector3 fromDirection, Vector3 toDirection)
|
||||
{
|
||||
this = Quaternion.FromToRotation(fromDirection, toDirection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a rotation with the specified /forward/ and /upwards/ directions.</para>
|
||||
/// </summary>
|
||||
/// <param name="forward">The direction to look in.</param>
|
||||
/// <param name="upwards">The vector that defines in which direction up is.</param>
|
||||
public static Quaternion LookRotation(Vector3 forward, Vector3 upwards)
|
||||
{
|
||||
return Quaternion.LookRotation(ref forward, ref upwards);
|
||||
}
|
||||
|
||||
public static Quaternion LookRotation(Vector3 forward)
|
||||
{
|
||||
Vector3 up = new Vector3(1, 0, 0);
|
||||
return Quaternion.LookRotation(ref forward, ref up);
|
||||
}
|
||||
|
||||
private static Quaternion LookRotation(ref Vector3 forward, ref Vector3 up)
|
||||
{
|
||||
forward = Vector3.Normalize(forward);
|
||||
Vector3 right = Vector3.Normalize(Vector3.Cross(up, forward));
|
||||
up = Vector3.Cross(forward, right);
|
||||
double m00 = right.x;
|
||||
double m01 = right.y;
|
||||
double m02 = right.z;
|
||||
double m10 = up.x;
|
||||
double m11 = up.y;
|
||||
double m12 = up.z;
|
||||
double m20 = forward.x;
|
||||
double m21 = forward.y;
|
||||
double m22 = forward.z;
|
||||
|
||||
double num8 = (m00 + m11) + m22;
|
||||
Quaternion quaternion = new Quaternion();
|
||||
if (num8 > 0)
|
||||
{
|
||||
double num = Math.Sqrt(num8 + 1);
|
||||
quaternion.w = num * 0.5;
|
||||
num = 0.5 / num;
|
||||
quaternion.x = (m12 - m21) * num;
|
||||
quaternion.y = (m20 - m02) * num;
|
||||
quaternion.z = (m01 - m10) * num;
|
||||
return quaternion;
|
||||
}
|
||||
if ((m00 >= m11) && (m00 >= m22))
|
||||
{
|
||||
double num7 = Math.Sqrt(((1 + m00) - m11) - m22);
|
||||
double num4 = 0.5 / num7;
|
||||
quaternion.x = 0.5 * num7;
|
||||
quaternion.y = (m01 + m10) * num4;
|
||||
quaternion.z = (m02 + m20) * num4;
|
||||
quaternion.w = (m12 - m21) * num4;
|
||||
return quaternion;
|
||||
}
|
||||
if (m11 > m22)
|
||||
{
|
||||
double num6 = Math.Sqrt(((1 + m11) - m00) - m22);
|
||||
double num3 = 0.5 / num6;
|
||||
quaternion.x = (m10 + m01) * num3;
|
||||
quaternion.y = 0.5 * num6;
|
||||
quaternion.z = (m21 + m12) * num3;
|
||||
quaternion.w = (m20 - m02) * num3;
|
||||
return quaternion;
|
||||
}
|
||||
double num5 = Math.Sqrt(((1 + m22) - m00) - m11);
|
||||
double num2 = 0.5 / num5;
|
||||
quaternion.x = (m20 + m02) * num2;
|
||||
quaternion.y = (m21 + m12) * num2;
|
||||
quaternion.z = 0.5 * num5;
|
||||
quaternion.w = (m01 - m10) * num2;
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
public void SetLookRotation(Vector3 view)
|
||||
{
|
||||
Vector3 up = new Vector3(1, 0, 0);
|
||||
SetLookRotation(view, up);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates a rotation with the specified /forward/ and /upwards/ directions.</para>
|
||||
/// </summary>
|
||||
/// <param name="view">The direction to look in.</param>
|
||||
/// <param name="up">The vector that defines in which direction up is.</param>
|
||||
public void SetLookRotation(Vector3 view, Vector3 up)
|
||||
{
|
||||
this = Quaternion.LookRotation(view, up);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Spherically interpolates between /a/ and /b/ by t. The parameter /t/ is clamped to the range [0, 1].</para>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="t"></param>
|
||||
public static Quaternion Slerp(Quaternion a, Quaternion b, double t)
|
||||
{
|
||||
return Quaternion.Slerp(ref a, ref b, t);
|
||||
}
|
||||
|
||||
private static Quaternion Slerp(ref Quaternion a, ref Quaternion b, double t)
|
||||
{
|
||||
if (t > 1)
|
||||
{
|
||||
t = 1;
|
||||
}
|
||||
|
||||
if (t < 0)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
|
||||
return SlerpUnclamped(ref a, ref b, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Spherically interpolates between /a/ and /b/ by t. The parameter /t/ is not clamped.</para>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="t"></param>
|
||||
public static Quaternion SlerpUnclamped(Quaternion a, Quaternion b, double t)
|
||||
{
|
||||
|
||||
return Quaternion.SlerpUnclamped(ref a, ref b, t);
|
||||
}
|
||||
private static Quaternion SlerpUnclamped(ref Quaternion a, ref Quaternion b, double t)
|
||||
{
|
||||
// if either input is zero, return the other.
|
||||
if (a.LengthSquared == 0.0)
|
||||
{
|
||||
if (b.LengthSquared == 0.0)
|
||||
{
|
||||
return identity;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
else if (b.LengthSquared == 0.0)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
double cosHalfAngle = a.w * b.w + Vector3.Dot(a.xyz, b.xyz);
|
||||
|
||||
if (cosHalfAngle >= 1.0 || cosHalfAngle <= -1.0)
|
||||
{
|
||||
// angle = 0.0f, so just return one input.
|
||||
return a;
|
||||
}
|
||||
else if (cosHalfAngle < 0.0)
|
||||
{
|
||||
b.xyz = -b.xyz;
|
||||
b.w = -b.w;
|
||||
cosHalfAngle = -cosHalfAngle;
|
||||
}
|
||||
|
||||
double blendA;
|
||||
double blendB;
|
||||
if (cosHalfAngle < 0.99)
|
||||
{
|
||||
// do proper slerp for big angles
|
||||
double halfAngle = Math.Acos(cosHalfAngle);
|
||||
double sinHalfAngle = Math.Sin(halfAngle);
|
||||
double oneOverSinHalfAngle = 1.0 / sinHalfAngle;
|
||||
blendA = Math.Sin(halfAngle * (1.0 - t)) * oneOverSinHalfAngle;
|
||||
blendB = Math.Sin(halfAngle * t) * oneOverSinHalfAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do lerp if angle is really small.
|
||||
blendA = 1.0f - t;
|
||||
blendB = t;
|
||||
}
|
||||
|
||||
Quaternion result = new Quaternion(blendA * a.xyz + blendB * b.xyz, blendA * a.w + blendB * b.w);
|
||||
if (result.LengthSquared > 0.0)
|
||||
{
|
||||
return Normalize(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return identity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Interpolates between /a/ and /b/ by /t/ and normalizes the result afterwards. The parameter /t/ is clamped to the range [0, 1].</para>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="t"></param>
|
||||
public static Quaternion Lerp(Quaternion a, Quaternion b, double t)
|
||||
{
|
||||
if (t > 1)
|
||||
{
|
||||
t = 1;
|
||||
}
|
||||
|
||||
if (t < 0)
|
||||
{
|
||||
t = 0;
|
||||
}
|
||||
|
||||
return Slerp(ref a, ref b, t); // TODO: use lerp not slerp, "Because quaternion works in 4D. Rotation in 4D are linear" ???
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Interpolates between /a/ and /b/ by /t/ and normalizes the result afterwards. The parameter /t/ is not clamped.</para>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <param name="t"></param>
|
||||
public static Quaternion LerpUnclamped(Quaternion a, Quaternion b, double t)
|
||||
{
|
||||
return Slerp(ref a, ref b, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Rotates a rotation /from/ towards /to/.</para>
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <param name="maxDegreesDelta"></param>
|
||||
public static Quaternion RotateTowards(Quaternion from, Quaternion to, double maxDegreesDelta)
|
||||
{
|
||||
double num = Quaternion.Angle(from, to);
|
||||
if (num == 0)
|
||||
{
|
||||
return to;
|
||||
}
|
||||
double t = Math.Min(1, maxDegreesDelta / num);
|
||||
return Quaternion.SlerpUnclamped(from, to, t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Returns the Inverse of /rotation/.</para>
|
||||
/// </summary>
|
||||
/// <param name="rotation"></param>
|
||||
public static Quaternion Inverse(Quaternion rotation)
|
||||
{
|
||||
double lengthSq = rotation.LengthSquared;
|
||||
if (lengthSq != 0.0)
|
||||
{
|
||||
double i = 1.0 / lengthSq;
|
||||
return new Quaternion(rotation.xyz * -i, rotation.w * i);
|
||||
}
|
||||
return rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Returns a nicely formatted string of the Quaternion.</para>
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{x}, {y}, {z}, {w}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Returns a nicely formatted string of the Quaternion.</para>
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
public string ToString(string format)
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Returns the angle in degrees between two rotations /a/ and /b/.</para>
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
public static double Angle(Quaternion a, Quaternion b)
|
||||
{
|
||||
double f = Quaternion.Dot(a, b);
|
||||
return Math.Acos(Math.Min(Math.Abs(f), 1)) * 2 * radToDeg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).</para>
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <param name="z"></param>
|
||||
public static Quaternion Euler(double x, double y, double z)
|
||||
{
|
||||
return Quaternion.FromEulerRad(new Vector3((double)x, (double)y, (double)z) * degToRad);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Returns a rotation that rotates z degrees around the z axis, x degrees around the x axis, and y degrees around the y axis (in that order).</para>
|
||||
/// </summary>
|
||||
/// <param name="euler"></param>
|
||||
public static Quaternion Euler(Vector3 euler)
|
||||
{
|
||||
return Quaternion.FromEulerRad(euler * degToRad);
|
||||
}
|
||||
|
||||
private static double NormalizeAngle(double angle)
|
||||
{
|
||||
while (angle > 360)
|
||||
{
|
||||
angle -= 360;
|
||||
}
|
||||
|
||||
while (angle < 0)
|
||||
{
|
||||
angle += 360;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
private static Quaternion FromEulerRad(Vector3 euler)
|
||||
{
|
||||
double yaw = euler.x;
|
||||
double pitch = euler.y;
|
||||
double roll = euler.z;
|
||||
double rollOver2 = roll * 0.5;
|
||||
double sinRollOver2 = (double)System.Math.Sin((double)rollOver2);
|
||||
double cosRollOver2 = (double)System.Math.Cos((double)rollOver2);
|
||||
double pitchOver2 = pitch * 0.5;
|
||||
double sinPitchOver2 = (double)System.Math.Sin((double)pitchOver2);
|
||||
double cosPitchOver2 = (double)System.Math.Cos((double)pitchOver2);
|
||||
double yawOver2 = yaw * 0.5;
|
||||
double sinYawOver2 = (double)System.Math.Sin((double)yawOver2);
|
||||
double cosYawOver2 = (double)System.Math.Cos((double)yawOver2);
|
||||
Quaternion result;
|
||||
result.x = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2;
|
||||
result.y = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
|
||||
result.z = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2;
|
||||
result.w = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ToAxisAngleRad(Quaternion q, out Vector3 axis, out double angle)
|
||||
{
|
||||
if (System.Math.Abs(q.w) > 1.0)
|
||||
{
|
||||
q.Normalize();
|
||||
}
|
||||
|
||||
angle = 2.0f * (double)System.Math.Acos(q.w); // angle
|
||||
double den = (double)System.Math.Sqrt(1.0 - q.w * q.w);
|
||||
if (den > 0.0001)
|
||||
{
|
||||
axis = q.xyz / den;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This occurs when the angle is zero.
|
||||
// Not a problem: just set an arbitrary normalized axis.
|
||||
axis = new Vector3(1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2 ^ w.GetHashCode() >> 1;
|
||||
}
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Quaternion))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Quaternion quaternion = (Quaternion)other;
|
||||
return x.Equals(quaternion.x) && y.Equals(quaternion.y) && z.Equals(quaternion.z) && w.Equals(quaternion.w);
|
||||
}
|
||||
|
||||
public bool Equals(Quaternion other)
|
||||
{
|
||||
return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z) && w.Equals(other.w);
|
||||
}
|
||||
|
||||
public static Quaternion operator *(Quaternion lhs, Quaternion rhs)
|
||||
{
|
||||
return new Quaternion(lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y, lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z, lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x, lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z);
|
||||
}
|
||||
|
||||
public static Vector3 operator *(Quaternion rotation, Vector3 point)
|
||||
{
|
||||
double num = rotation.x * 2;
|
||||
double num2 = rotation.y * 2;
|
||||
double num3 = rotation.z * 2;
|
||||
double num4 = rotation.x * num;
|
||||
double num5 = rotation.y * num2;
|
||||
double num6 = rotation.z * num3;
|
||||
double num7 = rotation.x * num2;
|
||||
double num8 = rotation.x * num3;
|
||||
double num9 = rotation.y * num3;
|
||||
double num10 = rotation.w * num;
|
||||
double num11 = rotation.w * num2;
|
||||
double num12 = rotation.w * num3;
|
||||
|
||||
return new Vector3(
|
||||
(1 - (num5 + num6)) * point.x + (num7 - num12) * point.y + (num8 + num11) * point.z,
|
||||
(num7 + num12) * point.x + (1 - (num4 + num6)) * point.y + (num9 - num10) * point.z,
|
||||
(num8 - num11) * point.x + (num9 + num10) * point.y + (1 - (num4 + num5)) * point.z);
|
||||
}
|
||||
|
||||
public static bool operator ==(Quaternion lhs, Quaternion rhs)
|
||||
{
|
||||
return Quaternion.Dot(lhs, rhs) > 0.999999999;
|
||||
}
|
||||
|
||||
public static bool operator !=(Quaternion lhs, Quaternion rhs)
|
||||
{
|
||||
return Quaternion.Dot(lhs, rhs) <= 0.999999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
LightlessSync/ThirdParty/Nanomesh/Base/SymmetricMatrix.cs
vendored
Normal file
97
LightlessSync/ThirdParty/Nanomesh/Base/SymmetricMatrix.cs
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct SymmetricMatrix
|
||||
{
|
||||
public readonly double m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
|
||||
|
||||
public SymmetricMatrix(in double m0, in double m1, in double m2, in double m3, in double m4, in double m5, in double m6, in double m7, in double m8, in double m9)
|
||||
{
|
||||
this.m0 = m0;
|
||||
this.m1 = m1;
|
||||
this.m2 = m2;
|
||||
this.m3 = m3;
|
||||
this.m4 = m4;
|
||||
this.m5 = m5;
|
||||
this.m6 = m6;
|
||||
this.m7 = m7;
|
||||
this.m8 = m8;
|
||||
this.m9 = m9;
|
||||
}
|
||||
|
||||
public SymmetricMatrix(in double a, in double b, in double c, in double d)
|
||||
{
|
||||
m0 = a * a;
|
||||
m1 = a * b;
|
||||
m2 = a * c;
|
||||
m3 = a * d;
|
||||
|
||||
m4 = b * b;
|
||||
m5 = b * c;
|
||||
m6 = b * d;
|
||||
|
||||
m7 = c * c;
|
||||
m8 = c * d;
|
||||
|
||||
m9 = d * d;
|
||||
}
|
||||
|
||||
public static SymmetricMatrix operator +(in SymmetricMatrix a, in SymmetricMatrix b)
|
||||
{
|
||||
return new SymmetricMatrix(
|
||||
a.m0 + b.m0, a.m1 + b.m1, a.m2 + b.m2, a.m3 + b.m3,
|
||||
a.m4 + b.m4, a.m5 + b.m5, a.m6 + b.m6,
|
||||
a.m7 + b.m7, a.m8 + b.m8,
|
||||
a.m9 + b.m9
|
||||
);
|
||||
}
|
||||
|
||||
public double DeterminantXYZ()
|
||||
{
|
||||
return
|
||||
m0 * m4 * m7 +
|
||||
m2 * m1 * m5 +
|
||||
m1 * m5 * m2 -
|
||||
m2 * m4 * m2 -
|
||||
m0 * m5 * m5 -
|
||||
m1 * m1 * m7;
|
||||
}
|
||||
|
||||
public double DeterminantX()
|
||||
{
|
||||
return
|
||||
m1 * m5 * m8 +
|
||||
m3 * m4 * m7 +
|
||||
m2 * m6 * m5 -
|
||||
m3 * m5 * m5 -
|
||||
m1 * m6 * m7 -
|
||||
m2 * m4 * m8;
|
||||
}
|
||||
|
||||
public double DeterminantY()
|
||||
{
|
||||
return
|
||||
m0 * m5 * m8 +
|
||||
m3 * m1 * m7 +
|
||||
m2 * m6 * m2 -
|
||||
m3 * m5 * m2 -
|
||||
m0 * m6 * m7 -
|
||||
m2 * m1 * m8;
|
||||
}
|
||||
|
||||
public double DeterminantZ()
|
||||
{
|
||||
return
|
||||
m0 * m4 * m8 +
|
||||
m3 * m1 * m5 +
|
||||
m1 * m6 * m2 -
|
||||
m3 * m4 * m2 -
|
||||
m0 * m6 * m5 -
|
||||
m1 * m1 * m8;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{m0} {m1} {m2} {m3}| {m4} {m5} {m6} | {m7} {m8} | {m9}";
|
||||
}
|
||||
}
|
||||
}
|
||||
26
LightlessSync/ThirdParty/Nanomesh/Base/TextUtils.cs
vendored
Normal file
26
LightlessSync/ThirdParty/Nanomesh/Base/TextUtils.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public static class TextUtils
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double ToDouble(this string text)
|
||||
{
|
||||
return double.Parse(text, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float ToFloat(this string text)
|
||||
{
|
||||
return float.Parse(text, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ToInt(this string text)
|
||||
{
|
||||
return int.Parse(text, CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
377
LightlessSync/ThirdParty/Nanomesh/Base/Vector2.cs
vendored
Normal file
377
LightlessSync/ThirdParty/Nanomesh/Base/Vector2.cs
vendored
Normal file
@@ -0,0 +1,377 @@
|
||||
using System;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct Vector2 : IEquatable<Vector2>, IInterpolable<Vector2>
|
||||
{
|
||||
public readonly double x;
|
||||
public readonly double y;
|
||||
|
||||
// Access the /x/ or /y/ component using [0] or [1] respectively.
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a new vector with given x, y components.
|
||||
public Vector2(double x, double y) { this.x = x; this.y = y; }
|
||||
|
||||
// Linearly interpolates between two vectors.
|
||||
public static Vector2 Lerp(Vector2 a, Vector2 b, double t)
|
||||
{
|
||||
t = MathF.Clamp(t, 0, 1);
|
||||
return new Vector2(
|
||||
a.x + (b.x - a.x) * t,
|
||||
a.y + (b.y - a.y) * t
|
||||
);
|
||||
}
|
||||
|
||||
// Linearly interpolates between two vectors without clamping the interpolant
|
||||
public static Vector2 LerpUnclamped(Vector2 a, Vector2 b, double t)
|
||||
{
|
||||
return new Vector2(
|
||||
a.x + (b.x - a.x) * t,
|
||||
a.y + (b.y - a.y) * t
|
||||
);
|
||||
}
|
||||
|
||||
// Moves a point /current/ towards /target/.
|
||||
public static Vector2 MoveTowards(Vector2 current, Vector2 target, double maxDistanceDelta)
|
||||
{
|
||||
// avoid vector ops because current scripting backends are terrible at inlining
|
||||
double toVector_x = target.x - current.x;
|
||||
double toVector_y = target.y - current.y;
|
||||
|
||||
double sqDist = toVector_x * toVector_x + toVector_y * toVector_y;
|
||||
|
||||
if (sqDist == 0 || (maxDistanceDelta >= 0 && sqDist <= maxDistanceDelta * maxDistanceDelta))
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
double dist = Math.Sqrt(sqDist);
|
||||
|
||||
return new Vector2(current.x + toVector_x / dist * maxDistanceDelta,
|
||||
current.y + toVector_y / dist * maxDistanceDelta);
|
||||
}
|
||||
|
||||
// Multiplies two vectors component-wise.
|
||||
public static Vector2 Scale(Vector2 a, Vector2 b) => new Vector2(a.x * b.x, a.y * b.y);
|
||||
|
||||
public static Vector2 Normalize(in Vector2 value)
|
||||
{
|
||||
double mag = Magnitude(in value);
|
||||
if (mag > K_EPSILON)
|
||||
{
|
||||
return value / mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 Normalize() => Normalize(in this);
|
||||
|
||||
public static double SqrMagnitude(in Vector2 a) => a.x * a.x + a.y * a.y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length of this vector (RO).
|
||||
/// </summary>
|
||||
public double SqrMagnitude() => SqrMagnitude(in this);
|
||||
|
||||
public static double Magnitude(in Vector2 vector) => Math.Sqrt(SqrMagnitude(in vector));
|
||||
|
||||
public double Magnitude() => Magnitude(this);
|
||||
|
||||
// used to allow Vector2s to be used as keys in hash tables
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ (y.GetHashCode() << 2);
|
||||
}
|
||||
|
||||
// also required for being able to use Vector2s as keys in hash tables
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((Vector2)other);
|
||||
}
|
||||
|
||||
|
||||
public bool Equals(Vector2 other)
|
||||
{
|
||||
return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
public static Vector2 Reflect(Vector2 inDirection, Vector2 inNormal)
|
||||
{
|
||||
double factor = -2F * Dot(inNormal, inDirection);
|
||||
return new Vector2(factor * inNormal.x + inDirection.x, factor * inNormal.y + inDirection.y);
|
||||
}
|
||||
|
||||
|
||||
public static Vector2 Perpendicular(Vector2 inDirection)
|
||||
{
|
||||
return new Vector2(-inDirection.y, inDirection.x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static double Dot(Vector2 lhs, Vector2 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle in radians between /from/ and /to/.
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <returns></returns>
|
||||
public static double AngleRadians(Vector2 from, Vector2 to)
|
||||
{
|
||||
// sqrt(a) * sqrt(b) = sqrt(a * b) -- valid for real numbers
|
||||
double denominator = Math.Sqrt(from.SqrMagnitude() * to.SqrMagnitude());
|
||||
if (denominator < K_EPSILON_NORMAL_SQRT)
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
|
||||
double dot = MathF.Clamp(Dot(from, to) / denominator, -1F, 1F);
|
||||
return Math.Acos(dot);
|
||||
}
|
||||
|
||||
public static double AngleDegrees(Vector2 from, Vector2 to)
|
||||
{
|
||||
return AngleRadians(from, to) / MathF.PI * 180f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the signed angle in degrees between /from/ and /to/. Always returns the smallest possible angle
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <returns></returns>
|
||||
public static double SignedAngle(Vector2 from, Vector2 to)
|
||||
{
|
||||
double unsigned_angle = AngleDegrees(from, to);
|
||||
double sign = Math.Sign(from.x * to.y - from.y * to.x);
|
||||
return unsigned_angle * sign;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between /a/ and /b/.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static double Distance(Vector2 a, Vector2 b)
|
||||
{
|
||||
double diff_x = a.x - b.x;
|
||||
double diff_y = a.y - b.y;
|
||||
return Math.Sqrt(diff_x * diff_x + diff_y * diff_y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of /vector/ with its magnitude clamped to /maxLength/.
|
||||
/// </summary>
|
||||
/// <param name="vector"></param>
|
||||
/// <param name="maxLength"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 ClampMagnitude(Vector2 vector, double maxLength)
|
||||
{
|
||||
double sqrMagnitude = vector.SqrMagnitude();
|
||||
if (sqrMagnitude > maxLength * maxLength)
|
||||
{
|
||||
double mag = Math.Sqrt(sqrMagnitude);
|
||||
|
||||
//these intermediate variables force the intermediate result to be
|
||||
//of double precision. without this, the intermediate result can be of higher
|
||||
//precision, which changes behavior.
|
||||
double normalized_x = vector.x / mag;
|
||||
double normalized_y = vector.y / mag;
|
||||
return new Vector2(normalized_x * maxLength,
|
||||
normalized_y * maxLength);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector that is made from the smallest components of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 Min(Vector2 lhs, Vector2 rhs) { return new Vector2(Math.Min(lhs.x, rhs.x), Math.Min(lhs.y, rhs.y)); }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector that is made from the largest components of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 Max(Vector2 lhs, Vector2 rhs) { return new Vector2(Math.Max(lhs.x, rhs.x), Math.Max(lhs.y, rhs.y)); }
|
||||
|
||||
public Vector2 Interpolate(Vector2 other, double ratio) => this * ratio + other * (1 - ratio);
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator +(Vector2 a, Vector2 b) { return new Vector2(a.x + b.x, a.y + b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one vector from another.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator -(Vector2 a, Vector2 b) { return new Vector2(a.x - b.x, a.y - b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies one vector by another.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator *(Vector2 a, Vector2 b) { return new Vector2(a.x * b.x, a.y * b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Divides one vector over another.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator /(Vector2 a, Vector2 b) { return new Vector2(a.x / b.x, a.y / b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Negates a vector.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator -(Vector2 a) { return new Vector2(-a.x, -a.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a number.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="d"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator *(Vector2 a, double d) { return new Vector2(a.x * d, a.y * d); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a number.
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator *(double d, Vector2 a) { return new Vector2(a.x * d, a.y * d); }
|
||||
|
||||
/// <summary>
|
||||
/// Divides a vector by a number.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="d"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2 operator /(Vector2 a, double d) { return new Vector2(a.x / d, a.y / d); }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(Vector2 lhs, Vector2 rhs)
|
||||
{
|
||||
// Returns false in the presence of NaN values.
|
||||
double diff_x = lhs.x - rhs.x;
|
||||
double diff_y = lhs.y - rhs.y;
|
||||
return (diff_x * diff_x + diff_y * diff_y) < K_EPSILON * K_EPSILON;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if vectors are different.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(Vector2 lhs, Vector2 rhs)
|
||||
{
|
||||
// Returns true in the presence of NaN values.
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a [[Vector3]] to a Vector2.
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
public static implicit operator Vector2(Vector3F v)
|
||||
{
|
||||
return new Vector2(v.x, v.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector2 to a [[Vector3]].
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
public static implicit operator Vector3(Vector2 v)
|
||||
{
|
||||
return new Vector3(v.x, v.y, 0);
|
||||
}
|
||||
|
||||
public static implicit operator Vector2F(Vector2 vec)
|
||||
{
|
||||
return new Vector2F((float)vec.x, (float)vec.y);
|
||||
}
|
||||
|
||||
public static explicit operator Vector2(Vector2F vec)
|
||||
{
|
||||
return new Vector2(vec.x, vec.y);
|
||||
}
|
||||
|
||||
public static readonly Vector2 zeroVector = new Vector2(0F, 0F);
|
||||
public static readonly Vector2 oneVector = new Vector2(1F, 1F);
|
||||
public static readonly Vector2 upVector = new Vector2(0F, 1F);
|
||||
public static readonly Vector2 downVector = new Vector2(0F, -1F);
|
||||
public static readonly Vector2 leftVector = new Vector2(-1F, 0F);
|
||||
public static readonly Vector2 rightVector = new Vector2(1F, 0F);
|
||||
public static readonly Vector2 positiveInfinityVector = new Vector2(double.PositiveInfinity, double.PositiveInfinity);
|
||||
public static readonly Vector2 negativeInfinityVector = new Vector2(double.NegativeInfinity, double.NegativeInfinity);
|
||||
|
||||
public static Vector2 Zero => zeroVector;
|
||||
|
||||
public static Vector2 One => oneVector;
|
||||
|
||||
public static Vector2 Up => upVector;
|
||||
|
||||
public static Vector2 Down => downVector;
|
||||
|
||||
public static Vector2 Left => leftVector;
|
||||
|
||||
public static Vector2 Right => rightVector;
|
||||
|
||||
public static Vector2 PositiveInfinity => positiveInfinityVector;
|
||||
|
||||
public static Vector2 NegativeInfinity => negativeInfinityVector;
|
||||
|
||||
public const double K_EPSILON = 0.00001F;
|
||||
|
||||
public const double K_EPSILON_NORMAL_SQRT = 1e-15f;
|
||||
}
|
||||
}
|
||||
371
LightlessSync/ThirdParty/Nanomesh/Base/Vector2F.cs
vendored
Normal file
371
LightlessSync/ThirdParty/Nanomesh/Base/Vector2F.cs
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
using System;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct Vector2F : IEquatable<Vector2F>, IInterpolable<Vector2F>
|
||||
{
|
||||
public readonly float x;
|
||||
public readonly float y;
|
||||
|
||||
// Access the /x/ or /y/ component using [0] or [1] respectively.
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector2 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a new vector with given x, y components.
|
||||
public Vector2F(float x, float y) { this.x = x; this.y = y; }
|
||||
|
||||
// Linearly interpolates between two vectors.
|
||||
public static Vector2F Lerp(Vector2F a, Vector2F b, float t)
|
||||
{
|
||||
t = MathF.Clamp(t, 0, 1);
|
||||
return new Vector2F(
|
||||
a.x + (b.x - a.x) * t,
|
||||
a.y + (b.y - a.y) * t
|
||||
);
|
||||
}
|
||||
|
||||
// Linearly interpolates between two vectors without clamping the interpolant
|
||||
public static Vector2F LerpUnclamped(Vector2F a, Vector2F b, float t)
|
||||
{
|
||||
return new Vector2F(
|
||||
a.x + (b.x - a.x) * t,
|
||||
a.y + (b.y - a.y) * t
|
||||
);
|
||||
}
|
||||
|
||||
// Moves a point /current/ towards /target/.
|
||||
public static Vector2F MoveTowards(Vector2F current, Vector2F target, float maxDistanceDelta)
|
||||
{
|
||||
// avoid vector ops because current scripting backends are terrible at inlining
|
||||
float toVector_x = target.x - current.x;
|
||||
float toVector_y = target.y - current.y;
|
||||
|
||||
float sqDist = toVector_x * toVector_x + toVector_y * toVector_y;
|
||||
|
||||
if (sqDist == 0 || (maxDistanceDelta >= 0 && sqDist <= maxDistanceDelta * maxDistanceDelta))
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
float dist = MathF.Sqrt(sqDist);
|
||||
|
||||
return new Vector2F(current.x + toVector_x / dist * maxDistanceDelta,
|
||||
current.y + toVector_y / dist * maxDistanceDelta);
|
||||
}
|
||||
|
||||
// Multiplies two vectors component-wise.
|
||||
public static Vector2F Scale(Vector2F a, Vector2F b) { return new Vector2F(a.x * b.x, a.y * b.y); }
|
||||
|
||||
public static Vector2F Normalize(in Vector2F value)
|
||||
{
|
||||
float mag = Magnitude(in value);
|
||||
if (mag > K_EPSILON)
|
||||
{
|
||||
return value / mag;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2F Normalize() => Normalize(in this);
|
||||
|
||||
public static float SqrMagnitude(in Vector2F a) => a.x * a.x + a.y * a.y;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the squared length of this vector (RO).
|
||||
/// </summary>
|
||||
public float SqrMagnitude() => SqrMagnitude(in this);
|
||||
|
||||
public static float Magnitude(in Vector2F vector) => (float)Math.Sqrt(SqrMagnitude(in vector));
|
||||
|
||||
public float Magnitude() => Magnitude(this);
|
||||
|
||||
// used to allow Vector2s to be used as keys in hash tables
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ (y.GetHashCode() << 2);
|
||||
}
|
||||
|
||||
// also required for being able to use Vector2s as keys in hash tables
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector2F))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((Vector2F)other);
|
||||
}
|
||||
|
||||
|
||||
public bool Equals(Vector2F other)
|
||||
{
|
||||
return Vector2FComparer.Default.Equals(this, other);
|
||||
//return x == other.x && y == other.y;
|
||||
}
|
||||
|
||||
public static Vector2F Reflect(Vector2F inDirection, Vector2F inNormal)
|
||||
{
|
||||
float factor = -2F * Dot(inNormal, inDirection);
|
||||
return new Vector2F(factor * inNormal.x + inDirection.x, factor * inNormal.y + inDirection.y);
|
||||
}
|
||||
|
||||
public static Vector2F Perpendicular(Vector2F inDirection)
|
||||
{
|
||||
return new Vector2F(-inDirection.y, inDirection.x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the dot Product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static float Dot(Vector2F lhs, Vector2F rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the angle in radians between /from/ and /to/.
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <returns></returns>
|
||||
public static float AngleRadians(Vector2F from, Vector2F to)
|
||||
{
|
||||
// sqrt(a) * sqrt(b) = sqrt(a * b) -- valid for real numbers
|
||||
float denominator = MathF.Sqrt(from.SqrMagnitude() * to.SqrMagnitude());
|
||||
if (denominator < K_EPSILON_NORMAL_SQRT)
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
|
||||
float dot = MathF.Clamp(Dot(from, to) / denominator, -1F, 1F);
|
||||
return MathF.Acos(dot);
|
||||
}
|
||||
|
||||
public static float AngleDegrees(Vector2F from, Vector2F to)
|
||||
{
|
||||
return AngleRadians(from, to) / MathF.PI * 180f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the signed angle in degrees between /from/ and /to/. Always returns the smallest possible angle
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <returns></returns>
|
||||
public static float SignedAngle(Vector2F from, Vector2F to)
|
||||
{
|
||||
float unsigned_angle = AngleDegrees(from, to);
|
||||
float sign = MathF.Sign(from.x * to.y - from.y * to.x);
|
||||
return unsigned_angle * sign;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the distance between /a/ and /b/.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static float Distance(Vector2F a, Vector2F b)
|
||||
{
|
||||
float diff_x = a.x - b.x;
|
||||
float diff_y = a.y - b.y;
|
||||
return MathF.Sqrt(diff_x * diff_x + diff_y * diff_y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a copy of /vector/ with its magnitude clamped to /maxLength/.
|
||||
/// </summary>
|
||||
/// <param name="vector"></param>
|
||||
/// <param name="maxLength"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F ClampMagnitude(Vector2F vector, float maxLength)
|
||||
{
|
||||
float sqrMagnitude = vector.SqrMagnitude();
|
||||
if (sqrMagnitude > maxLength * maxLength)
|
||||
{
|
||||
float mag = MathF.Sqrt(sqrMagnitude);
|
||||
|
||||
//these intermediate variables force the intermediate result to be
|
||||
//of float precision. without this, the intermediate result can be of higher
|
||||
//precision, which changes behavior.
|
||||
float normalized_x = vector.x / mag;
|
||||
float normalized_y = vector.y / mag;
|
||||
return new Vector2F(normalized_x * maxLength,
|
||||
normalized_y * maxLength);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector that is made from the smallest components of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F Min(Vector2F lhs, Vector2F rhs) { return new Vector2F(MathF.Min(lhs.x, rhs.x), MathF.Min(lhs.y, rhs.y)); }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a vector that is made from the largest components of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F Max(Vector2F lhs, Vector2F rhs) { return new Vector2F(MathF.Max(lhs.x, rhs.x), MathF.Max(lhs.y, rhs.y)); }
|
||||
|
||||
public Vector2F Interpolate(Vector2F other, double ratio) => this * ratio + other * (1 - ratio);
|
||||
|
||||
/// <summary>
|
||||
/// Adds two vectors.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator +(Vector2F a, Vector2F b) { return new Vector2F(a.x + b.x, a.y + b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Subtracts one vector from another.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator -(Vector2F a, Vector2F b) { return new Vector2F(a.x - b.x, a.y - b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies one vector by another.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator *(Vector2F a, Vector2F b) { return new Vector2F(a.x * b.x, a.y * b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Divides one vector over another.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator /(Vector2F a, Vector2F b) { return new Vector2F(a.x / b.x, a.y / b.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Negates a vector.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator -(Vector2F a) { return new Vector2F(-a.x, -a.y); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a number.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="d"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator *(Vector2F a, float d) { return new Vector2F(a.x * d, a.y * d); }
|
||||
|
||||
public static Vector2 operator *(Vector2F a, double d) { return new Vector2(a.x * d, a.y * d); }
|
||||
|
||||
/// <summary>
|
||||
/// Multiplies a vector by a number.
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator *(float d, Vector2F a) { return new Vector2F(a.x * d, a.y * d); }
|
||||
|
||||
public static Vector2 operator *(double d, Vector2F a) { return new Vector2(a.x * d, a.y * d); }
|
||||
|
||||
/// <summary>
|
||||
/// Divides a vector by a number.
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="d"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector2F operator /(Vector2F a, float d) { return new Vector2F(a.x / d, a.y / d); }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(Vector2F lhs, Vector2F rhs)
|
||||
{
|
||||
// Returns false in the presence of NaN values.
|
||||
float diff_x = lhs.x - rhs.x;
|
||||
float diff_y = lhs.y - rhs.y;
|
||||
return (diff_x * diff_x + diff_y * diff_y) < K_EPSILON * K_EPSILON;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if vectors are different.
|
||||
/// </summary>
|
||||
/// <param name="lhs"></param>
|
||||
/// <param name="rhs"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(Vector2F lhs, Vector2F rhs)
|
||||
{
|
||||
// Returns true in the presence of NaN values.
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a [[Vector3]] to a Vector2.
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
public static implicit operator Vector2F(Vector3F v)
|
||||
{
|
||||
return new Vector2F(v.x, v.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Vector2 to a [[Vector3]].
|
||||
/// </summary>
|
||||
/// <param name="v"></param>
|
||||
public static implicit operator Vector3(Vector2F v)
|
||||
{
|
||||
return new Vector3(v.x, v.y, 0);
|
||||
}
|
||||
|
||||
public static readonly Vector2F zeroVector = new Vector2F(0F, 0F);
|
||||
public static readonly Vector2F oneVector = new Vector2F(1F, 1F);
|
||||
public static readonly Vector2F upVector = new Vector2F(0F, 1F);
|
||||
public static readonly Vector2F downVector = new Vector2F(0F, -1F);
|
||||
public static readonly Vector2F leftVector = new Vector2F(-1F, 0F);
|
||||
public static readonly Vector2F rightVector = new Vector2F(1F, 0F);
|
||||
public static readonly Vector2F positiveInfinityVector = new Vector2F(float.PositiveInfinity, float.PositiveInfinity);
|
||||
public static readonly Vector2F negativeInfinityVector = new Vector2F(float.NegativeInfinity, float.NegativeInfinity);
|
||||
|
||||
public static Vector2F Zero => zeroVector;
|
||||
|
||||
public static Vector2F One => oneVector;
|
||||
|
||||
public static Vector2F Up => upVector;
|
||||
|
||||
public static Vector2F Down => downVector;
|
||||
|
||||
public static Vector2F Left => leftVector;
|
||||
|
||||
public static Vector2F Right => rightVector;
|
||||
|
||||
public static Vector2F PositiveInfinity => positiveInfinityVector;
|
||||
|
||||
public static Vector2F NegativeInfinity => negativeInfinityVector;
|
||||
|
||||
public const float K_EPSILON = 0.00001F;
|
||||
|
||||
public const float K_EPSILON_NORMAL_SQRT = 1e-15f;
|
||||
}
|
||||
}
|
||||
28
LightlessSync/ThirdParty/Nanomesh/Base/Vector2FComparer.cs
vendored
Normal file
28
LightlessSync/ThirdParty/Nanomesh/Base/Vector2FComparer.cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public class Vector2FComparer : IEqualityComparer<Vector2F>
|
||||
{
|
||||
private static Vector2FComparer _instance;
|
||||
public static Vector2FComparer Default => _instance ?? (_instance = new Vector2FComparer(0.0001f));
|
||||
|
||||
private readonly float _tolerance;
|
||||
|
||||
public Vector2FComparer(float tolerance)
|
||||
{
|
||||
_tolerance = tolerance;
|
||||
}
|
||||
|
||||
public bool Equals(Vector2F x, Vector2F y)
|
||||
{
|
||||
return (int)(x.x / _tolerance) == (int)(y.x / _tolerance)
|
||||
&& (int)(x.y / _tolerance) == (int)(y.y / _tolerance);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector2F obj)
|
||||
{
|
||||
return (int)(obj.x / _tolerance) ^ ((int)(obj.y / _tolerance) << 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
191
LightlessSync/ThirdParty/Nanomesh/Base/Vector3.cs
vendored
Normal file
191
LightlessSync/ThirdParty/Nanomesh/Base/Vector3.cs
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct Vector3 : IEquatable<Vector3>, IInterpolable<Vector3>
|
||||
{
|
||||
public readonly double x;
|
||||
public readonly double y;
|
||||
public readonly double z;
|
||||
|
||||
public Vector3(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Vector3(double x, double y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
z = 0.0;
|
||||
}
|
||||
|
||||
public double this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3 index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return x.GetHashCode() ^ (y.GetHashCode() << 2) ^ (z.GetHashCode() >> 2);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector3))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((Vector3)other);
|
||||
}
|
||||
|
||||
public bool Equals(Vector3 other)
|
||||
{
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
public static Vector3 operator +(in Vector3 a, in Vector3 b) { return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z); }
|
||||
|
||||
public static Vector3 operator -(in Vector3 a, in Vector3 b) { return new Vector3(a.x - b.x, a.y - b.y, a.z - b.z); }
|
||||
|
||||
public static Vector3 operator -(in Vector3 a) { return new Vector3(-a.x, -a.y, -a.z); }
|
||||
|
||||
public static Vector3 operator *(in Vector3 a, double d) { return new Vector3(a.x * d, a.y * d, a.z * d); }
|
||||
|
||||
public static Vector3 operator *(double d, in Vector3 a) { return new Vector3(a.x * d, a.y * d, a.z * d); }
|
||||
|
||||
public static Vector3 operator /(in Vector3 a, double d) { return new Vector3(MathUtils.DivideSafe(a.x, d), MathUtils.DivideSafe(a.y, d), MathUtils.DivideSafe(a.z, d)); }
|
||||
|
||||
public static bool operator ==(in Vector3 lhs, in Vector3 rhs)
|
||||
{
|
||||
double diff_x = lhs.x - rhs.x;
|
||||
double diff_y = lhs.y - rhs.y;
|
||||
double diff_z = lhs.z - rhs.z;
|
||||
double sqrmag = diff_x * diff_x + diff_y * diff_y + diff_z * diff_z;
|
||||
return sqrmag < MathUtils.EpsilonDouble;
|
||||
}
|
||||
|
||||
public static bool operator !=(in Vector3 lhs, in Vector3 rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
public static Vector3 Cross(in Vector3 lhs, in Vector3 rhs)
|
||||
{
|
||||
return new Vector3(
|
||||
lhs.y * rhs.z - lhs.z * rhs.y,
|
||||
lhs.z * rhs.x - lhs.x * rhs.z,
|
||||
lhs.x * rhs.y - lhs.y * rhs.x);
|
||||
}
|
||||
|
||||
public static implicit operator Vector3F(Vector3 vec)
|
||||
{
|
||||
return new Vector3F((float)vec.x, (float)vec.y, (float)vec.z);
|
||||
}
|
||||
|
||||
public static explicit operator Vector3(Vector3F vec)
|
||||
{
|
||||
return new Vector3(vec.x, vec.y, vec.z);
|
||||
}
|
||||
|
||||
public static double Dot(in Vector3 lhs, in Vector3 rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
|
||||
}
|
||||
|
||||
public static Vector3 Normalize(in Vector3 value)
|
||||
{
|
||||
double mag = Magnitude(value);
|
||||
return value / mag;
|
||||
}
|
||||
|
||||
public Vector3 Normalized => Vector3.Normalize(this);
|
||||
|
||||
public static double Distance(in Vector3 a, in Vector3 b)
|
||||
{
|
||||
double diff_x = a.x - b.x;
|
||||
double diff_y = a.y - b.y;
|
||||
double diff_z = a.z - b.z;
|
||||
return Math.Sqrt(diff_x * diff_x + diff_y * diff_y + diff_z * diff_z);
|
||||
}
|
||||
|
||||
public static double Magnitude(in Vector3 vector)
|
||||
{
|
||||
return Math.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
|
||||
}
|
||||
|
||||
public static Vector3 ProjectPointOnLine(in Vector3 linePoint, in Vector3 lineVec, in Vector3 point)
|
||||
{
|
||||
Vector3 linePointToPoint = point - linePoint;
|
||||
return linePoint + lineVec * Dot(linePointToPoint, lineVec);
|
||||
}
|
||||
|
||||
public static double DistancePointLine(in Vector3 point, in Vector3 lineStart, in Vector3 lineEnd)
|
||||
{
|
||||
return Magnitude(ProjectPointOnLine(lineStart, (lineEnd - lineStart).Normalized, point) - point);
|
||||
}
|
||||
|
||||
public double LengthSquared => x * x + y * y + z * z;
|
||||
|
||||
public double Length => Math.Sqrt(x * x + y * y + z * z);
|
||||
|
||||
public static Vector3 Min(in Vector3 lhs, in Vector3 rhs)
|
||||
{
|
||||
return new Vector3(Math.Min(lhs.x, rhs.x), Math.Min(lhs.y, rhs.y), Math.Min(lhs.z, rhs.z));
|
||||
}
|
||||
|
||||
public static Vector3 Max(in Vector3 lhs, in Vector3 rhs)
|
||||
{
|
||||
return new Vector3(Math.Max(lhs.x, rhs.x), Math.Max(lhs.y, rhs.y), Math.Max(lhs.z, rhs.z));
|
||||
}
|
||||
|
||||
public static readonly Vector3 zeroVector = new Vector3(0f, 0f, 0f);
|
||||
public static readonly Vector3 oneVector = new Vector3(1f, 1f, 1f);
|
||||
public static readonly Vector3 positiveInfinityVector = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
|
||||
public static readonly Vector3 negativeInfinityVector = new Vector3(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
|
||||
|
||||
public static Vector3 Zero => zeroVector;
|
||||
|
||||
public static Vector3 One => oneVector;
|
||||
|
||||
public static Vector3 PositiveInfinity => positiveInfinityVector;
|
||||
|
||||
public static Vector3 NegativeInfinity => negativeInfinityVector;
|
||||
|
||||
public static double AngleRadians(in Vector3 from, in Vector3 to)
|
||||
{
|
||||
double denominator = Math.Sqrt(from.LengthSquared * to.LengthSquared);
|
||||
if (denominator < 1e-15F)
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
|
||||
double dot = MathF.Clamp(Dot(from, to) / denominator, -1.0, 1.0);
|
||||
return Math.Acos(dot);
|
||||
}
|
||||
|
||||
public static double AngleDegrees(in Vector3 from, in Vector3 to)
|
||||
{
|
||||
return AngleRadians(from, to) / Math.PI * 180d;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{x}, {y}, {z}";
|
||||
}
|
||||
|
||||
public Vector3 Interpolate(Vector3 other, double ratio) => this * ratio + other * (1 - ratio);
|
||||
}
|
||||
}
|
||||
26
LightlessSync/ThirdParty/Nanomesh/Base/Vector3Comparer.cs
vendored
Normal file
26
LightlessSync/ThirdParty/Nanomesh/Base/Vector3Comparer.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public class Vector3Comparer : IEqualityComparer<Vector3>
|
||||
{
|
||||
private readonly double _tolerance;
|
||||
|
||||
public Vector3Comparer(double tolerance)
|
||||
{
|
||||
_tolerance = tolerance;
|
||||
}
|
||||
|
||||
public bool Equals(Vector3 x, Vector3 y)
|
||||
{
|
||||
return (int)(x.x / _tolerance) == (int)(y.x / _tolerance)
|
||||
&& (int)(x.y / _tolerance) == (int)(y.y / _tolerance)
|
||||
&& (int)(x.z / _tolerance) == (int)(y.z / _tolerance);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector3 obj)
|
||||
{
|
||||
return (int)(obj.x / _tolerance) ^ ((int)(obj.y / _tolerance) << 2) ^ ((int)(obj.z / _tolerance) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
172
LightlessSync/ThirdParty/Nanomesh/Base/Vector3F.cs
vendored
Normal file
172
LightlessSync/ThirdParty/Nanomesh/Base/Vector3F.cs
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct Vector3F : IEquatable<Vector3F>, IInterpolable<Vector3F>
|
||||
{
|
||||
public readonly float x;
|
||||
public readonly float y;
|
||||
public readonly float z;
|
||||
|
||||
public Vector3F(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public Vector3F(float x, float y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
z = 0F;
|
||||
}
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector3F index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Vector3FComparer.Default.GetHashCode(this);
|
||||
//return x.GetHashCode() ^ (y.GetHashCode() << 2) ^ (z.GetHashCode() >> 2);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector3F))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((Vector3F)other);
|
||||
}
|
||||
|
||||
public bool Equals(Vector3F other)
|
||||
{
|
||||
return Vector3FComparer.Default.Equals(this, other);
|
||||
//return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
public static Vector3F operator +(in Vector3F a, in Vector3F b) { return new Vector3F(a.x + b.x, a.y + b.y, a.z + b.z); }
|
||||
|
||||
public static Vector3F operator -(in Vector3F a, in Vector3F b) { return new Vector3F(a.x - b.x, a.y - b.y, a.z - b.z); }
|
||||
|
||||
public static Vector3F operator -(in Vector3F a) { return new Vector3F(-a.x, -a.y, -a.z); }
|
||||
|
||||
public static Vector3F operator *(in Vector3F a, float d) { return new Vector3F(a.x * d, a.y * d, a.z * d); }
|
||||
|
||||
public static Vector3F operator *(float d, in Vector3F a) { return new Vector3F(a.x * d, a.y * d, a.z * d); }
|
||||
|
||||
public static Vector3 operator *(double d, in Vector3F a) { return new Vector3(a.x * d, a.y * d, a.z * d); }
|
||||
|
||||
public static Vector3F operator /(in Vector3F a, float d) { return new Vector3F(MathUtils.DivideSafe(a.x, d), MathUtils.DivideSafe(a.y, d), MathUtils.DivideSafe(a.z, d)); }
|
||||
|
||||
public static bool operator ==(in Vector3F lhs, in Vector3F rhs)
|
||||
{
|
||||
float diff_x = lhs.x - rhs.x;
|
||||
float diff_y = lhs.y - rhs.y;
|
||||
float diff_z = lhs.z - rhs.z;
|
||||
float sqrmag = diff_x * diff_x + diff_y * diff_y + diff_z * diff_z;
|
||||
return sqrmag < MathUtils.EpsilonFloat;
|
||||
}
|
||||
|
||||
public static bool operator !=(in Vector3F lhs, in Vector3F rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
public static Vector3F Cross(in Vector3F lhs, in Vector3F rhs)
|
||||
{
|
||||
return new Vector3F(
|
||||
lhs.y * rhs.z - lhs.z * rhs.y,
|
||||
lhs.z * rhs.x - lhs.x * rhs.z,
|
||||
lhs.x * rhs.y - lhs.y * rhs.x);
|
||||
}
|
||||
|
||||
public static float Dot(in Vector3F lhs, in Vector3F rhs)
|
||||
{
|
||||
return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
|
||||
}
|
||||
|
||||
public static Vector3F Normalize(in Vector3F value)
|
||||
{
|
||||
float mag = Magnitude(value);
|
||||
return value / mag;
|
||||
}
|
||||
|
||||
public Vector3F Normalized => Vector3F.Normalize(this);
|
||||
|
||||
public static float Distance(in Vector3F a, in Vector3F b)
|
||||
{
|
||||
float diff_x = a.x - b.x;
|
||||
float diff_y = a.y - b.y;
|
||||
float diff_z = a.z - b.z;
|
||||
return MathF.Sqrt(diff_x * diff_x + diff_y * diff_y + diff_z * diff_z);
|
||||
}
|
||||
|
||||
public static float Magnitude(in Vector3F vector)
|
||||
{
|
||||
return MathF.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
|
||||
}
|
||||
|
||||
public float SqrMagnitude => x * x + y * y + z * z;
|
||||
|
||||
public static Vector3F Min(in Vector3F lhs, in Vector3F rhs)
|
||||
{
|
||||
return new Vector3F(MathF.Min(lhs.x, rhs.x), MathF.Min(lhs.y, rhs.y), MathF.Min(lhs.z, rhs.z));
|
||||
}
|
||||
|
||||
public static Vector3F Max(in Vector3F lhs, in Vector3F rhs)
|
||||
{
|
||||
return new Vector3F(MathF.Max(lhs.x, rhs.x), MathF.Max(lhs.y, rhs.y), MathF.Max(lhs.z, rhs.z));
|
||||
}
|
||||
|
||||
public static readonly Vector3F zeroVector = new Vector3F(0f, 0f, 0f);
|
||||
public static readonly Vector3F oneVector = new Vector3F(1f, 1f, 1f);
|
||||
public static readonly Vector3F positiveInfinityVector = new Vector3F(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity);
|
||||
public static readonly Vector3F negativeInfinityVector = new Vector3F(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity);
|
||||
|
||||
public static Vector3F Zero => zeroVector;
|
||||
|
||||
public static Vector3F One => oneVector;
|
||||
|
||||
public static Vector3F PositiveInfinity => positiveInfinityVector;
|
||||
|
||||
public static Vector3F NegativeInfinity => negativeInfinityVector;
|
||||
|
||||
public static float AngleRadians(in Vector3F from, in Vector3F to)
|
||||
{
|
||||
float denominator = MathF.Sqrt(from.SqrMagnitude * to.SqrMagnitude);
|
||||
if (denominator < 1e-15F)
|
||||
{
|
||||
return 0F;
|
||||
}
|
||||
|
||||
float dot = MathF.Clamp(Dot(from, to) / denominator, -1F, 1F);
|
||||
return MathF.Acos(dot);
|
||||
}
|
||||
|
||||
public static float AngleDegrees(in Vector3F from, in Vector3F to)
|
||||
{
|
||||
return AngleRadians(from, to) / MathF.PI * 180f;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{x}, {y}, {z}";
|
||||
}
|
||||
|
||||
public Vector3F Interpolate(Vector3F other, double ratio) => (ratio * this + (1 - ratio) * other).Normalized;
|
||||
}
|
||||
}
|
||||
29
LightlessSync/ThirdParty/Nanomesh/Base/Vector3FComparer.cs
vendored
Normal file
29
LightlessSync/ThirdParty/Nanomesh/Base/Vector3FComparer.cs
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public class Vector3FComparer : IEqualityComparer<Vector3F>
|
||||
{
|
||||
private static Vector3FComparer _instance;
|
||||
public static Vector3FComparer Default => _instance ?? (_instance = new Vector3FComparer(0.001f));
|
||||
|
||||
private readonly float _tolerance;
|
||||
|
||||
public Vector3FComparer(float tolerance)
|
||||
{
|
||||
_tolerance = tolerance;
|
||||
}
|
||||
|
||||
public bool Equals(Vector3F x, Vector3F y)
|
||||
{
|
||||
return (int)(x.x / _tolerance) == (int)(y.x / _tolerance)
|
||||
&& (int)(x.y / _tolerance) == (int)(y.y / _tolerance)
|
||||
&& (int)(x.z / _tolerance) == (int)(y.z / _tolerance);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector3F obj)
|
||||
{
|
||||
return (int)(obj.x / _tolerance) ^ ((int)(obj.y / _tolerance) << 2) ^ ((int)(obj.z / _tolerance) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
91
LightlessSync/ThirdParty/Nanomesh/Base/Vector4F.cs
vendored
Normal file
91
LightlessSync/ThirdParty/Nanomesh/Base/Vector4F.cs
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public readonly struct Vector4F : IEquatable<Vector4F>, IInterpolable<Vector4F>
|
||||
{
|
||||
public readonly float x;
|
||||
public readonly float y;
|
||||
public readonly float z;
|
||||
public readonly float w;
|
||||
|
||||
public Vector4F(float x, float y, float z, float w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
case 3: return w;
|
||||
default:
|
||||
throw new IndexOutOfRangeException("Invalid Vector4F index!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Vector4FComparer.Default.GetHashCode(this);
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is Vector4F))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((Vector4F)other);
|
||||
}
|
||||
|
||||
public bool Equals(Vector4F other)
|
||||
{
|
||||
return Vector4FComparer.Default.Equals(this, other);
|
||||
}
|
||||
|
||||
public static Vector4F operator +(in Vector4F a, in Vector4F b)
|
||||
=> new(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||
|
||||
public static Vector4F operator -(in Vector4F a, in Vector4F b)
|
||||
=> new(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||
|
||||
public static Vector4F operator *(in Vector4F a, float d)
|
||||
=> new(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
|
||||
public static Vector4F operator *(float d, in Vector4F a)
|
||||
=> new(a.x * d, a.y * d, a.z * d, a.w * d);
|
||||
|
||||
public static Vector4F operator /(in Vector4F a, float d)
|
||||
=> new(MathUtils.DivideSafe(a.x, d), MathUtils.DivideSafe(a.y, d), MathUtils.DivideSafe(a.z, d), MathUtils.DivideSafe(a.w, d));
|
||||
|
||||
public static bool operator ==(in Vector4F lhs, in Vector4F rhs)
|
||||
=> Vector4FComparer.Default.Equals(lhs, rhs);
|
||||
|
||||
public static bool operator !=(in Vector4F lhs, in Vector4F rhs)
|
||||
=> !Vector4FComparer.Default.Equals(lhs, rhs);
|
||||
|
||||
public static float Dot(in Vector4F lhs, in Vector4F rhs)
|
||||
=> (lhs.x * rhs.x) + (lhs.y * rhs.y) + (lhs.z * rhs.z) + (lhs.w * rhs.w);
|
||||
|
||||
public Vector4F Interpolate(Vector4F other, double ratio)
|
||||
{
|
||||
var t = (float)ratio;
|
||||
var inv = 1f - t;
|
||||
return new Vector4F(
|
||||
(x * inv) + (other.x * t),
|
||||
(y * inv) + (other.y * t),
|
||||
(z * inv) + (other.z * t),
|
||||
(w * inv) + (other.w * t));
|
||||
}
|
||||
}
|
||||
}
|
||||
33
LightlessSync/ThirdParty/Nanomesh/Base/Vector4FComparer.cs
vendored
Normal file
33
LightlessSync/ThirdParty/Nanomesh/Base/Vector4FComparer.cs
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public class Vector4FComparer : IEqualityComparer<Vector4F>
|
||||
{
|
||||
private static Vector4FComparer? _instance;
|
||||
public static Vector4FComparer Default => _instance ??= new Vector4FComparer(0.0001f);
|
||||
|
||||
private readonly float _tolerance;
|
||||
|
||||
public Vector4FComparer(float tolerance)
|
||||
{
|
||||
_tolerance = tolerance;
|
||||
}
|
||||
|
||||
public bool Equals(Vector4F x, Vector4F y)
|
||||
{
|
||||
return (int)(x.x / _tolerance) == (int)(y.x / _tolerance)
|
||||
&& (int)(x.y / _tolerance) == (int)(y.y / _tolerance)
|
||||
&& (int)(x.z / _tolerance) == (int)(y.z / _tolerance)
|
||||
&& (int)(x.w / _tolerance) == (int)(y.w / _tolerance);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector4F obj)
|
||||
{
|
||||
return (int)(obj.x / _tolerance)
|
||||
^ ((int)(obj.y / _tolerance) << 2)
|
||||
^ ((int)(obj.z / _tolerance) >> 2)
|
||||
^ ((int)(obj.w / _tolerance) << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
LightlessSync/ThirdParty/Nanomesh/Base/VertexData.cs
vendored
Normal file
48
LightlessSync/ThirdParty/Nanomesh/Base/VertexData.cs
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nanomesh
|
||||
{
|
||||
public struct VertexData : IEquatable<VertexData>
|
||||
{
|
||||
public int position;
|
||||
public List<object> attributes; // TODO : This is not optimal regarding memory
|
||||
|
||||
public VertexData(int pos)
|
||||
{
|
||||
position = pos;
|
||||
attributes = new List<object>();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 31 + position;
|
||||
foreach (object attr in attributes)
|
||||
{
|
||||
hash = hash * 31 + attr.GetHashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(VertexData other)
|
||||
{
|
||||
if (!position.Equals(other.position))
|
||||
return false;
|
||||
|
||||
if (attributes.Count != other.attributes.Count)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < attributes.Count; i++)
|
||||
{
|
||||
if (!attributes[i].Equals(other.attributes[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user