diff --git a/src/SceneKit/SCNMatrix4_dotnet.cs b/src/SceneKit/SCNMatrix4_dotnet.cs index 0bc1f3151650..1b0469c0d628 100644 --- a/src/SceneKit/SCNMatrix4_dotnet.cs +++ b/src/SceneKit/SCNMatrix4_dotnet.cs @@ -1,7 +1,4 @@ /* - * This keeps the code of OpenTK's Matrix4 almost intact, except we replace the - * Vector4 with a SCNVector4 - Copyright (c) 2006 - 2008 The Open Toolkit library. Copyright (c) 2014 Xamarin Inc. All rights reserved @@ -52,28 +49,55 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE namespace SceneKit { /// - /// Represents a 4x4 Matrix + /// Represents a 4x4 matrix using a column-major memory layout. /// [Serializable] public struct SCNMatrix4 : IEquatable { #region Fields + /* + * SCNMatrix4 is defined like this for iOS, tvOS and watchOS: + * + * typedef struct SCNMatrix4 { + * float m11, m12, m13, m14; + * float m21, m22, m23, m24; + * float m31, m32, m33, m34; + * float m41, m42, m43, m44; + * } SCNMatrix4; + * + * and like this for macOS: + * + * struct CATransform3D + * { + * CGFloat m11, m12, m13, m14; + * CGFloat m21, m22, m23, m24; + * CGFloat m31, m32, m33, m34; + * CGFloat m41, m42, m43, m44; + * }; + * typedef CATransform3D SCNMatrix4; + * + * It's not obvious from this definitions whether the matrix is row-major or column-major, and neither the documentation + * nor the headers are particularly helpful, but it's possible to do some math to figure it out. See this for more info: + * https://github.com/xamarin/xamarin-macios/issues/15094#issuecomment-1139699662 (result: SCNMatrix4 is using a column-major layout) + * + **/ + /// /// Left-most column of the matrix /// - public SCNVector4 Column0; + public SCNVector4 Column0; // m11, m12, m13, m14 /// /// 2nd column of the matrix /// - public SCNVector4 Column1; + public SCNVector4 Column1; // m21, m22, m23, m24 /// /// 3rd column of the matrix /// - public SCNVector4 Column2; + public SCNVector4 Column2; // m31, m32, m33, m34 /// /// Right-most column of the matrix /// - public SCNVector4 Column3; + public SCNVector4 Column3; // m41, m42, m43, m44 /// /// The identity matrix @@ -133,10 +157,10 @@ public SCNMatrix4 ( #if !WATCH public SCNMatrix4 (CoreAnimation.CATransform3D transform) { - Column0 = new SCNVector4 ((pfloat) transform.M11, (pfloat) transform.M21, (pfloat) transform.M31, (pfloat) transform.M41); - Column1 = new SCNVector4 ((pfloat) transform.M12, (pfloat) transform.M22, (pfloat) transform.M32, (pfloat) transform.M42); - Column2 = new SCNVector4 ((pfloat) transform.M13, (pfloat) transform.M23, (pfloat) transform.M33, (pfloat) transform.M43); - Column3 = new SCNVector4 ((pfloat) transform.M14, (pfloat) transform.M24, (pfloat) transform.M34, (pfloat) transform.M44); + Column0 = new SCNVector4 ((pfloat) transform.M11, (pfloat) transform.M12, (pfloat) transform.M13, (pfloat) transform.M14); + Column1 = new SCNVector4 ((pfloat) transform.M21, (pfloat) transform.M22, (pfloat) transform.M23, (pfloat) transform.M24); + Column2 = new SCNVector4 ((pfloat) transform.M31, (pfloat) transform.M32, (pfloat) transform.M33, (pfloat) transform.M34); + Column3 = new SCNVector4 ((pfloat) transform.M41, (pfloat) transform.M42, (pfloat) transform.M43, (pfloat) transform.M44); } #endif @@ -167,10 +191,10 @@ public pfloat Determinant { public SCNVector4 Row0 { get { return new SCNVector4 (Column0.X, Column1.X, Column2.X, Column3.X); } set { - M11 = value.X; - M12 = value.Y; - M13 = value.Z; - M14 = value.W; + Column0.X = value.X; + Column1.X = value.Y; + Column2.X = value.Z; + Column3.X = value.W; } } @@ -180,10 +204,10 @@ public SCNVector4 Row0 { public SCNVector4 Row1 { get { return new SCNVector4 (Column0.Y, Column1.Y, Column2.Y, Column3.Y); } set { - M21 = value.X; - M22 = value.Y; - M23 = value.Z; - M24 = value.W; + Column0.Y = value.X; + Column1.Y = value.Y; + Column2.Y = value.Z; + Column3.Y = value.W; } } @@ -193,10 +217,10 @@ public SCNVector4 Row1 { public SCNVector4 Row2 { get { return new SCNVector4 (Column0.Z, Column1.Z, Column2.Z, Column3.Z); } set { - M31 = value.X; - M32 = value.Y; - M33 = value.Z; - M34 = value.W; + Column0.Z = value.X; + Column1.Z = value.Y; + Column2.Z = value.Z; + Column3.Z = value.W; } } @@ -206,90 +230,90 @@ public SCNVector4 Row2 { public SCNVector4 Row3 { get { return new SCNVector4 (Column0.W, Column1.W, Column2.W, Column3.W); } set { - M41 = value.X; - M42 = value.Y; - M43 = value.Z; - M44 = value.W; + Column0.W = value.X; + Column1.W = value.Y; + Column2.W = value.Z; + Column3.W = value.W; } } /// - /// Gets or sets the value at row 1, column 1 of this instance. + /// Gets or sets the value at column 1, row 1 of this instance. /// public pfloat M11 { get { return Column0.X; } set { Column0.X = value; } } /// - /// Gets or sets the value at row 1, column 2 of this instance. + /// Gets or sets the value at column 1, row 2 of this instance. /// - public pfloat M12 { get { return Column1.X; } set { Column1.X = value; } } + public pfloat M12 { get { return Column0.Y; } set { Column0.Y = value; } } /// - /// Gets or sets the value at row 1, column 3 of this instance. + /// Gets or sets the value at column 1, row 3 of this instance. /// - public pfloat M13 { get { return Column2.X; } set { Column2.X = value; } } + public pfloat M13 { get { return Column0.Z; } set { Column0.Z = value; } } /// - /// Gets or sets the value at row 1, column 4 of this instance. + /// Gets or sets the value at column 1, row 4 of this instance. /// - public pfloat M14 { get { return Column3.X; } set { Column3.X = value; } } + public pfloat M14 { get { return Column0.W; } set { Column0.W = value; } } /// - /// Gets or sets the value at row 2, column 1 of this instance. + /// Gets or sets the value at column 2, row 1 of this instance. /// - public pfloat M21 { get { return Column0.Y; } set { Column0.Y = value; } } + public pfloat M21 { get { return Column1.X; } set { Column1.X = value; } } /// - /// Gets or sets the value at row 2, column 2 of this instance. + /// Gets or sets the value at column 2, row 2 of this instance. /// public pfloat M22 { get { return Column1.Y; } set { Column1.Y = value; } } /// - /// Gets or sets the value at row 2, column 3 of this instance. + /// Gets or sets the value at column 2, row 3 of this instance. /// - public pfloat M23 { get { return Column2.Y; } set { Column2.Y = value; } } + public pfloat M23 { get { return Column1.Z; } set { Column1.Z = value; } } /// - /// Gets or sets the value at row 2, column 4 of this instance. + /// Gets or sets the value at column 2, row 4 of this instance. /// - public pfloat M24 { get { return Column3.Y; } set { Column3.Y = value; } } + public pfloat M24 { get { return Column1.W; } set { Column1.W = value; } } /// - /// Gets or sets the value at row 3, column 1 of this instance. + /// Gets or sets the value at column 3, row 1 of this instance. /// - public pfloat M31 { get { return Column0.Z; } set { Column0.Z = value; } } + public pfloat M31 { get { return Column2.X; } set { Column2.X = value; } } /// - /// Gets or sets the value at row 3, column 2 of this instance. + /// Gets or sets the value at column 3, row 2 of this instance. /// - public pfloat M32 { get { return Column1.Z; } set { Column1.Z = value; } } + public pfloat M32 { get { return Column2.Y; } set { Column2.Y = value; } } /// - /// Gets or sets the value at row 3, column 3 of this instance. + /// Gets or sets the value at column 3, row 3 of this instance. /// public pfloat M33 { get { return Column2.Z; } set { Column2.Z = value; } } /// - /// Gets or sets the value at row 3, column 4 of this instance. + /// Gets or sets the value at column 3, row 4 of this instance. /// - public pfloat M34 { get { return Column3.Z; } set { Column3.Z = value; } } + public pfloat M34 { get { return Column2.W; } set { Column2.W = value; } } /// - /// Gets or sets the value at row 4, column 1 of this instance. + /// Gets or sets the value at column 4, row 1 of this instance. /// - public pfloat M41 { get { return Column0.W; } set { Column0.W = value; } } + public pfloat M41 { get { return Column3.X; } set { Column3.X = value; } } /// - /// Gets or sets the value at row 4, column 2 of this instance. + /// Gets or sets the value at column 4, row 2 of this instance. /// - public pfloat M42 { get { return Column1.W; } set { Column1.W = value; } } + public pfloat M42 { get { return Column3.Y; } set { Column3.Y = value; } } /// - /// Gets or sets the value at row 4, column 3 of this instance. + /// Gets or sets the value at column 4, row 3 of this instance. /// - public pfloat M43 { get { return Column2.W; } set { Column2.W = value; } } + public pfloat M43 { get { return Column3.Z; } set { Column3.Z = value; } } /// - /// Gets or sets the value at row 4, column 4 of this instance. + /// Gets or sets the value at column 4, row 4 of this instance. /// public pfloat M44 { get { return Column3.W; } set { Column3.W = value; } } @@ -358,44 +382,85 @@ public static void CreateFromColumns (SCNVector4 column0, SCNVector4 column1, SC /// A matrix instance. public static void CreateFromAxisAngle (SCNVector3 axis, pfloat angle, out SCNMatrix4 result) { - pfloat cos = (float) System.Math.Cos (-angle); - pfloat sin = (float) System.Math.Sin (-angle); - pfloat t = 1.0f - cos; - axis.Normalize (); - result = new SCNMatrix4 (t * axis.X * axis.X + cos, t * axis.X * axis.Y - sin * axis.Z, t * axis.X * axis.Z + sin * axis.Y, 0.0f, - t * axis.X * axis.Y + sin * axis.Z, t * axis.Y * axis.Y + cos, t * axis.Y * axis.Z - sin * axis.X, 0.0f, - t * axis.X * axis.Z - sin * axis.Y, t * axis.Y * axis.Z + sin * axis.X, t * axis.Z * axis.Z + cos, 0.0f, - 0, 0, 0, 1); + CreateFromAxisAngle (axis.X, axis.Y, axis.Z, angle, out result); } public static void CreateFromAxisAngle (Vector3 axis, float angle, out SCNMatrix4 result) { - pfloat cos = (float) System.Math.Cos (-angle); - pfloat sin = (float) System.Math.Sin (-angle); - pfloat t = 1.0f - cos; - axis = Vector3.Normalize (axis); - result = new SCNMatrix4 (t * axis.X * axis.X + cos, t * axis.X * axis.Y - sin * axis.Z, t * axis.X * axis.Z + sin * axis.Y, 0.0f, - t * axis.X * axis.Y + sin * axis.Z, t * axis.Y * axis.Y + cos, t * axis.Y * axis.Z - sin * axis.X, 0.0f, - t * axis.X * axis.Z - sin * axis.Y, t * axis.Y * axis.Z + sin * axis.X, t * axis.Z * axis.Z + cos, 0.0f, - 0, 0, 0, 1); + CreateFromAxisAngle (axis.X, axis.Y, axis.Z, angle, out result); } public static void CreateFromAxisAngle (Vector3d axis, double angle, out SCNMatrix4 result) { - double cos = System.Math.Cos (-angle); - double sin = System.Math.Sin (-angle); - double t = 1.0f - cos; - axis.Normalize (); - result = new SCNMatrix4 ((pfloat) (t * axis.X * axis.X + cos), (pfloat) (t * axis.X * axis.Y - sin * axis.Z), (pfloat) (t * axis.X * axis.Z + sin * axis.Y), (pfloat) (0.0f), - (pfloat) ( t * axis.X * axis.Y + sin * axis.Z), (pfloat) (t * axis.Y * axis.Y + cos), (pfloat) (t * axis.Y * axis.Z - sin * axis.X), (pfloat) 0.0f, - (pfloat) (t * axis.X * axis.Z - sin * axis.Y), (pfloat) (t * axis.Y * axis.Z + sin * axis.X), (pfloat) (t * axis.Z * axis.Z + cos), (pfloat) 0.0f, - 0, 0, 0, 1); + CreateFromAxisAngle (axis.X, axis.Y, axis.Z, angle, out result); + } + + /// + /// Build a rotation matrix from the specified axis/angle rotation. + /// + /// The x part of the normalized axis to rotate about. + /// The y part of the normalized axis to rotate about. + /// The z part of the normalized axis to rotate about. + /// Angle in radians to rotate counter-clockwise (looking in the direction of the given axis). + /// A matrix instance. + static void CreateFromAxisAngle (float x, float y, float z, float angle, out SCNMatrix4 result) + { + var cos = MathF.Cos (-angle); + var sin = MathF.Sin (-angle); + var t = 1.0f - cos; + + var m11 = t * x * x + cos; + var m12 = t * x * y - sin * z; + var m13 = t * x * z + sin * y; + var m21 = t * x * y + sin * z; + var m22 = t * y * y + cos; + var m23 = t * y * z - sin * x; + var m31 = t * x * z - sin * y; + var m32 = t * y * z + sin * x; + var m33 = t * z * z + cos; + + result = new SCNMatrix4 ( + m11, m21, m31, 0.0f, + m12, m22, m32, 0.0f, + m13, m23, m33, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + } + + /// + /// Build a rotation matrix from the specified axis/angle rotation. + /// + /// The x part of the normalized axis to rotate about. + /// The y part of the normalized axis to rotate about. + /// The z part of the normalized axis to rotate about. + /// Angle in radians to rotate counter-clockwise (looking in the direction of the given axis). + /// A matrix instance. + static void CreateFromAxisAngle (double x, double y, double z, double angle, out SCNMatrix4 result) + { + var cos = Math.Cos (-angle); + var sin = Math.Sin (-angle); + var t = 1.0f - cos; + + var m11 = (pfloat) (t * x * x + cos); + var m12 = (pfloat) (t * x * y - sin * z); + var m13 = (pfloat) (t * x * z + sin * y); + var m21 = (pfloat) (t * x * y + sin * z); + var m22 = (pfloat) (t * y * y + cos); + var m23 = (pfloat) (t * y * z - sin * x); + var m31 = (pfloat) (t * x * z - sin * y); + var m32 = (pfloat) (t * y * z + sin * x); + var m33 = (pfloat) (t * z * z + cos); + + result = new SCNMatrix4 ( + m11, m21, m31, 0.0f, + m12, m22, m32, 0.0f, + m13, m23, m33, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); } /// @@ -425,11 +490,11 @@ public static void CreateRotationX (pfloat angle, out SCNMatrix4 result) pfloat cos = (pfloat) System.Math.Cos (angle); pfloat sin = (pfloat) System.Math.Sin (angle); - result = new SCNMatrix4 (); - result.Row0 = SCNVector4.UnitX; - result.Row1 = new SCNVector4 (0.0f, cos, sin, 0.0f); - result.Row2 = new SCNVector4 (0.0f, -sin, cos, 0.0f); - result.Row3 = SCNVector4.UnitW; + result = new SCNMatrix4 ( + 1, 0, 0, 0, + 0, cos, -sin, 0, + 0, sin, cos, 0, + 0, 0, 0, 1); } /// @@ -454,11 +519,11 @@ public static void CreateRotationY (pfloat angle, out SCNMatrix4 result) pfloat cos = (pfloat) System.Math.Cos (angle); pfloat sin = (pfloat) System.Math.Sin (angle); - result = new SCNMatrix4 (); - result.Row0 = new SCNVector4 (cos, 0.0f, -sin, 0.0f); - result.Row1 = SCNVector4.UnitY; - result.Row2 = new SCNVector4 (sin, 0.0f, cos, 0.0f); - result.Row3 = SCNVector4.UnitW; + result = new SCNMatrix4 ( + cos, 0, sin, 0, + 0, 1, 0, 0, + -sin, 0, cos, 0, + 0, 0, 0, 1); } /// @@ -483,11 +548,11 @@ public static void CreateRotationZ (pfloat angle, out SCNMatrix4 result) pfloat cos = (pfloat) System.Math.Cos (angle); pfloat sin = (pfloat) System.Math.Sin (angle); - result = new SCNMatrix4 (); - result.Row0 = new SCNVector4 (cos, sin, 0.0f, 0.0f); - result.Row1 = new SCNVector4 (-sin, cos, 0.0f, 0.0f); - result.Row2 = SCNVector4.UnitZ; - result.Row3 = SCNVector4.UnitW; + result = new SCNMatrix4 ( + cos, -sin, 0, 0, + sin, cos, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); } /// @@ -515,8 +580,11 @@ public static SCNMatrix4 CreateRotationZ (pfloat angle) /// The resulting SCNMatrix4 instance. public static void CreateTranslation (pfloat x, pfloat y, pfloat z, out SCNMatrix4 result) { - result = Identity; - result.Row3 = new SCNVector4 (x, y, z, 1); + result = new SCNMatrix4 ( + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1); } /// @@ -526,8 +594,7 @@ public static void CreateTranslation (pfloat x, pfloat y, pfloat z, out SCNMatri /// The resulting SCNMatrix4 instance. public static void CreateTranslation (ref SCNVector3 vector, out SCNMatrix4 result) { - result = Identity; - result.Row3 = new SCNVector4 (vector.X, vector.Y, vector.Z, 1); + CreateTranslation (vector.X, vector.Y, vector.Z, out result); } /// @@ -743,10 +810,11 @@ public static void CreatePerspectiveOffCenter (pfloat left, pfloat right, pfloat pfloat c = -(zFar + zNear) / (zFar - zNear); pfloat d = -(2.0f * zFar * zNear) / (zFar - zNear); - result = new SCNMatrix4 (x, 0, 0, 0, - 0, y, 0, 0, - a, b, c, -1, - 0, 0, d, 0); + result = new SCNMatrix4 ( + x, 0, a, 0, + 0, y, b, 0, + 0, 0, c, d, + 0, 0, -1, 0); } /// @@ -807,12 +875,11 @@ public static SCNMatrix4 Scale (SCNVector3 scale) /// A scaling matrix public static SCNMatrix4 Scale (pfloat x, pfloat y, pfloat z) { - var result = new SCNMatrix4 (); - result.Row0 = SCNVector4.UnitX * x; - result.Row1 = SCNVector4.UnitY * y; - result.Row2 = SCNVector4.UnitZ * z; - result.Row3 = SCNVector4.UnitW; - return result; + return new SCNMatrix4 ( + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1); } #endregion @@ -865,10 +932,11 @@ public static SCNMatrix4 LookAt (SCNVector3 eye, SCNVector3 target, SCNVector3 u SCNVector3 x = SCNVector3.Normalize (SCNVector3.Cross (up, z)); SCNVector3 y = SCNVector3.Normalize (SCNVector3.Cross (z, x)); - SCNMatrix4 rot = new SCNMatrix4 (new SCNVector4 (x.X, y.X, z.X, 0.0f), - new SCNVector4 (x.Y, y.Y, z.Y, 0.0f), - new SCNVector4 (x.Z, y.Z, z.Z, 0.0f), - SCNVector4.UnitW); + SCNMatrix4 rot = new SCNMatrix4 ( + x.X, x.Y, x.Z, 0.0f, + y.X, y.Y, y.Z, 0.0f, + z.X, z.Y, z.Z, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); SCNMatrix4 trans = SCNMatrix4.CreateTranslation (-eye); @@ -898,43 +966,92 @@ public static SCNMatrix4 LookAt (pfloat eyeX, pfloat eyeY, pfloat eyeZ, pfloat t #region Multiply Functions /// - /// Multiplies two instances. + /// Combines two transformation matrices. /// - /// The left operand of the multiplication. - /// The right operand of the multiplication. - /// A new instance that is the result of the multiplication - public static SCNMatrix4 Mult (SCNMatrix4 left, SCNMatrix4 right) +#if XAMCORE_5_0 + /// + /// Combining two transformation matrices means using matrix multiplication to multiply them in the reverse order (secondTransformation * firstTransformation). + /// + /// The first transformation of the combination. + /// The second transformation of the combination. +#else + /// + /// Combining two transformation matrices means using matrix multiplication to multiply them in the reverse order (right * left). + /// + /// The first transformation of the combination. + /// The second transformation of the combination. +#endif + /// A new instance that is the result of the combination +#if XAMCORE_5_0 + public static SCNMatrix4 Mult (SCNMatrix4 firstTransformation, SCNMatrix4 secondTransformation) +#else + public static SCNMatrix4 Mult(SCNMatrix4 left, SCNMatrix4 right) +#endif { SCNMatrix4 result; - Mult (ref left, ref right, out result); + // the matrices are reversed: https://github.com/xamarin/xamarin-macios/issues/15094#issuecomment-1139699662 +#if XAMCORE_5_0 + MatrixMultiply (ref secondTransformation, ref firstTransformation, out result); +#else + MatrixMultiply (ref right, ref left, out result); +#endif return result; } /// - /// Multiplies two instances. + /// Combines two transformation matrices. /// - /// The left operand of the multiplication. - /// The right operand of the multiplication. - /// A new instance that is the result of the multiplication - public static void Mult (ref SCNMatrix4 left, ref SCNMatrix4 right, out SCNMatrix4 result) +#if XAMCORE_5_0 + /// + /// Combining two transformation matrices means using matrix multiplication to multiply them in the reverse order (secondTransformation * firstTransformation). + /// + /// The first transformation of the combination. + /// The second transformation of the combination. +#else + /// + /// Combining two transformation matrices means using matrix multiplication to multiply them in the reverse order (right * left). + /// + /// The first transformation of the combination. + /// The second transformation of the combination. +#endif + /// A new instance that is the result of the combination +#if XAMCORE_5_0 + public static void Mult (ref SCNMatrix4 firstTransformation, ref SCNMatrix4 secondTransformation, out SCNMatrix4 result) +#else + public static void Mult(ref SCNMatrix4 left, ref SCNMatrix4 right, out SCNMatrix4 result) +#endif { - result = new SCNMatrix4 ( - left.M11 * right.M11 + left.M12 * right.M21 + left.M13 * right.M31 + left.M14 * right.M41, - left.M11 * right.M12 + left.M12 * right.M22 + left.M13 * right.M32 + left.M14 * right.M42, - left.M11 * right.M13 + left.M12 * right.M23 + left.M13 * right.M33 + left.M14 * right.M43, - left.M11 * right.M14 + left.M12 * right.M24 + left.M13 * right.M34 + left.M14 * right.M44, - left.M21 * right.M11 + left.M22 * right.M21 + left.M23 * right.M31 + left.M24 * right.M41, - left.M21 * right.M12 + left.M22 * right.M22 + left.M23 * right.M32 + left.M24 * right.M42, - left.M21 * right.M13 + left.M22 * right.M23 + left.M23 * right.M33 + left.M24 * right.M43, - left.M21 * right.M14 + left.M22 * right.M24 + left.M23 * right.M34 + left.M24 * right.M44, - left.M31 * right.M11 + left.M32 * right.M21 + left.M33 * right.M31 + left.M34 * right.M41, - left.M31 * right.M12 + left.M32 * right.M22 + left.M33 * right.M32 + left.M34 * right.M42, - left.M31 * right.M13 + left.M32 * right.M23 + left.M33 * right.M33 + left.M34 * right.M43, - left.M31 * right.M14 + left.M32 * right.M24 + left.M33 * right.M34 + left.M34 * right.M44, - left.M41 * right.M11 + left.M42 * right.M21 + left.M43 * right.M31 + left.M44 * right.M41, - left.M41 * right.M12 + left.M42 * right.M22 + left.M43 * right.M32 + left.M44 * right.M42, - left.M41 * right.M13 + left.M42 * right.M23 + left.M43 * right.M33 + left.M44 * right.M43, - left.M41 * right.M14 + left.M42 * right.M24 + left.M43 * right.M34 + left.M44 * right.M44); + // the matrices are reversed: https://github.com/xamarin/xamarin-macios/issues/15094#issuecomment-1139699662 +#if XAMCORE_5_0 + MatrixMultiply (ref secondTransformation, ref firstTransformation, out result); +#else + MatrixMultiply (ref right, ref left, out result); +#endif + } + + // Multiply two matrices in the order you'd expect (left * right). + static void MatrixMultiply (ref SCNMatrix4 left, ref SCNMatrix4 right, out SCNMatrix4 result) + { + result = new SCNMatrix4( + left.Column0.X * right.Column0.X + left.Column1.X * right.Column0.Y + left.Column2.X * right.Column0.Z + left.Column3.X * right.Column0.W, + left.Column0.X * right.Column1.X + left.Column1.X * right.Column1.Y + left.Column2.X * right.Column1.Z + left.Column3.X * right.Column1.W, + left.Column0.X * right.Column2.X + left.Column1.X * right.Column2.Y + left.Column2.X * right.Column2.Z + left.Column3.X * right.Column2.W, + left.Column0.X * right.Column3.X + left.Column1.X * right.Column3.Y + left.Column2.X * right.Column3.Z + left.Column3.X * right.Column3.W, + + left.Column0.Y * right.Column0.X + left.Column1.Y * right.Column0.Y + left.Column2.Y * right.Column0.Z + left.Column3.Y * right.Column0.W, + left.Column0.Y * right.Column1.X + left.Column1.Y * right.Column1.Y + left.Column2.Y * right.Column1.Z + left.Column3.Y * right.Column1.W, + left.Column0.Y * right.Column2.X + left.Column1.Y * right.Column2.Y + left.Column2.Y * right.Column2.Z + left.Column3.Y * right.Column2.W, + left.Column0.Y * right.Column3.X + left.Column1.Y * right.Column3.Y + left.Column2.Y * right.Column3.Z + left.Column3.Y * right.Column3.W, + + left.Column0.Z * right.Column0.X + left.Column1.Z * right.Column0.Y + left.Column2.Z * right.Column0.Z + left.Column3.Z * right.Column0.W, + left.Column0.Z * right.Column1.X + left.Column1.Z * right.Column1.Y + left.Column2.Z * right.Column1.Z + left.Column3.Z * right.Column1.W, + left.Column0.Z * right.Column2.X + left.Column1.Z * right.Column2.Y + left.Column2.Z * right.Column2.Z + left.Column3.Z * right.Column2.W, + left.Column0.Z * right.Column3.X + left.Column1.Z * right.Column3.Y + left.Column2.Z * right.Column3.Z + left.Column3.Z * right.Column3.W, + + left.Column0.W * right.Column0.X + left.Column1.W * right.Column0.Y + left.Column2.W * right.Column0.Z + left.Column3.W * right.Column0.W, + left.Column0.W * right.Column1.X + left.Column1.W * right.Column1.Y + left.Column2.W * right.Column1.Z + left.Column3.W * right.Column1.W, + left.Column0.W * right.Column2.X + left.Column1.W * right.Column2.Y + left.Column2.W * right.Column2.Z + left.Column3.W * right.Column2.W, + left.Column0.W * right.Column3.X + left.Column1.W * right.Column3.Y + left.Column2.W * right.Column3.Z + left.Column3.W * right.Column3.W); } #endregion @@ -1102,7 +1219,7 @@ public static void Transpose (ref SCNMatrix4 mat, out SCNMatrix4 result) #region public override string ToString() /// - /// Returns a System.String that represents the current SCNMatrix44. + /// Returns a System.String that represents the current SCNMatrix4. /// /// public override string ToString () diff --git a/src/SceneKit/SCNVector3.cs b/src/SceneKit/SCNVector3.cs index 22ed3c65c5c4..b25d792bb14e 100644 --- a/src/SceneKit/SCNVector3.cs +++ b/src/SceneKit/SCNVector3.cs @@ -740,29 +740,53 @@ public static void BaryCentric(ref SCNVector3 a, ref SCNVector3 b, ref SCNVector #region Transform +#if NET + /// Transform a direction vector by the given Matrix + /// Assumes the matrix has a right-most column of (0,0,0,1), that is the translation part is ignored. + /// + /// The column vector to transform +#else /// Transform a direction vector by the given Matrix /// Assumes the matrix has a bottom row of (0,0,0,1), that is the translation part is ignored. /// - /// The vector to transform + /// The row vector to transform +#endif /// The desired transformation /// The transformed vector public static SCNVector3 TransformVector(SCNVector3 vec, SCNMatrix4 mat) { - SCNVector3 v; - v.X = SCNVector3.Dot(vec, new SCNVector3(mat.Column0)); - v.Y = SCNVector3.Dot(vec, new SCNVector3(mat.Column1)); - v.Z = SCNVector3.Dot(vec, new SCNVector3(mat.Column2)); + TransformVector (ref vec, ref mat, out var v); return v; } +#if NET + /// Transform a direction vector by the given Matrix + /// Assumes the matrix has a right-most column of (0,0,0,1), that is the translation part is ignored. + /// + /// The column vector to transform +#else /// Transform a direction vector by the given Matrix /// Assumes the matrix has a bottom row of (0,0,0,1), that is the translation part is ignored. /// - /// The vector to transform + /// The row vector to transform +#endif /// The desired transformation /// The transformed vector public static void TransformVector(ref SCNVector3 vec, ref SCNMatrix4 mat, out SCNVector3 result) { +#if NET + result.X = vec.X * mat.Row0.X + + vec.Y * mat.Row0.Y + + vec.Z * mat.Row0.Z; + + result.Y = vec.X * mat.Row1.X + + vec.Y * mat.Row1.Y + + vec.Z * mat.Row1.Z; + + result.Z = vec.X * mat.Row2.X + + vec.Y * mat.Row2.Y + + vec.Z * mat.Row2.Z; +#else result.X = vec.X * mat.Row0.X + vec.Y * mat.Row1.X + vec.Z * mat.Row2.X; @@ -774,6 +798,7 @@ public static void TransformVector(ref SCNVector3 vec, ref SCNMatrix4 mat, out S result.Z = vec.X * mat.Row0.Z + vec.Y * mat.Row1.Z + vec.Z * mat.Row2.Z; +#endif } /// Transform a Normal by the given Matrix @@ -781,7 +806,11 @@ public static void TransformVector(ref SCNVector3 vec, ref SCNMatrix4 mat, out S /// This calculates the inverse of the given matrix, use TransformNormalInverse if you /// already have the inverse to avoid this extra calculation /// - /// The normal to transform +#if NET + /// The column-based normal to transform +#else + /// The row-based normal to transform +#endif /// The desired transformation /// The transformed normal public static SCNVector3 TransformNormal(SCNVector3 norm, SCNMatrix4 mat) @@ -795,7 +824,11 @@ public static SCNVector3 TransformNormal(SCNVector3 norm, SCNMatrix4 mat) /// This calculates the inverse of the given matrix, use TransformNormalInverse if you /// already have the inverse to avoid this extra calculation /// - /// The normal to transform +#if NET + /// The column-based normal to transform +#else + /// The row-based normal to transform +#endif /// The desired transformation /// The transformed normal public static void TransformNormal(ref SCNVector3 norm, ref SCNMatrix4 mat, out SCNVector3 result) @@ -809,15 +842,16 @@ public static void TransformNormal(ref SCNVector3 norm, ref SCNMatrix4 mat, out /// This version doesn't calculate the inverse matrix. /// Use this version if you already have the inverse of the desired transform to hand /// - /// The normal to transform +#if NET + /// The column-based normal to transform +#else + /// The row-based normal to transform +#endif /// The inverse of the desired transformation /// The transformed normal public static SCNVector3 TransformNormalInverse(SCNVector3 norm, SCNMatrix4 invMat) { - SCNVector3 n; - n.X = SCNVector3.Dot(norm, new SCNVector3(invMat.Row0)); - n.Y = SCNVector3.Dot(norm, new SCNVector3(invMat.Row1)); - n.Z = SCNVector3.Dot(norm, new SCNVector3(invMat.Row2)); + TransformNormalInverse (ref norm, ref invMat, out var n); return n; } @@ -826,11 +860,28 @@ public static SCNVector3 TransformNormalInverse(SCNVector3 norm, SCNMatrix4 invM /// This version doesn't calculate the inverse matrix. /// Use this version if you already have the inverse of the desired transform to hand /// - /// The normal to transform +#if NET + /// The column-based normal to transform +#else + /// The row-based normal to transform +#endif /// The inverse of the desired transformation /// The transformed normal public static void TransformNormalInverse(ref SCNVector3 norm, ref SCNMatrix4 invMat, out SCNVector3 result) { +#if NET + result.X = norm.X * invMat.Column0.X + + norm.Y * invMat.Column0.Y + + norm.Z * invMat.Column0.Z; + + result.Y = norm.X * invMat.Column1.X + + norm.Y * invMat.Column1.Y + + norm.Z * invMat.Column1.Z; + + result.Z = norm.X * invMat.Column2.X + + norm.Y * invMat.Column2.Y + + norm.Z * invMat.Column2.Z; +#else result.X = norm.X * invMat.Row0.X + norm.Y * invMat.Row0.Y + norm.Z * invMat.Row0.Z; @@ -842,27 +893,49 @@ public static void TransformNormalInverse(ref SCNVector3 norm, ref SCNMatrix4 in result.Z = norm.X * invMat.Row2.X + norm.Y * invMat.Row2.Y + norm.Z * invMat.Row2.Z; +#endif } /// Transform a Position by the given Matrix - /// The position to transform +#if NET + /// The column-based position to transform +#else + /// The row-based position to transform +#endif /// The desired transformation /// The transformed position public static SCNVector3 TransformPosition(SCNVector3 pos, SCNMatrix4 mat) { - SCNVector3 p; - p.X = SCNVector3.Dot(pos, new SCNVector3(mat.Column0)) + mat.Row3.X; - p.Y = SCNVector3.Dot(pos, new SCNVector3(mat.Column1)) + mat.Row3.Y; - p.Z = SCNVector3.Dot(pos, new SCNVector3(mat.Column2)) + mat.Row3.Z; + TransformPosition (ref pos, ref mat, out var p); return p; } /// Transform a Position by the given Matrix - /// The position to transform +#if NET + /// The column-based position to transform +#else + /// The row-based position to transform +#endif /// The desired transformation /// The transformed position public static void TransformPosition(ref SCNVector3 pos, ref SCNMatrix4 mat, out SCNVector3 result) { +#if NET + result.X = mat.Row0.X * pos.X + + mat.Row0.Y * pos.Y + + mat.Row0.Z * pos.Z + + mat.Row0.W; + + result.Y = mat.Row1.X * pos.X + + mat.Row1.Y * pos.Y + + mat.Row1.Z * pos.Z + + mat.Row1.W; + + result.Z = mat.Row2.X * pos.X + + mat.Row2.Y * pos.Y + + mat.Row2.Z * pos.Z + + mat.Row2.W; +#else result.X = pos.X * mat.Row0.X + pos.Y * mat.Row1.X + pos.Z * mat.Row2.X + @@ -877,25 +950,29 @@ public static void TransformPosition(ref SCNVector3 pos, ref SCNMatrix4 mat, out pos.Y * mat.Row1.Z + pos.Z * mat.Row2.Z + mat.Row3.Z; +#endif } /// Transform a Vector by the given Matrix - /// The vector to transform +#if NET + /// The column vector to transform +#else + /// The row vector to transform +#endif /// The desired transformation /// The transformed vector public static SCNVector4 Transform(SCNVector3 vec, SCNMatrix4 mat) { SCNVector4 v4 = new SCNVector4(vec.X, vec.Y, vec.Z, 1.0f); - SCNVector4 result; - result.X = SCNVector4.Dot(v4, mat.Column0); - result.Y = SCNVector4.Dot(v4, mat.Column1); - result.Z = SCNVector4.Dot(v4, mat.Column2); - result.W = SCNVector4.Dot(v4, mat.Column3); - return result; + return SCNVector4.Transform (v4, mat); } /// Transform a Vector by the given Matrix - /// The vector to transform +#if NET + /// The column vector to transform +#else + /// The row vector to transform +#endif /// The desired transformation /// The transformed vector public static void Transform(ref SCNVector3 vec, ref SCNMatrix4 mat, out SCNVector4 result) diff --git a/src/SceneKit/SCNVector4.cs b/src/SceneKit/SCNVector4.cs index f8097c2cb4e8..d07bd7345437 100644 --- a/src/SceneKit/SCNVector4.cs +++ b/src/SceneKit/SCNVector4.cs @@ -843,25 +843,50 @@ public static void BaryCentric(ref SCNVector4 a, ref SCNVector4 b, ref SCNVector #region Transform /// Transform a Vector by the given Matrix - /// The vector to transform +#if NET + /// The column vector to transform +#else + /// The row vector to transform +#endif /// The desired transformation /// The transformed vector public static SCNVector4 Transform(SCNVector4 vec, SCNMatrix4 mat) { - SCNVector4 result; - result.X = SCNVector4.Dot(vec, mat.Column0); - result.Y = SCNVector4.Dot(vec, mat.Column1); - result.Z = SCNVector4.Dot(vec, mat.Column2); - result.W = SCNVector4.Dot(vec, mat.Column3); + Transform(ref vec, ref mat, out var result); return result; } - /// Transform a Vector by the given Matrix - /// The vector to transform + /// Transform a Vector by the given Matrix. +#if NET + /// The column vector to transform +#else + /// The row vector to transform +#endif /// The desired transformation /// The transformed vector public static void Transform(ref SCNVector4 vec, ref SCNMatrix4 mat, out SCNVector4 result) { +#if NET + result.X = vec.X * mat.Column0.X + + vec.Y * mat.Column1.X + + vec.Z * mat.Column2.X + + vec.W * mat.Column3.X; + + result.Y = vec.X * mat.Column0.Y + + vec.Y * mat.Column1.Y + + vec.Z * mat.Column2.Y + + vec.W * mat.Column3.Y; + + result.Z = vec.X * mat.Column0.Z + + vec.Y * mat.Column1.Z + + vec.Z * mat.Column2.Z + + vec.W * mat.Column3.Z; + + result.W = vec.X * mat.Column0.W + + vec.Y * mat.Column1.W + + vec.Z * mat.Column2.W + + vec.W * mat.Column3.W; +#else result.X = vec.X * mat.Row0.X + vec.Y * mat.Row1.X + vec.Z * mat.Row2.X + @@ -881,6 +906,7 @@ public static void Transform(ref SCNVector4 vec, ref SCNMatrix4 mat, out SCNVect vec.Y * mat.Row1.W + vec.Z * mat.Row2.W + vec.W * mat.Row3.W; +#endif } #endregion diff --git a/tests/bindings-test/StructsAndEnums.cs b/tests/bindings-test/StructsAndEnums.cs index e4737da9d9d6..a8c66280a921 100644 --- a/tests/bindings-test/StructsAndEnums.cs +++ b/tests/bindings-test/StructsAndEnums.cs @@ -3,6 +3,7 @@ using Foundation; using ObjCRuntime; +using SceneKit; #if NET using MatrixFloat2x2 = global::CoreGraphics.NMatrix2; @@ -16,6 +17,16 @@ using MatrixFloat4x4 = global::OpenTK.NMatrix4; #endif +#if __MACOS__ +#if NET +using pfloat = System.Runtime.InteropServices.NFloat; +#else +using pfloat = System.nfloat; +#endif +#else +using pfloat = System.Single; +#endif + public static class LibTest { [DllImport ("__Internal")] public static extern int theUltimateAnswer (); @@ -126,5 +137,14 @@ public static MatrixFloat4x4 MDLTransform_GetRotationMatrix (INativeObject obj, r3c0, r3c1, r3c2, r3c3); } #endif + + [DllImport ("__Internal")] + public static extern SCNMatrix4 x_SCNMatrix4MakeTranslation (pfloat tx, pfloat ty, pfloat tz); + + [DllImport ("__Internal")] + public static extern SCNMatrix4 x_SCNMatrix4MakeScale (pfloat tx, pfloat ty, pfloat tz); + + [DllImport ("__Internal")] + public static extern SCNMatrix4 x_SCNMatrix4Translate (SCNMatrix4 m, pfloat tx, pfloat ty, pfloat tz); } } diff --git a/tests/monotouch-test/Asserts.cs b/tests/monotouch-test/Asserts.cs index 9a4557d20557..852a5800bbdc 100644 --- a/tests/monotouch-test/Asserts.cs +++ b/tests/monotouch-test/Asserts.cs @@ -1,8 +1,15 @@ using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + #if !__WATCHOS__ using ModelIO; using MetalPerformanceShaders; #endif +#if HAS_SCENEKIT +using SceneKit; +#endif #if NET using System.Numerics; @@ -31,6 +38,16 @@ using VectorDouble3 = global::OpenTK.NVector3d; #endif +#if __MACOS__ +#if NET +using pfloat = System.Runtime.InteropServices.NFloat; +#else +using pfloat = System.nfloat; +#endif +#else +using pfloat = System.Single; +#endif + using NUnit.Framework; public static class Asserts @@ -714,4 +731,285 @@ public static void AreEqual (NMatrix4x3 expected, NMatrix4x3 actual, string mess AreEqual (expected.M34, actual.M34, $"{message} (M34) expected: {expected} actual: {actual}"); } #endregion + +#if HAS_SCENEKIT + public static void AreEqual (SCNVector3 expected, SCNVector3 actual, string message) + { + if (AreEqual (expected.X, actual.X, out var dX) & + AreEqual (expected.Y, actual.Y, out var dY) & + AreEqual (expected.Z, actual.Z, out var dZ)) + return; + + var diffString = $"({dX}, {dY}, {dZ})"; + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + public static void AreEqual (SCNVector3 expected, SCNVector3 actual, pfloat delta, string message) + { + if (AreEqual (expected.X, actual.X, delta, out var dX) & + AreEqual (expected.Y, actual.Y, delta, out var dY) & + AreEqual (expected.Z, actual.Z, delta, out var dZ)) + return; + + var diffString = $"({dX}, {dY}, {dZ})"; + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + public static void AreEqual (SCNVector4 expected, SCNVector4 actual, string message) + { + if (AreEqual (expected.X, actual.X, out var dX) & + AreEqual (expected.Y, actual.Y, out var dY) & + AreEqual (expected.Z, actual.Z, out var dZ) & + AreEqual (expected.W, actual.W, out var dW)) + return; + + var diffString = $"({dX}, {dY}, {dZ}, {dW})"; + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + + public static void AreEqual (SCNVector4 expected, SCNVector4 actual, pfloat delta, string message) + { + if (AreEqual (expected.X, actual.X, delta, out var dX) & + AreEqual (expected.Y, actual.Y, delta, out var dY) & + AreEqual (expected.Z, actual.Z, delta, out var dZ) & + AreEqual (expected.W, actual.W, delta, out var dW)) + return; + + var diffString = $"({dX}, {dY}, {dZ}, {dW})"; + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + public static void AreEqual (SCNQuaternion expected, SCNQuaternion actual, string message) + { + if (AreEqual (expected.X, actual.X, out var dX) & + AreEqual (expected.Y, actual.Y, out var dY) & + AreEqual (expected.Z, actual.Z, out var dZ) & + AreEqual (expected.W, actual.W, out var dW)) + return; + + var diffString = $"[{dX}, {dY}, {dZ}, {dW}]"; + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + public static void AreEqual (SCNQuaternion expected, SCNQuaternion actual, pfloat delta, string message) + { + if (AreEqual (expected.X, actual.X, delta, out var dX) & + AreEqual (expected.Y, actual.Y, delta, out var dY) & + AreEqual (expected.Z, actual.Z, delta, out var dZ) & + AreEqual (expected.W, actual.W, delta, out var dW)) + return; + + var diffString = $"[{dX}, {dY}, {dZ}, {dW}]"; + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + public static void AreEqual (SCNMatrix4 expected, SCNMatrix4 actual, string message) + { + if (AreEqual (expected.M11, actual.M11, out var d11) & + AreEqual (expected.M21, actual.M21, out var d21) & + AreEqual (expected.M31, actual.M31, out var d31) & + AreEqual (expected.M41, actual.M41, out var d41) & + AreEqual (expected.M12, actual.M12, out var d12) & + AreEqual (expected.M22, actual.M22, out var d22) & + AreEqual (expected.M32, actual.M32, out var d32) & + AreEqual (expected.M42, actual.M42, out var d42) & + AreEqual (expected.M13, actual.M13, out var d13) & + AreEqual (expected.M23, actual.M23, out var d23) & + AreEqual (expected.M33, actual.M33, out var d33) & + AreEqual (expected.M43, actual.M43, out var d43) & + AreEqual (expected.M14, actual.M14, out var d14) & + AreEqual (expected.M24, actual.M24, out var d24) & + AreEqual (expected.M34, actual.M34, out var d34) & + AreEqual (expected.M44, actual.M44, out var d44)) { + + var size = Marshal.SizeOf (typeof (SCNMatrix4)); + unsafe { + byte* e = (byte*) (void*) &expected; + byte* a = (byte*) (void*) &actual; + AreEqual (e, a, size, message); + } + return; + } + + var actualString = actual.ToString (); + + var expectedString = expected.ToString (); + + var diffRow1 = $"({d11}, {d12}, {d13}, {d14})"; + var diffRow2 = $"({d21}, {d22}, {d23}, {d24})"; + var diffRow3 = $"({d31}, {d32}, {d33}, {d34})"; + var diffRow4 = $"({d41}, {d42}, {d43}, {d44})"; + var diffString = $"{diffRow1}\n{diffRow2}\n{diffRow3}\n{diffRow4}"; + + var msg = $"{message}\nExpected:\n{expected}\nActual:\n{actual}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + public static void AreEqual (SCNMatrix4 expected, SCNMatrix4 actual, pfloat delta, string message) + { + if (AreEqual (expected.M11, actual.M11, delta, out var d11) & + AreEqual (expected.M21, actual.M21, delta, out var d21) & + AreEqual (expected.M31, actual.M31, delta, out var d31) & + AreEqual (expected.M41, actual.M41, delta, out var d41) & + AreEqual (expected.M12, actual.M12, delta, out var d12) & + AreEqual (expected.M22, actual.M22, delta, out var d22) & + AreEqual (expected.M32, actual.M32, delta, out var d32) & + AreEqual (expected.M42, actual.M42, delta, out var d42) & + AreEqual (expected.M13, actual.M13, delta, out var d13) & + AreEqual (expected.M23, actual.M23, delta, out var d23) & + AreEqual (expected.M33, actual.M33, delta, out var d33) & + AreEqual (expected.M43, actual.M43, delta, out var d43) & + AreEqual (expected.M14, actual.M14, delta, out var d14) & + AreEqual (expected.M24, actual.M24, delta, out var d24) & + AreEqual (expected.M34, actual.M34, delta, out var d34) & + AreEqual (expected.M44, actual.M44, delta, out var d44)) + return; + + var actualString = actual.ToString (); + var expectedString = expected.ToString (); + + var diffRow1 = $"({d11}, {d12}, {d13}, {d14})"; + var diffRow2 = $"({d21}, {d22}, {d23}, {d24})"; + var diffRow3 = $"({d31}, {d32}, {d33}, {d34})"; + var diffRow4 = $"({d41}, {d42}, {d43}, {d44})"; + var diffString = $"{diffRow1}\n{diffRow2}\n{diffRow3}\n{diffRow4}"; + + var msg = $"{message}\nExpected:\n{expectedString}\nActual:\n{actualString}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } + + + // The m## arguments correspond with the M## fields in SCNMatrix4 + // For .NET this means the first four values are the first column (and the first row for legacy Xamarin). + public static void AreEqual (SCNMatrix4 actual, string message, + pfloat m11, pfloat m12, pfloat m13, pfloat m14, + pfloat m21, pfloat m22, pfloat m23, pfloat m24, + pfloat m31, pfloat m32, pfloat m33, pfloat m34, + pfloat m41, pfloat m42, pfloat m43, pfloat m44) + { + AreEqual (actual, message, + m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44, + delta: 0); + } + + // The m## arguments correspond with the M## fields in SCNMatrix4 + // For .NET this means the first four values are the first column (and the first row for legacy Xamarin). + public static void AreEqual (SCNMatrix4 actual, string message, + pfloat m11, pfloat m12, pfloat m13, pfloat m14, + pfloat m21, pfloat m22, pfloat m23, pfloat m24, + pfloat m31, pfloat m32, pfloat m33, pfloat m34, + pfloat m41, pfloat m42, pfloat m43, pfloat m44, + pfloat delta + ) + { + if (AreEqual (m11, actual.M11, delta, out var d11) & + AreEqual (m21, actual.M21, delta, out var d21) & + AreEqual (m31, actual.M31, delta, out var d31) & + AreEqual (m41, actual.M41, delta, out var d41) & + AreEqual (m12, actual.M12, delta, out var d12) & + AreEqual (m22, actual.M22, delta, out var d22) & + AreEqual (m32, actual.M32, delta, out var d32) & + AreEqual (m42, actual.M42, delta, out var d42) & + AreEqual (m13, actual.M13, delta, out var d13) & + AreEqual (m23, actual.M23, delta, out var d23) & + AreEqual (m33, actual.M33, delta, out var d33) & + AreEqual (m43, actual.M43, delta, out var d43) & + AreEqual (m14, actual.M14, delta, out var d14) & + AreEqual (m24, actual.M24, delta, out var d24) & + AreEqual (m34, actual.M34, delta, out var d34) & + AreEqual (m44, actual.M44, delta, out var d44)) + return; + + var actualString = actual.ToString (); + +#if NET + var row1 = $"({m11}, {m21}, {m31}, {m41})"; + var row2 = $"({m12}, {m22}, {m32}, {m42})"; + var row3 = $"({m13}, {m23}, {m33}, {m43})"; + var row4 = $"({m14}, {m24}, {m34}, {m44})"; +#else + var row1 = $"({m11}, {m12}, {m13}, {m14})"; + var row2 = $"({m21}, {m22}, {m23}, {m24})"; + var row3 = $"({m31}, {m32}, {m33}, {m34})"; + var row4 = $"({m41}, {m42}, {m43}, {m44})"; +#endif + var expectedString = $"{row1}\n{row2}\n{row3}\n{row4}"; + + var diffRow1 = $"({d11}, {d12}, {d13}, {d14})"; + var diffRow2 = $"({d21}, {d22}, {d23}, {d24})"; + var diffRow3 = $"({d31}, {d32}, {d33}, {d34})"; + var diffRow4 = $"({d41}, {d42}, {d43}, {d44})"; + var diffString = $"{diffRow1}\n{diffRow2}\n{diffRow3}\n{diffRow4}"; + + var msg = $"{message}\nExpected:\n{expectedString}\nActual:\n{actualString}\nDiff:\n{diffString}"; + Assert.Fail (msg); + } +#endif // HAS_SCENEKIT + + static bool AreEqual (pfloat expected, pfloat actual, out string emojii) + { + return AreEqual (expected, actual, 0, out emojii); + } + + // Use our own implementation to compare two floating point numbers with a tolerance, because + // the NUnit version doesn't seem to work correctly in legacy Xamarin (older NUnit version?). + static bool AreEqual (pfloat expected, pfloat actual, pfloat tolerance, out string emojii) + { + bool rv; + + if (pfloat.IsNaN (expected) && pfloat.IsNaN (actual)) { + rv = true; + } else if (pfloat.IsInfinity (expected) || pfloat.IsNaN (expected) || pfloat.IsNaN (actual)) { + // Handle infinity specially since subtracting two infinite values gives + // NaN and the following test fails. mono also needs NaN to be handled + // specially although ms.net could use either method. Also, handle + // situation where no tolerance is used. + rv = expected.Equals (actual); + } else { + rv = Math.Abs (expected - actual) <= tolerance; + } + + emojii = rv ? "✅" : "❌"; + + return rv; + } + + public unsafe static void AreEqual (byte* expected, byte* actual, int length, string message) + { + // Check if the byte arrays are identical + var equal = true; + for (var i = 0; i < length; i++) { + var e = expected [i]; + var a = actual [i]; + equal &= e == a; + } + if (equal) + return; + // They're not. Create the assertion message and assert. + var e_sb = new StringBuilder (); + var a_sb = new StringBuilder (); + var d_sb = new StringBuilder (); + for (var i = 0; i < length; i++) { + var e = expected [i]; + var a = actual [i]; + e_sb.Append ($"0x{e:X2} "); + a_sb.Append ($"0x{a:X2} "); + if (e == a) { + d_sb.Append (" "); + } else { + d_sb.Append ("^^^^ "); + } + } + Assert.Fail ($"{message}\nExpected: {e_sb}\nActual: {a_sb}\n {d_sb}"); + } } diff --git a/tests/monotouch-test/SceneKit/SCNMatrixTest.cs b/tests/monotouch-test/SceneKit/SCNMatrixTest.cs index 7f10d5a9d829..f7e0ad37f602 100644 --- a/tests/monotouch-test/SceneKit/SCNMatrixTest.cs +++ b/tests/monotouch-test/SceneKit/SCNMatrixTest.cs @@ -12,6 +12,7 @@ #nullable enable using System; +using System.Runtime.InteropServices; using CoreAnimation; using Foundation; using SceneKit; @@ -57,67 +58,21 @@ public class SCNMatrix4Test { static pfloat SqrtTwelve = (pfloat) (Math.Sqrt (12)); // 3.464102 static pfloat OhPointFive = (pfloat) 0.5; - public static bool CloseEnough (double a, double b, double epsilon = 0.00001) - { - const double MinNormal = 2.2250738585072014E-308d; - var absA = Math.Abs (a); - var absB = Math.Abs (b); - var diff = Math.Abs (a - b); - - if (a == b) { - return true; - } else if (a == 0 || b == 0 || absA + absB < MinNormal) { - // a or b is zero or both are extremely close to it - // relative error is less meaningful here - return diff < (epsilon * MinNormal); - } else { // use relative error - return diff / (absA + absB) < epsilon; - } - } - - void AssertEqual (SCNMatrix4 matrix, string message, - pfloat m11, pfloat m12, pfloat m13, pfloat m14, - pfloat m21, pfloat m22, pfloat m23, pfloat m24, - pfloat m31, pfloat m32, pfloat m33, pfloat m34, - pfloat m41, pfloat m42, pfloat m43, pfloat m44 - ) - { - if (CloseEnough (m11, matrix.M11) && CloseEnough (m12, matrix.M12) && CloseEnough (m13, matrix.M13) && CloseEnough (m14, matrix.M14) && - CloseEnough (m21, matrix.M21) && CloseEnough (m22, matrix.M22) && CloseEnough (m23, matrix.M23) && CloseEnough (m24, matrix.M24) && - CloseEnough (m31, matrix.M31) && CloseEnough (m32, matrix.M32) && CloseEnough (m33, matrix.M33) && CloseEnough (m34, matrix.M34) && - CloseEnough (m41, matrix.M41) && CloseEnough (m42, matrix.M42) && CloseEnough (m43, matrix.M43) && CloseEnough (m44, matrix.M44)) - return; - - var actualString = matrix.ToString (); - - var row1 = $"({m11}, {m12}, {m13}, {m14})"; - var row2 = $"({m21}, {m22}, {m23}, {m24})"; - var row3 = $"({m31}, {m32}, {m33}, {m34})"; - var row4 = $"({m41}, {m42}, {m43}, {m44})"; - var expectedString = $"{row1}\n{row2}\n{row3}\n{row4}"; - Assert.Fail ($"Expected matrix:\n{expectedString}\nActual matrix:\n{actualString}\n{message}"); - } - - void AssertEqual (SCNVector4 vector, string message, pfloat m1, pfloat m2, pfloat m3, pfloat m4) - { - if (m1 == vector.X && m2 == vector.Y && m3 == vector.Z && m4 == vector.W) - return; - - var expectedString = vector.ToString (); - var actualString = $"({m1}, {m2}, {m3}, {m4})"; - - Assert.Fail ($"Expected vector:\n{expectedString}\nActual vector:\n{actualString}\n{message}"); - } - [Test] public void Identity () { var matrix = SCNMatrix4.Identity; - AssertEqual (matrix, "Identity", + var expected = new SCNMatrix4 ( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + Asserts.AreEqual (expected, matrix, "Identity"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (pos, transformed, "Transformed"); } [Test] @@ -128,11 +83,20 @@ public void Constructor_RowVectors () new SCNVector4 (21, 22, 23, 24), new SCNVector4 (31, 32, 33, 34), new SCNVector4 (41, 42, 43, 44)); - AssertEqual (matrix, "Constructor", + var expected = new SCNMatrix4 ( 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44); + Asserts.AreEqual (expected, matrix, "Constructor"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#endif } [Test] @@ -143,11 +107,27 @@ public void Constructor_Elements () 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44); - AssertEqual (matrix, "Constructor", + + Asserts.AreEqual (matrix, "Constructor", +#if NET + 11, 21, 31, 41, + 12, 22, 32, 42, + 13, 23, 33, 43, + 14, 24, 34, 44); +#else 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44); +#endif + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#endif } #if !WATCH @@ -155,29 +135,25 @@ public void Constructor_Elements () public void Constructor_CATransform3d () { var transform = new CATransform3D () { - M11 = 11, - M12 = 12, - M13 = 13, - M14 = 14, - M21 = 21, - M22 = 22, - M23 = 23, - M24 = 24, - M31 = 31, - M32 = 32, - M33 = 33, - M34 = 34, - M41 = 41, - M42 = 42, - M43 = 43, - M44 = 44, + M11 = 11, M12 = 12, M13 = 13, M14 = 14, + M21 = 21, M22 = 22, M23 = 23, M24 = 24, + M31 = 31, M32 = 32, M33 = 33, M34 = 34, + M41 = 41, M42 = 42, M43 = 43, M44 = 44, }; var matrix = new SCNMatrix4 (transform); - AssertEqual (matrix, "Constructor", - 11, 12, 13, 14, - 21, 22, 23, 24, - 31, 32, 33, 34, - 41, 42, 43, 44); + var expected = new SCNMatrix4 ( + 11, 21, 31, 41, + 12, 22, 32, 42, + 13, 23, 33, 43, + 14, 24, 34, 44); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "Constructor"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); } #endif @@ -201,20 +177,30 @@ public void Rows () 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44); - AssertEqual (matrix.Row0, "Row0", 11, 12, 13, 14); - AssertEqual (matrix.Row1, "Row1", 21, 22, 23, 24); - AssertEqual (matrix.Row2, "Row2", 31, 32, 33, 34); - AssertEqual (matrix.Row3, "Row3", 41, 42, 43, 44); + Asserts.AreEqual (matrix.Row0, new SCNVector4 (11, 12, 13, 14), "Row0"); + Asserts.AreEqual (matrix.Row1, new SCNVector4 (21, 22, 23, 24), "Row1"); + Asserts.AreEqual (matrix.Row2, new SCNVector4 (31, 32, 33, 34), "Row2"); + Asserts.AreEqual (matrix.Row3, new SCNVector4 (41, 42, 43, 44), "Row3"); } [Test] public void Elements () { + // We're column-major in .NET, which means the first number (M#.) is the column, + // and the second number (M.#) is the row. That's the reverse of how it's in legacy + // Xamarin. var matrix = new SCNMatrix4 ( +#if NET + 11, 21, 31, 41, + 12, 22, 32, 42, + 13, 23, 33, 43, + 14, 24, 34, 44); +#else 11, 12, 13, 14, 21, 22, 23, 24, 31, 32, 33, 34, 41, 42, 43, 44); +#endif Assert.AreEqual ((pfloat) 11, matrix.M11, "M11"); Assert.AreEqual ((pfloat) 12, matrix.M12, "M12"); Assert.AreEqual ((pfloat) 13, matrix.M13, "M13"); @@ -231,6 +217,10 @@ public void Elements () Assert.AreEqual ((pfloat) 42, matrix.M42, "M42"); Assert.AreEqual ((pfloat) 43, matrix.M43, "M43"); Assert.AreEqual ((pfloat) 44, matrix.M44, "M44"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); } #if NET // The legacy Invert implementation seems very wrong, so only verify .NET behavior @@ -250,13 +240,22 @@ public void Invert () 5, 3, 5, 8, 9, 6, 4, 2, 4, 6, 9, 8); + var originalMatrix = matrix; matrix.Invert (); - AssertEqual (matrix, "Invert", + Assert.AreEqual (SCNMatrix4Invert (originalMatrix), matrix, "Native"); + + var expected = new SCNMatrix4 ( (pfloat) (-0.6181818181818182), (pfloat) (0.3151515151515151), (pfloat) (-0.030303030303030304), (pfloat) (0.3878787878787879), (pfloat) (1.6363636363636365), (pfloat) (-0.696969696969697), (pfloat) (0.3939393939393939), (pfloat) (-1.2424242424242424), (pfloat) (-1.3818181818181818), (pfloat) (0.3515151515151515), (pfloat) (-0.30303030303030304), (pfloat) (1.2787878787878788), (pfloat) (0.6363636363636364), (pfloat) (-0.030303030303030304), (pfloat) (0.06060606060606061), (pfloat) (-0.5757575757575758)); + + Asserts.AreEqual (expected, matrix, (pfloat) 0.00001, "Invert"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (-0.4f, 13, -14.6f), transformed, 0.00001f, "Transformed"); } #endif @@ -269,11 +268,20 @@ public void Transpose () new SCNVector4 (31, 32, 33, 34), new SCNVector4 (41, 42, 43, 44)); matrix.Transpose (); - AssertEqual (matrix, "Transpose", + var expected = new SCNMatrix4 ( 11, 21, 31, 41, 12, 22, 32, 42, 13, 23, 33, 43, 14, 24, 34, 44); + Asserts.AreEqual (expected, matrix, "Transpose"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#endif } [Test] @@ -284,11 +292,20 @@ public void CreateFromColumns () new SCNVector4 (21, 22, 23, 24), new SCNVector4 (31, 32, 33, 34), new SCNVector4 (41, 42, 43, 44)); - AssertEqual (matrix, "CreateFromColumns", + var expected = new SCNMatrix4 ( 11, 21, 31, 41, 12, 22, 32, 42, 13, 23, 33, 43, 14, 24, 34, 44); + Asserts.AreEqual (expected, matrix, "CreateFromColumns"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#endif } [Test] @@ -300,132 +317,249 @@ public void CreateFromColumns_Out () new SCNVector4 (31, 32, 33, 34), new SCNVector4 (41, 42, 43, 44), out var matrix); - AssertEqual (matrix, "CreateFromColumns", + var expected = new SCNMatrix4 ( 11, 21, 31, 41, 12, 22, 32, 42, 13, 23, 33, 43, 14, 24, 34, 44); + Asserts.AreEqual (expected, matrix, "CreateFromColumns"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#endif } [Test] public void CreateFromAxisAngle_pfloat_Out () { SCNMatrix4.CreateFromAxisAngle (new SCNVector3 (2, 2, 2), (pfloat) (Math.PI / 3), out var matrix); - AssertEqual (matrix, "CreateFromAxisAngle", + var expected = new SCNMatrix4 ( + TwoThirds, -OneThird, TwoThirds, 0, TwoThirds, TwoThirds, -OneThird, 0, -OneThird, TwoThirds, TwoThirds, 0, - TwoThirds, -OneThird, TwoThirds, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateFromAxisAngle"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 10, 30), transformed, (pfloat) 0.00001, "Transformed"); } [Test] public void CreateFromAxisAngle_float_Out () { SCNMatrix4.CreateFromAxisAngle (new Vector3 (2, 2, 2), (float) (Math.PI / 3), out var matrix); - AssertEqual (matrix, "CreateFromAxisAngle", + var expected = new SCNMatrix4 ( + TwoThirds, -OneThird, TwoThirds, 0, TwoThirds, TwoThirds, -OneThird, 0, -OneThird, TwoThirds, TwoThirds, 0, - TwoThirds, -OneThird, TwoThirds, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateFromAxisAngle"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 10, 30), transformed, (pfloat) 0.00001, "Transformed"); } [Test] public void CreateFromAxisAngle_double_Out () { SCNMatrix4.CreateFromAxisAngle (new Vector3d (2, 2, 2), (double) (Math.PI / 3), out var matrix); - AssertEqual (matrix, "CreateFromAxisAngle", + var expected = new SCNMatrix4 ( + TwoThirds, -OneThird, TwoThirds, 0, TwoThirds, TwoThirds, -OneThird, 0, -OneThird, TwoThirds, TwoThirds, 0, - TwoThirds, -OneThird, TwoThirds, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateFromAxisAngle"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 10, 30), transformed, (pfloat) 0.000001, "Transformed"); } [Test] public void CreateFromAxisAngle () { var matrix = SCNMatrix4.CreateFromAxisAngle (new SCNVector3 (2, 2, 2), (pfloat) (Math.PI / 3)); - AssertEqual (matrix, "CreateFromAxisAngle", + var expected = new SCNMatrix4 ( + TwoThirds, -OneThird, TwoThirds, 0, TwoThirds, TwoThirds, -OneThird, 0, -OneThird, TwoThirds, TwoThirds, 0, - TwoThirds, -OneThird, TwoThirds, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateFromAxisAngle"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 10, 30), transformed, 0.00001f, "Transformed"); } [Test] public void CreateRotationX_Out () { - SCNMatrix4.CreateRotationX ((pfloat) (Math.PI / 3), out var matrix); - AssertEqual (matrix, "CreateRotationX", + var angle = (pfloat) (Math.PI / 3); + SCNMatrix4.CreateRotationX (angle, out var matrix); + var expected = new SCNMatrix4 ( 1, 0, 0, 0, - 0, OhPointFive, SqrtThreeHalved, 0, - 0, -SqrtThreeHalved, OhPointFive, 0, + 0, OhPointFive, -SqrtThreeHalved, 0, + 0, SqrtThreeHalved, OhPointFive, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateRotationX"); + + Asserts.AreEqual (SCNMatrix4MakeRotation (angle, 1, 0, 0), matrix, 0.00001f, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (10, -15.980762f, 32.320508f), transformed, 0.0001f, "Transformed"); } [Test] public void CreateRotationX () { - var matrix = SCNMatrix4.CreateRotationX ((pfloat) (Math.PI / 3)); - AssertEqual (matrix, "CreateRotationX", + var angle = (pfloat) (Math.PI / 3); + var matrix = SCNMatrix4.CreateRotationX (angle); + var expected = new SCNMatrix4 ( 1, 0, 0, 0, - 0, OhPointFive, SqrtThreeHalved, 0, - 0, -SqrtThreeHalved, OhPointFive, 0, + 0, OhPointFive, -SqrtThreeHalved, 0, + 0, SqrtThreeHalved, OhPointFive, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateRotationX"); + + Asserts.AreEqual (SCNMatrix4MakeRotation (angle, 1, 0, 0), matrix, 0.00001f, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (10, -15.980762f, 32.320508f), transformed, 0.0001f, "Transformed"); } [Test] public void CreateRotationY_Out () { + var angle = (pfloat) (Math.PI / 3); SCNMatrix4.CreateRotationY ((pfloat) (Math.PI / 3), out var matrix); - AssertEqual (matrix, "CreateRotationY", - OhPointFive, 0, -SqrtThreeHalved, 0, + var expected = new SCNMatrix4 ( + OhPointFive, 0, SqrtThreeHalved, 0, 0, 1, 0, 0, - SqrtThreeHalved, 0, OhPointFive, 0, + -SqrtThreeHalved, 0, OhPointFive, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateRotationY"); + + Asserts.AreEqual (SCNMatrix4MakeRotation (angle, 0, 1, 0), matrix, 0.00001f, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (30.98076f, 20, 6.33974f), transformed, 0.0001f, "Transformed"); } [Test] public void CreateRotationY () { - var matrix = SCNMatrix4.CreateRotationY ((pfloat) (Math.PI / 3)); - AssertEqual (matrix, "CreateRotationY", - OhPointFive, 0, -SqrtThreeHalved, 0, + var angle = (pfloat) (Math.PI / 3); + var matrix = SCNMatrix4.CreateRotationY (angle); + var expected = new SCNMatrix4 ( + OhPointFive, 0, SqrtThreeHalved, 0, 0, 1, 0, 0, - SqrtThreeHalved, 0, OhPointFive, 0, + -SqrtThreeHalved, 0, OhPointFive, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateRotationY"); + + Asserts.AreEqual (SCNMatrix4MakeRotation (angle, 0, 1, 0), matrix, 0.00001f, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (30.98076f, 20, 6.33974f), transformed, 0.00001f, "Transformed"); } [Test] public void CreateRotationZ_Out () { - SCNMatrix4.CreateRotationZ ((pfloat) (Math.PI / 3), out var matrix); - AssertEqual (matrix, "CreateRotationZ", - OhPointFive, SqrtThreeHalved, 0, 0, - -SqrtThreeHalved, OhPointFive, 0, 0, + var angle = (pfloat) (Math.PI / 3); + SCNMatrix4.CreateRotationZ (angle, out var matrix); + var expected = new SCNMatrix4 ( + OhPointFive, -SqrtThreeHalved, 0, 0, + SqrtThreeHalved, OhPointFive, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateRotationZ"); + + Asserts.AreEqual (SCNMatrix4MakeRotation (angle, 0, 0, 1), matrix, 0.00001f, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (-12.320508f, 18.66025f, 30), transformed, 0.00001f, "Transformed"); } [Test] public void CreateRotationZ () { - var matrix = SCNMatrix4.CreateRotationZ ((pfloat) (Math.PI / 3)); - AssertEqual (matrix, "CreateRotationZ", - OhPointFive, SqrtThreeHalved, 0, 0, - -SqrtThreeHalved, OhPointFive, 0, 0, + var angle = (pfloat) (Math.PI / 3); + var matrix = SCNMatrix4.CreateRotationZ (angle); + var expected = new SCNMatrix4 ( + OhPointFive, -SqrtThreeHalved, 0, 0, + SqrtThreeHalved, OhPointFive, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreateRotationZ"); + + Asserts.AreEqual (SCNMatrix4MakeRotation (angle, 0, 0, 1), matrix, 0.00001f, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (-12.320508f, 18.66025f, 30), transformed, 0.00001f, "Transformed"); } [Test] public void CreateTranslation_Out () { SCNMatrix4.CreateTranslation (1, 2, 3, out var matrix); - AssertEqual (matrix, "CreateTranslation", - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 2, 3, 1); + var expected = new SCNMatrix4 ( + 1, 0, 0, 1, + 0, 1, 0, 2, + 0, 0, 1, 3, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreateTranslation"); + + Asserts.AreEqual (SCNMatrix4MakeTranslation (new SCNVector3 (1, 2, 3)), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (11, 22, 33), transformed, "Transformed"); } [Test] @@ -433,22 +567,42 @@ public void CreateTranslation_Vector_Out () { var translation = new SCNVector3 (1, 2, 3); SCNMatrix4.CreateTranslation (ref translation, out var matrix); - AssertEqual (matrix, "CreateTranslation", - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 2, 3, 1); + var expected = new SCNMatrix4 ( + 1, 0, 0, 1, + 0, 1, 0, 2, + 0, 0, 1, 3, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreateTranslation"); + + Asserts.AreEqual (SCNMatrix4MakeTranslation (new SCNVector3 (1, 2, 3)), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (11, 22, 33), transformed, "Transformed"); } [Test] public void CreateTranslation () { var matrix = SCNMatrix4.CreateTranslation (1, 2, 3); - AssertEqual (matrix, "CreateTranslation", - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 2, 3, 1); + var expected = new SCNMatrix4 ( + 1, 0, 0, 1, + 0, 1, 0, 2, + 0, 0, 1, 3, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreateTranslation"); + + Asserts.AreEqual (SCNMatrix4MakeTranslation (new SCNVector3 (1, 2, 3)), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (11, 22, 33), transformed, "Transformed"); } [Test] @@ -456,132 +610,227 @@ public void CreateTranslation_Vector () { var translation = new SCNVector3 (1, 2, 3); var matrix = SCNMatrix4.CreateTranslation (translation); - AssertEqual (matrix, "CreateTranslation", - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 2, 3, 1); + var expected = new SCNMatrix4 ( + 1, 0, 0, 1, + 0, 1, 0, 2, + 0, 0, 1, 3, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreateTranslation"); + + Asserts.AreEqual (SCNMatrix4MakeTranslation (translation), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (11, 22, 33), transformed, "Transformed"); } [Test] public void CreateOrthographic_Out () { SCNMatrix4.CreateOrthographic (1, 2, 3, 4, out var matrix); - AssertEqual (matrix, "CreateOrthographic", + var expected = new SCNMatrix4 ( 2, 0, 0, 0, 0, 1, 0, 0, - 0, 0, -2, 0, - 0, 0, -7, 1); + 0, 0, -2, -7, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, 0, "CreateOrthographic"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 20, -67), transformed, 0.00001f, "Transformed"); } [Test] public void CreateOrthographic () { var matrix = SCNMatrix4.CreateOrthographic (1, 2, 3, 4); - AssertEqual (matrix, "CreateOrthographic", + var expected = new SCNMatrix4 ( 2, 0, 0, 0, 0, 1, 0, 0, - 0, 0, -2, 0, - 0, 0, -7, 1); + 0, 0, -2, -7, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, 0, "CreateOrthographic"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 20, -67), transformed, 0.00001f, "Transformed"); } [Test] public void CreateOrthographicOffCenter_Out () { SCNMatrix4.CreateOrthographicOffCenter (1, 2, 3, 4, 5, 6, out var matrix); - AssertEqual (matrix, "CreateOrthographicOffCenter", - 2, 0, 0, 0, - 0, 2, 0, 0, - 0, 0, -2, 0, - -3, -7, -11, 1); + var expected = new SCNMatrix4 ( + 2, 0, 0, -3, + 0, 2, 0, -7, + 0, 0, -2, -11, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreateOrthographicOffCenter"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (17, 33, -71), transformed, 0.00001f, "Transformed"); } [Test] public void CreateOrthographicOffCenter () { var matrix = SCNMatrix4.CreateOrthographicOffCenter (1, 2, 3, 4, 5, 6); - AssertEqual (matrix, "CreateOrthographicOffCenter", - 2, 0, 0, 0, - 0, 2, 0, 0, - 0, 0, -2, 0, - -3, -7, -11, 1); + var expected = new SCNMatrix4 ( + 2, 0, 0, -3, + 0, 2, 0, -7, + 0, 0, -2, -11, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreateOrthographicOffCenter"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (17, 33, -71), transformed, 0.00001f, "Transformed"); } [Test] public void CreatePerspectiveFieldOfView_Out () { SCNMatrix4.CreatePerspectiveFieldOfView ((pfloat) (Math.PI / 3), 2, 3, 4, out var matrix); - AssertEqual (matrix, "CreatePerspectiveFieldOfView", + var expected = new SCNMatrix4 ( SqrtThreeHalved, 0, 0, 0, 0, SqrtThree, 0, 0, - 0, 0, -7, -1, - 0, 0, -24, 0); + 0, 0, -7, -24, + 0, 0, -1, 0); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreatePerspectiveFieldOfView"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (8.660254f, 34.641016f, -234), transformed, 0.00001f, "Transformed"); } [Test] public void CreatePerspectiveFieldOfView () { var matrix = SCNMatrix4.CreatePerspectiveFieldOfView ((pfloat) (Math.PI / 3), 2, 3, 4); - AssertEqual (matrix, "CreatePerspectiveFieldOfView", + var expected = new SCNMatrix4 ( SqrtThreeHalved, 0, 0, 0, 0, SqrtThree, 0, 0, - 0, 0, -7, -1, - 0, 0, -24, 0); + 0, 0, -7, -24, + 0, 0, -1, 0); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.000001f, "CreatePerspectiveFieldOfView"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (8.660254f, 34.641016f, -234), transformed, 0.00001f, "Transformed"); } [Test] public void CreatePerspectiveOffCenter_Out () { SCNMatrix4.CreatePerspectiveOffCenter (1, 2, 3, 4, 5, 6, out var matrix); - AssertEqual (matrix, "CreatePerspectiveOffCenter", - 10, 0, 0, 0, - 0, 10, 0, 0, - 3, 7, -11, -1, - 0, 0, -60, 0); + var expected = new SCNMatrix4 ( + 10, 0, 3, 0, + 0, 10, 7, 0, + 0, 0, -11, -60, + 0, 0, -1, 0); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreatePerspectiveOffCenter"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (190, 410, -390), transformed, "Transformed"); } [Test] public void CreatePerspectiveOffCenter () { var matrix = SCNMatrix4.CreatePerspectiveOffCenter (1, 2, 3, 4, 5, 6); - AssertEqual (matrix, "CreatePerspectiveOffCenter", - 10, 0, 0, 0, - 0, 10, 0, 0, - 3, 7, -11, -1, - 0, 0, -60, 0); + var expected = new SCNMatrix4 ( + 10, 0, 3, 0, + 0, 10, 7, 0, + 0, 0, -11, -60, + 0, 0, -1, 0); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, "CreatePerspectiveOffCenter"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (190, 410, -390), transformed, "Transformed"); } [Test] public void Scale () { var matrix = SCNMatrix4.Scale (2); - AssertEqual (matrix, "CreateScale", + var expected = new SCNMatrix4 ( 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1); + Asserts.AreEqual (expected, matrix, "CreateScale"); + + Asserts.AreEqual (SCNMatrix4MakeScale (new SCNVector3 (2, 2, 2)), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (20, 40, 60), transformed, "Transformed"); } [Test] public void Scale_Vector () { var matrix = SCNMatrix4.Scale (new SCNVector3 (1, 2, 3)); - AssertEqual (matrix, "CreateScale", + var expected = new SCNMatrix4 ( 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1); + Asserts.AreEqual (expected, matrix, "CreateScale"); + + Asserts.AreEqual (SCNMatrix4MakeScale (new SCNVector3 (1, 2, 3)), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (10, 40, 90), transformed, "Transformed"); } [Test] public void Scale_3 () { var matrix = SCNMatrix4.Scale (1, 2, 3); - AssertEqual (matrix, "CreateScale", + var expected = new SCNMatrix4 ( 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1); + Asserts.AreEqual (expected, matrix, "CreateScale"); + + Asserts.AreEqual (SCNMatrix4MakeScale (new SCNVector3 (1, 2, 3)), matrix, "Native"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (10, 40, 90), transformed, "Transformed"); } [Test] @@ -589,11 +838,19 @@ public void Rotate () { var quaternion = new Quaternion (1, 2, 3, 4); var matrix = SCNMatrix4.Rotate (quaternion); - AssertEqual (matrix, "Rotate", - TwoFifteenths, 7 * TwoFifteenths, -OneThird, 0, - -TwoThirds, OneThird, TwoThirds, 0, - 11 * OneFifteenth, TwoFifteenths, TwoThirds, 0, + var expected = new SCNMatrix4 ( + TwoFifteenths, -TwoThirds, 11 * OneFifteenth, 0, + 7 * TwoFifteenths, OneThird, TwoFifteenths, 0, + -OneThird, TwoThirds, TwoThirds, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.00001, "Rotate"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (pos, transformed, 0.0001f, "Transformed"); } [Test] @@ -601,33 +858,57 @@ public void Rotate_d () { var quaternion = new Quaterniond (1, 2, 3, 4); var matrix = SCNMatrix4.Rotate (quaternion); - AssertEqual (matrix, "Rotate", - TwoFifteenths, 7 * TwoFifteenths, -OneThird, 0, - -TwoThirds, OneThird, TwoThirds, 0, - 11 * OneFifteenth, TwoFifteenths, TwoThirds, 0, + var expected = new SCNMatrix4 ( + TwoFifteenths, -TwoThirds, 11 * OneFifteenth, 0, + 7 * TwoFifteenths, OneThird, TwoFifteenths, 0, + -OneThird, TwoThirds, TwoThirds, 0, 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.00001, "Rotate"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (pos, transformed, 0.0001f, "Transformed"); } [Test] public void LookAt_Vectors () { var matrix = SCNMatrix4.LookAt (new SCNVector3 (1, 2, 3), new SCNVector3 (4, 5, 6), new SCNVector3 (7, 8, 9)); - AssertEqual (matrix, "LookAt", - SqrtSixInverted, -SqrtTwoHalved, -SqrtThreeInverted, 0, - -2 * SqrtSixInverted, 0, -SqrtThreeInverted, 0, - SqrtSixInverted, SqrtTwoHalved, -SqrtThreeInverted, 0, - 0, -SqrtTwo, SqrtTwelve, 1); + var expected = new SCNMatrix4 ( + SqrtSixInverted, -2 * SqrtSixInverted, SqrtSixInverted, 0, + -SqrtTwoHalved, 0, SqrtTwoHalved, -SqrtTwo, + -SqrtThreeInverted, -SqrtThreeInverted, -SqrtThreeInverted, SqrtTwelve, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.00001, "LookAt"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (0, 12.7279220f, -31.1769145f), transformed, 0.00001f, "Transformed"); } [Test] public void LookAt_Elements () { var matrix = SCNMatrix4.LookAt (1, 2, 3, 4, 5, 6, 7, 8, 9); - AssertEqual (matrix, "LookAt", - SqrtSixInverted, -SqrtTwoHalved, -SqrtThreeInverted, 0, - -2 * SqrtSixInverted, 0, -SqrtThreeInverted, 0, - SqrtSixInverted, SqrtTwoHalved, -SqrtThreeInverted, 0, - 0, -SqrtTwo, SqrtTwelve, 1); + var expected = new SCNMatrix4 ( + SqrtSixInverted, -2 * SqrtSixInverted, SqrtSixInverted, 0, + -SqrtTwoHalved, 0, SqrtTwoHalved, -SqrtTwo, + -SqrtThreeInverted, -SqrtThreeInverted, -SqrtThreeInverted, SqrtTwelve, + 0, 0, 0, 1); +#if !NET + expected.Transpose (); +#endif + Asserts.AreEqual (expected, matrix, (pfloat) 0.00001, "LookAt"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (0, 12.7279220f, -31.1769145f), transformed, 0.00001f, "Transformed"); } [Test] @@ -644,11 +925,29 @@ public void Mult () new SCNVector4 (931, 932, 933, 934), new SCNVector4 (941, 942, 943, 944)); var matrix = SCNMatrix4.Mult (a, b); - AssertEqual (matrix, "Mult", + + Asserts.AreEqual (SCNMatrix4Mult (a, b), matrix, "Native"); + var expected = new SCNMatrix4 ( +#if NET + 94950, 98600, 102250, 105900, + 95990, 99680, 103370, 107060, + 97030, 100760, 104490, 108220, + 98070, 101840, 105610, 109380); +#else 46350, 46400, 46450, 46500, 83390, 83480, 83570, 83660, 120430, 120560, 120690, 120820, 157470, 157640, 157810, 157980); +#endif + Asserts.AreEqual (expected, matrix, "Mult"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (6094900, 6161660, 6228420), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (5901670, 5908040, 5914410), transformed, "Transformed"); +#endif } [Test] @@ -665,11 +964,29 @@ public void Mult_ByRef () new SCNVector4 (931, 932, 933, 934), new SCNVector4 (941, 942, 943, 944)); SCNMatrix4.Mult (ref a, ref b, out var matrix); - AssertEqual (matrix, "Mult", + + Asserts.AreEqual (SCNMatrix4Mult (a, b), matrix, "Native"); + var expected = new SCNMatrix4 ( +#if NET + 94950, 98600, 102250, 105900, + 95990, 99680, 103370, 107060, + 97030, 100760, 104490, 108220, + 98070, 101840, 105610, 109380); +#else 46350, 46400, 46450, 46500, 83390, 83480, 83570, 83660, 120430, 120560, 120690, 120820, 157470, 157640, 157810, 157980); +#endif + Asserts.AreEqual (expected, matrix, "Mult"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (6094900, 6161660, 6228420), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (5901670, 5908040, 5914410), transformed, "Transformed"); +#endif } #if NET // The legacy Invert implementation seems very wrong, so only verify .NET behavior @@ -692,11 +1009,16 @@ public void Static_Invert () var matrix = SCNMatrix4.Invert (a); - AssertEqual (matrix, "Invert", + var expected = new SCNMatrix4 ( (pfloat) (-0.6181818181818182), (pfloat) (0.3151515151515151), (pfloat) (-0.030303030303030304), (pfloat) (0.3878787878787879), (pfloat) (1.6363636363636365), (pfloat) (-0.696969696969697), (pfloat) (0.3939393939393939), (pfloat) (-1.2424242424242424), (pfloat) (-1.3818181818181818), (pfloat) (0.3515151515151515), (pfloat) (-0.30303030303030304), (pfloat) (1.2787878787878788), (pfloat) (0.6363636363636364), (pfloat) (-0.030303030303030304), (pfloat) (0.06060606060606061), (pfloat) (-0.5757575757575758)); + Asserts.AreEqual (expected, matrix, (pfloat) 0.00001f, "Invert"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (-0.4f, 13, -14.6f), transformed, 0.00001f, "Transformed"); } #endif @@ -709,11 +1031,20 @@ public void Static_Transpose () new SCNVector4 (31, 32, 33, 34), new SCNVector4 (41, 42, 43, 44)); var matrix = SCNMatrix4.Transpose (a); - AssertEqual (matrix, "Transpose", + var expected = new SCNMatrix4 ( 11, 21, 31, 41, 12, 22, 32, 42, 13, 23, 33, 43, 14, 24, 34, 44); + Asserts.AreEqual (expected, matrix, "Transpose"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#endif } [Test] @@ -725,11 +1056,20 @@ public void Static_Transpose_ByRef () new SCNVector4 (31, 32, 33, 34), new SCNVector4 (41, 42, 43, 44)); SCNMatrix4.Transpose (ref a, out var matrix); - AssertEqual (matrix, "Transpose", + var expected = new SCNMatrix4 ( 11, 21, 31, 41, 12, 22, 32, 42, 13, 23, 33, 43, 14, 24, 34, 44); + Asserts.AreEqual (expected, matrix, "Transpose"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (1501, 1562, 1623), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); +#endif } [Test] @@ -746,11 +1086,28 @@ public void Operator_Multiply () new SCNVector4 (931, 932, 933, 934), new SCNVector4 (941, 942, 943, 944)); var matrix = a * b; - AssertEqual (matrix, "*", + Asserts.AreEqual (SCNMatrix4Mult (a, b), matrix, "Native"); + var expected = new SCNMatrix4 ( +#if NET + 94950, 98600, 102250, 105900, + 95990, 99680, 103370, 107060, + 97030, 100760, 104490, 108220, + 98070, 101840, 105610, 109380); +#else 46350, 46400, 46450, 46500, 83390, 83480, 83570, 83660, 120430, 120560, 120690, 120820, 157470, 157640, 157810, 157980); +#endif + Asserts.AreEqual (expected, matrix, "*"); + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); +#if NET + Asserts.AreEqual (new SCNVector3 (6094900, 6161660, 6228420), transformed, "Transformed"); +#else + Asserts.AreEqual (new SCNVector3 (5901670, 5908040, 5914410), transformed, "Transformed"); +#endif } [Test] @@ -827,6 +1184,151 @@ public void IEquatable_Equals () new SCNVector4 (941, 942, 943, 944)); Assert.IsFalse (((IEquatable) a).Equals (b), "object.Equals"); } + + [Test] + public void CreateRotationX_NodeComparison () + { + // Create a test node (it defaults to position 0,0,0) + var node = SCNNode.Create (); + // Create a translation matrix + var angle = (pfloat) (Math.PI / 2); + // Use that matrix to transform the node + node.Transform = SCNMatrix4.CreateRotationX (angle); + Asserts.AreEqual (new SCNVector3 (angle, 0, 0), node.EulerAngles, 0.000001f, "EulerAngles"); + Asserts.AreEqual (new SCNQuaternion (SqrtTwoHalved, 0, 0, SqrtTwoHalved), node.Orientation, 0.000001f, "Orientation"); + Asserts.AreEqual (new SCNVector3 (0, 0, 0), node.Position, "Position"); + Asserts.AreEqual (new SCNVector4 (1, 0, 0, angle), node.Rotation, 0.000001f, "Rotation"); + Asserts.AreEqual (new SCNVector3 (1, 1, 1), node.Scale, "Scale"); + } + + [Test] + public void CreateTranslationAndTransformPosition () + { + // Create test point + var point = new SCNVector3 (1, 2, 3); + // Create translation + var matrix = SCNMatrix4.CreateTranslation (10, 0, 0); + // Transform the point + var newPoint = SCNVector3.TransformPosition (point, matrix); + Asserts.AreEqual (new SCNVector3 (11, 2, 3), newPoint, "A"); + } + + [Test] + public void TranslationPosition_ret () + { + // Create test point + var point = new SCNVector3 (1, 2, 3); + // Create translation + var matrix = + SCNMatrix4.CreateTranslation (-1, 0, 0) * + SCNMatrix4.Scale (10, 1, 1); + // Transform the point + var newPoint = SCNVector3.TransformPosition (point, matrix); + Asserts.AreEqual (new SCNVector3 (0, 2, 3), newPoint, "A"); + } + + [Test] + public void TranslationPosition_out () + { + // Create test point + var point = new SCNVector3 (1, 2, 3); + // Create translation + var matrix = + SCNMatrix4.CreateTranslation (-1, 0, 0) * + SCNMatrix4.Scale (10, 1, 1); + // Transform the point + SCNVector3.TransformPosition (ref point, ref matrix, out var newPoint); + Asserts.AreEqual (new SCNVector3 (0, 2, 3), newPoint, "A"); + } + + [Test] + public void CreateTranslations_ret_floats () + { + // Create a test node (it defaults to position 0,0,0) + var node = SCNNode.Create (); + // Create a translation matrix + var matrix = SCNMatrix4.CreateTranslation (1, 2, 3); + // Use that matrix to transform the node + node.Transform = matrix; + // Ask the node to extract just the translation part of the matrix + var newPoint = node.Position; + // Verify that it is now positioned at (1,2,3) + Asserts.AreEqual (new SCNVector3 (1, 2, 3), newPoint, "A"); + } + + [Test] + public void CreateTranslations_ret_SCNVector3 () + { + // Create a test node (it defaults to position 0,0,0) + var node = SCNNode.Create (); + // Create a translation matrix + var matrix = SCNMatrix4.CreateTranslation (new SCNVector3 (1, 2, 3)); + // Use that matrix to transform the node + node.Transform = matrix; + // Ask the node to extract just the translation part of the matrix + var newPoint = node.Position; + // Verify that it is now positioned at (1,2,3) + Asserts.AreEqual (new SCNVector3 (1, 2, 3), newPoint, "A"); + } + + [Test] + public void CreateTranslations_out_floats () + { + // Create a test node (it defaults to position 0,0,0) + var node = SCNNode.Create (); + // Create a translation matrix + SCNMatrix4.CreateTranslation (1, 2, 3, out var matrix); + // Use that matrix to transform the node + node.Transform = matrix; + // Ask the node to extract just the translation part of the matrix + var newPoint = node.Position; + // Verify that it is now positioned at (1,2,3) + Asserts.AreEqual (new SCNVector3 (1, 2, 3), newPoint, "A"); + } + + [Test] + public void CreateTranslations_out_SCNVector3 () + { + // Create a test node (it defaults to position 0,0,0) + var node = SCNNode.Create (); + // Create a translation matrix + var vector = new SCNVector3 (1, 2, 3); + SCNMatrix4.CreateTranslation (ref vector, out var matrix); + // Use that matrix to transform the node + node.Transform = matrix; + // Ask the node to extract just the translation part of the matrix + var newPoint = node.Position; + // Verify that it is now positioned at (1,2,3) + Asserts.AreEqual (new SCNVector3 (1, 2, 3), newPoint, "A"); + } + + [Test] + public void SCNMatrix4Translate () + { + var translationVector = new SCNVector3 (1, 2, 3); + var managedTranslation = SCNMatrix4.CreateTranslation (translationVector); + var nativeTranslation = SCNMatrix4MakeTranslation (translationVector); + Asserts.AreEqual (nativeTranslation, managedTranslation, "A"); + } + + static SCNMatrix4 SCNMatrix4MakeTranslation (SCNVector3 v) + { + return global::Bindings.Test.CFunctions.x_SCNMatrix4MakeTranslation (v.X, v.Y, v.Z); + } + + static SCNMatrix4 SCNMatrix4MakeScale (SCNVector3 v) + { + return global::Bindings.Test.CFunctions.x_SCNMatrix4MakeScale (v.X, v.Y, v.Z); + } + + [DllImport (global::ObjCRuntime.Constants.SceneKitLibrary)] + static extern SCNMatrix4 SCNMatrix4MakeRotation (pfloat angle, pfloat x, pfloat y, pfloat z); + + [DllImport (global::ObjCRuntime.Constants.SceneKitLibrary)] + static extern SCNMatrix4 SCNMatrix4Mult (SCNMatrix4 a, SCNMatrix4 b); + + [DllImport (global::ObjCRuntime.Constants.SceneKitLibrary)] + static extern SCNMatrix4 SCNMatrix4Invert (SCNMatrix4 a); } } #endif // !__WATCHOS__ diff --git a/tests/monotouch-test/SceneKit/SCNVector3Test.cs b/tests/monotouch-test/SceneKit/SCNVector3Test.cs new file mode 100644 index 000000000000..381055025ff3 --- /dev/null +++ b/tests/monotouch-test/SceneKit/SCNVector3Test.cs @@ -0,0 +1,247 @@ +// +// Unit tests for SCNMatrix4 +// +// Authors: +// Sebastien Pouliot +// +// Copyright 2014 Xamarin Inc. All rights reserved. +// + +#if HAS_SCENEKIT + +#nullable enable + +using System; +using Foundation; +using SceneKit; + +using NUnit.Framework; + +#if __MACOS__ +#if NET +using pfloat = System.Runtime.InteropServices.NFloat; +#else +using pfloat = System.nfloat; +#endif +#else +using pfloat = System.Single; +#endif + +namespace MonoTouchFixtures.SceneKit { + + [TestFixture] + [Preserve (AllMembers = true)] + public class SCNVector3Test + { + static pfloat delta = (pfloat) 0.000001; + + [Test] + public void TransformVector() + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformVector (pos, matrix); + Asserts.AreEqual (new SCNVector3 (740, 1340, 1940), transformed, "Transformed"); + } + + [Test] + public void TransformVector_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + SCNVector3.TransformVector (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector3 (740, 1340, 1940), transformed, "Transformed"); + } + + [Test] + public void TransformNormal () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, -22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, -44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + pos.Normalize (); + var transformed = SCNVector3.TransformNormal (pos, matrix); + Asserts.AreEqual (new SCNVector3 ((pfloat) 0.406966, 0, (pfloat) (-0.151853)), transformed, delta, "Transformed"); + } + + [Test] + public void TransformNormal_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, -22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, -44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + pos.Normalize (); + SCNVector3.TransformNormal (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector3 ((pfloat) 0.406966, 0, (pfloat) (-0.151853)), transformed, delta, "Transformed"); + } + + [Test] + public void TransformNormalInverse () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + pos.Normalize (); + var transformed = SCNVector3.TransformNormalInverse (pos, matrix); + Asserts.AreEqual (new SCNVector3 ((pfloat) 39.0201413, (pfloat) 40.62370877, (pfloat) 42.2272762), transformed, delta, "Transformed"); + } + + [Test] + public void TransformNormalInverse_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + pos.Normalize (); + SCNVector3.TransformNormalInverse (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector3 ((pfloat) 39.0201413, (pfloat) 40.62370877, (pfloat) 42.2272762), transformed, delta, "Transformed"); + } + + [Test] + public void TransformPosition () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPosition (pos, matrix); + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); + } + + [Test] + public void TransformPosition_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + SCNVector3.TransformPosition (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector3 (754, 1364, 1974), transformed, "Transformed"); + } + + [Test] + public void Transform () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.Transform (pos, matrix); + Asserts.AreEqual (new SCNVector4 (754, 1364, 1974, 2584), transformed, "Transformed"); + } + + [Test] + public void Transform_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + SCNVector3.Transform (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector4 (754, 1364, 1974, 2584), transformed, "Transformed"); + } + + [Test] + public void TransformPerspective () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + var transformed = SCNVector3.TransformPerspective (pos, matrix); + Asserts.AreEqual (new SCNVector3 ((pfloat) 0.291795, (pfloat) 0.5278637, (pfloat) 0.76393188), transformed, delta, "Transformed"); + } + + [Test] + public void TransformPerspective_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector3 (10, 20, 30); + SCNVector3.TransformPerspective (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector3 ((pfloat) 0.291795, (pfloat) 0.5278637, (pfloat) 0.76393188), transformed, delta, "Transformed"); + } + } +} +#endif // HAS_SCENEKIT diff --git a/tests/monotouch-test/SceneKit/SCNVector4Test.cs b/tests/monotouch-test/SceneKit/SCNVector4Test.cs new file mode 100644 index 000000000000..8029638d5dda --- /dev/null +++ b/tests/monotouch-test/SceneKit/SCNVector4Test.cs @@ -0,0 +1,73 @@ +// +// Unit tests for SCNMatrix4 +// +// Authors: +// Sebastien Pouliot +// +// Copyright 2014 Xamarin Inc. All rights reserved. +// + +#if HAS_SCENEKIT + +#nullable enable + +using System; +using Foundation; +using SceneKit; + +using NUnit.Framework; + +#if __MACOS__ +#if NET +using pfloat = System.Runtime.InteropServices.NFloat; +#else +using pfloat = System.nfloat; +#endif +#else +using pfloat = System.Single; +#endif + +namespace MonoTouchFixtures.SceneKit { + + [TestFixture] + [Preserve (AllMembers = true)] + public class SCNVector4Test + { + [Test] + public void Transform () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector4 (10, 20, 30, 40); + var transformed = SCNVector4.Transform (pos, matrix); + Asserts.AreEqual (new SCNVector4 (1300, 2300, 3300, 4300), transformed, "Transformed"); + } + + [Test] + public void Transform_out () + { + var matrix = new SCNMatrix4 ( + 11, 12, 13, 14, + 21, 22, 23, 24, + 31, 32, 33, 34, + 41, 42, 43, 44); +#if !NET + matrix.Transpose (); +#endif + + var pos = new SCNVector4 (10, 20, 30, 40); + SCNVector4.Transform (ref pos, ref matrix, out var transformed); + Asserts.AreEqual (new SCNVector4 (1300, 2300, 3300, 4300), transformed, "Transformed"); + } + + } +} + +#endif // HAS_SCENEKIT diff --git a/tests/monotouch-test/monotouch-test.csproj b/tests/monotouch-test/monotouch-test.csproj index 8160ca4c9731..19e77aa2b97f 100644 --- a/tests/monotouch-test/monotouch-test.csproj +++ b/tests/monotouch-test/monotouch-test.csproj @@ -223,6 +223,8 @@ ApplePlatform.cs + + diff --git a/tests/test-libraries/libtest.h b/tests/test-libraries/libtest.h index 311327e0e4df..c0e7211e73e5 100644 --- a/tests/test-libraries/libtest.h +++ b/tests/test-libraries/libtest.h @@ -8,6 +8,8 @@ #import #endif +#import + #ifdef __cplusplus extern "C" { #endif @@ -29,6 +31,16 @@ void x_mdltransform_create_global_transform (MDLObject *object, NSTimeInterval t void x_mdltransform_get_rotation_matrix (MDLTransform *self, NSTimeInterval time, float* r0c0, float* r0c1, float* r0c2, float* r0c3, float* r1c0, float* r1c1, float* r1c2, float* r1c3, float* r2c0, float* r2c1, float* r2c2, float* r2c3, float* r3c0, float* r3c1, float* r3c2, float* r3c3); #endif +#if TARGET_OS_OSX +#define pfloat CGFloat +#else +#define pfloat float +#endif + +SCNMatrix4 x_SCNMatrix4MakeTranslation (pfloat tx, pfloat ty, pfloat tz); +SCNMatrix4 x_SCNMatrix4MakeScale (pfloat tx, pfloat ty, pfloat tz); +SCNMatrix4 x_SCNMatrix4Translate (SCNMatrix4 m, pfloat tx, pfloat ty, pfloat tz); + /* * Various structs used in ObjCRegistrarTest */ diff --git a/tests/test-libraries/libtest.m b/tests/test-libraries/libtest.m index 8b980bc0316b..689a950e0995 100644 --- a/tests/test-libraries/libtest.m +++ b/tests/test-libraries/libtest.m @@ -250,6 +250,24 @@ void x_call_block (x_block_callback block) } #endif // !TARGET_OS_WATCH +SCNMatrix4 +x_SCNMatrix4MakeTranslation (pfloat tx, pfloat ty, pfloat tz) +{ + return SCNMatrix4MakeTranslation (tx, ty, tz); +} + +SCNMatrix4 +x_SCNMatrix4MakeScale (pfloat tx, pfloat ty, pfloat tz) +{ + return SCNMatrix4MakeScale (tx, ty, tz); +} + +SCNMatrix4 +x_SCNMatrix4Translate (SCNMatrix4 m, pfloat tx, pfloat ty, pfloat tz) +{ + return SCNMatrix4Translate (m, tx, ty, tz); +} + @interface UltimateMachine : NSObject { } diff --git a/tests/test-libraries/rename.h b/tests/test-libraries/rename.h index 86146b801cf2..9f07f7da0eea 100644 --- a/tests/test-libraries/rename.h +++ b/tests/test-libraries/rename.h @@ -68,6 +68,9 @@ #define x_get_matrix_float3x3 object_x_get_matrix_float3x3 #define x_get_matrix_float2x2 object_x_get_matrix_float2x2 #define x_call_block object_x_call_block + #define x_SCNMatrix4MakeTranslation object_x_SCNMatrix4MakeTranslation + #define x_SCNMatrix4MakeScale object_x_SCNMatrix4MakeScale + #define x_SCNMatrix4Translate object_x_SCNMatrix4Translate #elif PREFIX == 2 #define theUltimateAnswer ar_theUltimateAnswer #define useZLib ar_useZLib @@ -137,6 +140,9 @@ #define x_get_matrix_float3x3 ar_x_get_matrix_float3x3 #define x_get_matrix_float2x2 ar_x_get_matrix_float2x2 #define x_call_block ar_x_call_block + #define x_SCNMatrix4MakeTranslation ar_x_SCNMatrix4MakeTranslation + #define x_SCNMatrix4MakeScale ar_x_SCNMatrix4MakeScale + #define x_SCNMatrix4Translate ar_x_SCNMatrix4Translate #else // keep original names #endif diff --git a/tests/xammac_tests/xammac_tests.csproj b/tests/xammac_tests/xammac_tests.csproj index 852d06b86265..e24917711cc2 100644 --- a/tests/xammac_tests/xammac_tests.csproj +++ b/tests/xammac_tests/xammac_tests.csproj @@ -14,6 +14,7 @@ latest PackageReference .. + $(RootTestsDirectory)\..\src\build\macos-defines.rsp true diff --git a/tools/devops/automation/templates/tests/run-tests.yml b/tools/devops/automation/templates/tests/run-tests.yml index 698f879f65b4..5d72aa808676 100644 --- a/tools/devops/automation/templates/tests/run-tests.yml +++ b/tools/devops/automation/templates/tests/run-tests.yml @@ -148,6 +148,7 @@ steps: make -C src build/ios.rsp make -C src build/ios-defines.rsp make -C src build/tvos-defines.rsp + make -C src build/macos-defines.rsp make -C src build/dotnet/macos-defines-dotnet.rsp make -C src build/dotnet/ios-defines-dotnet.rsp make -C src build/dotnet/maccatalyst-defines-dotnet.rsp