#region Copyright & License Information /* * Copyright (c) The OpenRA Developers and Contributors * 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.Mods.Cnc { public static class Util { // TD and RA used a nonlinear mapping between artwork frames and unit facings for units with 32 facings. // This table defines the exclusive maximum facing for the i'th sprite frame. // i.e. sprite frame 1 is used for facings 20-55, sprite frame 2 for 56-87, and so on. // Sprite frame 0 is used for facings smaller than 20 or larger than 999. static readonly int[] SpriteRanges = { 20, 56, 88, 132, 156, 184, 212, 240, 268, 296, 324, 352, 384, 416, 452, 488, 532, 568, 604, 644, 668, 696, 724, 752, 780, 808, 836, 864, 896, 928, 964, 1000 }; // The actual facing associated with each sprite frame. static readonly WAngle[] SpriteFacings = { WAngle.Zero, new(40), new(74), new(112), new(146), new(172), new(200), new(228), new(256), new(284), new(312), new(340), new(370), new(402), new(436), new(472), new(512), new(552), new(588), new(626), new(658), new(684), new(712), new(740), new(768), new(796), new(824), new(852), new(882), new(914), new(948), new(984) }; /// /// Calculate the frame index (between 0..numFrames) that /// should be used for the given facing value, accounting /// for the non-linear facing mapping for sprites with 32 directions. /// public static int ClassicIndexFacing(WAngle facing, int numFrames) { if (numFrames == 32) { var angle = facing.Angle; for (var i = 0; i < SpriteRanges.Length; i++) if (angle < SpriteRanges[i]) return i; return 0; } return Common.Util.IndexFacing(facing, numFrames); } /// /// Rounds the given facing value to the nearest quantized step, /// accounting for the non-linear facing mapping for sprites with 32 directions. /// public static WAngle ClassicQuantizeFacing(WAngle facing, int steps) { if (steps == 32) return SpriteFacings[ClassicIndexFacing(facing, steps)]; return Common.Util.QuantizeFacing(facing, steps); } public static float[] IdentityMatrix() { return new float[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; } public static float[] ScaleMatrix(float sx, float sy, float sz) { return new float[] { sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, 0, 0, 0, 1, }; } public static float[] TranslationMatrix(float x, float y, float z) { return new float[] { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1, }; } public static float[] MatrixMultiply(float[] lhs, float[] rhs) { var mtx = new float[16]; for (var i = 0; i < 4; i++) for (var j = 0; j < 4; j++) { mtx[4 * i + j] = 0; for (var k = 0; k < 4; k++) mtx[4 * i + j] += lhs[4 * k + j] * rhs[4 * i + k]; } return mtx; } public static float[] MatrixVectorMultiply(float[] mtx, float[] vec) { var ret = new float[4]; for (var j = 0; j < 4; j++) { ret[j] = 0; for (var k = 0; k < 4; k++) ret[j] += mtx[4 * k + j] * vec[k]; } return ret; } public static float[] MatrixInverse(float[] m) { var mtx = new float[16]; mtx[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; mtx[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; mtx[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; mtx[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; mtx[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; mtx[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; mtx[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; mtx[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; mtx[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; mtx[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; mtx[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; mtx[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; mtx[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; mtx[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; mtx[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; mtx[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; var det = m[0] * mtx[0] + m[1] * mtx[4] + m[2] * mtx[8] + m[3] * mtx[12]; if (det == 0) return null; for (var i = 0; i < 16; i++) mtx[i] *= 1 / det; return mtx; } public static float[] MakeFloatMatrix(Int32Matrix4x4 imtx) { var multipler = 1f / imtx.M44; return new[] { 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) { // Corner offsets. var ix = new uint[] { 0, 0, 0, 0, 3, 3, 3, 3 }; var iy = new uint[] { 1, 1, 4, 4, 1, 1, 4, 4 }; var iz = new uint[] { 2, 5, 2, 5, 2, 5, 2, 5 }; // Vectors to opposing corner. var ret = new[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MinValue, float.MinValue, float.MinValue }; // Transform vectors and find new bounding box. for (var i = 0; i < 8; i++) { var vec = new[] { bounds[ix[i]], bounds[iy[i]], bounds[iz[i]], 1 }; var tvec = MatrixVectorMultiply(mtx, vec); ret[0] = Math.Min(ret[0], tvec[0] / tvec[3]); ret[1] = Math.Min(ret[1], tvec[1] / tvec[3]); ret[2] = Math.Min(ret[2], tvec[2] / tvec[3]); ret[3] = Math.Max(ret[3], tvec[0] / tvec[3]); ret[4] = Math.Max(ret[4], tvec[1] / tvec[3]); ret[5] = Math.Max(ret[5], tvec[2] / tvec[3]); } return ret; } } }