From e17ede34ef6c0e1db649574141896262bfe22ee8 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sat, 17 Mar 2018 16:44:41 +0000 Subject: [PATCH] Add Int32Matrix4x4 struct. This allows matrices to be represented as a value type, and additionally allows avoiding array allocations when calculating rotations. --- OpenRA.Game/Graphics/Util.cs | 29 +++++-- OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Game/Primitives/Int32Matrix4x4.cs | 72 +++++++++++++++++ OpenRA.Game/WRot.cs | 77 ++++++++++--------- OpenRA.Game/WVec.cs | 13 ++-- .../Graphics/RangeCircleRenderable.cs | 8 +- 6 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 OpenRA.Game/Primitives/Int32Matrix4x4.cs diff --git a/OpenRA.Game/Graphics/Util.cs b/OpenRA.Game/Graphics/Util.cs index 8abdedbe53..cb99bd0039 100644 --- a/OpenRA.Game/Graphics/Util.cs +++ b/OpenRA.Game/Graphics/Util.cs @@ -323,12 +323,31 @@ namespace OpenRA.Graphics return mtx; } - public static float[] MakeFloatMatrix(int[] imtx) + public static float[] MakeFloatMatrix(Int32Matrix4x4 imtx) { - var fmtx = new float[16]; - for (var i = 0; i < 16; i++) - fmtx[i] = imtx[i] * 1f / imtx[15]; - return fmtx; + var multipler = 1f / imtx.M44; + return new float[] + { + imtx.M11 * multipler, + imtx.M12 * multipler, + imtx.M13 * multipler, + imtx.M14 * multipler, + + imtx.M21 * multipler, + imtx.M22 * multipler, + imtx.M23 * multipler, + imtx.M24 * multipler, + + imtx.M31 * multipler, + imtx.M32 * multipler, + imtx.M33 * multipler, + imtx.M34 * multipler, + + imtx.M41 * multipler, + imtx.M42 * multipler, + imtx.M43 * multipler, + imtx.M44 * multipler, + }; } public static float[] MatrixAABBMultiply(float[] mtx, float[] bounds) diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 94cc61b8e9..42091b6c03 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -106,6 +106,7 @@ + diff --git a/OpenRA.Game/Primitives/Int32Matrix4x4.cs b/OpenRA.Game/Primitives/Int32Matrix4x4.cs new file mode 100644 index 0000000000..6a6068bffd --- /dev/null +++ b/OpenRA.Game/Primitives/Int32Matrix4x4.cs @@ -0,0 +1,72 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; + +namespace OpenRA +{ + public struct Int32Matrix4x4 : IEquatable + { + public readonly int M11, M12, M13, M14, M21, M22, M23, M24, M31, M32, M33, M34, M41, M42, M43, M44; + + public Int32Matrix4x4( + int m11, int m12, int m13, int m14, + int m21, int m22, int m23, int m24, + int m31, int m32, int m33, int m34, + int m41, int m42, int m43, int m44) + { + M11 = m11; + M12 = m12; + M13 = m13; + M14 = m14; + + M21 = m21; + M22 = m22; + M23 = m23; + M24 = m24; + + M31 = m31; + M32 = m32; + M33 = m33; + M34 = m34; + + M41 = m41; + M42 = m42; + M43 = m43; + M44 = m44; + } + + public static bool operator ==(Int32Matrix4x4 me, Int32Matrix4x4 other) + { + return + me.M11 == other.M11 && me.M12 == other.M12 && me.M13 == other.M13 && me.M14 == other.M14 && + me.M21 == other.M21 && me.M22 == other.M22 && me.M23 == other.M23 && me.M24 == other.M24 && + me.M31 == other.M31 && me.M32 == other.M32 && me.M33 == other.M33 && me.M34 == other.M34 && + me.M41 == other.M41 && me.M42 == other.M42 && me.M43 == other.M43 && me.M44 == other.M44; + } + + public static bool operator !=(Int32Matrix4x4 me, Int32Matrix4x4 other) { return !(me == other); } + + public override int GetHashCode() { return M11 ^ M22 ^ M33 ^ M44; } + + public bool Equals(Int32Matrix4x4 other) { return other == this; } + public override bool Equals(object obj) { return obj is Int32Matrix4x4 && Equals((Int32Matrix4x4)obj); } + + public override string ToString() + { + return + "[" + M11 + " " + M12 + " " + M13 + " " + M14 + "],[" + + "[" + M21 + " " + M22 + " " + M23 + " " + M24 + "],[" + + "[" + M31 + " " + M32 + " " + M33 + " " + M34 + "],[" + + "[" + M41 + " " + M42 + " " + M43 + " " + M44 + "]"; + } + } +} diff --git a/OpenRA.Game/WRot.cs b/OpenRA.Game/WRot.cs index c7ff79d867..998aca4212 100644 --- a/OpenRA.Game/WRot.cs +++ b/OpenRA.Game/WRot.cs @@ -41,58 +41,61 @@ namespace OpenRA return new WRot(Roll, Pitch, yaw); } - public int[] AsQuarternion() + void AsQuarternion(out int x, out int y, out int z, out int w) { // Angles increase clockwise - var r = new WAngle(-Roll.Angle / 2); - var p = new WAngle(-Pitch.Angle / 2); - var y = new WAngle(-Yaw.Angle / 2); - var cr = (long)r.Cos(); - var sr = (long)r.Sin(); - var cp = (long)p.Cos(); - var sp = (long)p.Sin(); - var cy = (long)y.Cos(); - var sy = (long)y.Sin(); + var roll = new WAngle(-Roll.Angle / 2); + var pitch = new WAngle(-Pitch.Angle / 2); + var yaw = new WAngle(-Yaw.Angle / 2); + var cr = (long)roll.Cos(); + var sr = (long)roll.Sin(); + var cp = (long)pitch.Cos(); + var sp = (long)pitch.Sin(); + var cy = (long)yaw.Cos(); + var sy = (long)yaw.Sin(); // Normalized to 1024 == 1.0 - return new int[4] - { - (int)((sr * cp * cy - cr * sp * sy) / 1048576), // x - (int)((cr * sp * cy + sr * cp * sy) / 1048576), // y - (int)((cr * cp * sy - sr * sp * cy) / 1048576), // z - (int)((cr * cp * cy + sr * sp * sy) / 1048576) // w - }; + x = (int)((sr * cp * cy - cr * sp * sy) / 1048576); + y = (int)((cr * sp * cy + sr * cp * sy) / 1048576); + z = (int)((cr * cp * sy - sr * sp * cy) / 1048576); + w = (int)((cr * cp * cy + sr * sp * sy) / 1048576); } - public int[] AsMatrix() + public void AsMatrix(out Int32Matrix4x4 mtx) { - var q = AsQuarternion(); + int x, y, z, w; + AsQuarternion(out x, out y, out z, out w); // Theoretically 1024 * * 2, but may differ slightly due to rounding - var lsq = q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]; + var lsq = x * x + y * y + z * z + w * w; // Quaternion components use 10 bits, so there's no risk of overflow - var mtx = new int[16]; - mtx[0] = lsq - 2 * (q[1] * q[1] + q[2] * q[2]); - mtx[1] = 2 * (q[0] * q[1] + q[2] * q[3]); - mtx[2] = 2 * (q[0] * q[2] - q[1] * q[3]); - mtx[3] = 0; + mtx = new Int32Matrix4x4( + lsq - 2 * (y * y + z * z), + 2 * (x * y + z * w), + 2 * (x * z - y * w), + 0, - mtx[4] = 2 * (q[0] * q[1] - q[2] * q[3]); - mtx[5] = lsq - 2 * (q[0] * q[0] + q[2] * q[2]); - mtx[6] = 2 * (q[1] * q[2] + q[0] * q[3]); - mtx[7] = 0; + 2 * (x * y - z * w), + lsq - 2 * (x * x + z * z), + 2 * (y * z + x * w), + 0, - mtx[8] = 2 * (q[0] * q[2] + q[1] * q[3]); - mtx[9] = 2 * (q[1] * q[2] - q[0] * q[3]); - mtx[10] = lsq - 2 * (q[0] * q[0] + q[1] * q[1]); - mtx[11] = 0; + 2 * (x * z + y * w), + 2 * (y * z - x * w), + lsq - 2 * (x * x + y * y), + 0, - mtx[12] = 0; - mtx[13] = 0; - mtx[14] = 0; - mtx[15] = lsq; + 0, + 0, + 0, + lsq); + } + public Int32Matrix4x4 AsMatrix() + { + Int32Matrix4x4 mtx; + AsMatrix(out mtx); return mtx; } diff --git a/OpenRA.Game/WVec.cs b/OpenRA.Game/WVec.cs index 49dddb7426..f7830f33c9 100644 --- a/OpenRA.Game/WVec.cs +++ b/OpenRA.Game/WVec.cs @@ -46,19 +46,20 @@ namespace OpenRA public WVec Rotate(WRot rot) { - return Rotate(rot.AsMatrix()); + Int32Matrix4x4 mtx; + rot.AsMatrix(out mtx); + return Rotate(ref mtx); } - public WVec Rotate(int[] rotationMatrix) + public WVec Rotate(ref Int32Matrix4x4 mtx) { - var mtx = rotationMatrix; var lx = (long)X; var ly = (long)Y; var lz = (long)Z; return new WVec( - (int)((lx * mtx[0] + ly * mtx[4] + lz * mtx[8]) / mtx[15]), - (int)((lx * mtx[1] + ly * mtx[5] + lz * mtx[9]) / mtx[15]), - (int)((lx * mtx[2] + ly * mtx[6] + lz * mtx[10]) / mtx[15])); + (int)((lx * mtx.M11 + ly * mtx.M21 + lz * mtx.M31) / mtx.M44), + (int)((lx * mtx.M12 + ly * mtx.M22 + lz * mtx.M32) / mtx.M44), + (int)((lx * mtx.M13 + ly * mtx.M23 + lz * mtx.M33) / mtx.M44)); } public WAngle Yaw diff --git a/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs b/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs index f68cc03133..6b289c1a94 100644 --- a/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/RangeCircleRenderable.cs @@ -17,8 +17,8 @@ namespace OpenRA.Mods.Common.Graphics public struct RangeCircleRenderable : IRenderable, IFinalizedRenderable { const int RangeCircleSegments = 32; - static readonly int[][] RangeCircleStartRotations = Exts.MakeArray(RangeCircleSegments, i => WRot.FromFacing(8 * i).AsMatrix()); - static readonly int[][] RangeCircleEndRotations = Exts.MakeArray(RangeCircleSegments, i => WRot.FromFacing(8 * i + 6).AsMatrix()); + static readonly Int32Matrix4x4[] RangeCircleStartRotations = Exts.MakeArray(RangeCircleSegments, i => WRot.FromFacing(8 * i).AsMatrix()); + static readonly Int32Matrix4x4[] RangeCircleEndRotations = Exts.MakeArray(RangeCircleSegments, i => WRot.FromFacing(8 * i + 6).AsMatrix()); readonly WPos centerPosition; readonly WDist radius; @@ -58,8 +58,8 @@ namespace OpenRA.Mods.Common.Graphics var offset = new WVec(radius.Length, 0, 0); for (var i = 0; i < RangeCircleSegments; i++) { - var a = wr.Screen3DPosition(centerPosition + offset.Rotate(RangeCircleStartRotations[i])); - var b = wr.Screen3DPosition(centerPosition + offset.Rotate(RangeCircleEndRotations[i])); + var a = wr.Screen3DPosition(centerPosition + offset.Rotate(ref RangeCircleStartRotations[i])); + var b = wr.Screen3DPosition(centerPosition + offset.Rotate(ref RangeCircleEndRotations[i])); if (contrastWidth > 0) wcr.DrawLine(a, b, contrastWidth / wr.Viewport.Zoom, contrastColor);