private const float FLT_EPSILON = 1.192092896e-07F; // This is not the same value as Single.Epsilon #endregion Private Fields #region Public Methods public static Sym3x3 ComputeWeightedCovariance(int n, Vec3[] points, float[] weights) { // // compute the centroid // float total = 0.0f; Vec3 centroid = new Vec3(0.0f); for(int i = 0; i < n; ++i) { total += weights[i]; centroid += weights[i] * points[i]; } centroid /= total; // // accumulate the covariance matrix // Sym3x3 covariance = new Sym3x3(0.0f); for(int i = 0; i < n; ++i) { Vec3 a = points[i] - centroid; Vec3 b = weights[i] * a; covariance[0] += a.X * b.X; covariance[1] += a.X * b.Y; covariance[2] += a.X * b.Z; covariance[3] += a.Y * b.Y; covariance[4] += a.Y * b.Z; covariance[5] += a.Z * b.Z; } // // return it // return covariance; }
protected override unsafe void Compress4(byte *block) { // // cache some values // int count = colours.Count; Vec3[] values = colours.Points; // // create a codebook // Vec3[] codes = new Vec3[4]; codes[0] = start; codes[1] = end; codes[2] = 2.0f / 3.0f * start + 1.0f / 3.0f * end; codes[3] = 1.0f / 3.0f * start + 2.0f / 3.0f * end; // // match each point to the closest code // byte[] closest = new byte[16]; float error = 0.0f; for (int i = 0; i < count; ++i) { // // find the closest code // float dist = Single.MaxValue; int idx = 0; for (int j = 0; j < 4; ++j) { float d = Vec3.LengthSquared(metric * (values[i] - codes[j])); if (d < dist) { dist = d; idx = j; } } // // save the index // closest[i] = (byte)idx; // // accumulate the error // error += dist; } // // save this scheme if it wins // if (error < bestError) { // // remap the indices // byte[] indices = new byte[16]; colours.RemapIndices(closest, indices); // // save the block // ColourBlock.WriteColourBlock4(start, end, indices, block); // // save the error // bestError = error; } }
public static float LengthSquared(Vec3 v) { return Dot(v, v); }
public static Vec3 Truncate(Vec3 v) { return new Vec3((float)(v.x > 0.0f ? Math.Floor(v.x ) : Math.Ceiling(v.x)), (float)(v.y > 0.0f ? Math.Floor(v.y) : Math.Ceiling(v.y)), (float)(v.z > 0.0f ? Math.Floor(v.z ) : Math.Ceiling(v.z))); }
public static Vec3 Max(Vec3 left, Vec3 right) { return new Vec3(Math.Max(left.x, right.x), Math.Max(left.y, right.y), Math.Max(left.z, right.z)); }
private static Vec3 subtract(Vec3 left, Vec3 right) { left.x -= right.x; left.y -= right.y; left.z -= right.z; return left; }
public ClusterFit(ColourSet Colours, SquishFlags Flags) : base(Colours, Flags) { // // initialise the metric // bool isPerceptual = (flags & SquishFlags.ColourMetricPerceptual) != 0; metric = isPerceptual ? new Vec3(0.2126f, 0.7152f, 0.0722f) : new Vec3(1.0f); // // initialise the best error // bestError = Single.MaxValue; // // cache some values // int count = colours.Count; Vec3[] values = colours.Points; float[] weights = colours.Weights; // // get the covariance matrix // Sym3x3 covariance = Maths.ComputeWeightedCovariance(count, values, weights); // // compute the principle component // Vec3 principle = Maths.ComputePrincipleComponent(covariance); // // get the min and max range as the codebook endpoints // Vec3 startTemp = new Vec3(0.0f); Vec3 endTemp = new Vec3(0.0f); if (count > 0) { // // compute the range // startTemp = endTemp = values[0]; float min = Vec3.Dot(values[0], principle); float max = min; for(int i = 1; i < count; ++i) { float val = Vec3.Dot(values[i], principle); if (val < min) { startTemp = values[i]; min = val; } else { if (val > max) { endTemp = values[i]; max = val; } } } } // // clamp the output to [0, 1] // Vec3 one = new Vec3(1.0f); Vec3 zero = new Vec3(0.0f); startTemp = Vec3.Min(one, Vec3.Max(zero, startTemp)); endTemp = Vec3.Min(one, Vec3.Max(zero, endTemp)); // // clamp to the grid and save // Vec3 grid = new Vec3(31.0f, 63.0f, 31.0f); Vec3 gridrcp = new Vec3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); Vec3 half = new Vec3(0.5f); start = Vec3.Truncate(grid * startTemp + half) * gridrcp; end = Vec3.Truncate(grid * endTemp + half) * gridrcp; }
public unsafe ColourSet(byte *rgba, int mask, SquishFlags flags) { Count = 0; IsTransparent = false; // // check the compression mode for dxt1 // bool isDxt1 = (flags & SquishFlags.Dxt1) != 0; bool weightByAlpha = (flags & SquishFlags.WeightColourByAlpha) != 0; // // create the minimal set // for (int i = 0; i < 16; ++i) { // // check this pixel is enabled // int bit = 1 << i; if ((mask & bit) == 0) { remap[i] = -1; continue; } // // check for transparent pixels when using dxt1 // if (isDxt1 && rgba[4 * i + 3] < 128) { remap[i] = -1; IsTransparent = true; continue; } // // loop over previous points for a match // for(int j = 0; ; ++j) { // // allocate a new point // if (j == i) { // // normalise coordinates to [0,1] // float x = rgba[4 * i + 0] / 255.0f; float y = rgba[4 * i + 1] / 255.0f; float z = rgba[4 * i + 2] / 255.0f; // // ensure there is always non-zero weight even for zero alpha // float w = (rgba[4 * i + 3] + 1) / 256.0f; // // add the point // Points[Count] = new Vec3(x, y, z); Weights[Count] = weightByAlpha ? w : 1.0f; remap[i] = Count; // // advance // ++Count; break; } // // check for a match // int oldbit = 1 << j; bool match = ((mask & oldbit) != 0) && (rgba[4 * i + 0] == rgba[4 * j]) && (rgba[4 * i + 1] == rgba[4 * j + 1]) && (rgba[4 * i + 2] == rgba[4 * j + 2]) && (rgba[4 * j + 3] >= 128 || !isDxt1); if (match) { // // get the index of the match // int index = remap[j]; // // ensure there is always non-zero weight even for zero alpha // float w = (rgba[4 * i + 3] + 1) / 256.0f; // // map to this point and increase the weight // Weights[index] += weightByAlpha ? w : 1.0f; remap[i] = index; break; } } } // // square root the weights // for (int i = 0; i < Count; ++i) Weights[i] = (float)Math.Sqrt(Weights[i]); }
public static Vec3 operator*(Vec3 left, Vec3 right) { Vec3 copy = new Vec3(left); return multiply(copy, right); }
public static Vec3 operator-(Vec3 left, Vec3 right) { Vec3 copy = new Vec3(left); return subtract(copy, right); }
public static Vec3 operator+(Vec3 left, Vec3 right) { Vec3 copy = new Vec3(left); return add(copy, right); }
public Vec3(Vec3 copy) { x = copy.x; y = copy.y; z = copy.z; }
private static Vec3 divide(Vec3 left, float right) { return multiply(left, 1.0f / right); }
private static Vec3 multiply(Vec3 left, float right) { left.x *= right; left.y *= right; left.z *= right; return left; }
private static Vec3 multiply(Vec3 left, Vec3 right) { left.x *= right.x; left.y *= right.y; left.z *= right.z; return left; }
public ClusterFit(ColourSet Colours, SquishFlags Flags) : base(Colours, Flags) { // // initialise the metric // bool isPerceptual = (flags & SquishFlags.ColourMetricPerceptual) != 0; metric = isPerceptual ? new Vec3(0.2126f, 0.7152f, 0.0722f) : new Vec3(1.0f); // // initialise the best error // bestError = Single.MaxValue; // // cache some values // int count = colours.Count; Vec3[] values = colours.Points; float[] weights = colours.Weights; // // get the covariance matrix // Sym3x3 covariance = Maths.ComputeWeightedCovariance(count, values, weights); // // compute the principle component // Vec3 principle = Maths.ComputePrincipleComponent(covariance); // // get the min and max range as the codebook endpoints // Vec3 startTemp = new Vec3(0.0f); Vec3 endTemp = new Vec3(0.0f); if (count > 0) { // // compute the range // startTemp = endTemp = values[0]; float min = Vec3.Dot(values[0], principle); float max = min; for (int i = 1; i < count; ++i) { float val = Vec3.Dot(values[i], principle); if (val < min) { startTemp = values[i]; min = val; } else { if (val > max) { endTemp = values[i]; max = val; } } } } // // clamp the output to [0, 1] // Vec3 one = new Vec3(1.0f); Vec3 zero = new Vec3(0.0f); startTemp = Vec3.Min(one, Vec3.Max(zero, startTemp)); endTemp = Vec3.Min(one, Vec3.Max(zero, endTemp)); // // clamp to the grid and save // Vec3 grid = new Vec3(31.0f, 63.0f, 31.0f); Vec3 gridrcp = new Vec3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); Vec3 half = new Vec3(0.5f); start = Vec3.Truncate(grid * startTemp + half) * gridrcp; end = Vec3.Truncate(grid * endTemp + half) * gridrcp; }
public static Vec3 operator*(float left, Vec3 right) { Vec3 copy = new Vec3(right); return multiply(copy, left); }
public static Vec3 operator/(Vec3 left, float right) { Vec3 copy = new Vec3(left); return divide(copy, right); }
protected override unsafe void Compress4(byte *block) { // // cache some values // int count = colours.Count; Vec3[] values = colours.Points; // // create a codebook // Vec3[] codes = new Vec3[4]; codes[0] = start; codes[1] = end; codes[2] = 2.0f / 3.0f * start + 1.0f / 3.0f * end; codes[3] = 1.0f / 3.0f * start + 2.0f / 3.0f * end; // // match each point to the closest code // byte[] closest = new byte[16]; float error = 0.0f; for(int i = 0; i < count; ++i) { // // find the closest code // float dist = Single.MaxValue; int idx = 0; for(int j = 0; j < 4; ++j) { float d = Vec3.LengthSquared(metric * (values[i] - codes[j])); if (d < dist) { dist = d; idx = j; } } // // save the index // closest[i] = (byte)idx; // // accumulate the error // error += dist; } // // save this scheme if it wins // if (error < bestError) { // // remap the indices // byte[] indices = new byte[16]; colours.RemapIndices(closest, indices); // // save the block // ColourBlock.WriteColourBlock4(start, end, indices, block); // // save the error // bestError = error; } }
public static float Dot(Vec3 left, Vec3 right) { return left.x * right.x + left.y * right.y + left.z * right.z; }
private void computeEndPoints(SingleColourLookup[][] lookups) { // // check each index combination (endpoint or intermediate) // error = Int32.MaxValue; for(int i = 0; i < 2; ++i) { // // check the error for this codebook index // SourceBlock[] sources = new SourceBlock[3]; int err = 0; for(int channel = 0; channel < 3; ++channel) { // // grab the lookup table and index for this channel // SingleColourLookup[] lookup = lookups[channel]; int target = colour[channel]; // // store a pointer to the source for this channel // sources[channel] = lookup[target].sources[i]; // // accumulate the error // int diff = sources[channel].error; err += diff * diff; } // // keep it if the error is lower // if (err < error) { start = new Vec3(sources[0].start / 31.0f, sources[1].start / 63.0f, sources[2].start / 31.0f); end = new Vec3(sources[0].end / 31.0f, sources[1].end / 63.0f, sources[2].end / 31.0f); index = (byte)(2 * i); error = err; } } }
private static Vec3 add(Vec3 left, Vec3 right) { left.x += right.x; left.y += right.y; left.z += right.z; return left; }