public ClusterFit(ColourSet colours, SquishFlags flags) : base(colours, flags) { // set the iteration count m_iterationCount = ((m_flags & SquishFlags.ColourIterativeClusterFit) != 0) ? kMaxIterations : 1; // initialise the best error m_besterror = new Vec4(float.MaxValue); // initialise the metric bool perceptual = ((m_flags & SquishFlags.ColourMetricPerceptual) != 0); if (perceptual) m_metric = new Vec4(0.2126f, 0.7152f, 0.0722f, 0.0f); else m_metric = new Vec4(1.0f); // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); // get the covariance matrix Sym3x3 covariance = Sym3x3.ComputeWeightedCovariance(count, values, m_colours.GetWeights()); // compute the principle component m_principle = Sym3x3.ComputePrincipleComponent(covariance); }
public static bool CompareAnyLessThan(Vec4 a, Vec4 b) { return a.X() < b.X() || a.Y() < b.Y() || a.Z() < b.Z() || a.W() < b.W(); }
public static Vec4 Truncate(Vec4 v) { return new Vec4( v.X() > 0.0f ? CMath.Floor(v.X()) : CMath.Ceil(v.X()), v.Y() > 0.0f ? CMath.Floor(v.Y()) : CMath.Ceil(v.Y()), v.Z() > 0.0f ? CMath.Floor(v.Z()) : CMath.Ceil(v.Z()), v.W() > 0.0f ? CMath.Floor(v.W()) : CMath.Ceil(v.W()) ); }
public static Vec4 Max(Vec4 a, Vec4 b) { return new Vec4( Math.Max(a.X(), b.X()), Math.Max(a.Y(), b.Y()), Math.Max(a.Z(), b.Z()), Math.Max(a.W(), b.W()) ); }
public static Vec4 Reciprocal(Vec4 v) { return new Vec4( 1.0f / v.X(), 1.0f / v.Y(), 1.0f / v.Z(), 1.0f / v.W() ); }
public static Vec4 NegativeMultiplySubtract(Vec4 a, Vec4 b, Vec4 c) { return c - a * b; }
public static Vec4 MultiplyAdd(Vec4 a, Vec4 b, Vec4 c) { return a * b + c; }
protected override unsafe void Compress3(byte* block) { // declare variables int count = m_colours.GetCount(); Vec4 two = new Vec4(2.0f); Vec4 one = new Vec4(1.0f); Vec4 half_half2 = new Vec4(0.5f, 0.5f, 0.5f, 0.25f); Vec4 zero = new Vec4(0.0f); Vec4 half = new Vec4(0.5f); Vec4 grid = new Vec4(31.0f, 63.0f, 31.0f, 0.0f); Vec4 gridrcp = new Vec4(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f, 0.0f); // prepare an ordering using the principle axis ConstructOrdering(m_principle, 0); // check all possible clusters and iterate on the total order Vec4 beststart = new Vec4(0.0f); Vec4 bestend = new Vec4(0.0f); Vec4 besterror = m_besterror; byte[] bestindices = new byte[16]; int bestiteration = 0; int besti = 0, bestj = 0; // loop over iterations (we avoid the case that all points in first or last cluster) for (int iterationIndex = 0; ; ) { // first cluster [0,i) is at the start Vec4 part0 = new Vec4(0.0f); for (int i = 0; i < count; ++i) { // second cluster [i,j) is half along Vec4 part1 = (i == 0) ? m_points_weights[0] : new Vec4(0.0f); int jmin = (i == 0) ? 1 : i; for (int j = jmin; ; ) { // last cluster [j,count) is at the end Vec4 part2 = m_xsum_wsum - part1 - part0; // compute least squares terms directly Vec4 alphax_sum = Vec4.MultiplyAdd(part1, half_half2, part0); Vec4 alpha2_sum = alphax_sum.SplatW(); Vec4 betax_sum = Vec4.MultiplyAdd(part1, half_half2, part2); Vec4 beta2_sum = betax_sum.SplatW(); Vec4 alphabeta_sum = (part1 * half_half2).SplatW(); // compute the least-squares optimal points Vec4 factor = Vec4.Reciprocal(Vec4.NegativeMultiplySubtract(alphabeta_sum, alphabeta_sum, alpha2_sum * beta2_sum)); Vec4 a = Vec4.NegativeMultiplySubtract(betax_sum, alphabeta_sum, alphax_sum * beta2_sum) * factor; Vec4 b = Vec4.NegativeMultiplySubtract(alphax_sum, alphabeta_sum, betax_sum * alpha2_sum) * factor; // clamp to the grid a = Vec4.Min(one, Vec4.Max(zero, a)); b = Vec4.Min(one, Vec4.Max(zero, b)); a = Vec4.Truncate(Vec4.MultiplyAdd(grid, a, half)) * gridrcp; b = Vec4.Truncate(Vec4.MultiplyAdd(grid, b, half)) * gridrcp; // compute the error (we skip the constant xxsum) Vec4 e1 = Vec4.MultiplyAdd(a * a, alpha2_sum, b * b * beta2_sum); Vec4 e2 = Vec4.NegativeMultiplySubtract(a, alphax_sum, a * b * alphabeta_sum); Vec4 e3 = Vec4.NegativeMultiplySubtract(b, betax_sum, e2); Vec4 e4 = Vec4.MultiplyAdd(two, e3, e1); // apply the metric to the error term Vec4 e5 = e4 * m_metric; Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ(); // keep the solution if it wins if (Vec4.CompareAnyLessThan(error, besterror)) { beststart = a; bestend = b; besti = i; bestj = j; besterror = error; bestiteration = iterationIndex; } // advance if (j == count) break; part1 += m_points_weights[j]; ++j; } // advance part0 += m_points_weights[i]; } // stop if we didn't improve in this iteration if (bestiteration != iterationIndex) break; // advance if possible ++iterationIndex; if (iterationIndex == m_iterationCount) break; // stop if a new iteration is an ordering that has already been tried Vec3 axis = (bestend - beststart).GetVec3(); if (!ConstructOrdering(axis, iterationIndex)) break; } // save the block if necessary if (Vec4.CompareAnyLessThan(besterror, m_besterror)) { // remap the indices int orderIndex = 16 * bestiteration; byte[] unordered = new byte[16]; for (int m = 0; m < besti; ++m) unordered[m_order[orderIndex + m]] = 0; for (int m = besti; m < bestj; ++m) unordered[m_order[orderIndex + m]] = 2; for (int m = bestj; m < count; ++m) unordered[m_order[orderIndex + m]] = 1; fixed (byte* fixUnordered = unordered, fixBestIndices = bestindices) { m_colours.RemapIndices(fixUnordered, fixBestIndices); // save the block ColourBlock.WriteColourBlock3(beststart.GetVec3(), bestend.GetVec3(), fixBestIndices, block); } // save the error m_besterror = besterror; } }
private bool ConstructOrdering(Vec3 axis, int iteration) { // cache some values int count = m_colours.GetCount(); Vec3[] values = m_colours.GetPoints(); // build the list of dot products float[] dps = new float[16]; int orderIndex = 16 * iteration; for (int i = 0; i < count; ++i) { dps[i] = Vec3.Dot(values[i], axis); m_order[orderIndex + i] = (byte)i; } // stable sort using them for (int i = 0; i < count; ++i) { for (int j = i; j > 0 && dps[j] < dps[j - 1]; --j) { CMath.Swap<float>(ref dps[j], ref dps[j - 1]); CMath.Swap<byte>(ref m_order[orderIndex + j], ref m_order[orderIndex + (j - 1)]); } } // check this ordering is unique for (int it = 0; it < iteration; ++it) { int prevIndex = 16 * it; bool same = true; for (int i = 0; i < count; ++i) { if (m_order[orderIndex + i] != m_order[prevIndex + i]) { same = false; break; } } if (same) return false; } // copy the ordering and weight all the points Vec3[] unweighted = m_colours.GetPoints(); float[] weights = m_colours.GetWeights(); m_xsum_wsum = new Vec4(0.0f); for (int i = 0; i < count; ++i) { int j = m_order[orderIndex + i]; Vec4 p = new Vec4(unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f); Vec4 w = new Vec4(weights[j]); Vec4 x = p * w; m_points_weights[i] = x; m_xsum_wsum += x; } return true; }