All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m9s
# Patchnotes 2.1.0 The changes in this update are more than just "patches". With a new UI, a new feature, and a bunch of bug fixes, improvements and a new member on the dev team, we thought this was more of a minor update. We would like to introduce @tsubasahane of MareCN to the team! We’re happy to work with them to bring Lightless and its features to the CN client as well as having another talented dev bring features and ideas to us. Speaking of which: # Location Sharing (Big shout out to @tsubasahane for bringing this feature) - Are you TIRED of scrambling to find the address of the venue you're in to share with your friends? We are introducing Location Sharing! An optional feature where you can share your location with direct pairs temporarily [30 minutes, 1 hour, 3 hours] minutes or until you turn it off for them. That's up to you! [#125](<#125>) [#49](<Lightless-Sync/LightlessServer#49>) - To share your location with a pair, click the three dots beside the pair and choose a duration to share with them. [#125](<#125>) [#49](<Lightless-Sync/LightlessServer#49>) - To view the location of someone who's shared with you, simply hover over the globe icon! [#125](<#125>) [#49](<Lightless-Sync/LightlessServer#49>) [1] # Model Optimization (Mesh Decimating) - This new option can automatically “simplify” incoming character meshes to help performance by reducing triangle counts. You choose how strong the reduction is (default/recommended is 80%). [#131](<#131>) - Decimation only kicks in when a mesh is above a certain triangle threshold, and only for the items that qualify for it and you selected for. [#131](<#131>) - Hair meshes is always excluded, since simplifying hair meshes is very prone to breaking. - You can find everything under Settings → Performance → Model Optimization. [#131](<#131>) + ** IF YOU HAVE USED DECIMATION IN TESTING, PLEASE CLEAR YOUR CACHE ❗ ** [2] # Animation (PAP) Validation (Safer animations) - Lightless now checks your currently animations to see if they work with your local skeleton/bone mod. If an animation matches, it’s included in what gets sent to other players. If it doesn’t, Lightless will skip it and write a warning to your log showing how many were skipped due to skeleton changes. Its defaulted to Unsafe (off). turn it on if you experience crashes from others users. [#131](<#131>) - Lightless also does the same kind of check for incoming animation files, to make sure they match the body/skeleton they were sent with. [#131](<#131>) - Because these checks can sometimes be a little picky, you can adjust how strict they are in Settings -> General -> Animation & Bones to reduce false positives. [#131](<#131>) # UI Changes (Thanks to @kyuwu for UI Changes) - The top part of the main screen has gotten a makeover. You can adjust the colors of the gradiant in the Color settings of Lightless. [#127](<#127>) [3] - Settings have gotten some changes as well to make this change more universal, and will use the same color settings. [#127](<#127>) - The particle effects of the gradient are toggleable in 'Settings -> UI -> Behavior' [#127](<#127>) - Instead of showing download/upload on bottom of Main UI, it will show VRAM usage and triangles with their optimization options next to it [#138](<#138>) # LightFinder / ShellFinder - UI Changes that follow our new design follow the color codes for the Gradient top as the main screen does. [#127](<#127>) [4] Co-authored-by: defnotken <itsdefnotken@gmail.com> Co-authored-by: azyges <aaaaaa@aaa.aaa> Co-authored-by: cake <admin@cakeandbanana.nl> Co-authored-by: Tsubasa <tsubasa@noreply.git.lightless-sync.org> Co-authored-by: choco <choco@patat.nl> Co-authored-by: celine <aaa@aaa.aaa> Co-authored-by: celine <celine@noreply.git.lightless-sync.org> Co-authored-by: Tsubasahane <wozaiha@gmail.com> Co-authored-by: cake <cake@noreply.git.lightless-sync.org> Reviewed-on: #123
378 lines
13 KiB
C#
378 lines
13 KiB
C#
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;
|
|
}
|
|
}
|