using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using UnityEngine; namespace XericLibrary.Runtime.MacroLibrary { public static class Vector3Extend { #region 四则运算 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Add(this Vector3 a, Vector3 b) => new Vector3( a.x + b.x, a.y + b.y, a.z + b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Add(this Vector3 a, Vector3Int b) => new Vector3( a.x + b.x, a.y + b.y, a.z + b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Add(this Vector3 a, float b) => new Vector3( a.x + b, a.y + b, a.z + b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Sub(this Vector3 a, Vector3 b) => new Vector3( a.x - b.x, a.y - b.y, a.z - b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Sub(this Vector3 a, Vector3Int b) => new Vector3( a.x - b.x, a.y - b.y, a.z - b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Sub(this Vector3 a, float b) => new Vector3( a.x - b, a.y - b, a.z - b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Mul(this Vector3 a, Vector3 b) => new Vector3( a.x * b.x, a.y * b.y, a.z * b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Mul(this Vector3 a, Vector3Int b) => new Vector3( a.x * b.x, a.y * b.y, a.z * b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Mul(this Vector3 a, float b) => new Vector3( a.x * b, a.y * b, a.z * b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Div(this Vector3 a, Vector3 b) => new Vector3( a.x / b.x, a.y / b.y, a.z / b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Div(this Vector3 a, Vector3Int b) => new Vector3( a.x / b.x, a.y / b.y, a.z / b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Div(this Vector3 a, float b) => new Vector3( a.x / b, a.y / b, a.z / b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 OneMinus(this Vector3 a) => new Vector3(1 - a.x, 1 - a.y, 1 - a.z); #endregion #region 破坏运算 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Min(this Vector3 a, Vector3 b) { return new Vector3(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Max(this Vector3 a, Vector3 b) { return new Vector3(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float MinElement(this Vector3 a) { return Math.Min(Math.Min(a.x, a.y), a.z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float MaxElement(this Vector3 a) { return Math.Max(Math.Max(a.x, a.y), a.z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Abs(this Vector3 a) { return new Vector3(Mathf.Abs(a.x), Mathf.Abs(a.y), Mathf.Abs(a.z)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Negative(this Vector3 a) => new Vector3(-a.x, -a.y, -a.z); [MethodImpl(MethodImplOptions.NoInlining)] public static Vector2 ToVector2(this Vector3 vec, string format) { Vector2 result = new Vector2(); for(int i = 0; i < format.Length && i < 2; i++) { int index = MacroMath.MinPositive(format[i] - 'X', format[i] - 'x'); result[index] = format[i] switch { 'x' => vec.x, 'X' => vec.x, 'y' => vec.y, 'Y' => vec.y, 'z' => vec.z, 'Z' => vec.z, _ => 0 }; } return result; } [MethodImpl(MethodImplOptions.NoInlining)] public static Vector3 AxisZero(this Vector3 vec, string format) { Vector3 result = vec; for(int i = 0; i < format.Length && i < 2; i++) { int index = MacroMath.MinPositive(format[i] - 'X', format[i] - 'x'); result[index] = 0; } return result; } #endregion #region 扩展运算 [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Xonly(this Vector3 a) => new Vector3( a.x, 0f, 0f ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Yonly(this Vector3 a) => new Vector3( 0f, a.y, 0f ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Zonly(this Vector3 a) => new Vector3( 0f, 0f, a.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Mode(this Vector3 a, Vector3 b) { return new Vector3(a.x % b.x, a.y % b.y, a.z % b.z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Mode(this Vector3 a, float b) { return new Vector3(a.x % b, a.y % b, a.z % b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Sign(this Vector3 vec) => new Vector3( Math.Sign(vec.x), Math.Sign(vec.y), Math.Sign(vec.z) ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int CeilToInt(this Vector3 vec) => new Vector3Int((int)Mathf.Ceil(vec.x), (int)Mathf.Ceil(vec.y), (int)Mathf.Ceil(vec.z)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Ceil(this Vector3 vec) => new Vector3(Mathf.Ceil(vec.x), Mathf.Ceil(vec.y), Mathf.Ceil(vec.z)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int RoundToInt(this Vector3 vec) => new Vector3Int((int)Mathf.Round(vec.x), (int)Mathf.Round(vec.y), (int)Mathf.Round(vec.z)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Round(this Vector3 vec) => new Vector3(Mathf.Round(vec.x), Mathf.Round(vec.y), Mathf.Round(vec.z)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int FloorToInt(this Vector3 vec) => new Vector3Int((int)vec.x, (int)vec.y, (int)vec.z); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Floor(this Vector3 vec) => new Vector3((int)vec.x, (int)vec.y, (int)vec.z); /// /// Pows each element of the vector. /// /// /// /// public static Vector3 Pow(this Vector3 v, float p) { v.x = Mathf.Pow(v.x, p); v.y = Mathf.Pow(v.y, p); v.z = Mathf.Pow(v.z, p); return v; } public static float Distance(this Vector3 a, Vector3 b) => Vector3.Distance(a, b); #endregion #region 特殊运算 /// /// 将这个向量投影到一个给定坐标的向上的平面上,同时变换到平面坐标 /// /// 矢量或坐标 /// 平面原点 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 ProjectToUpPlane(this Vector3 vec, Vector3 planeOrigin) => vec - new Vector3(0, (vec - planeOrigin).y, 0); /// /// 向量吸附到网格上,在比例中央不进行吸附 /// /// 矢量或坐标 /// 偏移量 /// 网格单位 /// 阈值比例[0-1] /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 CoordinateGridAdsorb(this Vector3 vec, Vector3 offset, float unit, float thresholdPorp) { Vector3 vecInUnit = (vec + offset).Mode(unit); Vector3 sig = vecInUnit.Sign() * unit / 2; Vector3 dis = sig - vecInUnit; Vector3 drive = dis.Abs().Sub(unit * thresholdPorp).Sign(); Vector3 ofs = (dis - sig).Mul(drive * 2) + vecInUnit; return ofs; } /// /// 向量吸附到网格上 /// /// 矢量或坐标 /// 偏移量 /// 网格单位 /// 阈值比例[0-1] /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 CoordinateGridAdsorbRound(this Vector3 vec, Vector3 offset, float unit) { return ((vec + offset) / unit).Round() * unit; } /// /// 矢量正交化 /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Orthogonalization(this Vector3 vec) { Vector3[] axes = { Vector3.right, Vector3.up, Vector3.forward }; float minAngle = float.MaxValue; Vector3 minAngleAxis = Vector3.zero; foreach(Vector3 axis in axes) { float dot = Vector3.Dot(vec, axis); float angle = Mathf.Abs(dot); if(angle < minAngle) { minAngle = angle; minAngleAxis = axis * Mathf.Sign(dot); } } return minAngleAxis; } /// /// 获取与世界正交轴最近的一条轴线 /// /// /// [Obsolete("可以使用Orthogonalization代替")] public static Vector3 GetWorldNormalAxis(this Vector3 vec) { return vec.Orthogonalization(); } #endregion #region 未整理 private const float ZERO_TOLERANCE = 1E-06f; // // 摘要: // Distance from a point to a line. public static float PointDistanceToLine(Vector3 point, Vector3 a, Vector3 b) { return Mathf.Abs((b.x - a.x) * (a.y - point.y) - (a.x - point.x) * (b.y - a.y)) / Mathf.Sqrt(Mathf.Pow(b.x - a.x, 2f) + Mathf.Pow(b.y - a.y, 2f)); } // // 摘要: // Returns a smooth value between start and end based on t. // // 参数: // start: // First point. // // end: // Second point. // // t: // Position between 0 and 1. public static float Hermite(float start, float end, float t) { return Mathf.Lerp(start, end, t * t * (3f - 2f * t)); } // // 摘要: // Returns a smooth value between start and end based on t. // // 参数: // start: // First point. // // end: // Second point. // // t: // Position between 0 and 1. // // count: // Number of interpolations to make. public static float StackHermite(float start, float end, float t, int count) { for(int i = 0; i < count; i++) { t = Hermite(start, end, t); } return t; } // // 摘要: // Returns the fractional of the value. // // 参数: // value: // The value to get the fractional of. public static float Fract(float value) { return value - (float)Math.Truncate(value); } // // 摘要: // Returns the fractional of the value. // // 参数: // value: // The value to get the fractional of. public static Vector2 Fract(Vector2 value) { return new Vector3(Fract(value.x), Fract(value.y)); } // // 摘要: // Returns the fractional of the value. // // 参数: // value: // The value to get the fractional of. public static Vector3 Fract(Vector3 value) { return new Vector3(Fract(value.x), Fract(value.y), Fract(value.z)); } // // 摘要: // Returns a value based on t, that bounces faster and faster. // // 参数: // t: // The value to bounce. public static float BounceEaseInFastOut(float t) { return Mathf.Cos(t * t * (float)Math.PI * 2f) * -0.5f + 0.5f; } // // 摘要: // Returns a smooth value between 0 and 1 based on t. // // 参数: // t: // Position between 0 and 1. public static float Hermite01(float t) { return Mathf.Lerp(0f, 1f, t * t * (3f - 2f * t)); } // // 摘要: // Returns a smooth value between 0 and 1 based on t. // // 参数: // t: // Position between 0 and 1. // // count: // Number of interpolations to make. public static float StackHermite01(float t, int count) { for(int i = 0; i < count; i++) { t = Hermite01(t); } return t; } // // 摘要: // Returns an unclamped linear interpolation of two vectors. // // 参数: // from: // The first vector. // // to: // The second vector. // // amount: // The interpolation factor. public static Vector3 LerpUnclamped(Vector3 from, Vector3 to, float amount) { return from + (to - from) * amount; } // // 摘要: // Returns an unclamped linear interpolation of two vectors. // // 参数: // from: // The first vector. // // to: // The second vector. // // amount: // The interpolation factor. public static Vector2 LerpUnclamped(Vector2 from, Vector2 to, float amount) { return from + (to - from) * amount; } // // 摘要: // Returns a value that bounces between 0 and 1 based on value. // // 参数: // value: // The value to bounce. public static float Bounce(float value) { return Mathf.Abs(Mathf.Sin(value % 1f * (float)Math.PI)); } // // 摘要: // Returns a value that eases in elasticly. // // 参数: // value: // The value to ease in elasticly. // // amplitude: // The amplitude. // // length: // The length. public static float EaseInElastic(float value, float amplitude = 0.25f, float length = 0.6f) { value = Mathf.Clamp01(value); float num = Mathf.Clamp01(value * 7.5f); float num2 = 1f - num * num * (3f - 2f * num); float num3 = Mathf.Pow(1f - Mathf.Sin(Mathf.Min(value * (1f - length), 0.5f) * (float)Math.PI), 2f); float num4 = Mathf.Cos((float)Math.PI + value * 23f) * amplitude + num2 * (0f - (1f - amplitude)); return 1f + num4 * num3; } // // 摘要: // Returns a value that eases out elasticly. // // 参数: // value: // The value to ease out elasticly. // // amplitude: // The amplitude. // // length: // The length. public static float EaseOutElastic(float value, float amplitude = 0.25f, float length = 0.6f) { return 1f - EaseInElastic(1f - value, amplitude, length); } // // 摘要: // Returns a smooth value betweeen that peaks at t=0.5 and then comes back down // again. // // 参数: // t: // A value between 0 and 1. public static float EaseInOut(float t) { t = 1f - Mathf.Abs(Mathf.Clamp01(t) * 2f - 1f); t = t * t * (3f - 2f * t); return t; } // // 摘要: // Clamps the value of a Vector3. // // 参数: // value: // The vector to clamp. // // min: // The min value. // // max: // The max value. public static Vector3 Clamp(this Vector3 value, Vector3 min, Vector3 max) { return new Vector3(Mathf.Clamp(value.x, min.x, max.x), Mathf.Clamp(value.y, min.y, max.y), Mathf.Clamp(value.z, min.z, max.z)); } // // 摘要: // Clamps the value of a Vector2. // // 参数: // value: // The vector to clamp. // // min: // The min value. // // max: // The max value. public static Vector2 Clamp(this Vector2 value, Vector2 min, Vector2 max) { return new Vector2(Mathf.Clamp(value.x, min.x, max.x), Mathf.Clamp(value.y, min.y, max.y)); } // // 摘要: // Computes a hash for a byte array. // // 参数: // data: // The byte array. public static int ComputeByteArrayHash(byte[] data) { int num = -2128831035; for(int i = 0; i < data.Length; i++) { num = (num ^ data[i]) * 16777619; } num += num << 13; num ^= num >> 7; num += num << 3; num ^= num >> 17; return num + (num << 5); } // // 摘要: // Gives a smooth path between a collection of points. // // 参数: // path: // The collection of point. // // t: // The current position in the path. 0 is at the start of the path, 1 is at the // end of the path. public static Vector3 InterpolatePoints(Vector3[] path, float t) { t = Mathf.Clamp01(t * (1f - 1f / (float)path.Length)); int b = path.Length - 1; int num = Mathf.FloorToInt(t * (float)path.Length); float num2 = t * (float)path.Length - (float)num; Vector3 vector = path[Mathf.Max(0, --num)]; Vector3 vector2 = path[Mathf.Min(num + 1, b)]; Vector3 vector3 = path[Mathf.Min(num + 2, b)]; Vector3 vector4 = path[Mathf.Min(num + 3, b)]; return 0.5f * ((-vector + 3f * vector2 - 3f * vector3 + vector4) * (num2 * num2 * num2) + (2f * vector - 5f * vector2 + 4f * vector3 - vector4) * (num2 * num2) + (-vector + vector3) * num2 + 2f * vector2); } // // 摘要: // Checks if two given lines intersect with one another and returns the intersection // point (if any) in an out parameter. Source: http://stackoverflow.com/questions/3746274/line-intersection-with-aabb-rectangle. // Edited to implement Cohen-Sutherland type pruning for efficiency. // // 参数: // a1: // Starting point of line a. // // a2: // Ending point of line a. // // b1: // Starting point of line b. // // b2: // Ending point of line b. // // intersection: // The out parameter which contains the intersection point if there was any. // // 返回结果: // True if the two lines intersect, otherwise false. public static bool LineIntersectsLine(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 intersection) { intersection = Vector2.zero; Vector2 vector = new Vector2((b1.x < b2.x) ? b1.x : b2.x, (b1.y > b2.y) ? b1.y : b2.y); Vector2 vector2 = new Vector2((b1.x > b2.x) ? b1.x : b2.x, (b1.y < b2.y) ? b1.y : b2.y); if((a1.x < vector.x && a2.x < vector.x) || (a1.y > vector.y && a2.y > vector.y) || (a1.x > vector2.x && a2.x > vector2.x) || (a1.y < vector2.y && a2.y < vector2.y)) { return false; } Vector2 vector3 = a2 - a1; Vector2 vector4 = b2 - b1; float num = vector3.x * vector4.y - vector3.y * vector4.x; if(num == 0f) { return false; } Vector2 vector5 = b1 - a1; float num2 = (vector5.x * vector4.y - vector5.y * vector4.x) / num; if(num2 < 0f || num2 > 1f) { return false; } float num3 = (vector5.x * vector3.y - vector5.y * vector3.x) / num; if(num3 < 0f || num3 > 1f) { return false; } intersection = a1 + num2 * vector3; return true; } // // 摘要: // Returns the collision point between two infinite lines. public static Vector2 InfiniteLineIntersect(Vector2 ps1, Vector2 pe1, Vector2 ps2, Vector2 pe2) { float num = pe1.y - ps1.y; float num2 = ps1.x - pe1.x; float num3 = num * ps1.x + num2 * ps1.y; float num4 = pe2.y - ps2.y; float num5 = ps2.x - pe2.x; float num6 = num4 * ps2.x + num5 * ps2.y; float num7 = num * num5 - num4 * num2; if(num7 == 0f) { throw new Exception("Lines are parallel"); } return new Vector2((num5 * num3 - num2 * num6) / num7, (num * num6 - num4 * num3) / num7); } // // 摘要: // Distance from line to plane. // // 参数: // planeOrigin: // Position of the plane. // // planeNormal: // Surface normal of the plane. // // lineOrigin: // Origin of the line. // // lineDirectionNormalized: // Line direction normal. public static float LineDistToPlane(Vector3 planeOrigin, Vector3 planeNormal, Vector3 lineOrigin, Vector3 lineDirectionNormalized) { return Vector3.Dot(lineDirectionNormalized, planeNormal) * Vector3.Distance(planeOrigin, lineOrigin); } // // 摘要: // Distance from ray to plane. // // 参数: // ray: // The ray. // // plane: // The plane. public static float RayDistToPlane(Ray ray, Plane plane) { float num = Vector3.Dot(plane.normal, ray.direction); if(Mathf.Abs(num) < 1E-06f) { return 0f; } float num2 = Vector3.Dot(plane.normal, ray.origin); return (0f - plane.distance - num2) / num; } // // 摘要: // Rotates a Vector2 by an angle. // // 参数: // point: // The point to rotate. // // degrees: // The angle to rotate. public static Vector2 RotatePoint(Vector2 point, float degrees) { float f = degrees * ((float)Math.PI / 180f); float num = Mathf.Cos(f); float num2 = Mathf.Sin(f); return new Vector2(num * point.x - num2 * point.y, num2 * point.x + num * point.y); } // // 摘要: // Rotates a Vector2 around a point by an angle.. // // 参数: // point: // The point to rotate. // // around: // The point to rotate around. // // degrees: // The angle to rotate. public static Vector2 RotatePoint(Vector2 point, Vector2 around, float degrees) { float f = degrees * ((float)Math.PI / 180f); float num = Mathf.Cos(f); float num2 = Mathf.Sin(f); return new Vector2(num * (point.x - around.x) - num2 * (point.y - around.y) + around.x, num2 * (point.x - around.x) + num * (point.y - around.y) + around.y); } #endregion } public static class Vector3IntExtend { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Add(this Vector3Int a, Vector3Int b) => new Vector3Int( a.x + b.x, a.y + b.y, a.z + b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Add(this Vector3Int a, Vector3 b) => new Vector3Int( (int)(a.x + b.x), (int)(a.y + b.y), (int)(a.z + b.z) ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Add(this Vector3Int a, int b) => new Vector3Int( a.x + b, a.y + b, a.z + b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Sub(this Vector3Int a, Vector3Int b) => new Vector3Int( a.x - b.x, a.y - b.y, a.z - b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Sub(this Vector3Int a, Vector3 b) => new Vector3Int( (int)(a.x - b.x), (int)(a.y - b.y), (int)(a.z - b.z) ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Sub(this Vector3Int a, int b) => new Vector3Int( a.x - b, a.y - b, a.z - b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Mul(this Vector3Int a, Vector3Int b) => new Vector3Int( a.x * b.x, a.y * b.y, a.z * b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Mul(this Vector3Int a, Vector3 b) => new Vector3Int( (int)(a.x * b.x), (int)(a.y * b.y), (int)(a.z * b.z) ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Mul(this Vector3Int a, int b) => new Vector3Int( a.x * b, a.y * b, a.z * b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Div(this Vector3Int a, Vector3Int b) => new Vector3Int( a.x / b.x, a.y / b.y, a.z / b.z ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Div(this Vector3Int a, Vector3 b) => new Vector3Int( (int)(a.x / b.x), (int)(a.y / b.y), (int)(a.z / b.z) ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Div(this Vector3Int a, int b) => new Vector3Int( a.x / b, a.y / b, a.z / b ); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Negative(this Vector3Int a) => new Vector3Int(-a.x, -a.y, -a.z); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Min(this Vector3Int a, Vector3Int b) { return new Vector3Int(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3Int Max(this Vector3Int a, Vector3Int b) { return new Vector3Int(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValid(this Vector3Int lhs) { return lhs.x >= 0 && lhs.y >= 0 && lhs.z >= 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsInIndexRange(this Vector3Int lhs, Vector3Int max) { return lhs.x.IsInIndexRange(0, max.x) && lhs.y.IsInIndexRange(0, max.y) && lhs.z.IsInIndexRange(0, max.z); } } }