The HardwarePalette will now grow its palette buffer and texture in power-of-2 increments. This avoids it having to allocate memory for a full 256x256 texture up front. In practice the default mods use 22 or 23 palettes so a 32x256 texture is used. This means both the buffer and texture save neatly on memory. Additionally, HardwarePalette.ApplyModifiers sees a nice speedup as it has to transfer a much smaller amount of memory from the buffer to the texture. To facilitate this change, the MaxPalettes constant is no more. Instead the PaletteReference deals with the calculation of the index and this is passed into the appropriate methods.
294 lines
7.7 KiB
C#
294 lines
7.7 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2014 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. For more information,
|
|
* see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace OpenRA.Graphics
|
|
{
|
|
public static class Util
|
|
{
|
|
// yes, our channel order is nuts.
|
|
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
|
|
static readonly float[] ChannelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
|
|
|
|
public static void FastCreateQuad(Vertex[] vertices, float2 o, Sprite r, float paletteTextureIndex, int nv, float2 size)
|
|
{
|
|
var b = new float2(o.X + size.X, o.Y);
|
|
var c = new float2(o.X + size.X, o.Y + size.Y);
|
|
var d = new float2(o.X, o.Y + size.Y);
|
|
FastCreateQuad(vertices, o, b, c, d, r, paletteTextureIndex, nv);
|
|
}
|
|
|
|
public static void FastCreateQuad(Vertex[] vertices, float2 a, float2 b, float2 c, float2 d, Sprite r, float paletteTextureIndex, int nv)
|
|
{
|
|
var attribC = ChannelSelect[(int)r.Channel];
|
|
|
|
vertices[nv] = new Vertex(a, r.Left, r.Top, paletteTextureIndex, attribC);
|
|
vertices[nv + 1] = new Vertex(b, r.Right, r.Top, paletteTextureIndex, attribC);
|
|
vertices[nv + 2] = new Vertex(c, r.Right, r.Bottom, paletteTextureIndex, attribC);
|
|
vertices[nv + 3] = new Vertex(d, r.Left, r.Bottom, paletteTextureIndex, attribC);
|
|
}
|
|
|
|
public static void FastCopyIntoChannel(Sprite dest, byte[] src) { FastCopyIntoChannel(dest, 0, src); }
|
|
public static void FastCopyIntoChannel(Sprite dest, int channelOffset, byte[] src)
|
|
{
|
|
var data = dest.Sheet.GetData();
|
|
var srcStride = dest.Bounds.Width;
|
|
var destStride = dest.Sheet.Size.Width * 4;
|
|
var destOffset = destStride * dest.Bounds.Top + dest.Bounds.Left * 4 + ChannelMasks[(int)dest.Channel + channelOffset];
|
|
var destSkip = destStride - 4 * srcStride;
|
|
var height = dest.Bounds.Height;
|
|
|
|
var srcOffset = 0;
|
|
for (var j = 0; j < height; j++)
|
|
{
|
|
for (var i = 0; i < srcStride; i++, srcOffset++)
|
|
{
|
|
data[destOffset] = src[srcOffset];
|
|
destOffset += 4;
|
|
}
|
|
|
|
destOffset += destSkip;
|
|
}
|
|
}
|
|
|
|
public static void FastCopyIntoSprite(Sprite dest, Bitmap src)
|
|
{
|
|
var data = dest.Sheet.GetData();
|
|
var dataStride = dest.Sheet.Size.Width * 4;
|
|
var x = dest.Bounds.Left * 4;
|
|
var width = dest.Bounds.Width * 4;
|
|
var y = dest.Bounds.Top;
|
|
var height = dest.Bounds.Height;
|
|
|
|
var bd = src.LockBits(src.Bounds(),
|
|
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
|
for (var row = 0; row < height; row++)
|
|
Marshal.Copy(IntPtr.Add(bd.Scan0, row * bd.Stride), data, (y + row) * dataStride + x, width);
|
|
src.UnlockBits(bd);
|
|
}
|
|
|
|
public static float[] IdentityMatrix()
|
|
{
|
|
return Exts.MakeArray(16, j => (j % 5 == 0) ? 1.0f : 0);
|
|
}
|
|
|
|
public static float[] ScaleMatrix(float sx, float sy, float sz)
|
|
{
|
|
var mtx = IdentityMatrix();
|
|
mtx[0] = sx;
|
|
mtx[5] = sy;
|
|
mtx[10] = sz;
|
|
return mtx;
|
|
}
|
|
|
|
public static float[] TranslationMatrix(float x, float y, float z)
|
|
{
|
|
var mtx = IdentityMatrix();
|
|
mtx[12] = x;
|
|
mtx[13] = y;
|
|
mtx[14] = z;
|
|
return mtx;
|
|
}
|
|
|
|
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(int[] imtx)
|
|
{
|
|
var fmtx = new float[16];
|
|
for (var i = 0; i < 16; i++)
|
|
fmtx[i] = imtx[i] * 1f / imtx[15];
|
|
return fmtx;
|
|
}
|
|
|
|
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[] { 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 float[] { 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;
|
|
}
|
|
}
|
|
}
|