diff options
Diffstat (limited to 'modules/mono/glue/Managed/Files')
-rw-r--r-- | modules/mono/glue/Managed/Files/Array.cs | 192 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Basis.cs | 435 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/DebuggingUtils.cs | 6 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Dictionary.cs | 261 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/DynamicObject.cs | 213 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/GD.cs | 117 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/GodotTraceListener.cs | 37 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/MarshalUtils.cs | 60 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Mathf.cs | 4 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/MathfEx.cs | 5 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/NodePath.cs | 14 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Object.base.cs | 36 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Quat.cs | 27 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/RID.cs | 12 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Transform.cs | 38 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Transform2D.cs | 71 | ||||
-rw-r--r-- | modules/mono/glue/Managed/Files/Vector2.cs | 2 |
17 files changed, 997 insertions, 533 deletions
diff --git a/modules/mono/glue/Managed/Files/Array.cs b/modules/mono/glue/Managed/Files/Array.cs index d5a35d7ae0..2277c7bf18 100644 --- a/modules/mono/glue/Managed/Files/Array.cs +++ b/modules/mono/glue/Managed/Files/Array.cs @@ -28,7 +28,7 @@ namespace Godot.Collections } } - public class Array : IList<object>, ICollection<object>, IEnumerable<object>, IDisposable + public class Array : IList, IDisposable { ArraySafeHandle safeHandle; bool disposed = false; @@ -38,6 +38,14 @@ namespace Godot.Collections safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor()); } + public Array(IEnumerable collection) : this() + { + if (collection == null) + throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + + MarshalUtils.EnumerableToArray(collection, GetPtr()); + } + internal Array(ArraySafeHandle handle) { safeHandle = handle; @@ -50,9 +58,19 @@ namespace Godot.Collections internal IntPtr GetPtr() { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + return safeHandle.DangerousGetHandle(); } + public Error Resize(int newSize) + { + return godot_icall_Array_Resize(GetPtr(), newSize); + } + + // IDisposable + public void Dispose() { if (disposed) @@ -67,62 +85,55 @@ namespace Godot.Collections disposed = true; } + // IList + + public bool IsReadOnly => false; + + public bool IsFixedSize => false; + public object this[int index] { - get - { - return godot_icall_Array_At(GetPtr(), index); - } - set - { - godot_icall_Array_SetAt(GetPtr(), index, value); - } + get => godot_icall_Array_At(GetPtr(), index); + set => godot_icall_Array_SetAt(GetPtr(), index, value); } - public int Count - { - get - { - return godot_icall_Array_Count(GetPtr()); - } - } + public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); - public bool IsReadOnly - { - get - { - return false; - } - } + public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); - public void Add(object item) - { - godot_icall_Array_Add(GetPtr(), item); - } + public void Clear() => godot_icall_Array_Clear(GetPtr()); - public void Clear() - { - godot_icall_Array_Clear(GetPtr()); - } + public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); - public bool Contains(object item) - { - return godot_icall_Array_Contains(GetPtr(), item); - } + public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value); + + public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value); + + public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index); - public void CopyTo(object[] array, int arrayIndex) + // ICollection + + public int Count => godot_icall_Array_Count(GetPtr()); + + public object SyncRoot => this; + + public bool IsSynchronized => false; + + public void CopyTo(System.Array array, int index) { if (array == null) throw new ArgumentNullException(nameof(array), "Value cannot be null."); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); // Internal call may throw ArgumentException - godot_icall_Array_CopyTo(GetPtr(), array, arrayIndex); + godot_icall_Array_CopyTo(GetPtr(), array, index); } - public IEnumerator<object> GetEnumerator() + // IEnumerable + + public IEnumerator GetEnumerator() { int count = Count; @@ -132,31 +143,6 @@ namespace Godot.Collections } } - public int IndexOf(object item) - { - return godot_icall_Array_IndexOf(GetPtr(), item); - } - - public void Insert(int index, object item) - { - godot_icall_Array_Insert(GetPtr(), index, item); - } - - public bool Remove(object item) - { - return godot_icall_Array_Remove(GetPtr(), item); - } - - public void RemoveAt(int index) - { - godot_icall_Array_RemoveAt(GetPtr(), index); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Array_Ctor(); @@ -176,7 +162,7 @@ namespace Godot.Collections internal extern static int godot_icall_Array_Count(IntPtr ptr); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_Add(IntPtr ptr, object item); + internal extern static int godot_icall_Array_Add(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Clear(IntPtr ptr); @@ -185,7 +171,7 @@ namespace Godot.Collections internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, object[] array, int arrayIndex); + internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); @@ -200,6 +186,9 @@ namespace Godot.Collections internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index); [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize); + + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass); } @@ -220,6 +209,14 @@ namespace Godot.Collections objectArray = new Array(); } + public Array(IEnumerable<T> collection) + { + if (collection == null) + throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'"); + + objectArray = new Array(collection); + } + public Array(Array array) { objectArray = array; @@ -235,11 +232,23 @@ namespace Godot.Collections objectArray = new Array(handle); } + internal IntPtr GetPtr() + { + return objectArray.GetPtr(); + } + public static explicit operator Array(Array<T> from) { return from.objectArray; } + public Error Resize(int newSize) + { + return objectArray.Resize(newSize); + } + + // IList<T> + public T this[int index] { get @@ -252,6 +261,23 @@ namespace Godot.Collections } } + public int IndexOf(T item) + { + return objectArray.IndexOf(item); + } + + public void Insert(int index, T item) + { + objectArray.Insert(index, item); + } + + public void RemoveAt(int index) + { + objectArray.RemoveAt(index); + } + + // ICollection<T> + public int Count { get @@ -306,6 +332,13 @@ namespace Godot.Collections } } + public bool Remove(T item) + { + return Array.godot_icall_Array_Remove(GetPtr(), item); + } + + // IEnumerable<T> + public IEnumerator<T> GetEnumerator() { int count = objectArray.Count; @@ -316,34 +349,9 @@ namespace Godot.Collections } } - public int IndexOf(T item) - { - return objectArray.IndexOf(item); - } - - public void Insert(int index, T item) - { - objectArray.Insert(index, item); - } - - public bool Remove(T item) - { - return objectArray.Remove(item); - } - - public void RemoveAt(int index) - { - objectArray.RemoveAt(index); - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - - internal IntPtr GetPtr() - { - return objectArray.GetPtr(); - } } } diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index b318d96bb9..ac9576cebd 100644 --- a/modules/mono/glue/Managed/Files/Basis.cs +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -45,74 +45,119 @@ namespace Godot new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) }; + // NOTE: x, y and z are public-only. Use Column0, Column1 and Column2 internally. + + /// <summary> + /// Returns the basis matrix’s x vector. + /// This is equivalent to <see cref="Column0"/>. + /// </summary> public Vector3 x { - get { return GetAxis(0); } - set { SetAxis(0, value); } + get => Column0; + set => Column0 = value; } + /// <summary> + /// Returns the basis matrix’s y vector. + /// This is equivalent to <see cref="Column1"/>. + /// </summary> public Vector3 y { - get { return GetAxis(1); } - set { SetAxis(1, value); } + + get => Column1; + set => Column1 = value; } + /// <summary> + /// Returns the basis matrix’s z vector. + /// This is equivalent to <see cref="Column2"/>. + /// </summary> public Vector3 z { - get { return GetAxis(2); } - set { SetAxis(2, value); } + + get => Column2; + set => Column2 = value; } - private Vector3 _x; - private Vector3 _y; - private Vector3 _z; + public Vector3 Row0; + public Vector3 Row1; + public Vector3 Row2; - public static Basis Identity + public Vector3 Column0 + { + get => new Vector3(Row0.x, Row1.x, Row2.x); + set + { + this.Row0.x = value.x; + this.Row1.x = value.y; + this.Row2.x = value.z; + } + } + public Vector3 Column1 { - get { return identity; } + get => new Vector3(Row0.y, Row1.y, Row2.y); + set + { + this.Row0.y = value.x; + this.Row1.y = value.y; + this.Row2.y = value.z; + } + } + public Vector3 Column2 + { + get => new Vector3(Row0.z, Row1.z, Row2.z); + set + { + this.Row0.z = value.x; + this.Row1.z = value.y; + this.Row2.z = value.z; + } } + public static Basis Identity => identity; + public Vector3 Scale { get { - return new Vector3 + real_t detSign = Mathf.Sign(Determinant()); + return detSign * new Vector3 ( - new Vector3(this[0, 0], this[1, 0], this[2, 0]).Length(), - new Vector3(this[0, 1], this[1, 1], this[2, 1]).Length(), - new Vector3(this[0, 2], this[1, 2], this[2, 2]).Length() + new Vector3(this.Row0[0], this.Row1[0], this.Row2[0]).Length(), + new Vector3(this.Row0[1], this.Row1[1], this.Row2[1]).Length(), + new Vector3(this.Row0[2], this.Row1[2], this.Row2[2]).Length() ); } } - public Vector3 this[int index] + public Vector3 this[int columnIndex] { get { - switch (index) + switch (columnIndex) { case 0: - return _x; + return Column0; case 1: - return _y; + return Column1; case 2: - return _z; + return Column2; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (columnIndex) { case 0: - _x = value; + Column0 = value; return; case 1: - _y = value; + Column1 = value; return; case 2: - _z = value; + Column2 = value; return; default: throw new IndexOutOfRangeException(); @@ -120,51 +165,53 @@ namespace Godot } } - public real_t this[int index, int axis] + public real_t this[int columnIndex, int rowIndex] { get { - switch (index) + switch (columnIndex) { case 0: - return _x[axis]; + return Column0[rowIndex]; case 1: - return _y[axis]; + return Column1[rowIndex]; case 2: - return _z[axis]; + return Column2[rowIndex]; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (columnIndex) { case 0: - _x[axis] = value; + { + var column0 = Column0; + column0[rowIndex] = value; + Column0 = column0; return; + } case 1: - _y[axis] = value; + { + var column1 = Column1; + column1[rowIndex] = value; + Column1 = column1; return; + } case 2: - _z[axis] = value; + { + var column2 = Column2; + column2[rowIndex] = value; + Column2 = column2; return; + } default: throw new IndexOutOfRangeException(); } } } - internal static Basis CreateFromAxes(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) - { - return new Basis - ( - xAxis.x, yAxis.x, zAxis.x, - xAxis.y, yAxis.y, zAxis.y, - xAxis.z, yAxis.z, zAxis.z - ); - } - internal Quat RotationQuat() { Basis orthonormalizedBasis = Orthonormalized(); @@ -191,29 +238,19 @@ namespace Godot private void SetDiagonal(Vector3 diagonal) { - _x = new Vector3(diagonal.x, 0, 0); - _y = new Vector3(0, diagonal.y, 0); - _z = new Vector3(0, 0, diagonal.z); + Row0 = new Vector3(diagonal.x, 0, 0); + Row1 = new Vector3(0, diagonal.y, 0); + Row2 = new Vector3(0, 0, diagonal.z); } public real_t Determinant() { - return this[0, 0] * (this[1, 1] * this[2, 2] - this[2, 1] * this[1, 2]) - - this[1, 0] * (this[0, 1] * this[2, 2] - this[2, 1] * this[0, 2]) + - this[2, 0] * (this[0, 1] * this[1, 2] - this[1, 1] * this[0, 2]); - } + real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1]; + real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2]; + real_t cofac20 = Row1[0] * Row2[1] - Row1[1] * Row2[0]; - public Vector3 GetAxis(int axis) - { - return new Vector3(this[0, axis], this[1, axis], this[2, axis]); - } - - public void SetAxis(int axis, Vector3 value) - { - this[0, axis] = value.x; - this[1, axis] = value.y; - this[2, axis] = value.z; + return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; } public Vector3 GetEuler() @@ -223,32 +260,80 @@ namespace Godot Vector3 euler; euler.z = 0.0f; - real_t mxy = m[1, 2]; - + real_t mxy = m.Row1[2]; if (mxy < 1.0f) { if (mxy > -1.0f) { euler.x = Mathf.Asin(-mxy); - euler.y = Mathf.Atan2(m[0, 2], m[2, 2]); - euler.z = Mathf.Atan2(m[1, 0], m[1, 1]); + euler.y = Mathf.Atan2(m.Row0[2], m.Row2[2]); + euler.z = Mathf.Atan2(m.Row1[0], m.Row1[1]); } else { euler.x = Mathf.Pi * 0.5f; - euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); + euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]); } } else { euler.x = -Mathf.Pi * 0.5f; - euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]); + euler.y = -Mathf.Atan2(-m.Row0[1], m.Row0[0]); } return euler; } + public Vector3 GetRow(int index) + { + switch (index) + { + case 0: + return Row0; + case 1: + return Row1; + case 2: + return Row2; + default: + throw new IndexOutOfRangeException(); + } + } + + public void SetRow(int index, Vector3 value) + { + switch (index) + { + case 0: + Row0 = value; + return; + case 1: + Row1 = value; + return; + case 2: + Row2 = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + + public Vector3 GetColumn(int index) + { + return this[index]; + } + + public void SetColumn(int index, Vector3 value) + { + this[index] = value; + } + + [Obsolete("GetAxis is deprecated. Use GetColumn instead.")] + public Vector3 GetAxis(int axis) + { + return new Vector3(this.Row0[axis], this.Row1[axis], this.Row2[axis]); + } + public int GetOrthogonalIndex() { var orth = this; @@ -257,7 +342,9 @@ namespace Godot { for (int j = 0; j < 3; j++) { - real_t v = orth[i, j]; + var row = orth.GetRow(i); + + real_t v = row[j]; if (v > 0.5f) v = 1.0f; @@ -266,7 +353,9 @@ namespace Godot else v = 0f; - orth[i, j] = v; + row[j] = v; + + orth.SetRow(i, row); } } @@ -281,57 +370,45 @@ namespace Godot public Basis Inverse() { - var inv = this; - - real_t[] co = { - inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1], - inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2], - inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0] - }; + real_t cofac00 = Row1[1] * Row2[2] - Row1[2] * Row2[1]; + real_t cofac10 = Row1[2] * Row2[0] - Row1[0] * Row2[2]; + real_t cofac20 = Row1[0] * Row2[1] - Row1[1] * Row2[0]; - real_t det = inv[0, 0] * co[0] + inv[0, 1] * co[1] + inv[0, 2] * co[2]; + real_t det = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; if (det == 0) - { - return new Basis - ( - real_t.NaN, real_t.NaN, real_t.NaN, - real_t.NaN, real_t.NaN, real_t.NaN, - real_t.NaN, real_t.NaN, real_t.NaN - ); - } + throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); - real_t s = 1.0f / det; + real_t detInv = 1.0f / det; - inv = new Basis + real_t cofac01 = Row0[2] * Row2[1] - Row0[1] * Row2[2]; + real_t cofac02 = Row0[1] * Row1[2] - Row0[2] * Row1[1]; + real_t cofac11 = Row0[0] * Row2[2] - Row0[2] * Row2[0]; + real_t cofac12 = Row0[2] * Row1[0] - Row0[0] * Row1[2]; + real_t cofac21 = Row0[1] * Row2[0] - Row0[0] * Row2[1]; + real_t cofac22 = Row0[0] * Row1[1] - Row0[1] * Row1[0]; + + return new Basis ( - co[0] * s, - inv[0, 2] * inv[2, 1] - inv[0, 1] * inv[2, 2] * s, - inv[0, 1] * inv[1, 2] - inv[0, 2] * inv[1, 1] * s, - co[1] * s, - inv[0, 0] * inv[2, 2] - inv[0, 2] * inv[2, 0] * s, - inv[0, 2] * inv[1, 0] - inv[0, 0] * inv[1, 2] * s, - co[2] * s, - inv[0, 1] * inv[2, 0] - inv[0, 0] * inv[2, 1] * s, - inv[0, 0] * inv[1, 1] - inv[0, 1] * inv[1, 0] * s + cofac00 * detInv, cofac01 * detInv, cofac02 * detInv, + cofac10 * detInv, cofac11 * detInv, cofac12 * detInv, + cofac20 * detInv, cofac21 * detInv, cofac22 * detInv ); - - return inv; } public Basis Orthonormalized() { - Vector3 xAxis = GetAxis(0); - Vector3 yAxis = GetAxis(1); - Vector3 zAxis = GetAxis(2); + Vector3 column0 = GetColumn(0); + Vector3 column1 = GetColumn(1); + Vector3 column2 = GetColumn(2); - xAxis.Normalize(); - yAxis = yAxis - xAxis * xAxis.Dot(yAxis); - yAxis.Normalize(); - zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis); - zAxis.Normalize(); + column0.Normalize(); + column1 = column1 - column0 * column0.Dot(column1); + column1.Normalize(); + column2 = column2 - column0 * column0.Dot(column2) - column1 * column1.Dot(column2); + column2.Normalize(); - return CreateFromAxes(xAxis, yAxis, zAxis); + return new Basis(column0, column1, column2); } public Basis Rotated(Vector3 axis, real_t phi) @@ -343,49 +420,49 @@ namespace Godot { var m = this; - m[0, 0] *= scale.x; - m[0, 1] *= scale.x; - m[0, 2] *= scale.x; - m[1, 0] *= scale.y; - m[1, 1] *= scale.y; - m[1, 2] *= scale.y; - m[2, 0] *= scale.z; - m[2, 1] *= scale.z; - m[2, 2] *= scale.z; + m.Row0[0] *= scale.x; + m.Row0[1] *= scale.x; + m.Row0[2] *= scale.x; + m.Row1[0] *= scale.y; + m.Row1[1] *= scale.y; + m.Row1[2] *= scale.y; + m.Row2[0] *= scale.z; + m.Row2[1] *= scale.z; + m.Row2[2] *= scale.z; return m; } public real_t Tdotx(Vector3 with) { - return this[0, 0] * with[0] + this[1, 0] * with[1] + this[2, 0] * with[2]; + return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2]; } public real_t Tdoty(Vector3 with) { - return this[0, 1] * with[0] + this[1, 1] * with[1] + this[2, 1] * with[2]; + return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2]; } public real_t Tdotz(Vector3 with) { - return this[0, 2] * with[0] + this[1, 2] * with[1] + this[2, 2] * with[2]; + return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2]; } public Basis Transposed() { var tr = this; - real_t temp = tr[0, 1]; - tr[0, 1] = tr[1, 0]; - tr[1, 0] = temp; + real_t temp = tr.Row0[1]; + tr.Row0[1] = tr.Row1[0]; + tr.Row1[0] = temp; - temp = tr[0, 2]; - tr[0, 2] = tr[2, 0]; - tr[2, 0] = temp; + temp = tr.Row0[2]; + tr.Row0[2] = tr.Row2[0]; + tr.Row2[0] = temp; - temp = tr[1, 2]; - tr[1, 2] = tr[2, 1]; - tr[2, 1] = temp; + temp = tr.Row1[2]; + tr.Row1[2] = tr.Row2[1]; + tr.Row2[1] = temp; return tr; } @@ -394,9 +471,9 @@ namespace Godot { return new Vector3 ( - this[0].Dot(v), - this[1].Dot(v), - this[2].Dot(v) + this.Row0.Dot(v), + this.Row1.Dot(v), + this.Row2.Dot(v) ); } @@ -404,60 +481,60 @@ namespace Godot { return new Vector3 ( - this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z, - this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z, - this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z + this.Row0[0] * v.x + this.Row1[0] * v.y + this.Row2[0] * v.z, + this.Row0[1] * v.x + this.Row1[1] * v.y + this.Row2[1] * v.z, + this.Row0[2] * v.x + this.Row1[2] * v.y + this.Row2[2] * v.z ); } public Quat Quat() { - real_t trace = _x[0] + _y[1] + _z[2]; + real_t trace = Row0[0] + Row1[1] + Row2[2]; if (trace > 0.0f) { real_t s = Mathf.Sqrt(trace + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( - (_z[1] - _y[2]) * inv_s, - (_x[2] - _z[0]) * inv_s, - (_y[0] - _x[1]) * inv_s, + (Row2[1] - Row1[2]) * inv_s, + (Row0[2] - Row2[0]) * inv_s, + (Row1[0] - Row0[1]) * inv_s, s * 0.25f ); } - if (_x[0] > _y[1] && _x[0] > _z[2]) + if (Row0[0] > Row1[1] && Row0[0] > Row2[2]) { - real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f; + real_t s = Mathf.Sqrt(Row0[0] - Row1[1] - Row2[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( s * 0.25f, - (_x[1] + _y[0]) * inv_s, - (_x[2] + _z[0]) * inv_s, - (_z[1] - _y[2]) * inv_s + (Row0[1] + Row1[0]) * inv_s, + (Row0[2] + Row2[0]) * inv_s, + (Row2[1] - Row1[2]) * inv_s ); } - if (_y[1] > _z[2]) + if (Row1[1] > Row2[2]) { - real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f; + real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( - (_x[1] + _y[0]) * inv_s, + (Row0[1] + Row1[0]) * inv_s, s * 0.25f, - (_y[2] + _z[1]) * inv_s, - (_x[2] - _z[0]) * inv_s + (Row1[2] + Row2[1]) * inv_s, + (Row0[2] - Row2[0]) * inv_s ); } else { - real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f; + real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f; real_t inv_s = 1f / s; return new Quat( - (_x[2] + _z[0]) * inv_s, - (_y[2] + _z[1]) * inv_s, + (Row0[2] + Row2[0]) * inv_s, + (Row1[2] + Row2[1]) * inv_s, s * 0.25f, - (_y[0] - _x[1]) * inv_s + (Row1[0] - Row0[1]) * inv_s ); } } @@ -479,9 +556,9 @@ namespace Godot real_t yz = quat.y * zs; real_t zz = quat.z * zs; - _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); - _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); - _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); + Row0 = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy); + Row1 = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx); + Row2 = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy)); } public Basis(Vector3 euler) @@ -511,21 +588,21 @@ namespace Godot real_t cosine = Mathf.Cos(phi); real_t sine = Mathf.Sin(phi); - _x = new Vector3 + Row0 = new Vector3 ( axis_sq.x + cosine * (1.0f - axis_sq.x), axis.x * axis.y * (1.0f - cosine) - axis.z * sine, axis.z * axis.x * (1.0f - cosine) + axis.y * sine ); - _y = new Vector3 + Row1 = new Vector3 ( axis.x * axis.y * (1.0f - cosine) + axis.z * sine, axis_sq.y + cosine * (1.0f - axis_sq.y), axis.y * axis.z * (1.0f - cosine) - axis.x * sine ); - _z = new Vector3 + Row2 = new Vector3 ( axis.z * axis.x * (1.0f - cosine) - axis.y * sine, axis.y * axis.z * (1.0f - cosine) + axis.x * sine, @@ -533,32 +610,32 @@ namespace Godot ); } - public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) + public Basis(Vector3 column0, Vector3 column1, Vector3 column2) { - _x = new Vector3(xAxis.x, yAxis.x, zAxis.x); - _y = new Vector3(xAxis.y, yAxis.y, zAxis.y); - _z = new Vector3(xAxis.z, yAxis.z, zAxis.z); + Row0 = new Vector3(column0.x, column1.x, column2.x); + Row1 = new Vector3(column0.y, column1.y, column2.y); + Row2 = new Vector3(column0.z, column1.z, column2.z); // Same as: - // SetAxis(0, xAxis); - // SetAxis(1, yAxis); - // SetAxis(2, zAxis); - // We need to assign the struct fields so we can't do that... + // Column0 = column0; + // Column1 = column1; + // Column2 = column2; + // We need to assign the struct fields here first so we can't do it that way... } internal Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - _x = new Vector3(xx, xy, xz); - _y = new Vector3(yx, yy, yz); - _z = new Vector3(zx, zy, zz); + Row0 = new Vector3(xx, xy, xz); + Row1 = new Vector3(yx, yy, yz); + Row2 = new Vector3(zx, zy, zz); } public static Basis operator *(Basis left, Basis right) { return new Basis ( - right.Tdotx(left[0]), right.Tdoty(left[0]), right.Tdotz(left[0]), - right.Tdotx(left[1]), right.Tdoty(left[1]), right.Tdotz(left[1]), - right.Tdotx(left[2]), right.Tdoty(left[2]), right.Tdotz(left[2]) + right.Tdotx(left.Row0), right.Tdoty(left.Row0), right.Tdotz(left.Row0), + right.Tdotx(left.Row1), right.Tdoty(left.Row1), right.Tdotz(left.Row1), + right.Tdotx(left.Row2), right.Tdoty(left.Row2), right.Tdotz(left.Row2) ); } @@ -584,21 +661,21 @@ namespace Godot public bool Equals(Basis other) { - return _x.Equals(other[0]) && _y.Equals(other[1]) && _z.Equals(other[2]); + return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2); } public override int GetHashCode() { - return _x.GetHashCode() ^ _y.GetHashCode() ^ _z.GetHashCode(); + return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode(); } public override string ToString() { return String.Format("({0}, {1}, {2})", new object[] { - _x.ToString(), - _y.ToString(), - _z.ToString() + Row0.ToString(), + Row1.ToString(), + Row2.ToString() }); } @@ -606,9 +683,9 @@ namespace Godot { return String.Format("({0}, {1}, {2})", new object[] { - _x.ToString(format), - _y.ToString(format), - _z.ToString(format) + Row0.ToString(format), + Row1.ToString(format), + Row2.ToString(format) }); } } diff --git a/modules/mono/glue/Managed/Files/DebuggingUtils.cs b/modules/mono/glue/Managed/Files/DebuggingUtils.cs index b27816084e..edfe3464ec 100644 --- a/modules/mono/glue/Managed/Files/DebuggingUtils.cs +++ b/modules/mono/glue/Managed/Files/DebuggingUtils.cs @@ -19,6 +19,12 @@ namespace Godot sb.Append(" "); } + public static void InstallTraceListener() + { + Trace.Listeners.Clear(); + Trace.Listeners.Add(new GodotTraceListener()); + } + public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl) { fileName = frame.GetFileName(); diff --git a/modules/mono/glue/Managed/Files/Dictionary.cs b/modules/mono/glue/Managed/Files/Dictionary.cs index 7695f03cd6..af1782b79a 100644 --- a/modules/mono/glue/Managed/Files/Dictionary.cs +++ b/modules/mono/glue/Managed/Files/Dictionary.cs @@ -29,9 +29,7 @@ namespace Godot.Collections } public class Dictionary : - IDictionary<object, object>, - ICollection<KeyValuePair<object, object>>, - IEnumerable<KeyValuePair<object, object>>, + IDictionary, IDisposable { DictionarySafeHandle safeHandle; @@ -42,6 +40,14 @@ namespace Godot.Collections safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor()); } + public Dictionary(IDictionary dictionary) : this() + { + if (dictionary == null) + throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + + MarshalUtils.IDictionaryToDictionary(dictionary, GetPtr()); + } + internal Dictionary(DictionarySafeHandle handle) { safeHandle = handle; @@ -54,6 +60,9 @@ namespace Godot.Collections internal IntPtr GetPtr() { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + return safeHandle.DangerousGetHandle(); } @@ -71,19 +80,9 @@ namespace Godot.Collections disposed = true; } - public object this[object key] - { - get - { - return godot_icall_Dictionary_GetValue(GetPtr(), key); - } - set - { - godot_icall_Dictionary_SetValue(GetPtr(), key, value); - } - } + // IDictionary - public ICollection<object> Keys + public ICollection Keys { get { @@ -92,7 +91,7 @@ namespace Godot.Collections } } - public ICollection<object> Values + public ICollection Values { get { @@ -101,97 +100,97 @@ namespace Godot.Collections } } - public int Count - { - get - { - return godot_icall_Dictionary_Count(GetPtr()); - } - } + public bool IsFixedSize => false; - public bool IsReadOnly - { - get - { - return false; - } - } + public bool IsReadOnly => false; - public void Add(object key, object value) + public object this[object key] { - godot_icall_Dictionary_Add(GetPtr(), key, value); + get => godot_icall_Dictionary_GetValue(GetPtr(), key); + set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); } - public void Add(KeyValuePair<object, object> item) - { - Add(item.Key, item.Value); - } + public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); - public void Clear() - { - godot_icall_Dictionary_Clear(GetPtr()); - } + public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); - public bool Contains(KeyValuePair<object, object> item) - { - return godot_icall_Dictionary_Contains(GetPtr(), item.Key, item.Value); - } + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); - public bool ContainsKey(object key) - { - return godot_icall_Dictionary_ContainsKey(GetPtr(), key); - } + public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); - public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex) + // ICollection + + public object SyncRoot => this; + + public bool IsSynchronized => false; + + public int Count => godot_icall_Dictionary_Count(GetPtr()); + + public void CopyTo(System.Array array, int index) { - // TODO 3 internal calls, can reduce to 1 + // TODO Can be done with single internal call + + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension."); + Array keys = (Array)Keys; Array values = (Array)Values; int count = Count; + if (array.Length < (index + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + for (int i = 0; i < count; i++) { - // TODO 2 internal calls, can reduce to 1 - array[arrayIndex] = new KeyValuePair<object, object>(keys[i], values[i]); - arrayIndex++; + array.SetValue(new DictionaryEntry(keys[i], values[i]), index); + index++; } } - public IEnumerator<KeyValuePair<object, object>> GetEnumerator() + // IEnumerable + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private class DictionaryEnumerator : IDictionaryEnumerator { - // TODO 3 internal calls, can reduce to 1 - Array keys = (Array)Keys; - Array values = (Array)Values; - int count = Count; + Array keys; + Array values; + int count; + int index = -1; - for (int i = 0; i < count; i++) + public DictionaryEnumerator(Dictionary dictionary) { - // TODO 2 internal calls, can reduce to 1 - yield return new KeyValuePair<object, object>(keys[i], values[i]); + // TODO 3 internal calls, can reduce to 1 + keys = (Array)dictionary.Keys; + values = (Array)dictionary.Values; + count = dictionary.Count; } - } - public bool Remove(object key) - { - return godot_icall_Dictionary_RemoveKey(GetPtr(), key); - } + public object Current => Entry; - public bool Remove(KeyValuePair<object, object> item) - { - return godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); - } + public DictionaryEntry Entry => + // TODO 2 internal calls, can reduce to 1 + new DictionaryEntry(keys[index], values[index]); - public bool TryGetValue(object key, out object value) - { - object retValue; - bool found = godot_icall_Dictionary_TryGetValue(GetPtr(), key, out retValue); - value = found ? retValue : default(object); - return found; - } + public object Key => Entry.Key; - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); + public object Value => Entry.Value; + + public bool MoveNext() + { + index++; + return index < count; + } + + public void Reset() + { + index = -1; + } } [MethodImpl(MethodImplOptions.InternalCall)] @@ -247,9 +246,7 @@ namespace Godot.Collections } public class Dictionary<TKey, TValue> : - IDictionary<TKey, TValue>, - ICollection<KeyValuePair<TKey, TValue>>, - IEnumerable<KeyValuePair<TKey, TValue>> + IDictionary<TKey, TValue> { Dictionary objectDict; @@ -266,6 +263,23 @@ namespace Godot.Collections objectDict = new Dictionary(); } + public Dictionary(IDictionary<TKey, TValue> dictionary) + { + objectDict = new Dictionary(); + + if (dictionary == null) + throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'"); + + // TODO: Can be optimized + + IntPtr godotDictionaryPtr = GetPtr(); + + foreach (KeyValuePair<TKey, TValue> entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } + } + public Dictionary(Dictionary dictionary) { objectDict = dictionary; @@ -286,6 +300,13 @@ namespace Godot.Collections return from.objectDict; } + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + + // IDictionary<TKey, TValue> + public TValue this[TKey key] { get @@ -316,6 +337,31 @@ namespace Godot.Collections } } + public void Add(TKey key, TValue value) + { + objectDict.Add(key, value); + } + + public bool ContainsKey(TKey key) + { + return objectDict.Contains(key); + } + + public bool Remove(TKey key) + { + return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + object retValue; + bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); + value = found ? (TValue)retValue : default(TValue); + return found; + } + + // ICollection<KeyValuePair<TKey, TValue>> + public int Count { get @@ -332,11 +378,6 @@ namespace Godot.Collections } } - public void Add(TKey key, TValue value) - { - objectDict.Add(key, value); - } - public void Add(KeyValuePair<TKey, TValue> item) { objectDict.Add(item.Key, item.Value); @@ -352,18 +393,22 @@ namespace Godot.Collections return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); } - public bool ContainsKey(TKey key) - { - return objectDict.ContainsKey(key); - } - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { + if (array == null) + throw new ArgumentNullException(nameof(array), "Value cannot be null."); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension."); + // TODO 3 internal calls, can reduce to 1 Array<TKey> keys = (Array<TKey>)Keys; Array<TValue> values = (Array<TValue>)Values; int count = Count; + if (array.Length < (arrayIndex + count)) + throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds."); + for (int i = 0; i < count; i++) { // TODO 2 internal calls, can reduce to 1 @@ -372,6 +417,13 @@ namespace Godot.Collections } } + public bool Remove(KeyValuePair<TKey, TValue> item) + { + return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value); ; + } + + // IEnumerable<KeyValuePair<TKey, TValue>> + public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { // TODO 3 internal calls, can reduce to 1 @@ -386,32 +438,9 @@ namespace Godot.Collections } } - public bool Remove(TKey key) - { - return objectDict.Remove(key); - } - - public bool Remove(KeyValuePair<TKey, TValue> item) - { - return objectDict.Remove(new KeyValuePair<object, object>(item.Key, item.Value)); - } - - public bool TryGetValue(TKey key, out TValue value) - { - object retValue; - bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass); - value = found ? (TValue)retValue : default(TValue); - return found; - } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - - internal IntPtr GetPtr() - { - return objectDict.GetPtr(); - } } } diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs new file mode 100644 index 0000000000..9504415664 --- /dev/null +++ b/modules/mono/glue/Managed/Files/DynamicObject.cs @@ -0,0 +1,213 @@ + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; + +namespace Godot +{ + /// <summary> + /// Represents an <see cref="Godot.Object"/> whose members can be dynamically accessed at runtime through the Variant API. + /// </summary> + /// <remarks> + /// <para> + /// The <see cref="Godot.DynamicGodotObject"/> class enables access to the Variant + /// members of a <see cref="Godot.Object"/> instance at runtime. + /// </para> + /// <para> + /// This allows accessing the class members using their original names in the engine as well as the members from the + /// script attached to the <see cref="Godot.Object"/>, regardless of the scripting language it was written in. + /// </para> + /// </remarks> + /// <example> + /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Godot.Object"/>. + /// <code> + /// dynamic sprite = GetNode("Sprite").DynamicGodotObject; + /// sprite.add_child(this); + /// + /// if ((sprite.hframes * sprite.vframes) > 0) + /// sprite.frame = 0; + /// </code> + /// </example> + /// <example> + /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Godot.Object"/>. + /// <code> + /// dynamic childNode = GetNode("ChildNode").DynamicGodotObject; + /// + /// if (childNode.print_allowed) + /// { + /// childNode.message = "Hello from C#"; + /// childNode.print_message(3); + /// } + /// </code> + /// The <c>ChildNode</c> node has the following GDScript script attached: + /// <code> + /// // # ChildNode.gd + /// // var print_allowed = true + /// // var message = "" + /// // + /// // func print_message(times): + /// // for i in times: + /// // print(message) + /// </code> + /// </example> + public class DynamicGodotObject : DynamicObject + { + /// <summary> + /// Gets the <see cref="Godot.Object"/> associated with this <see cref="Godot.DynamicGodotObject"/>. + /// </summary> + public Object Value { get; } + + /// <summary> + /// Initializes a new instance of the <see cref="Godot.DynamicGodotObject"/> class. + /// </summary> + /// <param name="godotObject"> + /// The <see cref="Godot.Object"/> that will be associated with this <see cref="Godot.DynamicGodotObject"/>. + /// </param> + /// <exception cref="System.ArgumentNullException"> + /// Thrown when the <paramref name="godotObject"/> parameter is null. + /// </exception> + public DynamicGodotObject(Object godotObject) + { + if (godotObject == null) + throw new ArgumentNullException(nameof(godotObject)); + + this.Value = godotObject; + } + + public override IEnumerable<string> GetDynamicMemberNames() + { + return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value)); + } + + public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) + { + switch (binder.Operation) + { + case ExpressionType.Equal: + case ExpressionType.NotEqual: + if (binder.ReturnType == typeof(bool) || binder.ReturnType.IsAssignableFrom(typeof(bool))) + { + if (arg == null) + { + bool boolResult = Object.IsInstanceValid(Value); + + if (binder.Operation == ExpressionType.Equal) + boolResult = !boolResult; + + result = boolResult; + return true; + } + + if (arg is Object other) + { + bool boolResult = (Value == other); + + if (binder.Operation == ExpressionType.NotEqual) + boolResult = !boolResult; + + result = boolResult; + return true; + } + } + + break; + default: + // We're not implementing operators <, <=, >, and >= (LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual). + // These are used on the actual pointers in variant_op.cpp. It's better to let the user do that explicitly. + break; + } + + return base.TryBinaryOperation(binder, arg, out result); + } + + public override bool TryConvert(ConvertBinder binder, out object result) + { + if (binder.Type == typeof(Object)) + { + result = Value; + return true; + } + + if (typeof(Object).IsAssignableFrom(binder.Type)) + { + // Throws InvalidCastException when the cast fails + result = Convert.ChangeType(Value, binder.Type); + return true; + } + + return base.TryConvert(binder, out result); + } + + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + if (indexes.Length == 1) + { + if (indexes[0] is string name) + { + return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), name, out result); + } + } + + return base.TryGetIndex(binder, indexes, out result); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result); + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result); + } + + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + if (indexes.Length == 1) + { + if (indexes[0] is string name) + { + return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), name, value); + } + } + + return base.TrySetIndex(binder, indexes, value); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value); + + #region We don't override these methods + + // Looks like this is not usable from C# + //public override bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result); + + // Object members cannot be deleted + //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); + //public override bool TryDeleteMember(DeleteMemberBinder binder); + + // Invokation on the object itself, e.g.: obj(param) + //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result); + + // No unnary operations to handle + //public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result); + + #endregion + } +} diff --git a/modules/mono/glue/Managed/Files/GD.cs b/modules/mono/glue/Managed/Files/GD.cs index 75a35a9eea..d968f8a78f 100644 --- a/modules/mono/glue/Managed/Files/GD.cs +++ b/modules/mono/glue/Managed/Files/GD.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; #if REAL_T_IS_DOUBLE using real_t = System.Double; @@ -12,12 +13,12 @@ namespace Godot { public static partial class GD { - public static object Bytes2Var(byte[] bytes) + public static object Bytes2Var(byte[] bytes, bool allow_objects = false) { - return godot_icall_GD_bytes2var(bytes); + return godot_icall_GD_bytes2var(bytes, allow_objects); } - public static object Convert(object what, int type) + public static object Convert(object what, Variant.Type type) { return godot_icall_GD_convert(what, type); } @@ -50,7 +51,7 @@ namespace Godot return godot_icall_GD_hash(var); } - public static Object InstanceFromId(int instanceId) + public static Object InstanceFromId(ulong instanceId) { return godot_icall_GD_instance_from_id(instanceId); } @@ -110,71 +111,62 @@ namespace Godot godot_icall_GD_printt(what); } - public static int[] Range(int length) + public static double Randf() { - var ret = new int[length]; - - for (int i = 0; i < length; i++) - { - ret[i] = i; - } - - return ret; + return godot_icall_GD_randf(); } - public static int[] Range(int from, int to) + public static uint Randi() { - if (to < from) - return new int[0]; + return godot_icall_GD_randi(); + } - var ret = new int[to - from]; + public static void Randomize() + { + godot_icall_GD_randomize(); + } - for (int i = from; i < to; i++) - { - ret[i - from] = i; - } + public static double RandRange(double from, double to) + { + return godot_icall_GD_rand_range(from, to); + } - return ret; + public static uint RandSeed(ulong seed, out ulong newSeed) + { + return godot_icall_GD_rand_seed(seed, out newSeed); } - public static int[] Range(int from, int to, int increment) + public static IEnumerable<int> Range(int end) { - if (to < from && increment > 0) - return new int[0]; - if (to > from && increment < 0) - return new int[0]; + return Range(0, end, 1); + } - // Calculate count - int count; + public static IEnumerable<int> Range(int start, int end) + { + return Range(start, end, 1); + } - if (increment > 0) - count = (to - from - 1) / increment + 1; - else - count = (from - to - 1) / -increment + 1; + public static IEnumerable<int> Range(int start, int end, int step) + { + if (end < start && step > 0) + yield break; - var ret = new int[count]; + if (end > start && step < 0) + yield break; - if (increment > 0) + if (step > 0) { - int idx = 0; - for (int i = from; i < to; i += increment) - { - ret[idx++] = i; - } + for (int i = start; i < end; i += step) + yield return i; } else { - int idx = 0; - for (int i = from; i > to; i += increment) - { - ret[idx++] = i; - } + for (int i = start; i > end; i += step) + yield return i; } - - return ret; } - public static void Seed(int seed) + public static void Seed(ulong seed) { godot_icall_GD_seed(seed); } @@ -194,9 +186,9 @@ namespace Godot return godot_icall_GD_type_exists(type); } - public static byte[] Var2Bytes(object var) + public static byte[] Var2Bytes(object var, bool full_objects = false) { - return godot_icall_GD_var2bytes(var); + return godot_icall_GD_var2bytes(var, full_objects); } public static string Var2Str(object var) @@ -205,16 +197,16 @@ namespace Godot } [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_bytes2var(byte[] bytes); + internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allow_objects); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static object godot_icall_GD_convert(object what, int type); + internal extern static object godot_icall_GD_convert(object what, Variant.Type type); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static int godot_icall_GD_hash(object var); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static Object godot_icall_GD_instance_from_id(int instance_id); + internal extern static Object godot_icall_GD_instance_from_id(ulong instance_id); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static void godot_icall_GD_print(object[] what); @@ -232,7 +224,22 @@ namespace Godot internal extern static void godot_icall_GD_printt(object[] what); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static void godot_icall_GD_seed(int seed); + internal extern static double godot_icall_GD_randf(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static uint godot_icall_GD_randi(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_randomize(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static double godot_icall_GD_rand_range(double from, double to); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_seed(ulong seed); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static string godot_icall_GD_str(object[] what); @@ -244,7 +251,7 @@ namespace Godot internal extern static bool godot_icall_GD_type_exists(string type); [MethodImpl(MethodImplOptions.InternalCall)] - internal extern static byte[] godot_icall_GD_var2bytes(object what); + internal extern static byte[] godot_icall_GD_var2bytes(object what, bool full_objects); [MethodImpl(MethodImplOptions.InternalCall)] internal extern static string godot_icall_GD_var2str(object var); diff --git a/modules/mono/glue/Managed/Files/GodotTraceListener.cs b/modules/mono/glue/Managed/Files/GodotTraceListener.cs new file mode 100644 index 0000000000..f1a00ae0fa --- /dev/null +++ b/modules/mono/glue/Managed/Files/GodotTraceListener.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics; + +namespace Godot +{ + internal class GodotTraceListener : TraceListener + { + public override void Write(string message) + { + GD.PrintRaw(message); + } + + public override void WriteLine(string message) + { + GD.Print(message); + } + + public override void Fail(string message, string detailMessage) + { + GD.PrintErr("Assertion failed: ", message); + if (detailMessage != null) + { + GD.PrintErr(" Details: ", detailMessage); + } + + try + { + var stackTrace = new StackTrace(true).ToString(); + GD.PrintErr(stackTrace); + } + catch + { + // ignored + } + } + } +} diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs index f7699a15bf..7e72b0edb5 100644 --- a/modules/mono/glue/Managed/Files/MarshalUtils.cs +++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs @@ -1,18 +1,68 @@ using System; -using Godot.Collections; +using System.Collections; namespace Godot { + using Array = Godot.Collections.Array; + using Dictionary = Godot.Collections.Dictionary; + static class MarshalUtils { - static bool IsArrayGenericType(Type type) + static bool TypeIsGenericArray(Type type) { - return type.GetGenericTypeDefinition() == typeof(Array<>); + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); + } + + static bool TypeIsGenericDictionary(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); + } + + static void ArrayGetElementType(Type type, out Type elementType) + { + elementType = type.GetGenericArguments()[0]; + } + + static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType) + { + var genericArgs = type.GetGenericArguments(); + + keyType = genericArgs[0]; + valueType = genericArgs[1]; + } + + // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue> + // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized + + internal static void EnumerableToArray(IEnumerable enumerable, IntPtr godotArrayPtr) + { + if (enumerable is ICollection collection) + { + int count = collection.Count; + + object[] tempArray = new object[count]; + collection.CopyTo(tempArray, 0); + + for (int i = 0; i < count; i++) + { + Array.godot_icall_Array_Add(godotArrayPtr, tempArray[i]); + } + } + else + { + foreach (object element in enumerable) + { + Array.godot_icall_Array_Add(godotArrayPtr, element); + } + } } - static bool IsDictionaryGenericType(Type type) + internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) { - return type.GetGenericTypeDefinition() == typeof(Dictionary<, >); + foreach (DictionaryEntry entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } } } } diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index dcab3c1ffc..5f5de12959 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -289,13 +289,13 @@ namespace Godot public static int Wrap(int value, int min, int max) { int rng = max - min; - return min + ((value - min) % rng + rng) % rng; + return rng != 0 ? min + ((value - min) % rng + rng) % rng : min; } public static real_t Wrap(real_t value, real_t min, real_t max) { real_t rng = max - min; - return min + ((value - min) % rng + rng) % rng; + return !IsEqualApprox(rng, default(real_t)) ? min + ((value - min) % rng + rng) % rng : min; } } } diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index 2ef02cc288..414762f7b1 100644 --- a/modules/mono/glue/Managed/Files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -35,5 +35,10 @@ namespace Godot { return (int)Math.Round(s); } + + public static bool IsEqualApprox(real_t a, real_t b, real_t ratio = Mathf.Epsilon) + { + return Abs(a - b) < ratio; + } } }
\ No newline at end of file diff --git a/modules/mono/glue/Managed/Files/NodePath.cs b/modules/mono/glue/Managed/Files/NodePath.cs index 2c89bec87f..94a4ed1de9 100644 --- a/modules/mono/glue/Managed/Files/NodePath.cs +++ b/modules/mono/glue/Managed/Files/NodePath.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; namespace Godot { - public partial class NodePath : IDisposable + public sealed partial class NodePath : IDisposable { private bool disposed = false; @@ -11,7 +11,13 @@ namespace Godot internal static IntPtr GetPtr(NodePath instance) { - return instance == null ? IntPtr.Zero : instance.ptr; + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; } ~NodePath() @@ -25,7 +31,7 @@ namespace Godot GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposed) return; @@ -49,7 +55,7 @@ namespace Godot get { return ptr; } } - public NodePath() : this(string.Empty) {} + public NodePath() : this(string.Empty) { } public NodePath(string path) { diff --git a/modules/mono/glue/Managed/Files/Object.base.cs b/modules/mono/glue/Managed/Files/Object.base.cs index 30490a715f..e152d56871 100644 --- a/modules/mono/glue/Managed/Files/Object.base.cs +++ b/modules/mono/glue/Managed/Files/Object.base.cs @@ -30,7 +30,13 @@ namespace Godot internal static IntPtr GetPtr(Object instance) { - return instance == null ? IntPtr.Zero : instance.ptr; + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; } ~Object() @@ -67,11 +73,39 @@ namespace Godot disposed = true; } + /// <summary> + /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance + /// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter. + /// </summary> + /// <param name="source"> + /// The instance the awaiter will be listening to. + /// </param> + /// <param name="signal"> + /// The signal the awaiter will be waiting for. + /// </param> + /// <example> + /// This sample prints a message once every frame up to 100 times. + /// <code> + /// public override void _Ready() + /// { + /// for (int i = 0; i < 100; i++) + /// { + /// await ToSignal(GetTree(), "idle_frame"); + /// GD.Print($"Frame {i}"); + /// } + /// } + /// </code> + /// </example> public SignalAwaiter ToSignal(Object source, string signal) { return new SignalAwaiter(source, signal, this); } + /// <summary> + /// Gets a new <see cref="Godot.DynamicGodotObject"/> associated with this instance. + /// </summary> + public dynamic DynamicObject => new DynamicGodotObject(this); + [MethodImpl(MethodImplOptions.InternalCall)] internal extern static IntPtr godot_icall_Object_Ctor(Object obj); diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index d4dcff583a..d0c15146a5 100644 --- a/modules/mono/glue/Managed/Files/Quat.cs +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -123,22 +123,23 @@ namespace Godot // Calculate cosine real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; - var to1 = new real_t[4]; + var to1 = new Quat(); // Adjust signs if necessary if (cosom < 0.0) { - cosom = -cosom; to1[0] = -b.x; - to1[1] = -b.y; - to1[2] = -b.z; - to1[3] = -b.w; + cosom = -cosom; + to1.x = -b.x; + to1.y = -b.y; + to1.z = -b.z; + to1.w = -b.w; } else { - to1[0] = b.x; - to1[1] = b.y; - to1[2] = b.z; - to1[3] = b.w; + to1.x = b.x; + to1.y = b.y; + to1.z = b.z; + to1.w = b.w; } real_t sinom, scale0, scale1; @@ -162,10 +163,10 @@ namespace Godot // Calculate final values return new Quat ( - scale0 * x + scale1 * to1[0], - scale0 * y + scale1 * to1[1], - scale0 * z + scale1 * to1[2], - scale0 * w + scale1 * to1[3] + scale0 * x + scale1 * to1.x, + scale0 * y + scale1 * to1.y, + scale0 * z + scale1 * to1.z, + scale0 * w + scale1 * to1.w ); } diff --git a/modules/mono/glue/Managed/Files/RID.cs b/modules/mono/glue/Managed/Files/RID.cs index b862b8cac0..f1268c8518 100644 --- a/modules/mono/glue/Managed/Files/RID.cs +++ b/modules/mono/glue/Managed/Files/RID.cs @@ -3,7 +3,7 @@ using System.Runtime.CompilerServices; namespace Godot { - public partial class RID : IDisposable + public sealed partial class RID : IDisposable { private bool disposed = false; @@ -11,7 +11,13 @@ namespace Godot internal static IntPtr GetPtr(RID instance) { - return instance == null ? IntPtr.Zero : instance.ptr; + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; } ~RID() @@ -25,7 +31,7 @@ namespace Godot GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { if (disposed) return; diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index fa85855edd..bd79144873 100644 --- a/modules/mono/glue/Managed/Files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -71,21 +71,21 @@ namespace Godot { // Make rotation matrix // Z vector - Vector3 zAxis = eye - target; + Vector3 column2 = eye - target; - zAxis.Normalize(); + column2.Normalize(); - Vector3 yAxis = up; + Vector3 column1 = up; - Vector3 xAxis = yAxis.Cross(zAxis); + Vector3 column0 = column1.Cross(column2); // Recompute Y = Z cross X - yAxis = zAxis.Cross(xAxis); + column1 = column2.Cross(column0); - xAxis.Normalize(); - yAxis.Normalize(); + column0.Normalize(); + column1.Normalize(); - basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + basis = new Basis(column0, column1, column2); origin = eye; } @@ -94,9 +94,9 @@ namespace Godot { return new Transform(basis, new Vector3 ( - origin[0] += basis[0].Dot(ofs), - origin[1] += basis[1].Dot(ofs), - origin[2] += basis[2].Dot(ofs) + origin[0] += basis.Row0.Dot(ofs), + origin[1] += basis.Row1.Dot(ofs), + origin[2] += basis.Row2.Dot(ofs) )); } @@ -104,9 +104,9 @@ namespace Godot { return new Vector3 ( - basis[0].Dot(v) + origin.x, - basis[1].Dot(v) + origin.y, - basis[2].Dot(v) + origin.z + basis.Row0.Dot(v) + origin.x, + basis.Row1.Dot(v) + origin.y, + basis.Row2.Dot(v) + origin.z ); } @@ -116,9 +116,9 @@ namespace Godot return new Vector3 ( - basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z, - basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z, - basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z + basis.Row0[0] * vInv.x + basis.Row1[0] * vInv.y + basis.Row2[0] * vInv.z, + basis.Row0[1] * vInv.x + basis.Row1[1] * vInv.y + basis.Row2[1] * vInv.z, + basis.Row0[2] * vInv.x + basis.Row1[2] * vInv.y + basis.Row2[2] * vInv.z ); } @@ -134,9 +134,9 @@ namespace Godot public static Transform FlipZ { get { return _flipZ; } } // Constructors - public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin) + public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin) { - basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis); + basis = new Basis(column0, column1, column2); this.origin = origin; } diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs index df7ba3402d..f7bb41d523 100644 --- a/modules/mono/glue/Managed/Files/Transform2D.cs +++ b/modules/mono/glue/Managed/Files/Transform2D.cs @@ -53,11 +53,11 @@ namespace Godot } } - public Vector2 this[int index] + public Vector2 this[int rowIndex] { get { - switch (index) + switch (rowIndex) { case 0: return x; @@ -71,7 +71,7 @@ namespace Godot } set { - switch (index) + switch (rowIndex) { case 0: x = value; @@ -88,30 +88,29 @@ namespace Godot } } - - public real_t this[int index, int axis] + public real_t this[int rowIndex, int columnIndex] { get { - switch (index) + switch (rowIndex) { case 0: - return x[axis]; + return x[columnIndex]; case 1: - return y[axis]; + return y[columnIndex]; default: throw new IndexOutOfRangeException(); } } set { - switch (index) + switch (rowIndex) { case 0: - x[axis] = value; + x[columnIndex] = value; return; case 1: - y[axis] = value; + y[columnIndex] = value; return; default: throw new IndexOutOfRangeException(); @@ -121,30 +120,23 @@ namespace Godot public Transform2D AffineInverse() { - var inv = this; - real_t det = BasisDeterminant(); if (det == 0) - { - return new Transform2D - ( - real_t.NaN, real_t.NaN, - real_t.NaN, real_t.NaN, - real_t.NaN, real_t.NaN - ); - } + throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); - real_t detInv = 1.0f / det; + var inv = this; - real_t temp = this[0, 0]; - this[0, 0] = this[1, 1]; - this[1, 1] = temp; + real_t temp = inv[0, 0]; + inv[0, 0] = inv[1, 1]; + inv[1, 1] = temp; - this[0] *= new Vector2(detInv, -detInv); - this[1] *= new Vector2(-detInv, detInv); + real_t detInv = 1.0f / det; + + inv[0] *= new Vector2(detInv, -detInv); + inv[1] *= new Vector2(-detInv, detInv); - this[2] = BasisXform(-this[2]); + inv[2] = BasisXform(-inv[2]); return inv; } @@ -294,9 +286,9 @@ namespace Godot private static readonly Transform2D _flipX = new Transform2D(-1, 0, 0, 1, 0, 0); private static readonly Transform2D _flipY = new Transform2D(1, 0, 0, -1, 0, 0); - public static Transform2D Identity { get { return _identity; } } - public static Transform2D FlipX { get { return _flipX; } } - public static Transform2D FlipY { get { return _flipY; } } + public static Transform2D Identity => _identity; + public static Transform2D FlipX => _flipX; + public static Transform2D FlipY => _flipY; // Constructors public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos) @@ -325,12 +317,10 @@ namespace Godot { left.origin = left.Xform(right.origin); - real_t x0, x1, y0, y1; - - x0 = left.Tdotx(right.x); - x1 = left.Tdoty(right.x); - y0 = left.Tdotx(right.y); - y1 = left.Tdoty(right.y); + real_t x0 = left.Tdotx(right.x); + real_t x1 = left.Tdoty(right.x); + real_t y0 = left.Tdotx(right.y); + real_t y1 = left.Tdoty(right.y); left.x.x = x0; left.x.y = x1; @@ -352,12 +342,7 @@ namespace Godot public override bool Equals(object obj) { - if (obj is Transform2D) - { - return Equals((Transform2D)obj); - } - - return false; + return obj is Transform2D transform2D && Equals(transform2D); } public bool Equals(Transform2D other) diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index ce41886bfc..73a3252fdb 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -84,7 +84,7 @@ namespace Godot public real_t AngleToPoint(Vector2 to) { - return Mathf.Atan2(x - to.x, y - to.y); + return Mathf.Atan2(y - to.y, x - to.x); } public real_t Aspect() |