1 /* -----------------------------------------------------------------------------
3 Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
4 Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 -------------------------------------------------------------------------- */
27 #include "clusterfit.h"
28 #include "colourset.h"
29 #include "colourblock.h"
34 ClusterFit::ClusterFit( ColourSet
const* colours
, int flags
)
35 : ColourFit( colours
, flags
),
42 // set the iteration count
43 m_iterationCount
= ( m_flags
& kColourIterativeClusterFit
) ? kMaxIterations
: 1;
45 // initialise the best error
46 m_besterror
= VEC4_CONST( FLT_MAX
);
48 // initialise the metric
49 bool perceptual
= ( ( m_flags
& kColourMetricPerceptual
) != 0 );
51 m_metric
= Vec4( 0.2126f
, 0.7152f
, 0.0722f
, 0.0f
);
53 m_metric
= VEC4_CONST( 1.0f
);
56 int const count
= m_colours
->GetCount();
57 Vec3
const* values
= m_colours
->GetPoints();
59 // get the covariance matrix
60 Sym3x3 covariance
= ComputeWeightedCovariance( count
, values
, m_colours
->GetWeights() );
62 // compute the principle component
63 m_principle
= ComputePrincipleComponent( covariance
);
66 bool ClusterFit::ConstructOrdering( Vec3
const& axis
, int iteration
)
69 int const count
= m_colours
->GetCount();
70 Vec3
const* values
= m_colours
->GetPoints();
72 // build the list of dot products
74 u8
* order
= static_cast< u8
*>(m_order
) + 16*iteration
;
75 for( int i
= 0; i
< count
; ++i
)
77 dps
[i
] = Dot( values
[i
], axis
);
81 // stable sort using them
82 for( int i
= 0; i
< count
; ++i
)
84 for( int j
= i
; j
> 0 && dps
[j
] < dps
[j
- 1]; --j
)
86 std::swap( dps
[j
], dps
[j
- 1] );
87 std::swap( order
[j
], order
[j
- 1] );
91 // check this ordering is unique
92 for( int it
= 0; it
< iteration
; ++it
)
94 u8
const* prev
= static_cast< u8
*>(m_order
) + 16*it
;
96 for( int i
= 0; i
< count
; ++i
)
98 if( order
[i
] != prev
[i
] )
108 // copy the ordering and weight all the points
109 Vec3
const* unweighted
= m_colours
->GetPoints();
110 float const* weights
= m_colours
->GetWeights();
111 m_xsum_wsum
= VEC4_CONST( 0.0f
);
112 for( int i
= 0; i
< count
; ++i
)
115 Vec4
p( unweighted
[j
].X(), unweighted
[j
].Y(), unweighted
[j
].Z(), 1.0f
);
116 Vec4
w( weights
[j
] );
118 m_points_weights
[i
] = x
;
124 void ClusterFit::Compress3( void* block
)
127 int const count
= m_colours
->GetCount();
128 Vec4
const two
= VEC4_CONST( 2.0 );
129 Vec4
const one
= VEC4_CONST( 1.0f
);
130 Vec4
const half_half2( 0.5f
, 0.5f
, 0.5f
, 0.25f
);
131 Vec4
const zero
= VEC4_CONST( 0.0f
);
132 Vec4
const half
= VEC4_CONST( 0.5f
);
133 Vec4
const grid( 31.0f
, 63.0f
, 31.0f
, 0.0f
);
134 Vec4
const gridrcp( 1.0f
/31.0f
, 1.0f
/63.0f
, 1.0f
/31.0f
, 0.0f
);
136 // prepare an ordering using the principle axis
137 ConstructOrdering( m_principle
, 0 );
139 // check all possible clusters and iterate on the total order
140 Vec4 beststart
= VEC4_CONST( 0.0f
);
141 Vec4 bestend
= VEC4_CONST( 0.0f
);
142 Vec4 besterror
= m_besterror
;
144 int bestiteration
= 0;
145 int besti
= 0, bestj
= 0;
147 // loop over iterations (we avoid the case that all points in first or last cluster)
148 for( int iterationIndex
= 0;; )
150 // first cluster [0,i) is at the start
151 Vec4 part0
= VEC4_CONST( 0.0f
);
152 for( int i
= 0; i
< count
; ++i
)
154 // second cluster [i,j) is half along
155 Vec4 part1
= ( i
== 0 ) ? m_points_weights
[0] : VEC4_CONST( 0.0f
);
156 int jmin
= ( i
== 0 ) ? 1 : i
;
157 for( int j
= jmin
;; )
159 // last cluster [j,count) is at the end
160 Vec4 part2
= m_xsum_wsum
- part1
- part0
;
162 // compute least squares terms directly
163 Vec4 alphax_sum
= MultiplyAdd( part1
, half_half2
, part0
);
164 Vec4 alpha2_sum
= alphax_sum
.SplatW();
166 Vec4 betax_sum
= MultiplyAdd( part1
, half_half2
, part2
);
167 Vec4 beta2_sum
= betax_sum
.SplatW();
169 Vec4 alphabeta_sum
= ( part1
*half_half2
).SplatW();
171 // compute the least-squares optimal points
172 Vec4 factor
= Reciprocal( NegativeMultiplySubtract( alphabeta_sum
, alphabeta_sum
, alpha2_sum
*beta2_sum
) );
173 Vec4 a
= NegativeMultiplySubtract( betax_sum
, alphabeta_sum
, alphax_sum
*beta2_sum
)*factor
;
174 Vec4 b
= NegativeMultiplySubtract( alphax_sum
, alphabeta_sum
, betax_sum
*alpha2_sum
)*factor
;
177 a
= Min( one
, Max( zero
, a
) );
178 b
= Min( one
, Max( zero
, b
) );
179 a
= Truncate( MultiplyAdd( grid
, a
, half
) )*gridrcp
;
180 b
= Truncate( MultiplyAdd( grid
, b
, half
) )*gridrcp
;
182 // compute the error (we skip the constant xxsum)
183 Vec4 e1
= MultiplyAdd( a
*a
, alpha2_sum
, b
*b
*beta2_sum
);
184 Vec4 e2
= NegativeMultiplySubtract( a
, alphax_sum
, a
*b
*alphabeta_sum
);
185 Vec4 e3
= NegativeMultiplySubtract( b
, betax_sum
, e2
);
186 Vec4 e4
= MultiplyAdd( two
, e3
, e1
);
188 // apply the metric to the error term
189 Vec4 e5
= e4
*m_metric
;
190 Vec4 error
= e5
.SplatX() + e5
.SplatY() + e5
.SplatZ();
192 // keep the solution if it wins
193 if( CompareAnyLessThan( error
, besterror
) )
200 bestiteration
= iterationIndex
;
206 part1
+= m_points_weights
[j
];
211 part0
+= m_points_weights
[i
];
214 // stop if we didn't improve in this iteration
215 if( bestiteration
!= iterationIndex
)
218 // advance if possible
220 if( iterationIndex
== m_iterationCount
)
223 // stop if a new iteration is an ordering that has already been tried
224 Vec3 axis
= ( bestend
- beststart
).GetVec3();
225 if( !ConstructOrdering( axis
, iterationIndex
) )
229 // save the block if necessary
230 if( CompareAnyLessThan( besterror
, m_besterror
) )
233 u8
const* order
= static_cast<u8
*>(m_order
) + 16*bestiteration
;
236 for( int m
= 0; m
< besti
; ++m
)
237 unordered
[order
[m
]] = 0;
238 for( int m
= besti
; m
< bestj
; ++m
)
239 unordered
[order
[m
]] = 2;
240 for( int m
= bestj
; m
< count
; ++m
)
241 unordered
[order
[m
]] = 1;
243 m_colours
->RemapIndices( unordered
, bestindices
);
246 WriteColourBlock3( beststart
.GetVec3(), bestend
.GetVec3(), bestindices
, block
);
249 m_besterror
= besterror
;
253 void ClusterFit::Compress4( void* block
)
256 int const count
= m_colours
->GetCount();
257 Vec4
const two
= VEC4_CONST( 2.0f
);
258 Vec4
const one
= VEC4_CONST( 1.0f
);
259 Vec4
const onethird_onethird2( 1.0f
/3.0f
, 1.0f
/3.0f
, 1.0f
/3.0f
, 1.0f
/9.0f
);
260 Vec4
const twothirds_twothirds2( 2.0f
/3.0f
, 2.0f
/3.0f
, 2.0f
/3.0f
, 4.0f
/9.0f
);
261 Vec4
const twonineths
= VEC4_CONST( 2.0f
/9.0f
);
262 Vec4
const zero
= VEC4_CONST( 0.0f
);
263 Vec4
const half
= VEC4_CONST( 0.5f
);
264 Vec4
const grid( 31.0f
, 63.0f
, 31.0f
, 0.0f
);
265 Vec4
const gridrcp( 1.0f
/31.0f
, 1.0f
/63.0f
, 1.0f
/31.0f
, 0.0f
);
267 // prepare an ordering using the principle axis
268 ConstructOrdering( m_principle
, 0 );
270 // check all possible clusters and iterate on the total order
271 Vec4 beststart
= VEC4_CONST( 0.0f
);
272 Vec4 bestend
= VEC4_CONST( 0.0f
);
273 Vec4 besterror
= m_besterror
;
275 int bestiteration
= 0;
276 int besti
= 0, bestj
= 0, bestk
= 0;
278 // loop over iterations (we avoid the case that all points in first or last cluster)
279 for( int iterationIndex
= 0;; )
281 // first cluster [0,i) is at the start
282 Vec4 part0
= VEC4_CONST( 0.0f
);
283 for( int i
= 0; i
< count
; ++i
)
285 // second cluster [i,j) is one third along
286 Vec4 part1
= VEC4_CONST( 0.0f
);
289 // third cluster [j,k) is two thirds along
290 Vec4 part2
= ( j
== 0 ) ? m_points_weights
[0] : VEC4_CONST( 0.0f
);
291 int kmin
= ( j
== 0 ) ? 1 : j
;
292 for( int k
= kmin
;; )
294 // last cluster [k,count) is at the end
295 Vec4 part3
= m_xsum_wsum
- part2
- part1
- part0
;
297 // compute least squares terms directly
298 Vec4
const alphax_sum
= MultiplyAdd( part2
, onethird_onethird2
, MultiplyAdd( part1
, twothirds_twothirds2
, part0
) );
299 Vec4
const alpha2_sum
= alphax_sum
.SplatW();
301 Vec4
const betax_sum
= MultiplyAdd( part1
, onethird_onethird2
, MultiplyAdd( part2
, twothirds_twothirds2
, part3
) );
302 Vec4
const beta2_sum
= betax_sum
.SplatW();
304 Vec4
const alphabeta_sum
= twonineths
*( part1
+ part2
).SplatW();
306 // compute the least-squares optimal points
307 Vec4 factor
= Reciprocal( NegativeMultiplySubtract( alphabeta_sum
, alphabeta_sum
, alpha2_sum
*beta2_sum
) );
308 Vec4 a
= NegativeMultiplySubtract( betax_sum
, alphabeta_sum
, alphax_sum
*beta2_sum
)*factor
;
309 Vec4 b
= NegativeMultiplySubtract( alphax_sum
, alphabeta_sum
, betax_sum
*alpha2_sum
)*factor
;
312 a
= Min( one
, Max( zero
, a
) );
313 b
= Min( one
, Max( zero
, b
) );
314 a
= Truncate( MultiplyAdd( grid
, a
, half
) )*gridrcp
;
315 b
= Truncate( MultiplyAdd( grid
, b
, half
) )*gridrcp
;
317 // compute the error (we skip the constant xxsum)
318 Vec4 e1
= MultiplyAdd( a
*a
, alpha2_sum
, b
*b
*beta2_sum
);
319 Vec4 e2
= NegativeMultiplySubtract( a
, alphax_sum
, a
*b
*alphabeta_sum
);
320 Vec4 e3
= NegativeMultiplySubtract( b
, betax_sum
, e2
);
321 Vec4 e4
= MultiplyAdd( two
, e3
, e1
);
323 // apply the metric to the error term
324 Vec4 e5
= e4
*m_metric
;
325 Vec4 error
= e5
.SplatX() + e5
.SplatY() + e5
.SplatZ();
327 // keep the solution if it wins
328 if( CompareAnyLessThan( error
, besterror
) )
336 bestiteration
= iterationIndex
;
342 part2
+= m_points_weights
[k
];
349 part1
+= m_points_weights
[j
];
354 part0
+= m_points_weights
[i
];
357 // stop if we didn't improve in this iteration
358 if( bestiteration
!= iterationIndex
)
361 // advance if possible
363 if( iterationIndex
== m_iterationCount
)
366 // stop if a new iteration is an ordering that has already been tried
367 Vec3 axis
= ( bestend
- beststart
).GetVec3();
368 if( !ConstructOrdering( axis
, iterationIndex
) )
372 // save the block if necessary
373 if( CompareAnyLessThan( besterror
, m_besterror
) )
376 u8
const* order
= static_cast<u8
*>(m_order
) + 16*bestiteration
;
379 for( int m
= 0; m
< besti
; ++m
)
380 unordered
[order
[m
]] = 0;
381 for( int m
= besti
; m
< bestj
; ++m
)
382 unordered
[order
[m
]] = 2;
383 for( int m
= bestj
; m
< bestk
; ++m
)
384 unordered
[order
[m
]] = 3;
385 for( int m
= bestk
; m
< count
; ++m
)
386 unordered
[order
[m
]] = 1;
388 m_colours
->RemapIndices( unordered
, bestindices
);
391 WriteColourBlock4( beststart
.GetVec3(), bestend
.GetVec3(), bestindices
, block
);
394 m_besterror
= besterror
;
398 } // namespace squish