Large scale refactor to add class support!
Instance classes are now strongly typed with real property fields that are derived from the JSON API Dump! This required a lot of reworking across the board: - Classes and Enums are auto-generated in the 'Generated' folder now. This is done using a custom built-in plugin, which can be found in the Plugins folder of this project. - Property objects are now tied to .NET's reflection system. Reading and writing from them will try to redirect into a field of the Instance they are bound to. - Property types that were loosely defined now have proper data types (such as Color3uint8, Content, ProtectedString, SharedString, etc) - Fixed an error with the CFrame directional vectors. - The binary PRNT chunk now writes instances in child->parent order. - Enums are now generated correctly, with up-to-date values. - INST chunks are now referred to as 'Classes' instead of 'Types'. - Unary operator added to Vector2 and Vector3. - CollectionService tags can now be manipulated per-instance using the Instance.Tags member. - The Instance.Archivable property now works correctly. - XML files now save/load metadata correctly. - Cleaned up the property tokens directory. I probably missed a few things, but that's a general overview of everything that changed.
This commit is contained in:
@ -14,11 +14,23 @@ namespace RobloxFiles.DataTypes
|
||||
public float Y => m24;
|
||||
public float Z => m34;
|
||||
|
||||
public Vector3 Position => new Vector3(X, Y, Z);
|
||||
public Vector3 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(X, Y, Z);
|
||||
}
|
||||
set
|
||||
{
|
||||
m14 = value.X;
|
||||
m24 = value.Y;
|
||||
m34 = value.Z;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 LookVector => new Vector3(-m13, -m23, -m33);
|
||||
public Vector3 RightVector => new Vector3( m11, m21, m31);
|
||||
public Vector3 UpVector => new Vector3( m12, m22, m32);
|
||||
public Vector3 RightVector => new Vector3( m11, m12, m13);
|
||||
public Vector3 UpVector => new Vector3( m21, m22, m23);
|
||||
public Vector3 LookVector => new Vector3(-m31, -m32, -m33);
|
||||
|
||||
public CFrame()
|
||||
{
|
||||
@ -41,7 +53,6 @@ namespace RobloxFiles.DataTypes
|
||||
m34 = nz;
|
||||
}
|
||||
|
||||
|
||||
public CFrame(Vector3 eye, Vector3 look)
|
||||
{
|
||||
Vector3 zAxis = (eye - look).Unit;
|
||||
@ -127,9 +138,9 @@ namespace RobloxFiles.DataTypes
|
||||
{
|
||||
float[] ac = a.GetComponents();
|
||||
|
||||
float x = ac[0], y = ac[1], z = ac[2],
|
||||
m11 = ac[3], m12 = ac[4], m13 = ac[5],
|
||||
m21 = ac[6], m22 = ac[7], m23 = ac[8],
|
||||
float x = ac[0], y = ac[1], z = ac[2],
|
||||
m11 = ac[3], m12 = ac[4], m13 = ac[5],
|
||||
m21 = ac[6], m22 = ac[7], m23 = ac[8],
|
||||
m31 = ac[9], m32 = ac[10], m33 = ac[11];
|
||||
|
||||
return new CFrame(x - b.X, y - b.Y, z - b.Z, m11, m12, m13, m21, m22, m23, m31, m32, m33);
|
||||
@ -138,9 +149,10 @@ namespace RobloxFiles.DataTypes
|
||||
public static Vector3 operator *(CFrame a, Vector3 b)
|
||||
{
|
||||
float[] ac = a.GetComponents();
|
||||
float x = ac[0], y = ac[1], z = ac[2],
|
||||
m11 = ac[3], m12 = ac[4], m13 = ac[5],
|
||||
m21 = ac[6], m22 = ac[7], m23 = ac[8],
|
||||
|
||||
float x = ac[0], y = ac[1], z = ac[2],
|
||||
m11 = ac[3], m12 = ac[4], m13 = ac[5],
|
||||
m21 = ac[6], m22 = ac[7], m23 = ac[8],
|
||||
m31 = ac[9], m32 = ac[10], m33 = ac[11];
|
||||
|
||||
Vector3 right = new Vector3(m11, m21, m31);
|
||||
@ -154,14 +166,14 @@ namespace RobloxFiles.DataTypes
|
||||
float[] ac = a.GetComponents();
|
||||
float[] bc = b.GetComponents();
|
||||
|
||||
float a14 = ac[0], a24 = ac[1], a34 = ac[2],
|
||||
a11 = ac[3], a12 = ac[4], a13 = ac[5],
|
||||
a21 = ac[6], a22 = ac[7], a23 = ac[8],
|
||||
float a14 = ac[0], a24 = ac[1], a34 = ac[2],
|
||||
a11 = ac[3], a12 = ac[4], a13 = ac[5],
|
||||
a21 = ac[6], a22 = ac[7], a23 = ac[8],
|
||||
a31 = ac[9], a32 = ac[10], a33 = ac[11];
|
||||
|
||||
float b14 = bc[0], b24 = bc[1], b34 = bc[2],
|
||||
b11 = bc[3], b12 = bc[4], b13 = bc[5],
|
||||
b21 = bc[6], b22 = bc[7], b23 = bc[8],
|
||||
float b14 = bc[0], b24 = bc[1], b34 = bc[2],
|
||||
b11 = bc[3], b12 = bc[4], b13 = bc[5],
|
||||
b21 = bc[6], b22 = bc[7], b23 = bc[8],
|
||||
b31 = bc[9], b32 = bc[10], b33 = bc[11];
|
||||
|
||||
float n11 = a11 * b11 + a12 * b21 + a13 * b31 + a14 * m41;
|
||||
@ -345,14 +357,14 @@ namespace RobloxFiles.DataTypes
|
||||
|
||||
for (int i = 3; i < 12; i++)
|
||||
{
|
||||
float t = matrix[i];
|
||||
float t = Math.Abs(matrix[i]);
|
||||
|
||||
if (Math.Abs(t - 1f) < 10e-5f)
|
||||
if (t.FuzzyEquals(1))
|
||||
{
|
||||
// Approximately ±1
|
||||
sum1++;
|
||||
}
|
||||
else if (Math.Abs(t) < 10e-5f)
|
||||
else if (t.FuzzyEquals(0))
|
||||
{
|
||||
// Approximately ±0
|
||||
sum0++;
|
||||
@ -364,10 +376,10 @@ namespace RobloxFiles.DataTypes
|
||||
|
||||
private static bool IsLegalOrientId(int orientId)
|
||||
{
|
||||
int xNormalAbs = (orientId / 6) % 3;
|
||||
int yNormalAbs = orientId % 3;
|
||||
int xOrientId = (orientId / 6) % 3;
|
||||
int yOrientId = orientId % 3;
|
||||
|
||||
return (xNormalAbs != yNormalAbs);
|
||||
return (xOrientId != yOrientId);
|
||||
}
|
||||
|
||||
public int GetOrientId()
|
||||
|
41
DataTypes/Color3uint8.cs
Normal file
41
DataTypes/Color3uint8.cs
Normal file
@ -0,0 +1,41 @@
|
||||
namespace RobloxFiles.DataTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Color3uint8 functions as an interconvertible storage medium for Color3 types.
|
||||
/// It is used by property types that want their Color3 value encoded with bytes instead of floats.
|
||||
/// </summary>
|
||||
public class Color3uint8
|
||||
{
|
||||
public readonly byte R, G, B;
|
||||
|
||||
public Color3uint8(byte r = 0, byte g = 0, byte b = 0)
|
||||
{
|
||||
R = r;
|
||||
G = g;
|
||||
B = b;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(", ", R, G, B);
|
||||
}
|
||||
|
||||
public static implicit operator Color3(Color3uint8 color)
|
||||
{
|
||||
float r = color.R / 255f;
|
||||
float g = color.G / 255f;
|
||||
float b = color.B / 255f;
|
||||
|
||||
return new Color3(r, g, b);
|
||||
}
|
||||
|
||||
public static implicit operator Color3uint8(Color3 color)
|
||||
{
|
||||
byte r = (byte)(color.R * 255);
|
||||
byte g = (byte)(color.G * 255);
|
||||
byte b = (byte)(color.B * 255);
|
||||
|
||||
return new Color3uint8(r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,12 +6,8 @@ namespace RobloxFiles.DataTypes
|
||||
{
|
||||
public readonly ColorSequenceKeypoint[] Keypoints;
|
||||
|
||||
public ColorSequence(Color3 c)
|
||||
public ColorSequence(Color3 c) : this(c, c)
|
||||
{
|
||||
ColorSequenceKeypoint a = new ColorSequenceKeypoint(0, c);
|
||||
ColorSequenceKeypoint b = new ColorSequenceKeypoint(1, c);
|
||||
|
||||
Keypoints = new ColorSequenceKeypoint[2] { a, b };
|
||||
}
|
||||
|
||||
public ColorSequence(Color3 c0, Color3 c1)
|
||||
@ -35,10 +31,13 @@ namespace RobloxFiles.DataTypes
|
||||
if (keypoints[key - 1].Time > keypoints[key].Time)
|
||||
throw new Exception("ColorSequence: all keypoints must be ordered by time");
|
||||
|
||||
if (Math.Abs(keypoints[0].Time) >= 10e-5f)
|
||||
var first = keypoints[0];
|
||||
var last = keypoints[numKeys - 1];
|
||||
|
||||
if (!first.Time.FuzzyEquals(0))
|
||||
throw new Exception("ColorSequence must start at time=0.0");
|
||||
|
||||
if (Math.Abs(keypoints[numKeys - 1].Time - 1f) >= 10e-5f)
|
||||
if (!last.Time.FuzzyEquals(1))
|
||||
throw new Exception("ColorSequence must end at time=1.0");
|
||||
|
||||
Keypoints = keypoints;
|
||||
|
@ -4,18 +4,18 @@
|
||||
{
|
||||
public readonly float Time;
|
||||
public readonly Color3 Value;
|
||||
public readonly byte[] Reserved;
|
||||
public readonly int Envelope;
|
||||
|
||||
public ColorSequenceKeypoint(float time, Color3 value, byte[] reserved = null)
|
||||
public ColorSequenceKeypoint(float time, Color3 value, int envelope = 0)
|
||||
{
|
||||
Time = time;
|
||||
Value = value;
|
||||
Reserved = reserved;
|
||||
Envelope = envelope;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Join(" ", Time, Value.R, Value.G, Value.B, 0);
|
||||
return string.Join(" ", Time, Value.R, Value.G, Value.B, Envelope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
DataTypes/Content.cs
Normal file
32
DataTypes/Content.cs
Normal file
@ -0,0 +1,32 @@
|
||||
namespace RobloxFiles.DataTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Content is a type used by most url-based XML properties.
|
||||
/// Here, it only exists as a wrapper class for strings.
|
||||
/// </summary>
|
||||
public class Content
|
||||
{
|
||||
// TODO: Maybe introduce constraints to the value?
|
||||
public readonly string Data;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
public Content(string data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public static implicit operator string(Content content)
|
||||
{
|
||||
return content.Data;
|
||||
}
|
||||
|
||||
public static implicit operator Content(string data)
|
||||
{
|
||||
return new Content(data);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,12 @@ namespace RobloxFiles.DataTypes
|
||||
public readonly float Min;
|
||||
public readonly float Max;
|
||||
|
||||
public NumberRange(float num)
|
||||
{
|
||||
Min = num;
|
||||
Max = num;
|
||||
}
|
||||
|
||||
public NumberRange(float min = 0, float max = 0)
|
||||
{
|
||||
if (max - min < 0)
|
||||
|
@ -35,10 +35,13 @@ namespace RobloxFiles.DataTypes
|
||||
if (keypoints[key - 1].Time > keypoints[key].Time)
|
||||
throw new Exception("NumberSequence: all keypoints must be ordered by time");
|
||||
|
||||
if (Math.Abs(keypoints[0].Time) >= 10e-5f)
|
||||
var first = keypoints[0];
|
||||
var last = keypoints[numKeys - 1];
|
||||
|
||||
if (!first.Time.FuzzyEquals(0))
|
||||
throw new Exception("NumberSequence must start at time=0.0");
|
||||
|
||||
if (Math.Abs(keypoints[numKeys - 1].Time - 1f) >= 10e-5f)
|
||||
if (!last.Time.FuzzyEquals(1))
|
||||
throw new Exception("NumberSequence must end at time=1.0");
|
||||
|
||||
Keypoints = keypoints;
|
||||
|
31
DataTypes/ProtectedString.cs
Normal file
31
DataTypes/ProtectedString.cs
Normal file
@ -0,0 +1,31 @@
|
||||
namespace RobloxFiles.DataTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// ProtectedString is a type used by some of the XML properties.
|
||||
/// Here, it only exists as a wrapper class for strings.
|
||||
/// </summary>
|
||||
public class ProtectedString
|
||||
{
|
||||
public readonly string Value;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public ProtectedString(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator string(ProtectedString protectedString)
|
||||
{
|
||||
return protectedString.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ProtectedString(string value)
|
||||
{
|
||||
return new ProtectedString(value);
|
||||
}
|
||||
}
|
||||
}
|
208
DataTypes/Quaternion.cs
Normal file
208
DataTypes/Quaternion.cs
Normal file
@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using RobloxFiles.DataTypes;
|
||||
|
||||
namespace RobloxFiles.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Quaternion is a utility used by the CFrame DataType to handle rotation interpolation.
|
||||
/// It can be used as an independent Quaternion implementation if you so please!
|
||||
/// </summary>
|
||||
public class Quaternion
|
||||
{
|
||||
public readonly float X, Y, Z, W;
|
||||
|
||||
public float Magnitude
|
||||
{
|
||||
get
|
||||
{
|
||||
float squared = Dot(this);
|
||||
double magnitude = Math.Sqrt(squared);
|
||||
|
||||
return (float)magnitude;
|
||||
}
|
||||
}
|
||||
|
||||
public Quaternion(float x, float y, float z, float w)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
W = w;
|
||||
}
|
||||
|
||||
public Quaternion(Vector3 qv, float qw)
|
||||
{
|
||||
X = qv.X;
|
||||
Y = qv.Y;
|
||||
Z = qv.Z;
|
||||
W = qw;
|
||||
}
|
||||
|
||||
public Quaternion(CFrame cf)
|
||||
{
|
||||
CFrame matrix = (cf - cf.Position);
|
||||
float[] ac = cf.GetComponents();
|
||||
|
||||
float m11 = ac[3], m12 = ac[4], m13 = ac[5],
|
||||
m21 = ac[6], m22 = ac[7], m23 = ac[8],
|
||||
m31 = ac[9], m32 = ac[10], m33 = ac[11];
|
||||
|
||||
float trace = m11 + m22 + m33;
|
||||
|
||||
if (trace > 0)
|
||||
{
|
||||
float s = (float)Math.Sqrt(1 + trace);
|
||||
float r = 0.5f / s;
|
||||
|
||||
W = s * 0.5f;
|
||||
X = (m32 - m23) * r;
|
||||
Y = (m13 - m31) * r;
|
||||
Z = (m21 - m12) * r;
|
||||
}
|
||||
else
|
||||
{
|
||||
float big = Math.Max(Math.Max(m11, m22), m33);
|
||||
|
||||
if (big == m11)
|
||||
{
|
||||
float s = (float)Math.Sqrt(1 + m11 - m22 - m33);
|
||||
float r = 0.5f / s;
|
||||
|
||||
W = (m32 - m23) * r;
|
||||
X = 0.5f * s;
|
||||
Y = (m21 + m12) * r;
|
||||
Z = (m13 + m31) * r;
|
||||
}
|
||||
else if (big == m22)
|
||||
{
|
||||
float s = (float)Math.Sqrt(1 - m11 + m22 - m33);
|
||||
float r = 0.5f / s;
|
||||
|
||||
W = (m13 - m31) * r;
|
||||
X = (m21 + m12) * r;
|
||||
Y = 0.5f * s;
|
||||
Z = (m32 + m23) * r;
|
||||
}
|
||||
else if (big == m33)
|
||||
{
|
||||
float s = (float)Math.Sqrt(1 - m11 - m22 + m33);
|
||||
float r = 0.5f / s;
|
||||
|
||||
W = (m21 - m12) * r;
|
||||
X = (m13 + m31) * r;
|
||||
Y = (m32 + m23) * r;
|
||||
Z = 0.5f * s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float Dot(Quaternion other)
|
||||
{
|
||||
return (X * other.X) + (Y * other.Y) + (Z * other.Z) + (W * other.W);
|
||||
}
|
||||
|
||||
public Quaternion Lerp(Quaternion other, float alpha)
|
||||
{
|
||||
Quaternion result = this * (1.0f - alpha) + other * alpha;
|
||||
return result / result.Magnitude;
|
||||
}
|
||||
|
||||
public Quaternion Slerp(Quaternion other, float alpha)
|
||||
{
|
||||
float cosAng = Dot(other);
|
||||
|
||||
if (cosAng < 0)
|
||||
{
|
||||
other = -other;
|
||||
cosAng = -cosAng;
|
||||
}
|
||||
|
||||
double ang = Math.Acos(cosAng);
|
||||
|
||||
if (ang >= 0.05f)
|
||||
{
|
||||
float scale0 = (float)Math.Sin((1.0f - alpha) * ang);
|
||||
float scale1 = (float)Math.Sin(alpha * ang);
|
||||
float denom = (float)Math.Sin(ang);
|
||||
|
||||
return ((this * scale0) + (other * scale1)) / denom;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Lerp(other, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public CFrame ToCFrame()
|
||||
{
|
||||
float xc = X * 2f;
|
||||
float yc = Y * 2f;
|
||||
float zc = Z * 2f;
|
||||
|
||||
float xx = X * xc;
|
||||
float xy = X * yc;
|
||||
float xz = X * zc;
|
||||
|
||||
float wx = W * xc;
|
||||
float wy = W * yc;
|
||||
float wz = W * zc;
|
||||
|
||||
float yy = Y * yc;
|
||||
float yz = Y * zc;
|
||||
float zz = Z * zc;
|
||||
|
||||
return new CFrame
|
||||
(
|
||||
0, 0, 0,
|
||||
|
||||
1f - (yy + zz),
|
||||
xy - wz,
|
||||
xz + wy,
|
||||
|
||||
xy + wz,
|
||||
1f - (xx + zz),
|
||||
yz - wx,
|
||||
|
||||
xz - wy,
|
||||
yz + wx,
|
||||
1f - (xx + yy)
|
||||
);
|
||||
}
|
||||
|
||||
public static Quaternion operator +(Quaternion a, Quaternion b)
|
||||
{
|
||||
return new Quaternion(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W);
|
||||
}
|
||||
|
||||
public static Quaternion operator -(Quaternion a, Quaternion b)
|
||||
{
|
||||
return new Quaternion(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W);
|
||||
}
|
||||
|
||||
public static Quaternion operator *(Quaternion a, float f)
|
||||
{
|
||||
return new Quaternion(a.X * f, a.Y * f, a.Z * f, a.W * f);
|
||||
}
|
||||
|
||||
public static Quaternion operator /(Quaternion a, float f)
|
||||
{
|
||||
return new Quaternion(a.X / f, a.Y / f, a.Z / f, a.W / f);
|
||||
}
|
||||
|
||||
public static Quaternion operator -(Quaternion a)
|
||||
{
|
||||
return new Quaternion(-a.X, -a.Y, -a.Z, -a.W);
|
||||
}
|
||||
|
||||
public static Quaternion operator *(Quaternion a, Quaternion b)
|
||||
{
|
||||
Vector3 v1 = new Vector3(a.X, a.Y, a.Z);
|
||||
float s1 = a.W;
|
||||
|
||||
Vector3 v2 = new Vector3(b.X, b.Y, b.Z);
|
||||
float s2 = b.W;
|
||||
|
||||
return new Quaternion(s1 * v2 + s2 * v1 + v1.Cross(v2), s1 * s2 - v1.Dot(v2));
|
||||
}
|
||||
}
|
||||
}
|
@ -20,10 +20,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
public Ray(Vector3 origin, Vector3 direction)
|
||||
public Ray(Vector3 origin = null, Vector3 direction = null)
|
||||
{
|
||||
Origin = origin;
|
||||
Direction = direction;
|
||||
Origin = origin ?? new Vector3();
|
||||
Direction = direction ?? new Vector3();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
62
DataTypes/SharedString.cs
Normal file
62
DataTypes/SharedString.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace RobloxFiles.DataTypes
|
||||
{
|
||||
public class SharedString
|
||||
{
|
||||
private static Dictionary<string, byte[]> Records = new Dictionary<string, byte[]>();
|
||||
public readonly string MD5_Key;
|
||||
|
||||
public byte[] SharedValue => FindRecord(MD5_Key);
|
||||
public override string ToString() => $"MD5 Key: {MD5_Key}";
|
||||
|
||||
internal SharedString(string md5)
|
||||
{
|
||||
MD5_Key = md5;
|
||||
}
|
||||
|
||||
private SharedString(byte[] buffer)
|
||||
{
|
||||
using (MD5 md5 = MD5.Create())
|
||||
{
|
||||
byte[] hash = md5.ComputeHash(buffer);
|
||||
MD5_Key = Convert.ToBase64String(hash);
|
||||
}
|
||||
|
||||
if (Records.ContainsKey(MD5_Key))
|
||||
return;
|
||||
|
||||
Records.Add(MD5_Key, buffer);
|
||||
}
|
||||
|
||||
public static byte[] FindRecord(string key)
|
||||
{
|
||||
byte[] result = null;
|
||||
|
||||
if (Records.ContainsKey(key))
|
||||
result = Records[key];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SharedString FromBuffer(byte[] buffer)
|
||||
{
|
||||
return new SharedString(buffer);
|
||||
}
|
||||
|
||||
public static SharedString FromString(string value)
|
||||
{
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(value);
|
||||
return new SharedString(buffer);
|
||||
}
|
||||
|
||||
public static SharedString FromBase64(string base64)
|
||||
{
|
||||
byte[] buffer = Convert.FromBase64String(base64);
|
||||
return new SharedString(buffer);
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ namespace RobloxFiles.DataTypes
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public Vector2(float[] coords)
|
||||
internal Vector2(float[] coords)
|
||||
{
|
||||
X = coords.Length > 0 ? coords[0] : 0;
|
||||
Y = coords.Length > 1 ? coords[1] : 0;
|
||||
@ -69,6 +69,11 @@ namespace RobloxFiles.DataTypes
|
||||
public static Vector2 operator /(Vector2 v, float n) => upcastFloatOp(v, n, div);
|
||||
public static Vector2 operator /(float n, Vector2 v) => upcastFloatOp(n, v, div);
|
||||
|
||||
public static Vector2 operator -(Vector2 v)
|
||||
{
|
||||
return new Vector2(-v.X, -v.Y);
|
||||
}
|
||||
|
||||
public static Vector2 Zero => new Vector2(0, 0);
|
||||
|
||||
public override string ToString()
|
||||
|
@ -30,7 +30,7 @@ namespace RobloxFiles.DataTypes
|
||||
Z = z;
|
||||
}
|
||||
|
||||
public Vector3(float[] coords)
|
||||
internal Vector3(float[] coords)
|
||||
{
|
||||
X = coords.Length > 0 ? coords[0] : 0;
|
||||
Y = coords.Length > 1 ? coords[1] : 0;
|
||||
@ -92,6 +92,11 @@ namespace RobloxFiles.DataTypes
|
||||
public static Vector3 operator /(Vector3 v, float n) => upcastFloatOp(v, n, div);
|
||||
public static Vector3 operator /(float n, Vector3 v) => upcastFloatOp(n, v, div);
|
||||
|
||||
public static Vector3 operator -(Vector3 v)
|
||||
{
|
||||
return new Vector3(-v.X, -v.Y, -v.Z);
|
||||
}
|
||||
|
||||
public static Vector3 Zero => new Vector3(0, 0, 0);
|
||||
public static Vector3 Right => new Vector3(1, 0, 0);
|
||||
public static Vector3 Up => new Vector3(0, 1, 0);
|
||||
@ -141,7 +146,7 @@ namespace RobloxFiles.DataTypes
|
||||
|
||||
float dotProd = normal.Dot(this);
|
||||
|
||||
if (Math.Abs(dotProd - 1f) < 10e-5f)
|
||||
if (dotProd.FuzzyEquals(1))
|
||||
{
|
||||
result = i;
|
||||
break;
|
||||
|
Reference in New Issue
Block a user