diff options
Diffstat (limited to 'modules/mono/glue/GodotSharp')
45 files changed, 8900 insertions, 0 deletions
diff --git a/modules/mono/glue/GodotSharp/.gitignore b/modules/mono/glue/GodotSharp/.gitignore new file mode 100644 index 0000000000..aa9f614a9d --- /dev/null +++ b/modules/mono/glue/GodotSharp/.gitignore @@ -0,0 +1,3 @@ +# Generated Godot API sources directories +GodotSharp/Generated +GodotSharpEditor/Generated diff --git a/modules/mono/glue/GodotSharp/GodotSharp.sln b/modules/mono/glue/GodotSharp/GodotSharp.sln new file mode 100644 index 0000000000..a496e36da3 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "GodotSharp\GodotSharp.csproj", "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpEditor", "GodotSharpEditor\GodotSharpEditor.csproj", "{8FBEC238-D944-4074-8548-B3B524305905}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.Build.0 = Release|Any CPU + {8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FBEC238-D944-4074-8548-B3B524305905}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FBEC238-D944-4074-8548-B3B524305905}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs new file mode 100644 index 0000000000..6a4f785551 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs @@ -0,0 +1,489 @@ +// file: core/math/aabb.h +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/math/aabb.cpp +// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct AABB : IEquatable<AABB> + { + private Vector3 _position; + private Vector3 _size; + + public Vector3 Position + { + get { return _position; } + set { _position = value; } + } + + public Vector3 Size + { + get { return _size; } + set { _size = value; } + } + + public Vector3 End + { + get { return _position + _size; } + set { _size = value - _position; } + } + + public bool Encloses(AABB with) + { + Vector3 src_min = _position; + Vector3 src_max = _position + _size; + Vector3 dst_min = with._position; + Vector3 dst_max = with._position + with._size; + + return src_min.x <= dst_min.x && + src_max.x > dst_max.x && + src_min.y <= dst_min.y && + src_max.y > dst_max.y && + src_min.z <= dst_min.z && + src_max.z > dst_max.z; + } + + public AABB Expand(Vector3 point) + { + Vector3 begin = _position; + Vector3 end = _position + _size; + + if (point.x < begin.x) + begin.x = point.x; + if (point.y < begin.y) + begin.y = point.y; + if (point.z < begin.z) + begin.z = point.z; + + if (point.x > end.x) + end.x = point.x; + if (point.y > end.y) + end.y = point.y; + if (point.z > end.z) + end.z = point.z; + + return new AABB(begin, end - begin); + } + + public real_t GetArea() + { + return _size.x * _size.y * _size.z; + } + + public Vector3 GetEndpoint(int idx) + { + switch (idx) + { + case 0: + return new Vector3(_position.x, _position.y, _position.z); + case 1: + return new Vector3(_position.x, _position.y, _position.z + _size.z); + case 2: + return new Vector3(_position.x, _position.y + _size.y, _position.z); + case 3: + return new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z); + case 4: + return new Vector3(_position.x + _size.x, _position.y, _position.z); + case 5: + return new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z); + case 6: + return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z); + case 7: + return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z); + default: + throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx)); + } + } + + public Vector3 GetLongestAxis() + { + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = _size.x; + + if (_size.y > max_size) + { + axis = new Vector3(0f, 1f, 0f); + max_size = _size.y; + } + + if (_size.z > max_size) + { + axis = new Vector3(0f, 0f, 1f); + } + + return axis; + } + + public Vector3.Axis GetLongestAxisIndex() + { + var axis = Vector3.Axis.X; + real_t max_size = _size.x; + + if (_size.y > max_size) + { + axis = Vector3.Axis.Y; + max_size = _size.y; + } + + if (_size.z > max_size) + { + axis = Vector3.Axis.Z; + } + + return axis; + } + + public real_t GetLongestAxisSize() + { + real_t max_size = _size.x; + + if (_size.y > max_size) + max_size = _size.y; + + if (_size.z > max_size) + max_size = _size.z; + + return max_size; + } + + public Vector3 GetShortestAxis() + { + var axis = new Vector3(1f, 0f, 0f); + real_t max_size = _size.x; + + if (_size.y < max_size) + { + axis = new Vector3(0f, 1f, 0f); + max_size = _size.y; + } + + if (_size.z < max_size) + { + axis = new Vector3(0f, 0f, 1f); + } + + return axis; + } + + public Vector3.Axis GetShortestAxisIndex() + { + var axis = Vector3.Axis.X; + real_t max_size = _size.x; + + if (_size.y < max_size) + { + axis = Vector3.Axis.Y; + max_size = _size.y; + } + + if (_size.z < max_size) + { + axis = Vector3.Axis.Z; + } + + return axis; + } + + public real_t GetShortestAxisSize() + { + real_t max_size = _size.x; + + if (_size.y < max_size) + max_size = _size.y; + + if (_size.z < max_size) + max_size = _size.z; + + return max_size; + } + + public Vector3 GetSupport(Vector3 dir) + { + Vector3 half_extents = _size * 0.5f; + Vector3 ofs = _position + half_extents; + + return ofs + new Vector3( + dir.x > 0f ? -half_extents.x : half_extents.x, + dir.y > 0f ? -half_extents.y : half_extents.y, + dir.z > 0f ? -half_extents.z : half_extents.z); + } + + public AABB Grow(real_t by) + { + var res = this; + + res._position.x -= by; + res._position.y -= by; + res._position.z -= by; + res._size.x += 2.0f * by; + res._size.y += 2.0f * by; + res._size.z += 2.0f * by; + + return res; + } + + public bool HasNoArea() + { + return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f; + } + + public bool HasNoSurface() + { + return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f; + } + + public bool HasPoint(Vector3 point) + { + if (point.x < _position.x) + return false; + if (point.y < _position.y) + return false; + if (point.z < _position.z) + return false; + if (point.x > _position.x + _size.x) + return false; + if (point.y > _position.y + _size.y) + return false; + if (point.z > _position.z + _size.z) + return false; + + return true; + } + + public AABB Intersection(AABB with) + { + Vector3 src_min = _position; + Vector3 src_max = _position + _size; + Vector3 dst_min = with._position; + Vector3 dst_max = with._position + with._size; + + Vector3 min, max; + + if (src_min.x > dst_max.x || src_max.x < dst_min.x) + { + return new AABB(); + } + + min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x; + max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x; + + if (src_min.y > dst_max.y || src_max.y < dst_min.y) + { + return new AABB(); + } + + min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y; + max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y; + + if (src_min.z > dst_max.z || src_max.z < dst_min.z) + { + return new AABB(); + } + + min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z; + max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z; + + return new AABB(min, max - min); + } + + public bool Intersects(AABB with) + { + if (_position.x >= with._position.x + with._size.x) + return false; + if (_position.x + _size.x <= with._position.x) + return false; + if (_position.y >= with._position.y + with._size.y) + return false; + if (_position.y + _size.y <= with._position.y) + return false; + if (_position.z >= with._position.z + with._size.z) + return false; + if (_position.z + _size.z <= with._position.z) + return false; + + return true; + } + + public bool IntersectsPlane(Plane plane) + { + Vector3[] points = + { + new Vector3(_position.x, _position.y, _position.z), + new Vector3(_position.x, _position.y, _position.z + _size.z), + new Vector3(_position.x, _position.y + _size.y, _position.z), + new Vector3(_position.x, _position.y + _size.y, _position.z + _size.z), + new Vector3(_position.x + _size.x, _position.y, _position.z), + new Vector3(_position.x + _size.x, _position.y, _position.z + _size.z), + new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z), + new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z) + }; + + bool over = false; + bool under = false; + + for (int i = 0; i < 8; i++) + { + if (plane.DistanceTo(points[i]) > 0) + over = true; + else + under = true; + } + + return under && over; + } + + public bool IntersectsSegment(Vector3 from, Vector3 to) + { + real_t min = 0f; + real_t max = 1f; + + for (int i = 0; i < 3; i++) + { + real_t segFrom = from[i]; + real_t segTo = to[i]; + real_t boxBegin = _position[i]; + real_t boxEnd = boxBegin + _size[i]; + real_t cmin, cmax; + + if (segFrom < segTo) + { + if (segFrom > boxEnd || segTo < boxBegin) + return false; + + real_t length = segTo - segFrom; + cmin = segFrom < boxBegin ? (boxBegin - segFrom) / length : 0f; + cmax = segTo > boxEnd ? (boxEnd - segFrom) / length : 1f; + } + else + { + if (segTo > boxEnd || segFrom < boxBegin) + return false; + + real_t length = segTo - segFrom; + cmin = segFrom > boxEnd ? (boxEnd - segFrom) / length : 0f; + cmax = segTo < boxBegin ? (boxBegin - segFrom) / length : 1f; + } + + if (cmin > min) + { + min = cmin; + } + + if (cmax < max) + max = cmax; + if (max < min) + return false; + } + + return true; + } + + public AABB Merge(AABB with) + { + Vector3 beg1 = _position; + Vector3 beg2 = with._position; + var end1 = new Vector3(_size.x, _size.y, _size.z) + beg1; + var end2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg2; + + var min = new Vector3( + beg1.x < beg2.x ? beg1.x : beg2.x, + beg1.y < beg2.y ? beg1.y : beg2.y, + beg1.z < beg2.z ? beg1.z : beg2.z + ); + + var max = new Vector3( + end1.x > end2.x ? end1.x : end2.x, + end1.y > end2.y ? end1.y : end2.y, + end1.z > end2.z ? end1.z : end2.z + ); + + return new AABB(min, max - min); + } + + // Constructors + public AABB(Vector3 position, Vector3 size) + { + _position = position; + _size = size; + } + public AABB(Vector3 position, real_t width, real_t height, real_t depth) + { + _position = position; + _size = new Vector3(width, height, depth); + } + public AABB(real_t x, real_t y, real_t z, Vector3 size) + { + _position = new Vector3(x, y, z); + _size = size; + } + public AABB(real_t x, real_t y, real_t z, real_t width, real_t height, real_t depth) + { + _position = new Vector3(x, y, z); + _size = new Vector3(width, height, depth); + } + + public static bool operator ==(AABB left, AABB right) + { + return left.Equals(right); + } + + public static bool operator !=(AABB left, AABB right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is AABB) + { + return Equals((AABB)obj); + } + + return false; + } + + public bool Equals(AABB other) + { + return _position == other._position && _size == other._size; + } + + public bool IsEqualApprox(AABB other) + { + return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size); + } + + public override int GetHashCode() + { + return _position.GetHashCode() ^ _size.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0} - {1}", new object[] + { + _position.ToString(), + _size.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("{0} - {1}", new object[] + { + _position.ToString(format), + _size.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs new file mode 100644 index 0000000000..aba1065498 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -0,0 +1,380 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot.Collections +{ + class ArraySafeHandle : SafeHandle + { + public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Array.godot_icall_Array_Dtor(handle); + return true; + } + } + + public class Array : IList, IDisposable + { + ArraySafeHandle safeHandle; + bool disposed = false; + + public Array() + { + 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; + } + + internal Array(IntPtr handle) + { + safeHandle = new ArraySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + + return safeHandle.DangerousGetHandle(); + } + + public Array Duplicate(bool deep = false) + { + return new Array(godot_icall_Array_Duplicate(GetPtr(), deep)); + } + + public Error Resize(int newSize) + { + return godot_icall_Array_Resize(GetPtr(), newSize); + } + + // IDisposable + + public void Dispose() + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + // IList + + public bool IsReadOnly => false; + + public bool IsFixedSize => false; + + public object this[int index] + { + get => godot_icall_Array_At(GetPtr(), index); + set => godot_icall_Array_SetAt(GetPtr(), index, value); + } + + public int Add(object value) => godot_icall_Array_Add(GetPtr(), value); + + public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value); + + public void Clear() => godot_icall_Array_Clear(GetPtr()); + + public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value); + + 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); + + // 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 (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, index); + } + + // IEnumerable + + public IEnumerator GetEnumerator() + { + int count = Count; + + for (int i = 0; i < count; i++) + { + yield return this[i]; + } + } + + public override string ToString() + { + return godot_icall_Array_ToString(GetPtr()); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At(IntPtr ptr, int index); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + 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); + + [MethodImpl(MethodImplOptions.InternalCall)] + 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, System.Array array, int arrayIndex); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item); + + [MethodImpl(MethodImplOptions.InternalCall)] + 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); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Array_ToString(IntPtr ptr); + } + + public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T> + { + Array objectArray; + + internal static int elemTypeEncoding; + internal static IntPtr elemTypeClass; + + static Array() + { + Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass); + } + + public Array() + { + 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; + } + + internal Array(IntPtr handle) + { + objectArray = new Array(handle); + } + + internal Array(ArraySafeHandle handle) + { + objectArray = new Array(handle); + } + + internal IntPtr GetPtr() + { + return objectArray.GetPtr(); + } + + public static explicit operator Array(Array<T> from) + { + return from.objectArray; + } + + public Array<T> Duplicate(bool deep = false) + { + return new Array<T>(objectArray.Duplicate(deep)); + } + + public Error Resize(int newSize) + { + return objectArray.Resize(newSize); + } + + // IList<T> + + public T this[int index] + { + get + { + return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); + } + set + { + objectArray[index] = value; + } + } + + 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 + { + return objectArray.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectArray.IsReadOnly; + } + } + + public void Add(T item) + { + objectArray.Add(item); + } + + public void Clear() + { + objectArray.Clear(); + } + + public bool Contains(T item) + { + return objectArray.Contains(item); + } + + public void CopyTo(T[] 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 This may be quite slow because every element access is an internal call. + // It could be moved entirely to an internal call if we find out how to do the cast there. + + int count = objectArray.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++) + { + array[arrayIndex] = (T)this[i]; + arrayIndex++; + } + } + + public bool Remove(T item) + { + return Array.godot_icall_Array_Remove(GetPtr(), item); + } + + // IEnumerable<T> + + public IEnumerator<T> GetEnumerator() + { + int count = objectArray.Count; + + for (int i = 0; i < count; i++) + { + yield return (T)this[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override string ToString() => objectArray.ToString(); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs new file mode 100644 index 0000000000..6adf044886 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class ExportAttribute : Attribute + { + private PropertyHint hint; + private string hintString; + + public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "") + { + this.hint = hint; + this.hintString = hintString; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs new file mode 100644 index 0000000000..55848769d5 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Method)] + internal class GodotMethodAttribute : Attribute + { + private string methodName; + + public string MethodName { get { return methodName; } } + + public GodotMethodAttribute(string methodName) + { + this.methodName = methodName; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs new file mode 100644 index 0000000000..1bf6d5199a --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs @@ -0,0 +1,28 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class RemoteAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class SyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class MasterAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class PuppetAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class SlaveAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class RemoteSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class MasterSyncAttribute : Attribute {} + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] + public class PuppetSyncAttribute : Attribute {} +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs new file mode 100644 index 0000000000..3957387be9 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Delegate)] + public class SignalAttribute : Attribute + { + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs new file mode 100644 index 0000000000..d0437409af --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace Godot +{ + [AttributeUsage(AttributeTargets.Class)] + public class ToolAttribute : Attribute {} +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs new file mode 100644 index 0000000000..d38589013e --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -0,0 +1,700 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Basis : IEquatable<Basis> + { + // 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 => Column0; + set => Column0 = value; + } + + /// <summary> + /// Returns the basis matrix’s y vector. + /// This is equivalent to <see cref="Column1"/>. + /// </summary> + public Vector3 y + { + 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 => Column2; + set => Column2 = value; + } + + public Vector3 Row0; + public Vector3 Row1; + public Vector3 Row2; + + 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 => 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 Vector3 Scale + { + get + { + real_t detSign = Mathf.Sign(Determinant()); + return detSign * new Vector3 + ( + 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 columnIndex] + { + get + { + switch (columnIndex) + { + case 0: + return Column0; + case 1: + return Column1; + case 2: + return Column2; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (columnIndex) + { + case 0: + Column0 = value; + return; + case 1: + Column1 = value; + return; + case 2: + Column2 = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public real_t this[int columnIndex, int rowIndex] + { + get + { + switch (columnIndex) + { + case 0: + return Column0[rowIndex]; + case 1: + return Column1[rowIndex]; + case 2: + return Column2[rowIndex]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (columnIndex) + { + case 0: + { + var column0 = Column0; + column0[rowIndex] = value; + Column0 = column0; + return; + } + case 1: + { + var column1 = Column1; + column1[rowIndex] = value; + Column1 = column1; + return; + } + case 2: + { + var column2 = Column2; + column2[rowIndex] = value; + Column2 = column2; + return; + } + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal Quat RotationQuat() + { + Basis orthonormalizedBasis = Orthonormalized(); + real_t det = orthonormalizedBasis.Determinant(); + if (det < 0) + { + // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles. + orthonormalizedBasis = orthonormalizedBasis.Scaled(Vector3.NegOne); + } + + return orthonormalizedBasis.Quat(); + } + + internal void SetQuatScale(Quat quat, Vector3 scale) + { + SetDiagonal(scale); + Rotate(quat); + } + + private void Rotate(Quat quat) + { + this *= new Basis(quat); + } + + private void SetDiagonal(Vector3 diagonal) + { + 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() + { + 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]; + + return Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; + } + + public Vector3 GetEuler() + { + Basis m = Orthonormalized(); + + Vector3 euler; + euler.z = 0.0f; + + real_t mzy = m.Row1[2]; + + if (mzy < 1.0f) + { + if (mzy > -1.0f) + { + euler.x = Mathf.Asin(-mzy); + 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.Row0[1], m.Row0[0]); + } + } + else + { + euler.x = -Mathf.Pi * 0.5f; + 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; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + var row = orth.GetRow(i); + + real_t v = row[j]; + + if (v > 0.5f) + v = 1.0f; + else if (v < -0.5f) + v = -1.0f; + else + v = 0f; + + row[j] = v; + + orth.SetRow(i, row); + } + } + + for (int i = 0; i < 24; i++) + { + if (orth == _orthoBases[i]) + return i; + } + + return 0; + } + + public Basis Inverse() + { + 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 = Row0[0] * cofac00 + Row0[1] * cofac10 + Row0[2] * cofac20; + + if (det == 0) + throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); + + real_t detInv = 1.0f / det; + + 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 + ( + cofac00 * detInv, cofac01 * detInv, cofac02 * detInv, + cofac10 * detInv, cofac11 * detInv, cofac12 * detInv, + cofac20 * detInv, cofac21 * detInv, cofac22 * detInv + ); + } + + public Basis Orthonormalized() + { + Vector3 column0 = GetColumn(0); + Vector3 column1 = GetColumn(1); + Vector3 column2 = GetColumn(2); + + column0.Normalize(); + column1 = column1 - column0 * column0.Dot(column1); + column1.Normalize(); + column2 = column2 - column0 * column0.Dot(column2) - column1 * column1.Dot(column2); + column2.Normalize(); + + return new Basis(column0, column1, column2); + } + + public Basis Rotated(Vector3 axis, real_t phi) + { + return new Basis(axis, phi) * this; + } + + public Basis Scaled(Vector3 scale) + { + var b = this; + b.Row0 *= scale.x; + b.Row1 *= scale.y; + b.Row2 *= scale.z; + return b; + } + + public Basis Slerp(Basis target, real_t t) + { + var from = new Quat(this); + var to = new Quat(target); + + var b = new Basis(from.Slerp(to, t)); + b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), t); + b.Row1 *= Mathf.Lerp(Row1.Length(), target.Row1.Length(), t); + b.Row2 *= Mathf.Lerp(Row2.Length(), target.Row2.Length(), t); + + return b; + } + + public real_t Tdotx(Vector3 with) + { + return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2]; + } + + public real_t Tdoty(Vector3 with) + { + return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2]; + } + + public real_t Tdotz(Vector3 with) + { + 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.Row0[1]; + tr.Row0[1] = tr.Row1[0]; + tr.Row1[0] = temp; + + temp = tr.Row0[2]; + tr.Row0[2] = tr.Row2[0]; + tr.Row2[0] = temp; + + temp = tr.Row1[2]; + tr.Row1[2] = tr.Row2[1]; + tr.Row2[1] = temp; + + return tr; + } + + public Vector3 Xform(Vector3 v) + { + return new Vector3 + ( + this.Row0.Dot(v), + this.Row1.Dot(v), + this.Row2.Dot(v) + ); + } + + public Vector3 XformInv(Vector3 v) + { + return new Vector3 + ( + 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 = 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( + (Row2[1] - Row1[2]) * inv_s, + (Row0[2] - Row2[0]) * inv_s, + (Row1[0] - Row0[1]) * inv_s, + s * 0.25f + ); + } + + if (Row0[0] > Row1[1] && Row0[0] > Row2[2]) + { + 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, + (Row0[1] + Row1[0]) * inv_s, + (Row0[2] + Row2[0]) * inv_s, + (Row2[1] - Row1[2]) * inv_s + ); + } + + if (Row1[1] > Row2[2]) + { + real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (Row0[1] + Row1[0]) * inv_s, + s * 0.25f, + (Row1[2] + Row2[1]) * inv_s, + (Row0[2] - Row2[0]) * inv_s + ); + } + else + { + real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f; + real_t inv_s = 1f / s; + return new Quat( + (Row0[2] + Row2[0]) * inv_s, + (Row1[2] + Row2[1]) * inv_s, + s * 0.25f, + (Row1[0] - Row0[1]) * inv_s + ); + } + } + + private static readonly Basis[] _orthoBases = { + new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f), + new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f), + new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f), + new Basis(0f, 1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f), + new Basis(1f, 0f, 0f, 0f, 0f, -1f, 0f, 1f, 0f), + new Basis(0f, 0f, 1f, 1f, 0f, 0f, 0f, 1f, 0f), + new Basis(-1f, 0f, 0f, 0f, 0f, 1f, 0f, 1f, 0f), + new Basis(0f, 0f, -1f, -1f, 0f, 0f, 0f, 1f, 0f), + new Basis(1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, -1f), + new Basis(0f, 1f, 0f, 1f, 0f, 0f, 0f, 0f, -1f), + new Basis(-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, -1f), + new Basis(0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, -1f), + new Basis(1f, 0f, 0f, 0f, 0f, 1f, 0f, -1f, 0f), + new Basis(0f, 0f, -1f, 1f, 0f, 0f, 0f, -1f, 0f), + new Basis(-1f, 0f, 0f, 0f, 0f, -1f, 0f, -1f, 0f), + new Basis(0f, 0f, 1f, -1f, 0f, 0f, 0f, -1f, 0f), + new Basis(0f, 0f, 1f, 0f, 1f, 0f, -1f, 0f, 0f), + new Basis(0f, -1f, 0f, 0f, 0f, 1f, -1f, 0f, 0f), + new Basis(0f, 0f, -1f, 0f, -1f, 0f, -1f, 0f, 0f), + new Basis(0f, 1f, 0f, 0f, 0f, -1f, -1f, 0f, 0f), + new Basis(0f, 0f, 1f, 0f, -1f, 0f, 1f, 0f, 0f), + new Basis(0f, 1f, 0f, 0f, 0f, 1f, 1f, 0f, 0f), + new Basis(0f, 0f, -1f, 0f, 1f, 0f, 1f, 0f, 0f), + new Basis(0f, -1f, 0f, 0f, 0f, -1f, 1f, 0f, 0f) + }; + + private static readonly Basis _identity = new Basis(1, 0, 0, 0, 1, 0, 0, 0, 1); + private static readonly Basis _flipX = new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1); + private static readonly Basis _flipY = new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1); + private static readonly Basis _flipZ = new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1); + + public static Basis Identity { get { return _identity; } } + public static Basis FlipX { get { return _flipX; } } + public static Basis FlipY { get { return _flipY; } } + public static Basis FlipZ { get { return _flipZ; } } + + public Basis(Quat quat) + { + real_t s = 2.0f / quat.LengthSquared; + + real_t xs = quat.x * s; + real_t ys = quat.y * s; + real_t zs = quat.z * s; + real_t wx = quat.w * xs; + real_t wy = quat.w * ys; + real_t wz = quat.w * zs; + real_t xx = quat.x * xs; + real_t xy = quat.x * ys; + real_t xz = quat.x * zs; + real_t yy = quat.y * ys; + real_t yz = quat.y * zs; + real_t zz = quat.z * zs; + + 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) + { + real_t c; + real_t s; + + c = Mathf.Cos(euler.x); + s = Mathf.Sin(euler.x); + var xmat = new Basis(1, 0, 0, 0, c, -s, 0, s, c); + + c = Mathf.Cos(euler.y); + s = Mathf.Sin(euler.y); + var ymat = new Basis(c, 0, s, 0, 1, 0, -s, 0, c); + + c = Mathf.Cos(euler.z); + s = Mathf.Sin(euler.z); + var zmat = new Basis(c, -s, 0, s, c, 0, 0, 0, 1); + + this = ymat * xmat * zmat; + } + + public Basis(Vector3 axis, real_t phi) + { + Vector3 axisSq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z); + real_t cosine = Mathf.Cos(phi); + Row0.x = axisSq.x + cosine * (1.0f - axisSq.x); + Row1.y = axisSq.y + cosine * (1.0f - axisSq.y); + Row2.z = axisSq.z + cosine * (1.0f - axisSq.z); + + real_t sine = Mathf.Sin(phi); + real_t t = 1.0f - cosine; + + real_t xyzt = axis.x * axis.y * t; + real_t zyxs = axis.z * sine; + Row0.y = xyzt - zyxs; + Row1.x = xyzt + zyxs; + + xyzt = axis.x * axis.z * t; + zyxs = axis.y * sine; + Row0.z = xyzt + zyxs; + Row2.x = xyzt - zyxs; + + xyzt = axis.y * axis.z * t; + zyxs = axis.x * sine; + Row1.z = xyzt - zyxs; + Row2.y = xyzt + zyxs; + } + + public Basis(Vector3 column0, Vector3 column1, Vector3 column2) + { + 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: + // Column0 = column0; + // Column1 = column1; + // Column2 = column2; + // We need to assign the struct fields here first so we can't do it that way... + } + + // Arguments are named such that xy is equal to calling x.y + internal Basis(real_t xx, real_t yx, real_t zx, real_t xy, real_t yy, real_t zy, real_t xz, real_t yz, real_t zz) + { + Row0 = new Vector3(xx, yx, zx); + Row1 = new Vector3(xy, yy, zy); + Row2 = new Vector3(xz, yz, zz); + } + + public static Basis operator *(Basis left, Basis right) + { + return new Basis + ( + 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) + ); + } + + public static bool operator ==(Basis left, Basis right) + { + return left.Equals(right); + } + + public static bool operator !=(Basis left, Basis right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Basis) + { + return Equals((Basis)obj); + } + + return false; + } + + public bool Equals(Basis other) + { + return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2); + } + + public bool IsEqualApprox(Basis other) + { + return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2); + } + + public override int GetHashCode() + { + return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + Row0.ToString(), + Row1.ToString(), + Row2.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + Row0.ToString(format), + Row1.ToString(format), + Row2.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs new file mode 100644 index 0000000000..1d1a49945f --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -0,0 +1,755 @@ +using System; +using System.Runtime.InteropServices; + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Color : IEquatable<Color> + { + public float r; + public float g; + public float b; + public float a; + + public int r8 + { + get + { + return (int)Math.Round(r * 255.0f); + } + set + { + r = value / 255.0f; + } + } + + public int g8 + { + get + { + return (int)Math.Round(g * 255.0f); + } + set + { + g = value / 255.0f; + } + } + + public int b8 + { + get + { + return (int)Math.Round(b * 255.0f); + } + set + { + b = value / 255.0f; + } + } + + public int a8 + { + get + { + return (int)Math.Round(a * 255.0f); + } + set + { + a = value / 255.0f; + } + } + + public float h + { + get + { + float max = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); + + float delta = max - min; + + if (delta == 0) + return 0; + + float h; + + if (r == max) + h = (g - b) / delta; // Between yellow & magenta + else if (g == max) + h = 2 + (b - r) / delta; // Between cyan & yellow + else + h = 4 + (r - g) / delta; // Between magenta & cyan + + h /= 6.0f; + + if (h < 0) + h += 1.0f; + + return h; + } + set + { + this = FromHsv(value, s, v, a); + } + } + + public float s + { + get + { + float max = Math.Max(r, Math.Max(g, b)); + float min = Math.Min(r, Math.Min(g, b)); + + float delta = max - min; + + return max != 0 ? delta / max : 0; + } + set + { + this = FromHsv(h, value, v, a); + } + } + + public float v + { + get + { + return Math.Max(r, Math.Max(g, b)); + } + set + { + this = FromHsv(h, s, value, a); + } + } + + public static Color ColorN(string name, float alpha = 1f) + { + name = name.Replace(" ", String.Empty); + name = name.Replace("-", String.Empty); + name = name.Replace("_", String.Empty); + name = name.Replace("'", String.Empty); + name = name.Replace(".", String.Empty); + name = name.ToLower(); + + if (!Colors.namedColors.ContainsKey(name)) + { + throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}"); + } + + Color color = Colors.namedColors[name]; + color.a = alpha; + return color; + } + + public float this[int index] + { + get + { + switch (index) + { + case 0: + return r; + case 1: + return g; + case 2: + return b; + case 3: + return a; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + r = value; + return; + case 1: + g = value; + return; + case 2: + b = value; + return; + case 3: + a = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public void ToHsv(out float hue, out float saturation, out float value) + { + float max = (float)Mathf.Max(r, Mathf.Max(g, b)); + float min = (float)Mathf.Min(r, Mathf.Min(g, b)); + + float delta = max - min; + + if (delta == 0) + { + hue = 0; + } + else + { + if (r == max) + hue = (g - b) / delta; // Between yellow & magenta + else if (g == max) + hue = 2 + (b - r) / delta; // Between cyan & yellow + else + hue = 4 + (r - g) / delta; // Between magenta & cyan + + hue /= 6.0f; + + if (hue < 0) + hue += 1.0f; + } + + saturation = max == 0 ? 0 : 1f - 1f * min / max; + value = max; + } + + public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f) + { + if (saturation == 0) + { + // acp_hromatic (grey) + return new Color(value, value, value, alpha); + } + + int i; + float f, p, q, t; + + hue *= 6.0f; + hue %= 6f; + i = (int)hue; + + f = hue - i; + p = value * (1 - saturation); + q = value * (1 - saturation * f); + t = value * (1 - saturation * (1 - f)); + + switch (i) + { + case 0: // Red is the dominant color + return new Color(value, t, p, alpha); + case 1: // Green is the dominant color + return new Color(q, value, p, alpha); + case 2: + return new Color(p, value, t, alpha); + case 3: // Blue is the dominant color + return new Color(p, q, value, alpha); + case 4: + return new Color(t, p, value, alpha); + default: // (5) Red is the dominant color + return new Color(value, p, q, alpha); + } + } + + public Color Blend(Color over) + { + Color res; + + float sa = 1.0f - over.a; + res.a = a * sa + over.a; + + if (res.a == 0) + { + return new Color(0, 0, 0, 0); + } + + res.r = (r * a * sa + over.r * over.a) / res.a; + res.g = (g * a * sa + over.g * over.a) / res.a; + res.b = (b * a * sa + over.b * over.a) / res.a; + + return res; + } + + public Color Contrasted() + { + return new Color( + (r + 0.5f) % 1.0f, + (g + 0.5f) % 1.0f, + (b + 0.5f) % 1.0f, + a + ); + } + + public Color Darkened(float amount) + { + Color res = this; + res.r = res.r * (1.0f - amount); + res.g = res.g * (1.0f - amount); + res.b = res.b * (1.0f - amount); + return res; + } + + public Color Inverted() + { + return new Color( + 1.0f - r, + 1.0f - g, + 1.0f - b, + a + ); + } + + public Color Lightened(float amount) + { + Color res = this; + res.r = res.r + (1.0f - res.r) * amount; + res.g = res.g + (1.0f - res.g) * amount; + res.b = res.b + (1.0f - res.b) * amount; + return res; + } + + public Color LinearInterpolate(Color c, float t) + { + var res = this; + + res.r += t * (c.r - r); + res.g += t * (c.g - g); + res.b += t * (c.b - b); + res.a += t * (c.a - a); + + return res; + } + + public uint ToAbgr32() + { + uint c = (byte)Math.Round(a * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(r * 255); + + return c; + } + + public ulong ToAbgr64() + { + ulong c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); + + return c; + } + + public uint ToArgb32() + { + uint c = (byte)Math.Round(a * 255); + c <<= 8; + c |= (byte)Math.Round(r * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + + return c; + } + + public ulong ToArgb64() + { + ulong c = (ushort)Math.Round(a * 65535); + c <<= 16; + c |= (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + + return c; + } + + public uint ToRgba32() + { + uint c = (byte)Math.Round(r * 255); + c <<= 8; + c |= (byte)Math.Round(g * 255); + c <<= 8; + c |= (byte)Math.Round(b * 255); + c <<= 8; + c |= (byte)Math.Round(a * 255); + + return c; + } + + public ulong ToRgba64() + { + ulong c = (ushort)Math.Round(r * 65535); + c <<= 16; + c |= (ushort)Math.Round(g * 65535); + c <<= 16; + c |= (ushort)Math.Round(b * 65535); + c <<= 16; + c |= (ushort)Math.Round(a * 65535); + + return c; + } + + public string ToHtml(bool includeAlpha = true) + { + var txt = string.Empty; + + txt += ToHex32(r); + txt += ToHex32(g); + txt += ToHex32(b); + + if (includeAlpha) + txt = ToHex32(a) + txt; + + return txt; + } + + // Constructors + public Color(float r, float g, float b, float a = 1.0f) + { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public Color(uint rgba) + { + a = (rgba & 0xFF) / 255.0f; + rgba >>= 8; + b = (rgba & 0xFF) / 255.0f; + rgba >>= 8; + g = (rgba & 0xFF) / 255.0f; + rgba >>= 8; + r = (rgba & 0xFF) / 255.0f; + } + + public Color(ulong rgba) + { + a = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + b = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + g = (rgba & 0xFFFF) / 65535.0f; + rgba >>= 16; + r = (rgba & 0xFFFF) / 65535.0f; + } + + private static int ParseCol8(string str, int ofs) + { + int ig = 0; + + for (int i = 0; i < 2; i++) + { + int c = str[i + ofs]; + int v; + + if (c >= '0' && c <= '9') + { + v = c - '0'; + } + else if (c >= 'a' && c <= 'f') + { + v = c - 'a'; + v += 10; + } + else if (c >= 'A' && c <= 'F') + { + v = c - 'A'; + v += 10; + } + else + { + return -1; + } + + if (i == 0) + ig += v * 16; + else + ig += v; + } + + return ig; + } + + private String ToHex32(float val) + { + int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255)); + + var ret = string.Empty; + + for (int i = 0; i < 2; i++) + { + char c; + int lv = v & 0xF; + + if (lv < 10) + c = (char)('0' + lv); + else + c = (char)('a' + lv - 10); + + v >>= 4; + ret = c + ret; + } + + return ret; + } + + internal static bool HtmlIsValid(string color) + { + if (color.Length == 0) + return false; + + if (color[0] == '#') + color = color.Substring(1, color.Length - 1); + + bool alpha; + + switch (color.Length) + { + case 8: + alpha = true; + break; + case 6: + alpha = false; + break; + default: + return false; + } + + if (alpha) + { + if (ParseCol8(color, 0) < 0) + return false; + } + + int from = alpha ? 2 : 0; + + if (ParseCol8(color, from + 0) < 0) + return false; + if (ParseCol8(color, from + 2) < 0) + return false; + if (ParseCol8(color, from + 4) < 0) + return false; + + return true; + } + + public static Color Color8(byte r8, byte g8, byte b8, byte a8 = 255) + { + return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f); + } + + public Color(string rgba) + { + if (rgba.Length == 0) + { + r = 0f; + g = 0f; + b = 0f; + a = 1.0f; + return; + } + + if (rgba[0] == '#') + rgba = rgba.Substring(1); + + bool alpha; + + if (rgba.Length == 8) + { + alpha = true; + } + else if (rgba.Length == 6) + { + alpha = false; + } + else + { + throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba); + } + + if (alpha) + { + a = ParseCol8(rgba, 0) / 255f; + + if (a < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba); + } + else + { + a = 1.0f; + } + + int from = alpha ? 2 : 0; + + r = ParseCol8(rgba, from + 0) / 255f; + + if (r < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba); + + g = ParseCol8(rgba, from + 2) / 255f; + + if (g < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba); + + b = ParseCol8(rgba, from + 4) / 255f; + + if (b < 0) + throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba); + } + + public static Color operator +(Color left, Color right) + { + left.r += right.r; + left.g += right.g; + left.b += right.b; + left.a += right.a; + return left; + } + + public static Color operator -(Color left, Color right) + { + left.r -= right.r; + left.g -= right.g; + left.b -= right.b; + left.a -= right.a; + return left; + } + + public static Color operator -(Color color) + { + return Colors.White - color; + } + + public static Color operator *(Color color, float scale) + { + color.r *= scale; + color.g *= scale; + color.b *= scale; + color.a *= scale; + return color; + } + + public static Color operator *(float scale, Color color) + { + color.r *= scale; + color.g *= scale; + color.b *= scale; + color.a *= scale; + return color; + } + + public static Color operator *(Color left, Color right) + { + left.r *= right.r; + left.g *= right.g; + left.b *= right.b; + left.a *= right.a; + return left; + } + + public static Color operator /(Color color, float scale) + { + color.r /= scale; + color.g /= scale; + color.b /= scale; + color.a /= scale; + return color; + } + + public static Color operator /(Color left, Color right) + { + left.r /= right.r; + left.g /= right.g; + left.b /= right.b; + left.a /= right.a; + return left; + } + + public static bool operator ==(Color left, Color right) + { + return left.Equals(right); + } + + public static bool operator !=(Color left, Color right) + { + return !left.Equals(right); + } + + public static bool operator <(Color left, Color right) + { + if (Mathf.IsEqualApprox(left.r, right.r)) + { + if (Mathf.IsEqualApprox(left.g, right.g)) + { + if (Mathf.IsEqualApprox(left.b, right.b)) + return left.a < right.a; + return left.b < right.b; + } + + return left.g < right.g; + } + + return left.r < right.r; + } + + public static bool operator >(Color left, Color right) + { + if (Mathf.IsEqualApprox(left.r, right.r)) + { + if (Mathf.IsEqualApprox(left.g, right.g)) + { + if (Mathf.IsEqualApprox(left.b, right.b)) + return left.a > right.a; + return left.b > right.b; + } + + return left.g > right.g; + } + + return left.r > right.r; + } + + public override bool Equals(object obj) + { + if (obj is Color) + { + return Equals((Color)obj); + } + + return false; + } + + public bool Equals(Color other) + { + return r == other.r && g == other.g && b == other.b && a == other.a; + } + + public bool IsEqualApprox(Color other) + { + return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); + } + + public override int GetHashCode() + { + return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.ToString()); + } + + public string ToString(string format) + { + return String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format)); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs new file mode 100644 index 0000000000..f41f5e9fc8 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; + +namespace Godot +{ + public static class Colors + { + // Color names and values are derived from core/color_names.inc + internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> { + {"aliceblue", new Color(0.94f, 0.97f, 1.00f)}, + {"antiquewhite", new Color(0.98f, 0.92f, 0.84f)}, + {"aqua", new Color(0.00f, 1.00f, 1.00f)}, + {"aquamarine", new Color(0.50f, 1.00f, 0.83f)}, + {"azure", new Color(0.94f, 1.00f, 1.00f)}, + {"beige", new Color(0.96f, 0.96f, 0.86f)}, + {"bisque", new Color(1.00f, 0.89f, 0.77f)}, + {"black", new Color(0.00f, 0.00f, 0.00f)}, + {"blanchedalmond", new Color(1.00f, 0.92f, 0.80f)}, + {"blue", new Color(0.00f, 0.00f, 1.00f)}, + {"blueviolet", new Color(0.54f, 0.17f, 0.89f)}, + {"brown", new Color(0.65f, 0.16f, 0.16f)}, + {"burlywood", new Color(0.87f, 0.72f, 0.53f)}, + {"cadetblue", new Color(0.37f, 0.62f, 0.63f)}, + {"chartreuse", new Color(0.50f, 1.00f, 0.00f)}, + {"chocolate", new Color(0.82f, 0.41f, 0.12f)}, + {"coral", new Color(1.00f, 0.50f, 0.31f)}, + {"cornflower", new Color(0.39f, 0.58f, 0.93f)}, + {"cornsilk", new Color(1.00f, 0.97f, 0.86f)}, + {"crimson", new Color(0.86f, 0.08f, 0.24f)}, + {"cyan", new Color(0.00f, 1.00f, 1.00f)}, + {"darkblue", new Color(0.00f, 0.00f, 0.55f)}, + {"darkcyan", new Color(0.00f, 0.55f, 0.55f)}, + {"darkgoldenrod", new Color(0.72f, 0.53f, 0.04f)}, + {"darkgray", new Color(0.66f, 0.66f, 0.66f)}, + {"darkgreen", new Color(0.00f, 0.39f, 0.00f)}, + {"darkkhaki", new Color(0.74f, 0.72f, 0.42f)}, + {"darkmagenta", new Color(0.55f, 0.00f, 0.55f)}, + {"darkolivegreen", new Color(0.33f, 0.42f, 0.18f)}, + {"darkorange", new Color(1.00f, 0.55f, 0.00f)}, + {"darkorchid", new Color(0.60f, 0.20f, 0.80f)}, + {"darkred", new Color(0.55f, 0.00f, 0.00f)}, + {"darksalmon", new Color(0.91f, 0.59f, 0.48f)}, + {"darkseagreen", new Color(0.56f, 0.74f, 0.56f)}, + {"darkslateblue", new Color(0.28f, 0.24f, 0.55f)}, + {"darkslategray", new Color(0.18f, 0.31f, 0.31f)}, + {"darkturquoise", new Color(0.00f, 0.81f, 0.82f)}, + {"darkviolet", new Color(0.58f, 0.00f, 0.83f)}, + {"deeppink", new Color(1.00f, 0.08f, 0.58f)}, + {"deepskyblue", new Color(0.00f, 0.75f, 1.00f)}, + {"dimgray", new Color(0.41f, 0.41f, 0.41f)}, + {"dodgerblue", new Color(0.12f, 0.56f, 1.00f)}, + {"firebrick", new Color(0.70f, 0.13f, 0.13f)}, + {"floralwhite", new Color(1.00f, 0.98f, 0.94f)}, + {"forestgreen", new Color(0.13f, 0.55f, 0.13f)}, + {"fuchsia", new Color(1.00f, 0.00f, 1.00f)}, + {"gainsboro", new Color(0.86f, 0.86f, 0.86f)}, + {"ghostwhite", new Color(0.97f, 0.97f, 1.00f)}, + {"gold", new Color(1.00f, 0.84f, 0.00f)}, + {"goldenrod", new Color(0.85f, 0.65f, 0.13f)}, + {"gray", new Color(0.75f, 0.75f, 0.75f)}, + {"green", new Color(0.00f, 1.00f, 0.00f)}, + {"greenyellow", new Color(0.68f, 1.00f, 0.18f)}, + {"honeydew", new Color(0.94f, 1.00f, 0.94f)}, + {"hotpink", new Color(1.00f, 0.41f, 0.71f)}, + {"indianred", new Color(0.80f, 0.36f, 0.36f)}, + {"indigo", new Color(0.29f, 0.00f, 0.51f)}, + {"ivory", new Color(1.00f, 1.00f, 0.94f)}, + {"khaki", new Color(0.94f, 0.90f, 0.55f)}, + {"lavender", new Color(0.90f, 0.90f, 0.98f)}, + {"lavenderblush", new Color(1.00f, 0.94f, 0.96f)}, + {"lawngreen", new Color(0.49f, 0.99f, 0.00f)}, + {"lemonchiffon", new Color(1.00f, 0.98f, 0.80f)}, + {"lightblue", new Color(0.68f, 0.85f, 0.90f)}, + {"lightcoral", new Color(0.94f, 0.50f, 0.50f)}, + {"lightcyan", new Color(0.88f, 1.00f, 1.00f)}, + {"lightgoldenrod", new Color(0.98f, 0.98f, 0.82f)}, + {"lightgray", new Color(0.83f, 0.83f, 0.83f)}, + {"lightgreen", new Color(0.56f, 0.93f, 0.56f)}, + {"lightpink", new Color(1.00f, 0.71f, 0.76f)}, + {"lightsalmon", new Color(1.00f, 0.63f, 0.48f)}, + {"lightseagreen", new Color(0.13f, 0.70f, 0.67f)}, + {"lightskyblue", new Color(0.53f, 0.81f, 0.98f)}, + {"lightslategray", new Color(0.47f, 0.53f, 0.60f)}, + {"lightsteelblue", new Color(0.69f, 0.77f, 0.87f)}, + {"lightyellow", new Color(1.00f, 1.00f, 0.88f)}, + {"lime", new Color(0.00f, 1.00f, 0.00f)}, + {"limegreen", new Color(0.20f, 0.80f, 0.20f)}, + {"linen", new Color(0.98f, 0.94f, 0.90f)}, + {"magenta", new Color(1.00f, 0.00f, 1.00f)}, + {"maroon", new Color(0.69f, 0.19f, 0.38f)}, + {"mediumaquamarine", new Color(0.40f, 0.80f, 0.67f)}, + {"mediumblue", new Color(0.00f, 0.00f, 0.80f)}, + {"mediumorchid", new Color(0.73f, 0.33f, 0.83f)}, + {"mediumpurple", new Color(0.58f, 0.44f, 0.86f)}, + {"mediumseagreen", new Color(0.24f, 0.70f, 0.44f)}, + {"mediumslateblue", new Color(0.48f, 0.41f, 0.93f)}, + {"mediumspringgreen", new Color(0.00f, 0.98f, 0.60f)}, + {"mediumturquoise", new Color(0.28f, 0.82f, 0.80f)}, + {"mediumvioletred", new Color(0.78f, 0.08f, 0.52f)}, + {"midnightblue", new Color(0.10f, 0.10f, 0.44f)}, + {"mintcream", new Color(0.96f, 1.00f, 0.98f)}, + {"mistyrose", new Color(1.00f, 0.89f, 0.88f)}, + {"moccasin", new Color(1.00f, 0.89f, 0.71f)}, + {"navajowhite", new Color(1.00f, 0.87f, 0.68f)}, + {"navyblue", new Color(0.00f, 0.00f, 0.50f)}, + {"oldlace", new Color(0.99f, 0.96f, 0.90f)}, + {"olive", new Color(0.50f, 0.50f, 0.00f)}, + {"olivedrab", new Color(0.42f, 0.56f, 0.14f)}, + {"orange", new Color(1.00f, 0.65f, 0.00f)}, + {"orangered", new Color(1.00f, 0.27f, 0.00f)}, + {"orchid", new Color(0.85f, 0.44f, 0.84f)}, + {"palegoldenrod", new Color(0.93f, 0.91f, 0.67f)}, + {"palegreen", new Color(0.60f, 0.98f, 0.60f)}, + {"paleturquoise", new Color(0.69f, 0.93f, 0.93f)}, + {"palevioletred", new Color(0.86f, 0.44f, 0.58f)}, + {"papayawhip", new Color(1.00f, 0.94f, 0.84f)}, + {"peachpuff", new Color(1.00f, 0.85f, 0.73f)}, + {"peru", new Color(0.80f, 0.52f, 0.25f)}, + {"pink", new Color(1.00f, 0.75f, 0.80f)}, + {"plum", new Color(0.87f, 0.63f, 0.87f)}, + {"powderblue", new Color(0.69f, 0.88f, 0.90f)}, + {"purple", new Color(0.63f, 0.13f, 0.94f)}, + {"rebeccapurple", new Color(0.40f, 0.20f, 0.60f)}, + {"red", new Color(1.00f, 0.00f, 0.00f)}, + {"rosybrown", new Color(0.74f, 0.56f, 0.56f)}, + {"royalblue", new Color(0.25f, 0.41f, 0.88f)}, + {"saddlebrown", new Color(0.55f, 0.27f, 0.07f)}, + {"salmon", new Color(0.98f, 0.50f, 0.45f)}, + {"sandybrown", new Color(0.96f, 0.64f, 0.38f)}, + {"seagreen", new Color(0.18f, 0.55f, 0.34f)}, + {"seashell", new Color(1.00f, 0.96f, 0.93f)}, + {"sienna", new Color(0.63f, 0.32f, 0.18f)}, + {"silver", new Color(0.75f, 0.75f, 0.75f)}, + {"skyblue", new Color(0.53f, 0.81f, 0.92f)}, + {"slateblue", new Color(0.42f, 0.35f, 0.80f)}, + {"slategray", new Color(0.44f, 0.50f, 0.56f)}, + {"snow", new Color(1.00f, 0.98f, 0.98f)}, + {"springgreen", new Color(0.00f, 1.00f, 0.50f)}, + {"steelblue", new Color(0.27f, 0.51f, 0.71f)}, + {"tan", new Color(0.82f, 0.71f, 0.55f)}, + {"teal", new Color(0.00f, 0.50f, 0.50f)}, + {"thistle", new Color(0.85f, 0.75f, 0.85f)}, + {"tomato", new Color(1.00f, 0.39f, 0.28f)}, + {"transparent", new Color(1.00f, 1.00f, 1.00f, 0.00f)}, + {"turquoise", new Color(0.25f, 0.88f, 0.82f)}, + {"violet", new Color(0.93f, 0.51f, 0.93f)}, + {"webgreen", new Color(0.00f, 0.50f, 0.00f)}, + {"webgray", new Color(0.50f, 0.50f, 0.50f)}, + {"webmaroon", new Color(0.50f, 0.00f, 0.00f)}, + {"webpurple", new Color(0.50f, 0.00f, 0.50f)}, + {"wheat", new Color(0.96f, 0.87f, 0.70f)}, + {"white", new Color(1.00f, 1.00f, 1.00f)}, + {"whitesmoke", new Color(0.96f, 0.96f, 0.96f)}, + {"yellow", new Color(1.00f, 1.00f, 0.00f)}, + {"yellowgreen", new Color(0.60f, 0.80f, 0.20f)}, + }; + + public static Color AliceBlue { get { return namedColors["aliceblue"]; } } + public static Color AntiqueWhite { get { return namedColors["antiquewhite"]; } } + public static Color Aqua { get { return namedColors["aqua"]; } } + public static Color Aquamarine { get { return namedColors["aquamarine"]; } } + public static Color Azure { get { return namedColors["azure"]; } } + public static Color Beige { get { return namedColors["beige"]; } } + public static Color Bisque { get { return namedColors["bisque"]; } } + public static Color Black { get { return namedColors["black"]; } } + public static Color BlanchedAlmond { get { return namedColors["blanchedalmond"]; } } + public static Color Blue { get { return namedColors["blue"]; } } + public static Color BlueViolet { get { return namedColors["blueviolet"]; } } + public static Color Brown { get { return namedColors["brown"]; } } + public static Color BurlyWood { get { return namedColors["burlywood"]; } } + public static Color CadetBlue { get { return namedColors["cadetblue"]; } } + public static Color Chartreuse { get { return namedColors["chartreuse"]; } } + public static Color Chocolate { get { return namedColors["chocolate"]; } } + public static Color Coral { get { return namedColors["coral"]; } } + public static Color Cornflower { get { return namedColors["cornflower"]; } } + public static Color Cornsilk { get { return namedColors["cornsilk"]; } } + public static Color Crimson { get { return namedColors["crimson"]; } } + public static Color Cyan { get { return namedColors["cyan"]; } } + public static Color DarkBlue { get { return namedColors["darkblue"]; } } + public static Color DarkCyan { get { return namedColors["darkcyan"]; } } + public static Color DarkGoldenrod { get { return namedColors["darkgoldenrod"]; } } + public static Color DarkGray { get { return namedColors["darkgray"]; } } + public static Color DarkGreen { get { return namedColors["darkgreen"]; } } + public static Color DarkKhaki { get { return namedColors["darkkhaki"]; } } + public static Color DarkMagenta { get { return namedColors["darkmagenta"]; } } + public static Color DarkOliveGreen { get { return namedColors["darkolivegreen"]; } } + public static Color DarkOrange { get { return namedColors["darkorange"]; } } + public static Color DarkOrchid { get { return namedColors["darkorchid"]; } } + public static Color DarkRed { get { return namedColors["darkred"]; } } + public static Color DarkSalmon { get { return namedColors["darksalmon"]; } } + public static Color DarkSeaGreen { get { return namedColors["darkseagreen"]; } } + public static Color DarkSlateBlue { get { return namedColors["darkslateblue"]; } } + public static Color DarkSlateGray { get { return namedColors["darkslategray"]; } } + public static Color DarkTurquoise { get { return namedColors["darkturquoise"]; } } + public static Color DarkViolet { get { return namedColors["darkviolet"]; } } + public static Color DeepPink { get { return namedColors["deeppink"]; } } + public static Color DeepSkyBlue { get { return namedColors["deepskyblue"]; } } + public static Color DimGray { get { return namedColors["dimgray"]; } } + public static Color DodgerBlue { get { return namedColors["dodgerblue"]; } } + public static Color Firebrick { get { return namedColors["firebrick"]; } } + public static Color FloralWhite { get { return namedColors["floralwhite"]; } } + public static Color ForestGreen { get { return namedColors["forestgreen"]; } } + public static Color Fuchsia { get { return namedColors["fuchsia"]; } } + public static Color Gainsboro { get { return namedColors["gainsboro"]; } } + public static Color GhostWhite { get { return namedColors["ghostwhite"]; } } + public static Color Gold { get { return namedColors["gold"]; } } + public static Color Goldenrod { get { return namedColors["goldenrod"]; } } + public static Color Gray { get { return namedColors["gray"]; } } + public static Color Green { get { return namedColors["green"]; } } + public static Color GreenYellow { get { return namedColors["greenyellow"]; } } + public static Color Honeydew { get { return namedColors["honeydew"]; } } + public static Color HotPink { get { return namedColors["hotpink"]; } } + public static Color IndianRed { get { return namedColors["indianred"]; } } + public static Color Indigo { get { return namedColors["indigo"]; } } + public static Color Ivory { get { return namedColors["ivory"]; } } + public static Color Khaki { get { return namedColors["khaki"]; } } + public static Color Lavender { get { return namedColors["lavender"]; } } + public static Color LavenderBlush { get { return namedColors["lavenderblush"]; } } + public static Color LawnGreen { get { return namedColors["lawngreen"]; } } + public static Color LemonChiffon { get { return namedColors["lemonchiffon"]; } } + public static Color LightBlue { get { return namedColors["lightblue"]; } } + public static Color LightCoral { get { return namedColors["lightcoral"]; } } + public static Color LightCyan { get { return namedColors["lightcyan"]; } } + public static Color LightGoldenrod { get { return namedColors["lightgoldenrod"]; } } + public static Color LightGray { get { return namedColors["lightgray"]; } } + public static Color LightGreen { get { return namedColors["lightgreen"]; } } + public static Color LightPink { get { return namedColors["lightpink"]; } } + public static Color LightSalmon { get { return namedColors["lightsalmon"]; } } + public static Color LightSeaGreen { get { return namedColors["lightseagreen"]; } } + public static Color LightSkyBlue { get { return namedColors["lightskyblue"]; } } + public static Color LightSlateGray { get { return namedColors["lightslategray"]; } } + public static Color LightSteelBlue { get { return namedColors["lightsteelblue"]; } } + public static Color LightYellow { get { return namedColors["lightyellow"]; } } + public static Color Lime { get { return namedColors["lime"]; } } + public static Color Limegreen { get { return namedColors["limegreen"]; } } + public static Color Linen { get { return namedColors["linen"]; } } + public static Color Magenta { get { return namedColors["magenta"]; } } + public static Color Maroon { get { return namedColors["maroon"]; } } + public static Color MediumAquamarine { get { return namedColors["mediumaquamarine"]; } } + public static Color MediumBlue { get { return namedColors["mediumblue"]; } } + public static Color MediumOrchid { get { return namedColors["mediumorchid"]; } } + public static Color MediumPurple { get { return namedColors["mediumpurple"]; } } + public static Color MediumSeaGreen { get { return namedColors["mediumseagreen"]; } } + public static Color MediumSlateBlue { get { return namedColors["mediumslateblue"]; } } + public static Color MediumSpringGreen { get { return namedColors["mediumspringgreen"]; } } + public static Color MediumTurquoise { get { return namedColors["mediumturquoise"]; } } + public static Color MediumVioletRed { get { return namedColors["mediumvioletred"]; } } + public static Color MidnightBlue { get { return namedColors["midnightblue"]; } } + public static Color MintCream { get { return namedColors["mintcream"]; } } + public static Color MistyRose { get { return namedColors["mistyrose"]; } } + public static Color Moccasin { get { return namedColors["moccasin"]; } } + public static Color NavajoWhite { get { return namedColors["navajowhite"]; } } + public static Color NavyBlue { get { return namedColors["navyblue"]; } } + public static Color OldLace { get { return namedColors["oldlace"]; } } + public static Color Olive { get { return namedColors["olive"]; } } + public static Color OliveDrab { get { return namedColors["olivedrab"]; } } + public static Color Orange { get { return namedColors["orange"]; } } + public static Color OrangeRed { get { return namedColors["orangered"]; } } + public static Color Orchid { get { return namedColors["orchid"]; } } + public static Color PaleGoldenrod { get { return namedColors["palegoldenrod"]; } } + public static Color PaleGreen { get { return namedColors["palegreen"]; } } + public static Color PaleTurquoise { get { return namedColors["paleturquoise"]; } } + public static Color PaleVioletRed { get { return namedColors["palevioletred"]; } } + public static Color PapayaWhip { get { return namedColors["papayawhip"]; } } + public static Color PeachPuff { get { return namedColors["peachpuff"]; } } + public static Color Peru { get { return namedColors["peru"]; } } + public static Color Pink { get { return namedColors["pink"]; } } + public static Color Plum { get { return namedColors["plum"]; } } + public static Color PowderBlue { get { return namedColors["powderblue"]; } } + public static Color Purple { get { return namedColors["purple"]; } } + public static Color RebeccaPurple { get { return namedColors["rebeccapurple"]; } } + public static Color Red { get { return namedColors["red"]; } } + public static Color RosyBrown { get { return namedColors["rosybrown"]; } } + public static Color RoyalBlue { get { return namedColors["royalblue"]; } } + public static Color SaddleBrown { get { return namedColors["saddlebrown"]; } } + public static Color Salmon { get { return namedColors["salmon"]; } } + public static Color SandyBrown { get { return namedColors["sandybrown"]; } } + public static Color SeaGreen { get { return namedColors["seagreen"]; } } + public static Color SeaShell { get { return namedColors["seashell"]; } } + public static Color Sienna { get { return namedColors["sienna"]; } } + public static Color Silver { get { return namedColors["silver"]; } } + public static Color SkyBlue { get { return namedColors["skyblue"]; } } + public static Color SlateBlue { get { return namedColors["slateblue"]; } } + public static Color SlateGray { get { return namedColors["slategray"]; } } + public static Color Snow { get { return namedColors["snow"]; } } + public static Color SpringGreen { get { return namedColors["springgreen"]; } } + public static Color SteelBlue { get { return namedColors["steelblue"]; } } + public static Color Tan { get { return namedColors["tan"]; } } + public static Color Teal { get { return namedColors["teal"]; } } + public static Color Thistle { get { return namedColors["thistle"]; } } + public static Color Tomato { get { return namedColors["tomato"]; } } + public static Color Transparent { get { return namedColors["transparent"]; } } + public static Color Turquoise { get { return namedColors["turquoise"]; } } + public static Color Violet { get { return namedColors["violet"]; } } + public static Color WebGreen { get { return namedColors["webgreen"]; } } + public static Color WebGray { get { return namedColors["webgray"]; } } + public static Color WebMaroon { get { return namedColors["webmaroon"]; } } + public static Color WebPurple { get { return namedColors["webpurple"]; } } + public static Color Wheat { get { return namedColors["wheat"]; } } + public static Color White { get { return namedColors["white"]; } } + public static Color WhiteSmoke { get { return namedColors["whitesmoke"]; } } + public static Color Yellow { get { return namedColors["yellow"]; } } + public static Color YellowGreen { get { return namedColors["yellowgreen"]; } } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs new file mode 100644 index 0000000000..edfe3464ec --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -0,0 +1,89 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using System.Text; + +namespace Godot +{ + internal static class DebuggingUtils + { + internal static void AppendTypeName(this StringBuilder sb, Type type) + { + if (type.IsPrimitive) + sb.Append(type.Name); + else if (type == typeof(void)) + sb.Append("void"); + else + sb.Append(type); + + 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(); + fileLineNumber = frame.GetFileLineNumber(); + + MethodBase methodBase = frame.GetMethod(); + + if (methodBase == null) + { + methodDecl = string.Empty; + return; + } + + var sb = new StringBuilder(); + + if (methodBase is MethodInfo) + sb.AppendTypeName(((MethodInfo)methodBase).ReturnType); + + sb.Append(methodBase.DeclaringType.FullName); + sb.Append("."); + sb.Append(methodBase.Name); + + if (methodBase.IsGenericMethod) + { + Type[] genericParams = methodBase.GetGenericArguments(); + + sb.Append("<"); + + for (int j = 0; j < genericParams.Length; j++) + { + if (j > 0) + sb.Append(", "); + + sb.AppendTypeName(genericParams[j]); + } + + sb.Append(">"); + } + + sb.Append("("); + + bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0; + + ParameterInfo[] parameter = methodBase.GetParameters(); + + for (int i = 0; i < parameter.Length; i++) + { + if (i > 0) + sb.Append(", "); + + if (i == parameter.Length - 1 && varArgs) + sb.Append("params "); + + sb.AppendTypeName(parameter[i].ParameterType); + } + + sb.Append(")"); + + methodDecl = sb.ToString(); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs new file mode 100644 index 0000000000..d72109de92 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -0,0 +1,469 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Godot.Collections +{ + class DictionarySafeHandle : SafeHandle + { + public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true) + { + this.handle = handle; + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + + protected override bool ReleaseHandle() + { + Dictionary.godot_icall_Dictionary_Dtor(handle); + return true; + } + } + + public class Dictionary : + IDictionary, + IDisposable + { + DictionarySafeHandle safeHandle; + bool disposed = false; + + public Dictionary() + { + 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; + } + + internal Dictionary(IntPtr handle) + { + safeHandle = new DictionarySafeHandle(handle); + } + + internal IntPtr GetPtr() + { + if (disposed) + throw new ObjectDisposedException(GetType().FullName); + + return safeHandle.DangerousGetHandle(); + } + + public void Dispose() + { + if (disposed) + return; + + if (safeHandle != null) + { + safeHandle.Dispose(); + safeHandle = null; + } + + disposed = true; + } + + public Dictionary Duplicate(bool deep = false) + { + return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep)); + } + + // IDictionary + + public ICollection Keys + { + get + { + IntPtr handle = godot_icall_Dictionary_Keys(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public ICollection Values + { + get + { + IntPtr handle = godot_icall_Dictionary_Values(GetPtr()); + return new Array(new ArraySafeHandle(handle)); + } + } + + public bool IsFixedSize => false; + + public bool IsReadOnly => false; + + public object this[object key] + { + get => godot_icall_Dictionary_GetValue(GetPtr(), key); + set => godot_icall_Dictionary_SetValue(GetPtr(), key, value); + } + + public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value); + + public void Clear() => godot_icall_Dictionary_Clear(GetPtr()); + + public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key); + + public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this); + + public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key); + + // 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 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++) + { + array.SetValue(new DictionaryEntry(keys[i], values[i]), index); + index++; + } + } + + // IEnumerable + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private class DictionaryEnumerator : IDictionaryEnumerator + { + Array keys; + Array values; + int count; + int index = -1; + + public DictionaryEnumerator(Dictionary dictionary) + { + // TODO 3 internal calls, can reduce to 1 + keys = (Array)dictionary.Keys; + values = (Array)dictionary.Values; + count = dictionary.Count; + } + + public object Current => Entry; + + public DictionaryEntry Entry => + // TODO 2 internal calls, can reduce to 1 + new DictionaryEntry(keys[index], values[index]); + + public object Key => Entry.Key; + + public object Value => Entry.Value; + + public bool MoveNext() + { + index++; + return index < count; + } + + public void Reset() + { + index = -1; + } + } + + public override string ToString() + { + return godot_icall_Dictionary_ToString(GetPtr()); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Ctor(); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_Dictionary_Count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr); + } + + public class Dictionary<TKey, TValue> : + IDictionary<TKey, TValue> + { + Dictionary objectDict; + + internal static int valTypeEncoding; + internal static IntPtr valTypeClass; + + static Dictionary() + { + Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass); + } + + public Dictionary() + { + 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; + } + + internal Dictionary(IntPtr handle) + { + objectDict = new Dictionary(handle); + } + + internal Dictionary(DictionarySafeHandle handle) + { + objectDict = new Dictionary(handle); + } + + public static explicit operator Dictionary(Dictionary<TKey, TValue> from) + { + return from.objectDict; + } + + internal IntPtr GetPtr() + { + return objectDict.GetPtr(); + } + + public Dictionary<TKey, TValue> Duplicate(bool deep = false) + { + return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep)); + } + + // IDictionary<TKey, TValue> + + public TValue this[TKey key] + { + get + { + return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); + } + set + { + objectDict[key] = value; + } + } + + public ICollection<TKey> Keys + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr()); + return new Array<TKey>(new ArraySafeHandle(handle)); + } + } + + public ICollection<TValue> Values + { + get + { + IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr()); + return new Array<TValue>(new ArraySafeHandle(handle)); + } + } + + 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 + { + return objectDict.Count; + } + } + + public bool IsReadOnly + { + get + { + return objectDict.IsReadOnly; + } + } + + public void Add(KeyValuePair<TKey, TValue> item) + { + objectDict.Add(item.Key, item.Value); + } + + public void Clear() + { + objectDict.Clear(); + } + + public bool Contains(KeyValuePair<TKey, TValue> item) + { + return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value)); + } + + 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 + array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]); + arrayIndex++; + } + } + + 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 + Array<TKey> keys = (Array<TKey>)Keys; + Array<TValue> values = (Array<TValue>)Values; + int count = Count; + + for (int i = 0; i < count; i++) + { + // TODO 2 internal calls, can reduce to 1 + yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override string ToString() => objectDict.ToString(); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs new file mode 100644 index 0000000000..072e0f20ff --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs @@ -0,0 +1,13 @@ +using System.Runtime.CompilerServices; + +namespace Godot +{ + public static class Dispatcher + { + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler(); + + public static GodotSynchronizationContext SynchronizationContext => + godot_icall_DefaultGodotTaskScheduler().Context; + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs new file mode 100644 index 0000000000..a0f105d55e --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/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); + + // Invocation 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/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs new file mode 100644 index 0000000000..5d16260f5d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs @@ -0,0 +1,45 @@ +namespace Godot +{ + public partial class Node + { + public T GetNode<T>(NodePath path) where T : class + { + return (T)(object)GetNode(path); + } + + public T GetNodeOrNull<T>(NodePath path) where T : class + { + return GetNodeOrNull(path) as T; + } + + public T GetChild<T>(int idx) where T : class + { + return (T)(object)GetChild(idx); + } + + public T GetChildOrNull<T>(int idx) where T : class + { + return GetChild(idx) as T; + } + + public T GetOwner<T>() where T : class + { + return (T)(object)Owner; + } + + public T GetOwnerOrNull<T>() where T : class + { + return Owner as T; + } + + public T GetParent<T>() where T : class + { + return (T)(object)GetParent(); + } + + public T GetParentOrNull<T>() where T : class + { + return GetParent() as T; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000000..9ef0959750 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class Object + { + public static bool IsInstanceValid(Object instance) + { + return instance != null && instance.NativeInstance != IntPtr.Zero; + } + + public static WeakRef WeakRef(Object obj) + { + return godot_icall_Object_weakref(Object.GetPtr(obj)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs new file mode 100644 index 0000000000..684d160b57 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs @@ -0,0 +1,10 @@ +namespace Godot +{ + public static partial class ResourceLoader + { + public static T Load<T>(string path) where T : class + { + return (T)(object)Load(path); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs new file mode 100644 index 0000000000..19962d418a --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +// TODO: Add comments describing what this class does. It is not obvious. + +namespace Godot +{ + public static partial class GD + { + public static object Bytes2Var(byte[] bytes, bool allow_objects = false) + { + return godot_icall_GD_bytes2var(bytes, allow_objects); + } + + public static object Convert(object what, Variant.Type type) + { + return godot_icall_GD_convert(what, type); + } + + public static real_t Db2Linear(real_t db) + { + return (real_t)Math.Exp(db * 0.11512925464970228420089957273422); + } + + public static real_t DecTime(real_t value, real_t amount, real_t step) + { + real_t sgn = Mathf.Sign(value); + real_t val = Mathf.Abs(value); + val -= amount * step; + if (val < 0) + val = 0; + return val * sgn; + } + + public static FuncRef FuncRef(Object instance, string funcname) + { + var ret = new FuncRef(); + ret.SetInstance(instance); + ret.SetFunction(funcname); + return ret; + } + + public static int Hash(object var) + { + return godot_icall_GD_hash(var); + } + + public static Object InstanceFromId(ulong instanceId) + { + return godot_icall_GD_instance_from_id(instanceId); + } + + public static real_t Linear2Db(real_t linear) + { + return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321); + } + + public static Resource Load(string path) + { + return ResourceLoader.Load(path); + } + + public static T Load<T>(string path) where T : class + { + return ResourceLoader.Load<T>(path); + } + + public static void PushError(string message) + { + godot_icall_GD_pusherror(message); + } + + public static void PushWarning(string message) + { + godot_icall_GD_pushwarning(message); + } + + public static void Print(params object[] what) + { + godot_icall_GD_print(Array.ConvertAll(what, x => x.ToString())); + } + + public static void PrintStack() + { + Print(System.Environment.StackTrace); + } + + public static void PrintErr(params object[] what) + { + godot_icall_GD_printerr(Array.ConvertAll(what, x => x?.ToString())); + } + + public static void PrintRaw(params object[] what) + { + godot_icall_GD_printraw(Array.ConvertAll(what, x => x?.ToString())); + } + + public static void PrintS(params object[] what) + { + godot_icall_GD_prints(Array.ConvertAll(what, x => x?.ToString())); + } + + public static void PrintT(params object[] what) + { + godot_icall_GD_printt(Array.ConvertAll(what, x => x?.ToString())); + } + + public static float Randf() + { + return godot_icall_GD_randf(); + } + + public static uint Randi() + { + return godot_icall_GD_randi(); + } + + public static void Randomize() + { + godot_icall_GD_randomize(); + } + + public static double RandRange(double from, double to) + { + return godot_icall_GD_rand_range(from, to); + } + + public static uint RandSeed(ulong seed, out ulong newSeed) + { + return godot_icall_GD_rand_seed(seed, out newSeed); + } + + public static IEnumerable<int> Range(int end) + { + return Range(0, end, 1); + } + + public static IEnumerable<int> Range(int start, int end) + { + return Range(start, end, 1); + } + + public static IEnumerable<int> Range(int start, int end, int step) + { + if (end < start && step > 0) + yield break; + + if (end > start && step < 0) + yield break; + + if (step > 0) + { + for (int i = start; i < end; i += step) + yield return i; + } + else + { + for (int i = start; i > end; i += step) + yield return i; + } + } + + public static void Seed(ulong seed) + { + godot_icall_GD_seed(seed); + } + + public static string Str(params object[] what) + { + return godot_icall_GD_str(what); + } + + public static object Str2Var(string str) + { + return godot_icall_GD_str2var(str); + } + + public static bool TypeExists(string type) + { + return godot_icall_GD_type_exists(type); + } + + public static byte[] Var2Bytes(object var, bool full_objects = false) + { + return godot_icall_GD_var2bytes(var, full_objects); + } + + public static string Var2Str(object var) + { + return godot_icall_GD_var2str(var); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + 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, 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(ulong instance_id); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_print(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printerr(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printraw(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_prints(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_printt(object[] what); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static float 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); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static object godot_icall_GD_str2var(string str); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_GD_type_exists(string type); + + [MethodImpl(MethodImplOptions.InternalCall)] + 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); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_pusherror(string type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_GD_pushwarning(string type); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs new file mode 100644 index 0000000000..4b5e3f8761 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs @@ -0,0 +1,24 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +namespace Godot +{ + public class GodotSynchronizationContext : SynchronizationContext + { + private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>(); + + public override void Post(SendOrPostCallback d, object state) + { + _queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state)); + } + + public void ExecutePendingContinuations() + { + while (_queue.TryTake(out var workItem)) + { + workItem.Key(workItem.Value); + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs new file mode 100644 index 0000000000..8eaeea50dc --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Godot +{ + public class GodotTaskScheduler : TaskScheduler + { + internal GodotSynchronizationContext Context { get; } + private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); + + public GodotTaskScheduler() + { + Context = new GodotSynchronizationContext(); + SynchronizationContext.SetSynchronizationContext(Context); + } + + protected sealed override void QueueTask(Task task) + { + lock (_tasks) + { + _tasks.AddLast(task); + } + } + + protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + if (SynchronizationContext.Current != Context) + return false; + + if (taskWasPreviouslyQueued) + TryDequeue(task); + + return TryExecuteTask(task); + } + + protected sealed override bool TryDequeue(Task task) + { + lock (_tasks) + { + return _tasks.Remove(task); + } + } + + protected sealed override IEnumerable<Task> GetScheduledTasks() + { + lock (_tasks) + { + foreach (Task task in _tasks) + yield return task; + } + } + + public void Activate() + { + ExecuteQueuedTasks(); + Context.ExecutePendingContinuations(); + } + + private void ExecuteQueuedTasks() + { + while (true) + { + Task task; + + lock (_tasks) + { + if (_tasks.Any()) + { + task = _tasks.First.Value; + _tasks.RemoveFirst(); + } + else + { + break; + } + } + + if (task != null) + { + if (!TryExecuteTask(task)) + { + throw new InvalidOperationException(); + } + } + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs new file mode 100644 index 0000000000..f1a00ae0fa --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/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/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs new file mode 100644 index 0000000000..0397957d00 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs @@ -0,0 +1,12 @@ +namespace Godot +{ + public interface IAwaitable + { + IAwaiter GetAwaiter(); + } + + public interface IAwaitable<out TResult> + { + IAwaiter<TResult> GetAwaiter(); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs new file mode 100644 index 0000000000..d3be9d781c --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; + +namespace Godot +{ + public interface IAwaiter : INotifyCompletion + { + bool IsCompleted { get; } + + void GetResult(); + } + + public interface IAwaiter<out TResult> : INotifyCompletion + { + bool IsCompleted { get; } + + TResult GetResult(); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs new file mode 100644 index 0000000000..c3fa2f3e82 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs @@ -0,0 +1,8 @@ +namespace Godot +{ + public interface ISerializationListener + { + void OnBeforeSerialize(); + void OnAfterDeserialize(); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs new file mode 100644 index 0000000000..a1d63a62ef --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Godot +{ + using Array = Godot.Collections.Array; + using Dictionary = Godot.Collections.Dictionary; + + static class MarshalUtils + { + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="System.InvalidOperationException"> + /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// </exception> + static bool TypeIsGenericArray(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); + } + + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="System.InvalidOperationException"> + /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// </exception> + static bool TypeIsGenericDictionary(Type type) + { + return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); + } + + static void ArrayGetElementType(Type arrayType, out Type elementType) + { + elementType = arrayType.GetGenericArguments()[0]; + } + + static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) + { + var genericArgs = dictionaryType.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + } + + static bool GenericIEnumerableIsAssignableFromType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + return true; + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + return true; + } + + Type baseType = type.BaseType; + + if (baseType == null) + return false; + + return GenericIEnumerableIsAssignableFromType(baseType); + } + + static bool GenericIDictionaryIsAssignableFromType(Type type) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + return true; + } + + Type baseType = type.BaseType; + + if (baseType == null) + return false; + + return GenericIDictionaryIsAssignableFromType(baseType); + } + + static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = type.GetGenericArguments()[0]; + return true; + } + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = interfaceType.GetGenericArguments()[0]; + return true; + } + } + + Type baseType = type.BaseType; + + if (baseType == null) + { + elementType = null; + return false; + } + + return GenericIEnumerableIsAssignableFromType(baseType, out elementType); + } + + static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var genericArgs = type.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + return true; + } + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var genericArgs = interfaceType.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + return true; + } + } + + Type baseType = type.BaseType; + + if (baseType == null) + { + keyType = null; + valueType = null; + return false; + } + + return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType); + } + + static Type MakeGenericArrayType(Type elemType) + { + return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); + } + + static Type MakeGenericDictionaryType(Type keyType, Type valueType) + { + return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); + } + + // 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); + } + } + } + + internal static void IDictionaryToDictionary(IDictionary dictionary, IntPtr godotDictionaryPtr) + { + foreach (DictionaryEntry entry in dictionary) + { + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); + } + } + + internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr) + { +#if DEBUG + if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType())) + throw new InvalidOperationException("The type does not implement IDictionary<,>"); +#endif + + // TODO: Can we optimize this? + + var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator(); + var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator(); + + while (keys.MoveNext() && values.MoveNext()) + { + object key = keys.Current; + object value = values.Current; + + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value); + } + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs new file mode 100644 index 0000000000..ddfed180b5 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -0,0 +1,361 @@ +using System; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public static partial class Mathf + { + // Define constants with Decimal precision and cast down to double or float. + + public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959 + public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979 + public const real_t Inf = real_t.PositiveInfinity; + public const real_t NaN = real_t.NaN; + + private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433 + private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823 + + public static int Abs(int s) + { + return Math.Abs(s); + } + + public static real_t Abs(real_t s) + { + return Math.Abs(s); + } + + public static real_t Acos(real_t s) + { + return (real_t)Math.Acos(s); + } + + public static real_t Asin(real_t s) + { + return (real_t)Math.Asin(s); + } + + public static real_t Atan(real_t s) + { + return (real_t)Math.Atan(s); + } + + public static real_t Atan2(real_t y, real_t x) + { + return (real_t)Math.Atan2(y, x); + } + + public static Vector2 Cartesian2Polar(real_t x, real_t y) + { + return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); + } + + public static real_t Ceil(real_t s) + { + return (real_t)Math.Ceiling(s); + } + + public static int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } + + public static real_t Clamp(real_t value, real_t min, real_t max) + { + return value < min ? min : value > max ? max : value; + } + + public static real_t Cos(real_t s) + { + return (real_t)Math.Cos(s); + } + + public static real_t Cosh(real_t s) + { + return (real_t)Math.Cosh(s); + } + + public static real_t Deg2Rad(real_t deg) + { + return deg * Deg2RadConst; + } + + public static real_t Ease(real_t s, real_t curve) + { + if (s < 0f) + { + s = 0f; + } + else if (s > 1.0f) + { + s = 1.0f; + } + + if (curve > 0f) + { + if (curve < 1.0f) + { + return 1.0f - Pow(1.0f - s, 1.0f / curve); + } + + return Pow(s, curve); + } + + if (curve < 0f) + { + if (s < 0.5f) + { + return Pow(s * 2.0f, -curve) * 0.5f; + } + + return (1.0f - Pow(1.0f - (s - 0.5f) * 2.0f, -curve)) * 0.5f + 0.5f; + } + + return 0f; + } + + public static real_t Exp(real_t s) + { + return (real_t)Math.Exp(s); + } + + public static real_t Floor(real_t s) + { + return (real_t)Math.Floor(s); + } + + public static real_t InverseLerp(real_t from, real_t to, real_t weight) + { + return (weight - from) / (to - from); + } + + public static bool IsEqualApprox(real_t a, real_t b) + { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) + { + return true; + } + // Then check for approximate equality. + real_t tolerance = Epsilon * Abs(a); + if (tolerance < Epsilon) + { + tolerance = Epsilon; + } + return Abs(a - b) < tolerance; + } + + public static bool IsInf(real_t s) + { + return real_t.IsInfinity(s); + } + + public static bool IsNaN(real_t s) + { + return real_t.IsNaN(s); + } + + public static bool IsZeroApprox(real_t s) + { + return Abs(s) < Epsilon; + } + + public static real_t Lerp(real_t from, real_t to, real_t weight) + { + return from + (to - from) * weight; + } + + public static real_t LerpAngle(real_t from, real_t to, real_t weight) + { + real_t difference = (to - from) % Mathf.Tau; + real_t distance = ((2 * difference) % Mathf.Tau) - difference; + return from + distance * weight; + } + + public static real_t Log(real_t s) + { + return (real_t)Math.Log(s); + } + + public static int Max(int a, int b) + { + return a > b ? a : b; + } + + public static real_t Max(real_t a, real_t b) + { + return a > b ? a : b; + } + + public static int Min(int a, int b) + { + return a < b ? a : b; + } + + public static real_t Min(real_t a, real_t b) + { + return a < b ? a : b; + } + + public static real_t MoveToward(real_t from, real_t to, real_t delta) + { + return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta; + } + + public static int NearestPo2(int value) + { + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value++; + return value; + } + + public static Vector2 Polar2Cartesian(real_t r, real_t th) + { + return new Vector2(r * Cos(th), r * Sin(th)); + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// </summary> + public static int PosMod(int a, int b) + { + int c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + /// <summary> + /// Performs a canonical Modulus operation, where the output is on the range [0, b). + /// </summary> + public static real_t PosMod(real_t a, real_t b) + { + real_t c = a % b; + if ((c < 0 && b > 0) || (c > 0 && b < 0)) + { + c += b; + } + return c; + } + + public static real_t Pow(real_t x, real_t y) + { + return (real_t)Math.Pow(x, y); + } + + public static real_t Rad2Deg(real_t rad) + { + return rad * Rad2DegConst; + } + + public static real_t Round(real_t s) + { + return (real_t)Math.Round(s); + } + + public static int Sign(int s) + { + if (s == 0) return 0; + return s < 0 ? -1 : 1; + } + + public static int Sign(real_t s) + { + if (s == 0) return 0; + return s < 0 ? -1 : 1; + } + + public static real_t Sin(real_t s) + { + return (real_t)Math.Sin(s); + } + + public static real_t Sinh(real_t s) + { + return (real_t)Math.Sinh(s); + } + + public static real_t SmoothStep(real_t from, real_t to, real_t weight) + { + if (IsEqualApprox(from, to)) + { + return from; + } + real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0); + return x * x * (3 - 2 * x); + } + + public static real_t Sqrt(real_t s) + { + return (real_t)Math.Sqrt(s); + } + + public static int StepDecimals(real_t step) + { + double[] sd = new double[] { + 0.9999, + 0.09999, + 0.009999, + 0.0009999, + 0.00009999, + 0.000009999, + 0.0000009999, + 0.00000009999, + 0.000000009999, + }; + double abs = Mathf.Abs(step); + double decs = abs - (int)abs; // Strip away integer part + for (int i = 0; i < sd.Length; i++) + { + if (decs >= sd[i]) + { + return i; + } + } + return 0; + } + + public static real_t Stepify(real_t s, real_t step) + { + if (step != 0f) + { + s = Floor(s / step + 0.5f) * step; + } + + return s; + } + + public static real_t Tan(real_t s) + { + return (real_t)Math.Tan(s); + } + + public static real_t Tanh(real_t s) + { + return (real_t)Math.Tanh(s); + } + + public static int Wrap(int value, int min, int max) + { + int range = max - min; + return range == 0 ? min : min + ((value - min) % range + range) % range; + } + + public static real_t Wrap(real_t value, real_t min, real_t max) + { + real_t range = max - min; + return IsZeroApprox(range) ? min : min + ((value - min) % range + range) % range; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs new file mode 100644 index 0000000000..1b7fd4906f --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs @@ -0,0 +1,60 @@ +using System; + +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + public static partial class Mathf + { + // Define constants with Decimal precision and cast down to double or float. + + public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045 + public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095 + +#if REAL_T_IS_DOUBLE + public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used. +#else + public const real_t Epsilon = 1e-06f; +#endif + + public static int DecimalCount(real_t s) + { + return DecimalCount((decimal)s); + } + + public static int DecimalCount(decimal s) + { + return BitConverter.GetBytes(decimal.GetBits(s)[3])[2]; + } + + public static int CeilToInt(real_t s) + { + return (int)Math.Ceiling(s); + } + + public static int FloorToInt(real_t s) + { + return (int)Math.Floor(s); + } + + public static int RoundToInt(real_t s) + { + return (int)Math.Round(s); + } + + public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) + { + // Check for exact equality first, required to handle "infinity" values. + if (a == b) + { + return true; + } + // Then check for approximate equality. + return Abs(a - b) < tolerance; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs new file mode 100644 index 0000000000..8c5872ba5a --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -0,0 +1,153 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public sealed partial class NodePath : IDisposable + { + private bool disposed = false; + + internal IntPtr ptr; + + internal static IntPtr GetPtr(NodePath instance) + { + if (instance == null) + throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null."); + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; + } + + ~NodePath() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + godot_icall_NodePath_Dtor(ptr); + ptr = IntPtr.Zero; + } + + disposed = true; + } + + internal NodePath(IntPtr ptr) + { + this.ptr = ptr; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + public NodePath() : this(string.Empty) {} + + public NodePath(string path) + { + this.ptr = godot_icall_NodePath_Ctor(path); + } + + public static implicit operator NodePath(string from) + { + return new NodePath(from); + } + + public static implicit operator string(NodePath from) + { + return godot_icall_NodePath_operator_String(NodePath.GetPtr(from)); + } + + public override string ToString() + { + return (string)this; + } + + public NodePath GetAsPropertyPath() + { + return new NodePath(godot_icall_NodePath_get_as_property_path(NodePath.GetPtr(this))); + } + + public string GetConcatenatedSubnames() + { + return godot_icall_NodePath_get_concatenated_subnames(NodePath.GetPtr(this)); + } + + public string GetName(int idx) + { + return godot_icall_NodePath_get_name(NodePath.GetPtr(this), idx); + } + + public int GetNameCount() + { + return godot_icall_NodePath_get_name_count(NodePath.GetPtr(this)); + } + + public string GetSubname(int idx) + { + return godot_icall_NodePath_get_subname(NodePath.GetPtr(this), idx); + } + + public int GetSubnameCount() + { + return godot_icall_NodePath_get_subname_count(NodePath.GetPtr(this)); + } + + public bool IsAbsolute() + { + return godot_icall_NodePath_is_absolute(NodePath.GetPtr(this)); + } + + public bool IsEmpty() + { + return godot_icall_NodePath_is_empty(NodePath.GetPtr(this)); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_NodePath_Ctor(string path); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_NodePath_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_operator_String(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_name(IntPtr ptr, int arg1); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_NodePath_get_name_count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_NodePath_get_subname(IntPtr ptr, int arg1); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_NodePath_get_subname_count(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_NodePath_is_absolute(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static bool godot_icall_NodePath_is_empty(IntPtr ptr); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs new file mode 100644 index 0000000000..de80f7fddc --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -0,0 +1,130 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public partial class Object : IDisposable + { + private bool disposed = false; + + private const string nativeName = "Object"; + + internal IntPtr ptr; + internal bool memoryOwn; + + public Object() : this(false) + { + if (ptr == IntPtr.Zero) + ptr = godot_icall_Object_Ctor(this); + } + + internal Object(bool memoryOwn) + { + this.memoryOwn = memoryOwn; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + internal static IntPtr GetPtr(Object instance) + { + if (instance == null) + return IntPtr.Zero; + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; + } + + ~Object() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + if (memoryOwn) + { + memoryOwn = false; + godot_icall_Reference_Disposed(this, ptr, !disposing); + } + else + { + godot_icall_Object_Disposed(this, ptr); + } + + this.ptr = IntPtr.Zero; + } + + disposed = true; + } + + public override string ToString() + { + return godot_icall_Object_ToString(GetPtr(this)); + } + + /// <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); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Object_Disposed(Object obj, IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_Object_ToString(IntPtr ptr); + + // Used by the generated API + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_Object_ClassDB_get_method(string type, string method); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs new file mode 100644 index 0000000000..885845e3a4 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -0,0 +1,238 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Plane : IEquatable<Plane> + { + private Vector3 _normal; + + public Vector3 Normal + { + get { return _normal; } + set { _normal = value; } + } + + public real_t x + { + get + { + return _normal.x; + } + set + { + _normal.x = value; + } + } + + public real_t y + { + get + { + return _normal.y; + } + set + { + _normal.y = value; + } + } + + public real_t z + { + get + { + return _normal.z; + } + set + { + _normal.z = value; + } + } + + public real_t D { get; set; } + + public Vector3 Center + { + get + { + return _normal * D; + } + } + + public real_t DistanceTo(Vector3 point) + { + return _normal.Dot(point) - D; + } + + public Vector3 GetAnyPoint() + { + return _normal * D; + } + + public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) + { + real_t dist = _normal.Dot(point) - D; + return Mathf.Abs(dist) <= epsilon; + } + + public Vector3? Intersect3(Plane b, Plane c) + { + real_t denom = _normal.Cross(b._normal).Dot(c._normal); + + if (Mathf.IsZeroApprox(denom)) + return null; + + Vector3 result = b._normal.Cross(c._normal) * D + + c._normal.Cross(_normal) * b.D + + _normal.Cross(b._normal) * c.D; + + return result / denom; + } + + public Vector3? IntersectRay(Vector3 from, Vector3 dir) + { + real_t den = _normal.Dot(dir); + + if (Mathf.IsZeroApprox(den)) + return null; + + real_t dist = (_normal.Dot(from) - D) / den; + + // This is a ray, before the emitting pos (from) does not exist + if (dist > Mathf.Epsilon) + return null; + + return from + dir * -dist; + } + + public Vector3? IntersectSegment(Vector3 begin, Vector3 end) + { + Vector3 segment = begin - end; + real_t den = _normal.Dot(segment); + + if (Mathf.IsZeroApprox(den)) + return null; + + real_t dist = (_normal.Dot(begin) - D) / den; + + // Only allow dist to be in the range of 0 to 1, with tolerance. + if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon) + return null; + + return begin + segment * -dist; + } + + public bool IsPointOver(Vector3 point) + { + return _normal.Dot(point) > D; + } + + public Plane Normalized() + { + real_t len = _normal.Length(); + + if (len == 0) + return new Plane(0, 0, 0, 0); + + return new Plane(_normal / len, D / len); + } + + public Vector3 Project(Vector3 point) + { + return point - _normal * DistanceTo(point); + } + + // Constants + private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0); + private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0); + private static readonly Plane _planeXY = new Plane(0, 0, 1, 0); + + public static Plane PlaneYZ { get { return _planeYZ; } } + public static Plane PlaneXZ { get { return _planeXZ; } } + public static Plane PlaneXY { get { return _planeXY; } } + + // Constructors + public Plane(real_t a, real_t b, real_t c, real_t d) + { + _normal = new Vector3(a, b, c); + this.D = d; + } + public Plane(Vector3 normal, real_t d) + { + this._normal = normal; + this.D = d; + } + + public Plane(Vector3 v1, Vector3 v2, Vector3 v3) + { + _normal = (v1 - v3).Cross(v1 - v2); + _normal.Normalize(); + D = _normal.Dot(v1); + } + + public static Plane operator -(Plane plane) + { + return new Plane(-plane._normal, -plane.D); + } + + public static bool operator ==(Plane left, Plane right) + { + return left.Equals(right); + } + + public static bool operator !=(Plane left, Plane right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Plane) + { + return Equals((Plane)obj); + } + + return false; + } + + public bool Equals(Plane other) + { + return _normal == other._normal && D == other.D; + } + + public bool IsEqualApprox(Plane other) + { + return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D); + } + + public override int GetHashCode() + { + return _normal.GetHashCode() ^ D.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + _normal.ToString(), + D.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + _normal.ToString(format), + D.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs new file mode 100644 index 0000000000..6702634c51 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs @@ -0,0 +1,414 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Quat : IEquatable<Quat> + { + public real_t x; + public real_t y; + public real_t z; + public real_t w; + + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + case 3: + w = value; + break; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public real_t Length + { + get { return Mathf.Sqrt(LengthSquared); } + } + + public real_t LengthSquared + { + get { return Dot(this); } + } + + public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t) + { + real_t t2 = (1.0f - t) * t * 2f; + Quat sp = Slerp(b, t); + Quat sq = preA.Slerpni(postB, t); + return sp.Slerpni(sq, t2); + } + + public real_t Dot(Quat b) + { + return x * b.x + y * b.y + z * b.z + w * b.w; + } + + public Vector3 GetEuler() + { +#if DEBUG + if (!IsNormalized()) + throw new InvalidOperationException("Quat is not normalized"); +#endif + var basis = new Basis(this); + return basis.GetEuler(); + } + + public Quat Inverse() + { +#if DEBUG + if (!IsNormalized()) + throw new InvalidOperationException("Quat is not normalized"); +#endif + return new Quat(-x, -y, -z, w); + } + + public Quat Normalized() + { + return this / Length; + } + + [Obsolete("Set is deprecated. Use the Quat(" + nameof(real_t) + ", " + nameof(real_t) + ", " + nameof(real_t) + ", " + nameof(real_t) + ") constructor instead.", error: true)] + public void Set(real_t x, real_t y, real_t z, real_t w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + [Obsolete("Set is deprecated. Use the Quat(" + nameof(Quat) + ") constructor instead.", error: true)] + public void Set(Quat q) + { + this = q; + } + + [Obsolete("SetAxisAngle is deprecated. Use the Quat(" + nameof(Vector3) + ", " + nameof(real_t) + ") constructor instead.", error: true)] + public void SetAxisAngle(Vector3 axis, real_t angle) + { + this = new Quat(axis, angle); + } + + [Obsolete("SetEuler is deprecated. Use the Quat(" + nameof(Vector3) + ") constructor instead.", error: true)] + public void SetEuler(Vector3 eulerYXZ) + { + this = new Quat(eulerYXZ); + } + + public Quat Slerp(Quat b, real_t t) + { +#if DEBUG + if (!IsNormalized()) + throw new InvalidOperationException("Quat is not normalized"); + if (!b.IsNormalized()) + throw new ArgumentException("Argument is not normalized", nameof(b)); +#endif + + // Calculate cosine + real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w; + + var to1 = new Quat(); + + // Adjust signs if necessary + if (cosom < 0.0) + { + cosom = -cosom; + to1.x = -b.x; + to1.y = -b.y; + to1.z = -b.z; + to1.w = -b.w; + } + else + { + to1.x = b.x; + to1.y = b.y; + to1.z = b.z; + to1.w = b.w; + } + + real_t sinom, scale0, scale1; + + // Calculate coefficients + if (1.0 - cosom > Mathf.Epsilon) + { + // Standard case (Slerp) + real_t omega = Mathf.Acos(cosom); + sinom = Mathf.Sin(omega); + scale0 = Mathf.Sin((1.0f - t) * omega) / sinom; + scale1 = Mathf.Sin(t * omega) / sinom; + } + else + { + // Quaternions are very close so we can do a linear interpolation + scale0 = 1.0f - t; + scale1 = t; + } + + // Calculate final values + return new Quat + ( + scale0 * x + scale1 * to1.x, + scale0 * y + scale1 * to1.y, + scale0 * z + scale1 * to1.z, + scale0 * w + scale1 * to1.w + ); + } + + public Quat Slerpni(Quat b, real_t t) + { + real_t dot = Dot(b); + + if (Mathf.Abs(dot) > 0.9999f) + { + return this; + } + + real_t theta = Mathf.Acos(dot); + real_t sinT = 1.0f / Mathf.Sin(theta); + real_t newFactor = Mathf.Sin(t * theta) * sinT; + real_t invFactor = Mathf.Sin((1.0f - t) * theta) * sinT; + + return new Quat + ( + invFactor * x + newFactor * b.x, + invFactor * y + newFactor * b.y, + invFactor * z + newFactor * b.z, + invFactor * w + newFactor * b.w + ); + } + + public Vector3 Xform(Vector3 v) + { +#if DEBUG + if (!IsNormalized()) + throw new InvalidOperationException("Quat is not normalized"); +#endif + var u = new Vector3(x, y, z); + Vector3 uv = u.Cross(v); + return v + ((uv * w) + u.Cross(uv)) * 2; + } + + // Static Readonly Properties + public static Quat Identity { get; } = new Quat(0f, 0f, 0f, 1f); + + // Constructors + public Quat(real_t x, real_t y, real_t z, real_t w) + { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon; + } + + public Quat(Quat q) + { + this = q; + } + + public Quat(Basis basis) + { + this = basis.Quat(); + } + + public Quat(Vector3 eulerYXZ) + { + real_t half_a1 = eulerYXZ.y * 0.5f; + real_t half_a2 = eulerYXZ.x * 0.5f; + real_t half_a3 = eulerYXZ.z * 0.5f; + + // R = Y(a1).X(a2).Z(a3) convention for Euler angles. + // Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6) + // a3 is the angle of the first rotation, following the notation in this reference. + + real_t cos_a1 = Mathf.Cos(half_a1); + real_t sin_a1 = Mathf.Sin(half_a1); + real_t cos_a2 = Mathf.Cos(half_a2); + real_t sin_a2 = Mathf.Sin(half_a2); + real_t cos_a3 = Mathf.Cos(half_a3); + real_t sin_a3 = Mathf.Sin(half_a3); + + x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3; + y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3; + z = cos_a1 * cos_a2 * sin_a3 - sin_a1 * sin_a2 * cos_a3; + w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3; + } + + public Quat(Vector3 axis, real_t angle) + { +#if DEBUG + if (!axis.IsNormalized()) + throw new ArgumentException("Argument is not normalized", nameof(axis)); +#endif + + real_t d = axis.Length(); + + if (d == 0f) + { + x = 0f; + y = 0f; + z = 0f; + w = 0f; + } + else + { + real_t sinAngle = Mathf.Sin(angle * 0.5f); + real_t cosAngle = Mathf.Cos(angle * 0.5f); + real_t s = sinAngle / d; + + x = axis.x * s; + y = axis.y * s; + z = axis.z * s; + w = cosAngle; + } + } + + public static Quat operator *(Quat left, Quat right) + { + return new Quat + ( + left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y, + left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z, + left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x, + left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z + ); + } + + public static Quat operator +(Quat left, Quat right) + { + return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + } + + public static Quat operator -(Quat left, Quat right) + { + return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + } + + public static Quat operator -(Quat left) + { + return new Quat(-left.x, -left.y, -left.z, -left.w); + } + + public static Quat operator *(Quat left, Vector3 right) + { + return new Quat + ( + left.w * right.x + left.y * right.z - left.z * right.y, + left.w * right.y + left.z * right.x - left.x * right.z, + left.w * right.z + left.x * right.y - left.y * right.x, + -left.x * right.x - left.y * right.y - left.z * right.z + ); + } + + public static Quat operator *(Vector3 left, Quat right) + { + return new Quat + ( + right.w * left.x + right.y * left.z - right.z * left.y, + right.w * left.y + right.z * left.x - right.x * left.z, + right.w * left.z + right.x * left.y - right.y * left.x, + -right.x * left.x - right.y * left.y - right.z * left.z + ); + } + + public static Quat operator *(Quat left, real_t right) + { + return new Quat(left.x * right, left.y * right, left.z * right, left.w * right); + } + + public static Quat operator *(real_t left, Quat right) + { + return new Quat(right.x * left, right.y * left, right.z * left, right.w * left); + } + + public static Quat operator /(Quat left, real_t right) + { + return left * (1.0f / right); + } + + public static bool operator ==(Quat left, Quat right) + { + return left.Equals(right); + } + + public static bool operator !=(Quat left, Quat right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Quat) + { + return Equals((Quat)obj); + } + + return false; + } + + public bool Equals(Quat other) + { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + public bool IsEqualApprox(Quat other) + { + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString()); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format)); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs new file mode 100644 index 0000000000..94761531b1 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs @@ -0,0 +1,84 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public sealed partial class RID : IDisposable + { + private bool disposed = false; + + internal IntPtr ptr; + + internal static IntPtr GetPtr(RID instance) + { + if (instance == null) + throw new NullReferenceException($"The instance of type {nameof(RID)} is null."); + + if (instance.disposed) + throw new ObjectDisposedException(instance.GetType().FullName); + + return instance.ptr; + } + + ~RID() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + return; + + if (ptr != IntPtr.Zero) + { + godot_icall_RID_Dtor(ptr); + ptr = IntPtr.Zero; + } + + disposed = true; + } + + internal RID(IntPtr ptr) + { + this.ptr = ptr; + } + + public IntPtr NativeInstance + { + get { return ptr; } + } + + internal RID() + { + this.ptr = IntPtr.Zero; + } + + public RID(Object from) + { + this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from)); + } + + public int GetId() + { + return godot_icall_RID_get_id(RID.GetPtr(this)); + } + + public override string ToString() => "[RID]"; + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static void godot_icall_RID_Dtor(IntPtr ptr); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_RID_get_id(IntPtr ptr); + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs new file mode 100644 index 0000000000..91e614dc7b --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs @@ -0,0 +1,262 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Rect2 : IEquatable<Rect2> + { + private Vector2 _position; + private Vector2 _size; + + public Vector2 Position + { + get { return _position; } + set { _position = value; } + } + + public Vector2 Size + { + get { return _size; } + set { _size = value; } + } + + public Vector2 End + { + get { return _position + _size; } + set { _size = value - _position; } + } + + public real_t Area + { + get { return GetArea(); } + } + + public Rect2 Abs() + { + Vector2 end = End; + Vector2 topLeft = new Vector2(Mathf.Min(_position.x, end.x), Mathf.Min(_position.y, end.y)); + return new Rect2(topLeft, _size.Abs()); + } + + public Rect2 Clip(Rect2 b) + { + var newRect = b; + + if (!Intersects(newRect)) + return new Rect2(); + + newRect._position.x = Mathf.Max(b._position.x, _position.x); + newRect._position.y = Mathf.Max(b._position.y, _position.y); + + Vector2 bEnd = b._position + b._size; + Vector2 end = _position + _size; + + newRect._size.x = Mathf.Min(bEnd.x, end.x) - newRect._position.x; + newRect._size.y = Mathf.Min(bEnd.y, end.y) - newRect._position.y; + + return newRect; + } + + public bool Encloses(Rect2 b) + { + return b._position.x >= _position.x && b._position.y >= _position.y && + b._position.x + b._size.x < _position.x + _size.x && + b._position.y + b._size.y < _position.y + _size.y; + } + + public Rect2 Expand(Vector2 to) + { + var expanded = this; + + Vector2 begin = expanded._position; + Vector2 end = expanded._position + expanded._size; + + if (to.x < begin.x) + begin.x = to.x; + if (to.y < begin.y) + begin.y = to.y; + + if (to.x > end.x) + end.x = to.x; + if (to.y > end.y) + end.y = to.y; + + expanded._position = begin; + expanded._size = end - begin; + + return expanded; + } + + public real_t GetArea() + { + return _size.x * _size.y; + } + + public Rect2 Grow(real_t by) + { + var g = this; + + g._position.x -= by; + g._position.y -= by; + g._size.x += by * 2; + g._size.y += by * 2; + + return g; + } + + public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom) + { + var g = this; + + g._position.x -= left; + g._position.y -= top; + g._size.x += left + right; + g._size.y += top + bottom; + + return g; + } + + public Rect2 GrowMargin(Margin margin, real_t by) + { + var g = this; + + g.GrowIndividual(Margin.Left == margin ? by : 0, + Margin.Top == margin ? by : 0, + Margin.Right == margin ? by : 0, + Margin.Bottom == margin ? by : 0); + + return g; + } + + public bool HasNoArea() + { + return _size.x <= 0 || _size.y <= 0; + } + + public bool HasPoint(Vector2 point) + { + if (point.x < _position.x) + return false; + if (point.y < _position.y) + return false; + + if (point.x >= _position.x + _size.x) + return false; + if (point.y >= _position.y + _size.y) + return false; + + return true; + } + + public bool Intersects(Rect2 b) + { + if (_position.x >= b._position.x + b._size.x) + return false; + if (_position.x + _size.x <= b._position.x) + return false; + if (_position.y >= b._position.y + b._size.y) + return false; + if (_position.y + _size.y <= b._position.y) + return false; + + return true; + } + + public Rect2 Merge(Rect2 b) + { + Rect2 newRect; + + newRect._position.x = Mathf.Min(b._position.x, _position.x); + newRect._position.y = Mathf.Min(b._position.y, _position.y); + + newRect._size.x = Mathf.Max(b._position.x + b._size.x, _position.x + _size.x); + newRect._size.y = Mathf.Max(b._position.y + b._size.y, _position.y + _size.y); + + newRect._size = newRect._size - newRect._position; // Make relative again + + return newRect; + } + + // Constructors + public Rect2(Vector2 position, Vector2 size) + { + _position = position; + _size = size; + } + public Rect2(Vector2 position, real_t width, real_t height) + { + _position = position; + _size = new Vector2(width, height); + } + public Rect2(real_t x, real_t y, Vector2 size) + { + _position = new Vector2(x, y); + _size = size; + } + public Rect2(real_t x, real_t y, real_t width, real_t height) + { + _position = new Vector2(x, y); + _size = new Vector2(width, height); + } + + public static bool operator ==(Rect2 left, Rect2 right) + { + return left.Equals(right); + } + + public static bool operator !=(Rect2 left, Rect2 right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Rect2) + { + return Equals((Rect2)obj); + } + + return false; + } + + public bool Equals(Rect2 other) + { + return _position.Equals(other._position) && _size.Equals(other._size); + } + + public bool IsEqualApprox(Rect2 other) + { + return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size); + } + + public override int GetHashCode() + { + return _position.GetHashCode() ^ _size.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + _position.ToString(), + _size.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + _position.ToString(format), + _size.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs new file mode 100644 index 0000000000..9483b6ffb4 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Godot +{ + public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]> + { + private bool completed; + private object[] result; + private Action action; + + public SignalAwaiter(Object source, string signal, Object target) + { + godot_icall_SignalAwaiter_connect(Object.GetPtr(source), signal, Object.GetPtr(target), this); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, string signal, IntPtr target, SignalAwaiter awaiter); + + public bool IsCompleted + { + get + { + return completed; + } + } + + public void OnCompleted(Action action) + { + this.action = action; + } + + public object[] GetResult() + { + return result; + } + + public IAwaiter<object[]> GetAwaiter() + { + return this; + } + + internal void SignalCallback(object[] args) + { + completed = true; + result = args; + + if (action != null) + { + action(); + } + } + + internal void FailureCallback() + { + action = null; + completed = true; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs new file mode 100644 index 0000000000..e096d37a6f --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs @@ -0,0 +1,1046 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; + +namespace Godot +{ + public static class StringExtensions + { + private static int GetSliceCount(this string instance, string splitter) + { + if (instance.Empty() || splitter.Empty()) + return 0; + + int pos = 0; + int slices = 1; + + while ((pos = instance.Find(splitter, pos, caseSensitive: true)) >= 0) + { + slices++; + pos += splitter.Length; + } + + return slices; + } + + private static string GetSliceCharacter(this string instance, char splitter, int slice) + { + if (!instance.Empty() && slice >= 0) + { + int i = 0; + int prev = 0; + int count = 0; + + while (true) + { + bool end = instance.Length <= i; + + if (end || instance[i] == splitter) + { + if (slice == count) + { + return instance.Substring(prev, i - prev); + } + else if (end) + { + return string.Empty; + } + + count++; + prev = i + 1; + } + + i++; + } + } + + return string.Empty; + } + + // <summary> + // If the string is a path to a file, return the path to the file without the extension. + // </summary> + public static string BaseName(this string instance) + { + int index = instance.LastIndexOf('.'); + + if (index > 0) + return instance.Substring(0, index); + + return instance; + } + + // <summary> + // Return true if the strings begins with the given string. + // </summary> + public static bool BeginsWith(this string instance, string text) + { + return instance.StartsWith(text); + } + + // <summary> + // Return the bigrams (pairs of consecutive letters) of this string. + // </summary> + public static string[] Bigrams(this string instance) + { + var b = new string[instance.Length - 1]; + + for (int i = 0; i < b.Length; i++) + { + b[i] = instance.Substring(i, 2); + } + + return b; + } + + // <summary> + // Return the amount of substrings in string. + // </summary> + public static int Count(this string instance, string what, bool caseSensitive = true, int from = 0, int to = 0) + { + if (what.Length == 0) + { + return 0; + } + + int len = instance.Length; + int slen = what.Length; + + if (len < slen) + { + return 0; + } + + string str; + + if (from >= 0 && to >= 0) + { + if (to == 0) + { + to = len; + } + else if (from >= to) + { + return 0; + } + if (from == 0 && to == len) + { + str = instance; + } + else + { + str = instance.Substring(from, to - from); + } + } + else + { + return 0; + } + + int c = 0; + int idx; + + do + { + idx = str.IndexOf(what, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + if (idx != -1) + { + str = str.Substring(idx + slen); + ++c; + } + } while (idx != -1); + + return c; + } + + // <summary> + // Return a copy of the string with special characters escaped using the C language standard. + // </summary> + public static string CEscape(this string instance) + { + var sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\", "\\\\"); + sb.Replace("\a", "\\a"); + sb.Replace("\b", "\\b"); + sb.Replace("\f", "\\f"); + sb.Replace("\n", "\\n"); + sb.Replace("\r", "\\r"); + sb.Replace("\t", "\\t"); + sb.Replace("\v", "\\v"); + sb.Replace("\'", "\\'"); + sb.Replace("\"", "\\\""); + sb.Replace("?", "\\?"); + + return sb.ToString(); + } + + // <summary> + // Return a copy of the string with escaped characters replaced by their meanings according to the C language standard. + // </summary> + public static string CUnescape(this string instance) + { + var sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\a", "\a"); + sb.Replace("\\b", "\b"); + sb.Replace("\\f", "\f"); + sb.Replace("\\n", "\n"); + sb.Replace("\\r", "\r"); + sb.Replace("\\t", "\t"); + sb.Replace("\\v", "\v"); + sb.Replace("\\'", "\'"); + sb.Replace("\\\"", "\""); + sb.Replace("\\?", "?"); + sb.Replace("\\\\", "\\"); + + return sb.ToString(); + } + + // <summary> + // Change the case of some letters. Replace underscores with spaces, convert all letters to lowercase then capitalize first and every letter following the space character. For [code]capitalize camelCase mixed_with_underscores[/code] it will return [code]Capitalize Camelcase Mixed With Underscores[/code]. + // </summary> + public static string Capitalize(this string instance) + { + string aux = instance.Replace("_", " ").ToLower(); + var cap = string.Empty; + + for (int i = 0; i < aux.GetSliceCount(" "); i++) + { + string slice = aux.GetSliceCharacter(' ', i); + if (slice.Length > 0) + { + slice = char.ToUpper(slice[0]) + slice.Substring(1); + if (i > 0) + cap += " "; + cap += slice; + } + } + + return cap; + } + + // <summary> + // Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int CasecmpTo(this string instance, string to) + { + return instance.CompareTo(to, caseSensitive: true); + } + + // <summary> + // Perform a comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int CompareTo(this string instance, string to, bool caseSensitive = true) + { + if (instance.Empty()) + return to.Empty() ? 0 : -1; + + if (to.Empty()) + return 1; + + int instanceIndex = 0; + int toIndex = 0; + + if (caseSensitive) // Outside while loop to avoid checking multiple times, despite some code duplication. + { + while (true) + { + if (to[toIndex] == 0 && instance[instanceIndex] == 0) + return 0; // We're equal + if (instance[instanceIndex] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + if (to[toIndex] == 0) + return 1; // Otherwise the other one is smaller... + if (instance[instanceIndex] < to[toIndex]) // More than + return -1; + if (instance[instanceIndex] > to[toIndex]) // Less than + return 1; + + instanceIndex++; + toIndex++; + } + } else + { + while (true) + { + if (to[toIndex] == 0 && instance[instanceIndex] == 0) + return 0; // We're equal + if (instance[instanceIndex] == 0) + return -1; // If this is empty, and the other one is not, then we're less... I think? + if (to[toIndex] == 0) + return 1; // Otherwise the other one is smaller.. + if (char.ToUpper(instance[instanceIndex]) < char.ToUpper(to[toIndex])) // More than + return -1; + if (char.ToUpper(instance[instanceIndex]) > char.ToUpper(to[toIndex])) // Less than + return 1; + + instanceIndex++; + toIndex++; + } + } + } + + // <summary> + // Return true if the string is empty. + // </summary> + public static bool Empty(this string instance) + { + return string.IsNullOrEmpty(instance); + } + + // <summary> + // Return true if the strings ends with the given string. + // </summary> + public static bool EndsWith(this string instance, string text) + { + return instance.EndsWith(text); + } + + // <summary> + // Erase [code]chars[/code] characters from the string starting from [code]pos[/code]. + // </summary> + public static void Erase(this StringBuilder instance, int pos, int chars) + { + instance.Remove(pos, chars); + } + + // <summary> + // If the string is a path to a file, return the extension. + // </summary> + public static string Extension(this string instance) + { + int pos = instance.FindLast("."); + + if (pos < 0) + return instance; + + return instance.Substring(pos + 1); + } + + /// <summary>Find the first occurrence of a substring. Optionally, the search starting position can be passed.</summary> + /// <returns>The starting position of the substring, or -1 if not found.</returns> + public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true) + { + return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + } + + /// <summary>Find the last occurrence of a substring.</summary> + /// <returns>The starting position of the substring, or -1 if not found.</returns> + public static int FindLast(this string instance, string what, bool caseSensitive = true) + { + return instance.FindLast(what, instance.Length - 1, caseSensitive); + } + + /// <summary>Find the last occurrence of a substring specifying the search starting position.</summary> + /// <returns>The starting position of the substring, or -1 if not found.</returns> + public static int FindLast(this string instance, string what, int from, bool caseSensitive = true) + { + return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase); + } + + /// <summary>Find the first occurrence of a substring but search as case-insensitive. Optionally, the search starting position can be passed.</summary> + /// <returns>The starting position of the substring, or -1 if not found.</returns> + public static int FindN(this string instance, string what, int from = 0) + { + return instance.IndexOf(what, from, StringComparison.OrdinalIgnoreCase); + } + + // <summary> + // If the string is a path to a file, return the base directory. + // </summary> + public static string GetBaseDir(this string instance) + { + int basepos = instance.Find("://"); + + string rs; + var @base = string.Empty; + + if (basepos != -1) + { + var end = basepos + 3; + rs = instance.Substring(end); + @base = instance.Substring(0, end); + } + else + { + if (instance.BeginsWith("/")) + { + rs = instance.Substring(1); + @base = "/"; + } + else + { + rs = instance; + } + } + + int sep = Mathf.Max(rs.FindLast("/"), rs.FindLast("\\")); + + if (sep == -1) + return @base; + + return @base + rs.Substr(0, sep); + } + + // <summary> + // If the string is a path to a file, return the file and ignore the base directory. + // </summary> + public static string GetFile(this string instance) + { + int sep = Mathf.Max(instance.FindLast("/"), instance.FindLast("\\")); + + if (sep == -1) + return instance; + + return instance.Substring(sep + 1); + } + + // <summary> + // Hash the string and return a 32 bits integer. + // </summary> + public static int Hash(this string instance) + { + int index = 0; + int hashv = 5381; + int c; + + while ((c = instance[index++]) != 0) + hashv = (hashv << 5) + hashv + c; // hash * 33 + c + + return hashv; + } + + // <summary> + // Convert a string containing an hexadecimal number into an int. + // </summary> + public static int HexToInt(this string instance) + { + int sign = 1; + + if (instance[0] == '-') + { + sign = -1; + instance = instance.Substring(1); + } + + if (!instance.StartsWith("0x")) + return 0; + + return sign * int.Parse(instance.Substring(2), NumberStyles.HexNumber); + } + + // <summary> + // Insert a substring at a given position. + // </summary> + public static string Insert(this string instance, int pos, string what) + { + return instance.Insert(pos, what); + } + + // <summary> + // If the string is a path to a file or directory, return true if the path is absolute. + // </summary> + public static bool IsAbsPath(this string instance) + { + return System.IO.Path.IsPathRooted(instance); + } + + // <summary> + // If the string is a path to a file or directory, return true if the path is relative. + // </summary> + public static bool IsRelPath(this string instance) + { + return !System.IO.Path.IsPathRooted(instance); + } + + // <summary> + // Check whether this string is a subsequence of the given string. + // </summary> + public static bool IsSubsequenceOf(this string instance, string text, bool caseSensitive = true) + { + int len = instance.Length; + + if (len == 0) + return true; // Technically an empty string is subsequence of any string + + if (len > text.Length) + return false; + + int source = 0; + int target = 0; + + while (source < len && target < text.Length) + { + bool match; + + if (!caseSensitive) + { + char sourcec = char.ToLower(instance[source]); + char targetc = char.ToLower(text[target]); + match = sourcec == targetc; + } + else + { + match = instance[source] == text[target]; + } + if (match) + { + source++; + if (source >= len) + return true; + } + + target++; + } + + return false; + } + + // <summary> + // Check whether this string is a subsequence of the given string, ignoring case differences. + // </summary> + public static bool IsSubsequenceOfI(this string instance, string text) + { + return instance.IsSubsequenceOf(text, caseSensitive: false); + } + + // <summary> + // Check whether the string contains a valid float. + // </summary> + public static bool IsValidFloat(this string instance) + { + float f; + return float.TryParse(instance, out f); + } + + // <summary> + // Check whether the string contains a valid color in HTML notation. + // </summary> + public static bool IsValidHtmlColor(this string instance) + { + return Color.HtmlIsValid(instance); + } + + // <summary> + // Check whether the string is a valid identifier. As is common in programming languages, a valid identifier may contain only letters, digits and underscores (_) and the first character may not be a digit. + // </summary> + public static bool IsValidIdentifier(this string instance) + { + int len = instance.Length; + + if (len == 0) + return false; + + for (int i = 0; i < len; i++) + { + if (i == 0) + { + if (instance[0] >= '0' && instance[0] <= '9') + return false; // Don't start with number plz + } + + bool validChar = instance[i] >= '0' && + instance[i] <= '9' || instance[i] >= 'a' && + instance[i] <= 'z' || instance[i] >= 'A' && + instance[i] <= 'Z' || instance[i] == '_'; + + if (!validChar) + return false; + } + + return true; + } + + // <summary> + // Check whether the string contains a valid integer. + // </summary> + public static bool IsValidInteger(this string instance) + { + int f; + return int.TryParse(instance, out f); + } + + // <summary> + // Check whether the string contains a valid IP address. + // </summary> + public static bool IsValidIPAddress(this string instance) + { + // TODO: Support IPv6 addresses + string[] ip = instance.Split("."); + + if (ip.Length != 4) + return false; + + for (int i = 0; i < ip.Length; i++) + { + string n = ip[i]; + if (!n.IsValidInteger()) + return false; + + int val = n.ToInt(); + if (val < 0 || val > 255) + return false; + } + + return true; + } + + // <summary> + // Return a copy of the string with special characters escaped using the JSON standard. + // </summary> + public static string JSONEscape(this string instance) + { + var sb = new StringBuilder(string.Copy(instance)); + + sb.Replace("\\", "\\\\"); + sb.Replace("\b", "\\b"); + sb.Replace("\f", "\\f"); + sb.Replace("\n", "\\n"); + sb.Replace("\r", "\\r"); + sb.Replace("\t", "\\t"); + sb.Replace("\v", "\\v"); + sb.Replace("\"", "\\\""); + + return sb.ToString(); + } + + // <summary> + // Return an amount of characters from the left of the string. + // </summary> + public static string Left(this string instance, int pos) + { + if (pos <= 0) + return string.Empty; + + if (pos >= instance.Length) + return instance; + + return instance.Substring(0, pos); + } + + /// <summary> + /// Return the length of the string in characters. + /// </summary> + public static int Length(this string instance) + { + return instance.Length; + } + + // <summary> + // Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'. + // </summary> + public static bool ExprMatch(this string instance, string expr, bool caseSensitive) + { + if (expr.Length == 0 || instance.Length == 0) + return false; + + switch (expr[0]) + { + case '\0': + return instance[0] == 0; + case '*': + return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive); + case '?': + return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive); + default: + return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && + ExprMatch(expr + 1, instance + 1, caseSensitive); + } + } + + // <summary> + // Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]). + // </summary> + public static bool Match(this string instance, string expr, bool caseSensitive = true) + { + return instance.ExprMatch(expr, caseSensitive); + } + + // <summary> + // Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]). + // </summary> + public static bool MatchN(this string instance, string expr) + { + return instance.ExprMatch(expr, caseSensitive: false); + } + + // <summary> + // Return the MD5 hash of the string as an array of bytes. + // </summary> + public static byte[] MD5Buffer(this string instance) + { + return godot_icall_String_md5_buffer(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_String_md5_buffer(string str); + + // <summary> + // Return the MD5 hash of the string as a string. + // </summary> + public static string MD5Text(this string instance) + { + return godot_icall_String_md5_text(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_md5_text(string str); + + // <summary> + // Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater. + // </summary> + public static int NocasecmpTo(this string instance, string to) + { + return instance.CompareTo(to, caseSensitive: false); + } + + // <summary> + // Return the character code at position [code]at[/code]. + // </summary> + public static int OrdAt(this string instance, int at) + { + return instance[at]; + } + + // <summary> + // Format a number to have an exact number of [code]digits[/code] after the decimal point. + // </summary> + public static string PadDecimals(this string instance, int digits) + { + int c = instance.Find("."); + + if (c == -1) + { + if (digits <= 0) + return instance; + + instance += "."; + c = instance.Length - 1; + } + else + { + if (digits <= 0) + return instance.Substring(0, c); + } + + if (instance.Length - (c + 1) > digits) + { + instance = instance.Substring(0, c + digits + 1); + } + else + { + while (instance.Length - (c + 1) < digits) + { + instance += "0"; + } + } + + return instance; + } + + // <summary> + // Format a number to have an exact number of [code]digits[/code] before the decimal point. + // </summary> + public static string PadZeros(this string instance, int digits) + { + string s = instance; + int end = s.Find("."); + + if (end == -1) + end = s.Length; + + if (end == 0) + return s; + + int begin = 0; + + while (begin < end && (s[begin] < '0' || s[begin] > '9')) + { + begin++; + } + + if (begin >= end) + return s; + + while (end - begin < digits) + { + s = s.Insert(begin, "0"); + end++; + } + + return s; + } + + // <summary> + // Decode a percent-encoded string. See [method percent_encode]. + // </summary> + public static string PercentDecode(this string instance) + { + return Uri.UnescapeDataString(instance); + } + + // <summary> + // Percent-encode a string. This is meant to encode parameters in a URL when sending a HTTP GET request and bodies of form-urlencoded POST request. + // </summary> + public static string PercentEncode(this string instance) + { + return Uri.EscapeDataString(instance); + } + + // <summary> + // If the string is a path, this concatenates [code]file[/code] at the end of the string as a subpath. E.g. [code]"this/is".plus_file("path") == "this/is/path"[/code]. + // </summary> + public static string PlusFile(this string instance, string file) + { + if (instance.Length > 0 && instance[instance.Length - 1] == '/') + return instance + file; + return instance + "/" + file; + } + + // <summary> + // Replace occurrences of a substring for different ones inside the string. + // </summary> + public static string Replace(this string instance, string what, string forwhat) + { + return instance.Replace(what, forwhat); + } + + // <summary> + // Replace occurrences of a substring for different ones inside the string, but search case-insensitive. + // </summary> + public static string ReplaceN(this string instance, string what, string forwhat) + { + return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase); + } + + // <summary> + // Perform a search for a substring, but start from the end of the string instead of the beginning. + // </summary> + public static int RFind(this string instance, string what, int from = -1) + { + return godot_icall_String_rfind(instance, what, from); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_String_rfind(string str, string what, int from); + + // <summary> + // Perform a search for a substring, but start from the end of the string instead of the beginning. Also search case-insensitive. + // </summary> + public static int RFindN(this string instance, string what, int from = -1) + { + return godot_icall_String_rfindn(instance, what, from); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static int godot_icall_String_rfindn(string str, string what, int from); + + // <summary> + // Return the right side of the string from a given position. + // </summary> + public static string Right(this string instance, int pos) + { + if (pos >= instance.Length) + return instance; + + if (pos < 0) + return string.Empty; + + return instance.Substring(pos, instance.Length - pos); + } + + public static byte[] SHA256Buffer(this string instance) + { + return godot_icall_String_sha256_buffer(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static byte[] godot_icall_String_sha256_buffer(string str); + + // <summary> + // Return the SHA-256 hash of the string as a string. + // </summary> + public static string SHA256Text(this string instance) + { + return godot_icall_String_sha256_text(instance); + } + + [MethodImpl(MethodImplOptions.InternalCall)] + internal extern static string godot_icall_String_sha256_text(string str); + + // <summary> + // Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar. + // </summary> + public static float Similarity(this string instance, string text) + { + if (instance == text) + { + // Equal strings are totally similar + return 1.0f; + } + if (instance.Length < 2 || text.Length < 2) + { + // No way to calculate similarity without a single bigram + return 0.0f; + } + + string[] sourceBigrams = instance.Bigrams(); + string[] targetBigrams = text.Bigrams(); + + int sourceSize = sourceBigrams.Length; + int targetSize = targetBigrams.Length; + + float sum = sourceSize + targetSize; + float inter = 0; + + for (int i = 0; i < sourceSize; i++) + { + for (int j = 0; j < targetSize; j++) + { + if (sourceBigrams[i] == targetBigrams[j]) + { + inter++; + break; + } + } + } + + return 2.0f * inter / sum; + } + + // <summary> + // Split the string by a divisor string, return an array of the substrings. Example "One,Two,Three" will return ["One","Two","Three"] if split by ",". + // </summary> + public static string[] Split(this string instance, string divisor, bool allowEmpty = true) + { + return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries); + } + + // <summary> + // Split the string in floats by using a divisor string, return an array of the substrings. Example "1,2.5,3" will return [1,2.5,3] if split by ",". + // </summary> + public static float[] SplitFloats(this string instance, string divisor, bool allowEmpty = true) + { + var ret = new List<float>(); + int from = 0; + int len = instance.Length; + + while (true) + { + int end = instance.Find(divisor, from, caseSensitive: true); + if (end < 0) + end = len; + if (allowEmpty || end > from) + ret.Add(float.Parse(instance.Substring(from))); + if (end == len) + break; + + from = end + divisor.Length; + } + + return ret.ToArray(); + } + + private static readonly char[] _nonPrintable = { + (char)00, (char)01, (char)02, (char)03, (char)04, (char)05, + (char)06, (char)07, (char)08, (char)09, (char)10, (char)11, + (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, + (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, + (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, + (char)30, (char)31, (char)32 + }; + + // <summary> + // Return a copy of the string stripped of any non-printable character at the beginning and the end. The optional arguments are used to toggle stripping on the left and right edges respectively. + // </summary> + public static string StripEdges(this string instance, bool left = true, bool right = true) + { + if (left) + { + if (right) + return instance.Trim(_nonPrintable); + return instance.TrimStart(_nonPrintable); + } + + return instance.TrimEnd(_nonPrintable); + } + + // <summary> + // Return part of the string from the position [code]from[/code], with length [code]len[/code]. + // </summary> + public static string Substr(this string instance, int from, int len) + { + int max = instance.Length - from; + return instance.Substring(from, len > max ? max : len); + } + + // <summary> + // Convert the String (which is a character array) to PoolByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters. + // </summary> + public static byte[] ToAscii(this string instance) + { + return Encoding.ASCII.GetBytes(instance); + } + + // <summary> + // Convert a string, containing a decimal number, into a [code]float[/code]. + // </summary> + public static float ToFloat(this string instance) + { + return float.Parse(instance); + } + + // <summary> + // Convert a string, containing an integer number, into an [code]int[/code]. + // </summary> + public static int ToInt(this string instance) + { + return int.Parse(instance); + } + + // <summary> + // Return the string converted to lowercase. + // </summary> + public static string ToLower(this string instance) + { + return instance.ToLower(); + } + + // <summary> + // Return the string converted to uppercase. + // </summary> + public static string ToUpper(this string instance) + { + return instance.ToUpper(); + } + + // <summary> + // Convert the String (which is an array of characters) to PoolByteArray (which is an array of bytes). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii(). + // </summary> + public static byte[] ToUTF8(this string instance) + { + return Encoding.UTF8.GetBytes(instance); + } + + // <summary> + // Return a copy of the string with special characters escaped using the XML standard. + // </summary> + public static string XMLEscape(this string instance) + { + return SecurityElement.Escape(instance); + } + + // <summary> + // Return a copy of the string with escaped characters replaced by their meanings according to the XML standard. + // </summary> + public static string XMLUnescape(this string instance) + { + return SecurityElement.FromString(instance).Text; + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs new file mode 100644 index 0000000000..0b84050f07 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs @@ -0,0 +1,216 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Transform : IEquatable<Transform> + { + public Basis basis; + public Vector3 origin; + + public Transform AffineInverse() + { + Basis basisInv = basis.Inverse(); + return new Transform(basisInv, basisInv.Xform(-origin)); + } + + public Transform InterpolateWith(Transform transform, real_t c) + { + /* not sure if very "efficient" but good enough? */ + + Vector3 sourceScale = basis.Scale; + Quat sourceRotation = basis.RotationQuat(); + Vector3 sourceLocation = origin; + + Vector3 destinationScale = transform.basis.Scale; + Quat destinationRotation = transform.basis.RotationQuat(); + Vector3 destinationLocation = transform.origin; + + var interpolated = new Transform(); + interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, c).Normalized(), sourceScale.LinearInterpolate(destinationScale, c)); + interpolated.origin = sourceLocation.LinearInterpolate(destinationLocation, c); + + return interpolated; + } + + public Transform Inverse() + { + Basis basisTr = basis.Transposed(); + return new Transform(basisTr, basisTr.Xform(-origin)); + } + + public Transform LookingAt(Vector3 target, Vector3 up) + { + var t = this; + t.SetLookAt(origin, target, up); + return t; + } + + public Transform Orthonormalized() + { + return new Transform(basis.Orthonormalized(), origin); + } + + public Transform Rotated(Vector3 axis, real_t phi) + { + return new Transform(new Basis(axis, phi), new Vector3()) * this; + } + + public Transform Scaled(Vector3 scale) + { + return new Transform(basis.Scaled(scale), origin * scale); + } + + public void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) + { + // Make rotation matrix + // Z vector + Vector3 column2 = eye - target; + + column2.Normalize(); + + Vector3 column1 = up; + + Vector3 column0 = column1.Cross(column2); + + // Recompute Y = Z cross X + column1 = column2.Cross(column0); + + column0.Normalize(); + column1.Normalize(); + + basis = new Basis(column0, column1, column2); + + origin = eye; + } + + public Transform Translated(Vector3 ofs) + { + return new Transform(basis, new Vector3 + ( + origin[0] += basis.Row0.Dot(ofs), + origin[1] += basis.Row1.Dot(ofs), + origin[2] += basis.Row2.Dot(ofs) + )); + } + + public Vector3 Xform(Vector3 v) + { + return new Vector3 + ( + basis.Row0.Dot(v) + origin.x, + basis.Row1.Dot(v) + origin.y, + basis.Row2.Dot(v) + origin.z + ); + } + + public Vector3 XformInv(Vector3 v) + { + Vector3 vInv = v - origin; + + return new Vector3 + ( + 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 + ); + } + + // Constants + private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero); + private static readonly Transform _flipX = new Transform(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero); + private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero); + private static readonly Transform _flipZ = new Transform(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero); + + public static Transform Identity { get { return _identity; } } + public static Transform FlipX { get { return _flipX; } } + public static Transform FlipY { get { return _flipY; } } + public static Transform FlipZ { get { return _flipZ; } } + + // Constructors + public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin) + { + basis = new Basis(column0, column1, column2); + this.origin = origin; + } + + public Transform(Quat quat, Vector3 origin) + { + basis = new Basis(quat); + this.origin = origin; + } + + public Transform(Basis basis, Vector3 origin) + { + this.basis = basis; + this.origin = origin; + } + + public static Transform operator *(Transform left, Transform right) + { + left.origin = left.Xform(right.origin); + left.basis *= right.basis; + return left; + } + + public static bool operator ==(Transform left, Transform right) + { + return left.Equals(right); + } + + public static bool operator !=(Transform left, Transform right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + if (obj is Transform) + { + return Equals((Transform)obj); + } + + return false; + } + + public bool Equals(Transform other) + { + return basis.Equals(other.basis) && origin.Equals(other.origin); + } + + public bool IsEqualApprox(Transform other) + { + return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin); + } + + public override int GetHashCode() + { + return basis.GetHashCode() ^ origin.GetHashCode(); + } + + public override string ToString() + { + return String.Format("{0} - {1}", new object[] + { + basis.ToString(), + origin.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("{0} - {1}", new object[] + { + basis.ToString(format), + origin.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs new file mode 100644 index 0000000000..77ea3e5830 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -0,0 +1,390 @@ +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Transform2D : IEquatable<Transform2D> + { + public Vector2 x; + public Vector2 y; + public Vector2 origin; + + public real_t Rotation + { + get + { + real_t det = BasisDeterminant(); + Transform2D t = Orthonormalized(); + if (det < 0) + { + t.ScaleBasis(new Vector2(1, -1)); + } + return Mathf.Atan2(t.x.y, t.x.x); + } + set + { + Vector2 scale = Scale; + x.x = y.y = Mathf.Cos(value); + x.y = y.x = Mathf.Sin(value); + y.x *= -1; + Scale = scale; + } + } + + public Vector2 Scale + { + get + { + real_t detSign = Mathf.Sign(BasisDeterminant()); + return new Vector2(x.Length(), detSign * y.Length()); + } + set + { + x = x.Normalized(); + y = y.Normalized(); + x *= value.x; + y *= value.y; + } + } + + public Vector2 this[int rowIndex] + { + get + { + switch (rowIndex) + { + case 0: + return x; + case 1: + return y; + case 2: + return origin; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (rowIndex) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + origin = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public real_t this[int rowIndex, int columnIndex] + { + get + { + switch (rowIndex) + { + case 0: + return x[columnIndex]; + case 1: + return y[columnIndex]; + case 2: + return origin[columnIndex]; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (rowIndex) + { + case 0: + x[columnIndex] = value; + return; + case 1: + y[columnIndex] = value; + return; + case 2: + origin[columnIndex] = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + public Transform2D AffineInverse() + { + real_t det = BasisDeterminant(); + + if (det == 0) + throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted."); + + var inv = this; + + real_t temp = inv[0, 0]; + inv[0, 0] = inv[1, 1]; + inv[1, 1] = temp; + + real_t detInv = 1.0f / det; + + inv[0] *= new Vector2(detInv, -detInv); + inv[1] *= new Vector2(-detInv, detInv); + + inv[2] = inv.BasisXform(-inv[2]); + + return inv; + } + + private real_t BasisDeterminant() + { + return x.x * y.y - x.y * y.x; + } + + public Vector2 BasisXform(Vector2 v) + { + return new Vector2(Tdotx(v), Tdoty(v)); + } + + public Vector2 BasisXformInv(Vector2 v) + { + return new Vector2(x.Dot(v), y.Dot(v)); + } + + public Transform2D InterpolateWith(Transform2D m, real_t c) + { + real_t r1 = Rotation; + real_t r2 = m.Rotation; + + Vector2 s1 = Scale; + Vector2 s2 = m.Scale; + + // Slerp rotation + var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1)); + var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2)); + + real_t dot = v1.Dot(v2); + + // Clamp dot to [-1, 1] + dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot); + + Vector2 v; + + if (dot > 0.9995f) + { + // Linearly interpolate to avoid numerical precision issues + v = v1.LinearInterpolate(v2, c).Normalized(); + } + else + { + real_t angle = c * Mathf.Acos(dot); + Vector2 v3 = (v2 - v1 * dot).Normalized(); + v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle); + } + + // Extract parameters + Vector2 p1 = origin; + Vector2 p2 = m.origin; + + // Construct matrix + var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c)); + Vector2 scale = s1.LinearInterpolate(s2, c); + res.x *= scale; + res.y *= scale; + + return res; + } + + public Transform2D Inverse() + { + var inv = this; + + // Swap + real_t temp = inv.x.y; + inv.x.y = inv.y.x; + inv.y.x = temp; + + inv.origin = inv.BasisXform(-inv.origin); + + return inv; + } + + public Transform2D Orthonormalized() + { + var on = this; + + Vector2 onX = on.x; + Vector2 onY = on.y; + + onX.Normalize(); + onY = onY - onX * onX.Dot(onY); + onY.Normalize(); + + on.x = onX; + on.y = onY; + + return on; + } + + public Transform2D Rotated(real_t phi) + { + return this * new Transform2D(phi, new Vector2()); + } + + public Transform2D Scaled(Vector2 scale) + { + var copy = this; + copy.x *= scale; + copy.y *= scale; + copy.origin *= scale; + return copy; + } + + private void ScaleBasis(Vector2 scale) + { + x.x *= scale.x; + x.y *= scale.y; + y.x *= scale.x; + y.y *= scale.y; + } + + private real_t Tdotx(Vector2 with) + { + return this[0, 0] * with[0] + this[1, 0] * with[1]; + } + + private real_t Tdoty(Vector2 with) + { + return this[0, 1] * with[0] + this[1, 1] * with[1]; + } + + public Transform2D Translated(Vector2 offset) + { + var copy = this; + copy.origin += copy.BasisXform(offset); + return copy; + } + + public Vector2 Xform(Vector2 v) + { + return new Vector2(Tdotx(v), Tdoty(v)) + origin; + } + + public Vector2 XformInv(Vector2 v) + { + Vector2 vInv = v - origin; + return new Vector2(x.Dot(vInv), y.Dot(vInv)); + } + + // Constants + private static readonly Transform2D _identity = new Transform2D(1, 0, 0, 1, 0, 0); + 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 => _identity; + public static Transform2D FlipX => _flipX; + public static Transform2D FlipY => _flipY; + + // Constructors + public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 originPos) + { + x = xAxis; + y = yAxis; + origin = originPos; + } + + // Arguments are named such that xy is equal to calling x.y + public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy) + { + x = new Vector2(xx, xy); + y = new Vector2(yx, yy); + origin = new Vector2(ox, oy); + } + + public Transform2D(real_t rot, Vector2 pos) + { + x.x = y.y = Mathf.Cos(rot); + x.y = y.x = Mathf.Sin(rot); + y.x *= -1; + origin = pos; + } + + public static Transform2D operator *(Transform2D left, Transform2D right) + { + left.origin = left.Xform(right.origin); + + 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; + left.y.x = y0; + left.y.y = y1; + + return left; + } + + public static bool operator ==(Transform2D left, Transform2D right) + { + return left.Equals(right); + } + + public static bool operator !=(Transform2D left, Transform2D right) + { + return !left.Equals(right); + } + + public override bool Equals(object obj) + { + return obj is Transform2D transform2D && Equals(transform2D); + } + + public bool Equals(Transform2D other) + { + return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin); + } + + public bool IsEqualApprox(Transform2D other) + { + return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin); + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(), + y.ToString(), + origin.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(format), + y.ToString(format), + origin.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs new file mode 100644 index 0000000000..f92453f546 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -0,0 +1,489 @@ +// file: core/math/math_2d.h +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/math/math_2d.cpp +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + /// <summary> + /// 2-element structure that can be used to represent positions in 2D space or any other pair of numeric values. + /// </summary> + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Vector2 : IEquatable<Vector2> + { + public enum Axis + { + X = 0, + Y + } + + public real_t x; + public real_t y; + + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal void Normalize() + { + real_t lengthsq = LengthSquared(); + + if (lengthsq == 0) + { + x = y = 0f; + } + else + { + real_t length = Mathf.Sqrt(lengthsq); + x /= length; + y /= length; + } + } + + public real_t Cross(Vector2 b) + { + return x * b.y - y * b.x; + } + + public Vector2 Abs() + { + return new Vector2(Mathf.Abs(x), Mathf.Abs(y)); + } + + public real_t Angle() + { + return Mathf.Atan2(y, x); + } + + public real_t AngleTo(Vector2 to) + { + return Mathf.Atan2(Cross(to), Dot(to)); + } + + public real_t AngleToPoint(Vector2 to) + { + return Mathf.Atan2(y - to.y, x - to.x); + } + + public real_t Aspect() + { + return x / y; + } + + public Vector2 Bounce(Vector2 n) + { + return -Reflect(n); + } + + public Vector2 Ceil() + { + return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y)); + } + + public Vector2 Clamped(real_t length) + { + var v = this; + real_t l = Length(); + + if (l > 0 && length < l) + { + v /= l; + v *= length; + } + + return v; + } + + public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t) + { + var p0 = preA; + var p1 = this; + var p2 = b; + var p3 = postB; + + real_t t2 = t * t; + real_t t3 = t2 * t; + + return 0.5f * (p1 * 2.0f + + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); + } + + public Vector2 DirectionTo(Vector2 b) + { + return new Vector2(b.x - x, b.y - y).Normalized(); + } + + public real_t DistanceSquaredTo(Vector2 to) + { + return (x - to.x) * (x - to.x) + (y - to.y) * (y - to.y); + } + + public real_t DistanceTo(Vector2 to) + { + return Mathf.Sqrt((x - to.x) * (x - to.x) + (y - to.y) * (y - to.y)); + } + + public real_t Dot(Vector2 with) + { + return x * with.x + y * with.y; + } + + public Vector2 Floor() + { + return new Vector2(Mathf.Floor(x), Mathf.Floor(y)); + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; + } + + public real_t Length() + { + return Mathf.Sqrt(x * x + y * y); + } + + public real_t LengthSquared() + { + return x * x + y * y; + } + + public Vector2 LinearInterpolate(Vector2 b, real_t t) + { + var res = this; + + res.x += t * (b.x - x); + res.y += t * (b.y - y); + + return res; + } + + public Vector2 MoveToward(Vector2 to, real_t delta) + { + var v = this; + var vd = to - v; + var len = vd.Length(); + return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + } + + public Vector2 Normalized() + { + var v = this; + v.Normalize(); + return v; + } + + public Vector2 PosMod(real_t mod) + { + Vector2 v; + v.x = Mathf.PosMod(x, mod); + v.y = Mathf.PosMod(y, mod); + return v; + } + + public Vector2 PosMod(Vector2 modv) + { + Vector2 v; + v.x = Mathf.PosMod(x, modv.x); + v.y = Mathf.PosMod(y, modv.y); + return v; + } + + public Vector2 Project(Vector2 onNormal) + { + return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); + } + + public Vector2 Reflect(Vector2 n) + { + return 2.0f * n * Dot(n) - this; + } + + public Vector2 Rotated(real_t phi) + { + real_t rads = Angle() + phi; + return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length(); + } + + public Vector2 Round() + { + return new Vector2(Mathf.Round(x), Mathf.Round(y)); + } + + [Obsolete("Set is deprecated. Use the Vector2(" + nameof(real_t) + ", " + nameof(real_t) + ") constructor instead.", error: true)] + public void Set(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + [Obsolete("Set is deprecated. Use the Vector2(" + nameof(Vector2) + ") constructor instead.", error: true)] + public void Set(Vector2 v) + { + x = v.x; + y = v.y; + } + + public Vector2 Sign() + { + Vector2 v; + v.x = Mathf.Sign(x); + v.y = Mathf.Sign(y); + return v; + } + + public Vector2 Slerp(Vector2 b, real_t t) + { + real_t theta = AngleTo(b); + return Rotated(theta * t); + } + + public Vector2 Slide(Vector2 n) + { + return this - n * Dot(n); + } + + public Vector2 Snapped(Vector2 by) + { + return new Vector2(Mathf.Stepify(x, by.x), Mathf.Stepify(y, by.y)); + } + + public Vector2 Tangent() + { + return new Vector2(y, -x); + } + + // Constants + private static readonly Vector2 _zero = new Vector2(0, 0); + private static readonly Vector2 _one = new Vector2(1, 1); + private static readonly Vector2 _negOne = new Vector2(-1, -1); + private static readonly Vector2 _inf = new Vector2(Mathf.Inf, Mathf.Inf); + + private static readonly Vector2 _up = new Vector2(0, -1); + private static readonly Vector2 _down = new Vector2(0, 1); + private static readonly Vector2 _right = new Vector2(1, 0); + private static readonly Vector2 _left = new Vector2(-1, 0); + + public static Vector2 Zero { get { return _zero; } } + public static Vector2 NegOne { get { return _negOne; } } + public static Vector2 One { get { return _one; } } + public static Vector2 Inf { get { return _inf; } } + + public static Vector2 Up { get { return _up; } } + public static Vector2 Down { get { return _down; } } + public static Vector2 Right { get { return _right; } } + public static Vector2 Left { get { return _left; } } + + // Constructors + public Vector2(real_t x, real_t y) + { + this.x = x; + this.y = y; + } + public Vector2(Vector2 v) + { + x = v.x; + y = v.y; + } + + public static Vector2 operator +(Vector2 left, Vector2 right) + { + left.x += right.x; + left.y += right.y; + return left; + } + + public static Vector2 operator -(Vector2 left, Vector2 right) + { + left.x -= right.x; + left.y -= right.y; + return left; + } + + public static Vector2 operator -(Vector2 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + return vec; + } + + public static Vector2 operator *(Vector2 vec, real_t scale) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2 operator *(real_t scale, Vector2 vec) + { + vec.x *= scale; + vec.y *= scale; + return vec; + } + + public static Vector2 operator *(Vector2 left, Vector2 right) + { + left.x *= right.x; + left.y *= right.y; + return left; + } + + public static Vector2 operator /(Vector2 vec, real_t scale) + { + vec.x /= scale; + vec.y /= scale; + return vec; + } + + public static Vector2 operator /(Vector2 left, Vector2 right) + { + left.x /= right.x; + left.y /= right.y; + return left; + } + + public static Vector2 operator %(Vector2 vec, real_t divisor) + { + vec.x %= divisor; + vec.y %= divisor; + return vec; + } + + public static Vector2 operator %(Vector2 vec, Vector2 divisorv) + { + vec.x %= divisorv.x; + vec.y %= divisorv.y; + return vec; + } + + public static bool operator ==(Vector2 left, Vector2 right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector2 left, Vector2 right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector2 left, Vector2 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >(Vector2 left, Vector2 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + return left.y > right.y; + } + + return left.x > right.x; + } + + public static bool operator <=(Vector2 left, Vector2 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + return left.y <= right.y; + } + + return left.x <= right.x; + } + + public static bool operator >=(Vector2 left, Vector2 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + return left.y >= right.y; + } + + return left.x >= right.x; + } + + public override bool Equals(object obj) + { + if (obj is Vector2) + { + return Equals((Vector2)obj); + } + + return false; + } + + public bool Equals(Vector2 other) + { + return x == other.x && y == other.y; + } + + public bool IsEqualApprox(Vector2 other) + { + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1})", new object[] + { + x.ToString(), + y.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1})", new object[] + { + x.ToString(format), + y.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs new file mode 100644 index 0000000000..fded34002d --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -0,0 +1,553 @@ +// file: core/math/vector3.h +// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 +// file: core/math/vector3.cpp +// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451 +// file: core/variant_call.cpp +// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685 +using System; +using System.Runtime.InteropServices; +#if REAL_T_IS_DOUBLE +using real_t = System.Double; +#else +using real_t = System.Single; +#endif + +namespace Godot +{ + /// <summary> + /// 3-element structure that can be used to represent positions in 3D space or any other pair of numeric values. + /// </summary> + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct Vector3 : IEquatable<Vector3> + { + public enum Axis + { + X = 0, + Y, + Z + } + + public real_t x; + public real_t y; + public real_t z; + + public real_t this[int index] + { + get + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + throw new IndexOutOfRangeException(); + } + } + set + { + switch (index) + { + case 0: + x = value; + return; + case 1: + y = value; + return; + case 2: + z = value; + return; + default: + throw new IndexOutOfRangeException(); + } + } + } + + internal void Normalize() + { + real_t lengthsq = LengthSquared(); + + if (lengthsq == 0) + { + x = y = z = 0f; + } + else + { + real_t length = Mathf.Sqrt(lengthsq); + x /= length; + y /= length; + z /= length; + } + } + + public Vector3 Abs() + { + return new Vector3(Mathf.Abs(x), Mathf.Abs(y), Mathf.Abs(z)); + } + + public real_t AngleTo(Vector3 to) + { + return Mathf.Atan2(Cross(to).Length(), Dot(to)); + } + + public Vector3 Bounce(Vector3 n) + { + return -Reflect(n); + } + + public Vector3 Ceil() + { + return new Vector3(Mathf.Ceil(x), Mathf.Ceil(y), Mathf.Ceil(z)); + } + + public Vector3 Cross(Vector3 b) + { + return new Vector3 + ( + y * b.z - z * b.y, + z * b.x - x * b.z, + x * b.y - y * b.x + ); + } + + public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t) + { + var p0 = preA; + var p1 = this; + var p2 = b; + var p3 = postB; + + real_t t2 = t * t; + real_t t3 = t2 * t; + + return 0.5f * ( + p1 * 2.0f + (-p0 + p2) * t + + (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 + + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3 + ); + } + + public Vector3 DirectionTo(Vector3 b) + { + return new Vector3(b.x - x, b.y - y, b.z - z).Normalized(); + } + + public real_t DistanceSquaredTo(Vector3 b) + { + return (b - this).LengthSquared(); + } + + public real_t DistanceTo(Vector3 b) + { + return (b - this).Length(); + } + + public real_t Dot(Vector3 b) + { + return x * b.x + y * b.y + z * b.z; + } + + public Vector3 Floor() + { + return new Vector3(Mathf.Floor(x), Mathf.Floor(y), Mathf.Floor(z)); + } + + public Vector3 Inverse() + { + return new Vector3(1.0f / x, 1.0f / y, 1.0f / z); + } + + public bool IsNormalized() + { + return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon; + } + + public real_t Length() + { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return Mathf.Sqrt(x2 + y2 + z2); + } + + public real_t LengthSquared() + { + real_t x2 = x * x; + real_t y2 = y * y; + real_t z2 = z * z; + + return x2 + y2 + z2; + } + + public Vector3 LinearInterpolate(Vector3 b, real_t t) + { + return new Vector3 + ( + x + t * (b.x - x), + y + t * (b.y - y), + z + t * (b.z - z) + ); + } + + public Vector3 MoveToward(Vector3 to, real_t delta) + { + var v = this; + var vd = to - v; + var len = vd.Length(); + return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta; + } + + public Axis MaxAxis() + { + return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X); + } + + public Axis MinAxis() + { + return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z); + } + + public Vector3 Normalized() + { + var v = this; + v.Normalize(); + return v; + } + + public Basis Outer(Vector3 b) + { + return new Basis( + x * b.x, x * b.y, x * b.z, + y * b.x, y * b.y, y * b.z, + z * b.x, z * b.y, z * b.z + ); + } + + public Vector3 PosMod(real_t mod) + { + Vector3 v; + v.x = Mathf.PosMod(x, mod); + v.y = Mathf.PosMod(y, mod); + v.z = Mathf.PosMod(z, mod); + return v; + } + + public Vector3 PosMod(Vector3 modv) + { + Vector3 v; + v.x = Mathf.PosMod(x, modv.x); + v.y = Mathf.PosMod(y, modv.y); + v.z = Mathf.PosMod(z, modv.z); + return v; + } + + public Vector3 Project(Vector3 onNormal) + { + return onNormal * (Dot(onNormal) / onNormal.LengthSquared()); + } + + public Vector3 Reflect(Vector3 n) + { +#if DEBUG + if (!n.IsNormalized()) + throw new ArgumentException("Argument is not normalized", nameof(n)); +#endif + return 2.0f * n * Dot(n) - this; + } + + public Vector3 Round() + { + return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z)); + } + + public Vector3 Rotated(Vector3 axis, real_t phi) + { + return new Basis(axis, phi).Xform(this); + } + + [Obsolete("Set is deprecated. Use the Vector3(" + nameof(real_t) + ", " + nameof(real_t) + ", " + nameof(real_t) + ") constructor instead.", error: true)] + public void Set(real_t x, real_t y, real_t z) + { + this.x = x; + this.y = y; + this.z = z; + } + [Obsolete("Set is deprecated. Use the Vector3(" + nameof(Vector3) + ") constructor instead.", error: true)] + public void Set(Vector3 v) + { + x = v.x; + y = v.y; + z = v.z; + } + + public Vector3 Sign() + { + Vector3 v; + v.x = Mathf.Sign(x); + v.y = Mathf.Sign(y); + v.z = Mathf.Sign(z); + return v; + } + + public Vector3 Slerp(Vector3 b, real_t t) + { +#if DEBUG + if (!IsNormalized()) + throw new InvalidOperationException("Vector3 is not normalized"); +#endif + real_t theta = AngleTo(b); + return Rotated(Cross(b), theta * t); + } + + public Vector3 Slide(Vector3 n) + { + return this - n * Dot(n); + } + + public Vector3 Snapped(Vector3 by) + { + return new Vector3 + ( + Mathf.Stepify(x, by.x), + Mathf.Stepify(y, by.y), + Mathf.Stepify(z, by.z) + ); + } + + public Basis ToDiagonalMatrix() + { + return new Basis( + x, 0f, 0f, + 0f, y, 0f, + 0f, 0f, z + ); + } + + // Constants + private static readonly Vector3 _zero = new Vector3(0, 0, 0); + private static readonly Vector3 _one = new Vector3(1, 1, 1); + private static readonly Vector3 _negOne = new Vector3(-1, -1, -1); + private static readonly Vector3 _inf = new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf); + + private static readonly Vector3 _up = new Vector3(0, 1, 0); + private static readonly Vector3 _down = new Vector3(0, -1, 0); + private static readonly Vector3 _right = new Vector3(1, 0, 0); + private static readonly Vector3 _left = new Vector3(-1, 0, 0); + private static readonly Vector3 _forward = new Vector3(0, 0, -1); + private static readonly Vector3 _back = new Vector3(0, 0, 1); + + public static Vector3 Zero { get { return _zero; } } + public static Vector3 One { get { return _one; } } + public static Vector3 NegOne { get { return _negOne; } } + public static Vector3 Inf { get { return _inf; } } + + public static Vector3 Up { get { return _up; } } + public static Vector3 Down { get { return _down; } } + public static Vector3 Right { get { return _right; } } + public static Vector3 Left { get { return _left; } } + public static Vector3 Forward { get { return _forward; } } + public static Vector3 Back { get { return _back; } } + + // Constructors + public Vector3(real_t x, real_t y, real_t z) + { + this.x = x; + this.y = y; + this.z = z; + } + public Vector3(Vector3 v) + { + x = v.x; + y = v.y; + z = v.z; + } + + public static Vector3 operator +(Vector3 left, Vector3 right) + { + left.x += right.x; + left.y += right.y; + left.z += right.z; + return left; + } + + public static Vector3 operator -(Vector3 left, Vector3 right) + { + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + return left; + } + + public static Vector3 operator -(Vector3 vec) + { + vec.x = -vec.x; + vec.y = -vec.y; + vec.z = -vec.z; + return vec; + } + + public static Vector3 operator *(Vector3 vec, real_t scale) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3 operator *(real_t scale, Vector3 vec) + { + vec.x *= scale; + vec.y *= scale; + vec.z *= scale; + return vec; + } + + public static Vector3 operator *(Vector3 left, Vector3 right) + { + left.x *= right.x; + left.y *= right.y; + left.z *= right.z; + return left; + } + + public static Vector3 operator /(Vector3 vec, real_t scale) + { + vec.x /= scale; + vec.y /= scale; + vec.z /= scale; + return vec; + } + + public static Vector3 operator /(Vector3 left, Vector3 right) + { + left.x /= right.x; + left.y /= right.y; + left.z /= right.z; + return left; + } + + public static Vector3 operator %(Vector3 vec, real_t divisor) + { + vec.x %= divisor; + vec.y %= divisor; + vec.z %= divisor; + return vec; + } + + public static Vector3 operator %(Vector3 vec, Vector3 divisorv) + { + vec.x %= divisorv.x; + vec.y %= divisorv.y; + vec.z %= divisorv.z; + return vec; + } + + public static bool operator ==(Vector3 left, Vector3 right) + { + return left.Equals(right); + } + + public static bool operator !=(Vector3 left, Vector3 right) + { + return !left.Equals(right); + } + + public static bool operator <(Vector3 left, Vector3 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + if (Mathf.IsEqualApprox(left.y, right.y)) + return left.z < right.z; + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >(Vector3 left, Vector3 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + if (Mathf.IsEqualApprox(left.y, right.y)) + return left.z > right.z; + return left.y > right.y; + } + + return left.x > right.x; + } + + public static bool operator <=(Vector3 left, Vector3 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + if (Mathf.IsEqualApprox(left.y, right.y)) + return left.z <= right.z; + return left.y < right.y; + } + + return left.x < right.x; + } + + public static bool operator >=(Vector3 left, Vector3 right) + { + if (Mathf.IsEqualApprox(left.x, right.x)) + { + if (Mathf.IsEqualApprox(left.y, right.y)) + return left.z >= right.z; + return left.y > right.y; + } + + return left.x > right.x; + } + + public override bool Equals(object obj) + { + if (obj is Vector3) + { + return Equals((Vector3)obj); + } + + return false; + } + + public bool Equals(Vector3 other) + { + return x == other.x && y == other.y && z == other.z; + } + + public bool IsEqualApprox(Vector3 other) + { + return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z); + } + + public override int GetHashCode() + { + return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode(); + } + + public override string ToString() + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(), + y.ToString(), + z.ToString() + }); + } + + public string ToString(string format) + { + return String.Format("({0}, {1}, {2})", new object[] + { + x.ToString(format), + y.ToString(format), + z.ToString(format) + }); + } + } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj new file mode 100644 index 0000000000..5419cd06e6 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{AEBF0036-DA76-4341-B651-A3F2856AB2FA}</ProjectGuid> + <OutputType>Library</OutputType> + <OutputPath>bin/$(Configuration)</OutputPath> + <RootNamespace>Godot</RootNamespace> + <AssemblyName>GodotSharp</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile> + <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>portable</DebugType> + <Optimize>false</Optimize> + <DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>portable</DebugType> + <Optimize>true</Optimize> + <DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Core\AABB.cs" /> + <Compile Include="Core\Array.cs" /> + <Compile Include="Core\Attributes\ExportAttribute.cs" /> + <Compile Include="Core\Attributes\GodotMethodAttribute.cs" /> + <Compile Include="Core\Attributes\RPCAttributes.cs" /> + <Compile Include="Core\Attributes\SignalAttribute.cs" /> + <Compile Include="Core\Attributes\ToolAttribute.cs" /> + <Compile Include="Core\Basis.cs" /> + <Compile Include="Core\Color.cs" /> + <Compile Include="Core\Colors.cs" /> + <Compile Include="Core\DebuggingUtils.cs" /> + <Compile Include="Core\Dictionary.cs" /> + <Compile Include="Core\Dispatcher.cs" /> + <Compile Include="Core\DynamicObject.cs" /> + <Compile Include="Core\Extensions\NodeExtensions.cs" /> + <Compile Include="Core\Extensions\ObjectExtensions.cs" /> + <Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" /> + <Compile Include="Core\GD.cs" /> + <Compile Include="Core\GodotSynchronizationContext.cs" /> + <Compile Include="Core\GodotTaskScheduler.cs" /> + <Compile Include="Core\GodotTraceListener.cs" /> + <Compile Include="Core\Interfaces\IAwaitable.cs" /> + <Compile Include="Core\Interfaces\IAwaiter.cs" /> + <Compile Include="Core\Interfaces\ISerializationListener.cs" /> + <Compile Include="Core\MarshalUtils.cs" /> + <Compile Include="Core\Mathf.cs" /> + <Compile Include="Core\MathfEx.cs" /> + <Compile Include="Core\NodePath.cs" /> + <Compile Include="Core\Object.base.cs" /> + <Compile Include="Core\Plane.cs" /> + <Compile Include="Core\Quat.cs" /> + <Compile Include="Core\Rect2.cs" /> + <Compile Include="Core\RID.cs" /> + <Compile Include="Core\SignalAwaiter.cs" /> + <Compile Include="Core\StringExtensions.cs" /> + <Compile Include="Core\Transform.cs" /> + <Compile Include="Core\Transform2D.cs" /> + <Compile Include="Core\Vector2.cs" /> + <Compile Include="Core\Vector3.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <!-- + We import a props file with auto-generated includes. This works well with Rider. + However, Visual Studio and MonoDevelop won't list them in the solution explorer. + We can't use wildcards as there may be undesired old files still hanging around. + Fortunately code completion, go to definition and such still work. + --> + <Import Project="Generated\GeneratedIncludes.props" /> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..f84e0183f6 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("GodotSharp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] +[assembly: InternalsVisibleTo("GodotSharpEditor")] diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj new file mode 100644 index 0000000000..22853797c1 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{8FBEC238-D944-4074-8548-B3B524305905}</ProjectGuid> + <OutputType>Library</OutputType> + <OutputPath>bin/$(Configuration)</OutputPath> + <RootNamespace>Godot</RootNamespace> + <AssemblyName>GodotSharpEditor</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <DocumentationFile>$(OutputPath)/$(AssemblyName).xml</DocumentationFile> + <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>portable</DebugType> + <Optimize>false</Optimize> + <DefineConstants>$(GodotDefineConstants);GODOT;DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>portable</DebugType> + <Optimize>true</Optimize> + <DefineConstants>$(GodotDefineConstants);GODOT;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <Import Project="Generated\GeneratedIncludes.props" /> + <ItemGroup> + <ProjectReference Include="..\GodotSharp\GodotSharp.csproj"> + <Private>False</Private> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project> diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs b/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3684b7a3cb --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/Properties/AssemblyInfo.cs @@ -0,0 +1,25 @@ +using System.Reflection; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("GodotSharpEditor")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] |