medfall

A super great game engine
Log | Files | Refs

rangefit.cc (5058B)


      1 /* -----------------------------------------------------------------------------
      2 
      3 	Copyright (c) 2006 Simon Brown                          si@sjbrown.co.uk
      4 
      5 	Permission is hereby granted, free of charge, to any person obtaining
      6 	a copy of this software and associated documentation files (the 
      7 	"Software"), to	deal in the Software without restriction, including
      8 	without limitation the rights to use, copy, modify, merge, publish,
      9 	distribute, sublicense, and/or sell copies of the Software, and to 
     10 	permit persons to whom the Software is furnished to do so, subject to 
     11 	the following conditions:
     12 
     13 	The above copyright notice and this permission notice shall be included
     14 	in all copies or substantial portions of the Software.
     15 
     16 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
     18 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     19 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
     20 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
     21 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
     22 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23 	
     24    -------------------------------------------------------------------------- */
     25    
     26 #include "rangefit.h"
     27 #include "colourset.h"
     28 #include "colourblock.h"
     29 #include <cfloat>
     30 
     31 namespace squish {
     32 
     33 RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric ) 
     34   : ColourFit( colours, flags )
     35 {
     36 	// initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
     37 	if( metric )
     38 		m_metric = Vec3( metric[0], metric[1], metric[2] );
     39 	else
     40 		m_metric = Vec3( 1.0f );	
     41 
     42 	// initialise the best error
     43 	m_besterror = FLT_MAX;
     44 
     45 	// cache some values
     46 	int const count = m_colours->GetCount();
     47 	Vec3 const* values = m_colours->GetPoints();
     48 	float const* weights = m_colours->GetWeights();
     49 	
     50 	// get the covariance matrix
     51 	Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights );
     52 	
     53 	// compute the principle component
     54 	Vec3 principle = ComputePrincipleComponent( covariance );
     55 
     56 	// get the min and max range as the codebook endpoints
     57 	Vec3 start( 0.0f );
     58 	Vec3 end( 0.0f );
     59 	if( count > 0 )
     60 	{
     61 		float min, max;
     62 		
     63 		// compute the range
     64 		start = end = values[0];
     65 		min = max = Dot( values[0], principle );
     66 		for( int i = 1; i < count; ++i )
     67 		{
     68 			float val = Dot( values[i], principle );
     69 			if( val < min )
     70 			{
     71 				start = values[i];
     72 				min = val;
     73 			}
     74 			else if( val > max )
     75 			{
     76 				end = values[i];
     77 				max = val;
     78 			}
     79 		}
     80 	}
     81 			
     82 	// clamp the output to [0, 1]
     83 	Vec3 const one( 1.0f );
     84 	Vec3 const zero( 0.0f );
     85 	start = Min( one, Max( zero, start ) );
     86 	end = Min( one, Max( zero, end ) );
     87 
     88 	// clamp to the grid and save
     89 	Vec3 const grid( 31.0f, 63.0f, 31.0f );
     90 	Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f );
     91 	Vec3 const half( 0.5f );
     92 	m_start = Truncate( grid*start + half )*gridrcp;
     93 	m_end = Truncate( grid*end + half )*gridrcp;
     94 }
     95 
     96 void RangeFit::Compress3( void* block )
     97 {
     98 	// cache some values
     99 	int const count = m_colours->GetCount();
    100 	Vec3 const* values = m_colours->GetPoints();
    101 	
    102 	// create a codebook
    103 	Vec3 codes[3];
    104 	codes[0] = m_start;
    105 	codes[1] = m_end;
    106 	codes[2] = 0.5f*m_start + 0.5f*m_end;
    107 
    108 	// match each point to the closest code
    109 	u8 closest[16];
    110 	float error = 0.0f;
    111 	for( int i = 0; i < count; ++i )
    112 	{
    113 		// find the closest code
    114 		float dist = FLT_MAX;
    115 		int idx = 0;
    116 		for( int j = 0; j < 3; ++j )
    117 		{
    118 			float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
    119 			if( d < dist )
    120 			{
    121 				dist = d;
    122 				idx = j;
    123 			}
    124 		}
    125 		
    126 		// save the index
    127 		closest[i] = ( u8 )idx;
    128 		
    129 		// accumulate the error
    130 		error += dist;
    131 	}
    132 	
    133 	// save this scheme if it wins
    134 	if( error < m_besterror )
    135 	{
    136 		// remap the indices
    137 		u8 indices[16];
    138 		m_colours->RemapIndices( closest, indices );
    139 		
    140 		// save the block
    141 		WriteColourBlock3( m_start, m_end, indices, block );
    142 		
    143 		// save the error
    144 		m_besterror = error;
    145 	}
    146 }
    147 
    148 void RangeFit::Compress4( void* block )
    149 {
    150 	// cache some values
    151 	int const count = m_colours->GetCount();
    152 	Vec3 const* values = m_colours->GetPoints();
    153 	
    154 	// create a codebook
    155 	Vec3 codes[4];
    156 	codes[0] = m_start;
    157 	codes[1] = m_end;
    158 	codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end;
    159 	codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end;
    160 
    161 	// match each point to the closest code
    162 	u8 closest[16];
    163 	float error = 0.0f;
    164 	for( int i = 0; i < count; ++i )
    165 	{
    166 		// find the closest code
    167 		float dist = FLT_MAX;
    168 		int idx = 0;
    169 		for( int j = 0; j < 4; ++j )
    170 		{
    171 			float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
    172 			if( d < dist )
    173 			{
    174 				dist = d;
    175 				idx = j;
    176 			}
    177 		}
    178 		
    179 		// save the index
    180 		closest[i] = ( u8 )idx;
    181 		
    182 		// accumulate the error
    183 		error += dist;
    184 	}
    185 	
    186 	// save this scheme if it wins
    187 	if( error < m_besterror )
    188 	{
    189 		// remap the indices
    190 		u8 indices[16];
    191 		m_colours->RemapIndices( closest, indices );
    192 		
    193 		// save the block
    194 		WriteColourBlock4( m_start, m_end, indices, block );
    195 
    196 		// save the error
    197 		m_besterror = error;
    198 	}
    199 }
    200 
    201 } // namespace squish