00001
00004 #include <ctype.h>
00005 #include "SPalette.h"
00006 #define USE_ERRORMAT // use difference matrix for error minimize.
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #define SQRT_31 5.5677643628300219221194712989185
00019
00020 namespace coool
00021 {
00022 using namespace coool;
00023
00024 SPalette::SPalette() :
00025 m_UseV7(false), m_WSmoothness(0.0f), m_WErrorMin(0.0f),
00026 m_CXF31toRGB(3,31,vuColourRGBa::getCXF31toRGB()),
00027 m_CXF7toRGB(3,7,vuColourRGBa::getCXF7toRGB()),
00028 m_CXF31to7(7,31,vuColour7a::getCXF31to7())
00029 {
00030
00031 SSpectrum tmp(vuColour31a(1.0f));
00032 m_Lights.add(tmp);
00033 m_Refls.add(tmp);
00034 }
00035
00036 SPalette::~SPalette()
00037 {
00038
00039 }
00040
00041 void SPalette::reset()
00042 {
00043 m_Lights.removeRange(0,m_Lights.getLength());
00044 m_Refls.removeRange(0,m_Refls.getLength());
00045 }
00046
00047 int SPalette::addLight(const vuColour31a& light)
00048 {
00049 dword index = m_Lights.getLength();
00050 SSpectrum tmp(light,index);
00051 m_Lights.add(tmp);
00052
00053 SDesignColour dcol;
00054 dword nrefl = m_Refls.getLength();
00055 for(dword i=0;i<nrefl;i++)
00056 m_DesCol[i][index] = dcol;
00057 return (int)index;
00058 }
00059
00060 void SPalette::removeLight(word index)
00061 {
00062 m_Lights.remove(index);
00063 dword nrefl = m_Refls.getLength();
00064 for(dword i=0;i<nrefl;i++)
00065 m_DesCol[i].remove(index);
00066 }
00067
00068 int SPalette::addReflectance(const vuColour31a& refl)
00069 {
00070 dword index = m_Refls.getLength();
00071 SSpectrum tmp(refl,index);
00072 m_Refls.add(tmp);
00073
00074 SDesignColour dcol;
00075 int nlights = (int)m_Lights.getLength();
00076 for(int i=nlights-1;i>=0;i--)
00077 {
00078 m_DesCol[index][i] = dcol;
00079 }
00080 return (int)index;
00081 }
00082
00083 void SPalette::removeReflectance(word index)
00084 {
00085 m_Refls.remove(index);
00086 m_DesCol.remove(index);
00087 }
00088
00090 vuColourRGBa SPalette::getRLColour(dword rid, dword lid, bool useV7)
00091 {
00092 if(rid<m_Refls.getLength() && lid<m_Lights.getLength())
00093 {
00094 if(useV7)
00095 return vuColourRGBa(vuColour7a(m_Lights[lid].spec)*
00096 vuColour7a(m_Refls[rid].spec)
00097 ).clampTo01();
00098 else
00099 return vuColourRGBa(m_Lights[lid].spec*m_Refls[rid].spec
00100 ).clampTo01();
00101 }
00102 return vuColourRGBa(0.0f);
00103 }
00104
00105 void SPalette::setDesignRGBW(dword rid, dword lid, const vuColourRGBa& col)
00106 {
00107 if(rid<m_Refls.getLength() && lid<m_Lights.getLength())
00108 {
00109 m_DesCol[rid][lid].rgbw = col;
00110 m_DesCol[rid][lid].design = true;
00111 }
00112 }
00113
00115 vuColourRGBa& SPalette::getDesignRGBW(dword rid, dword lid)
00116 {
00117 if(rid<m_Refls.getLength() && lid<m_Lights.getLength())
00118 return m_DesCol[rid][lid].rgbw;
00119 else throw "vuSpecPalette::getDesignRGBW: Out of bounds!";
00120 }
00121
00122 void SPalette::attachFreeColour(dword rid, dword lid, int fcid)
00123 {
00124 if(rid<m_Refls.getLength() && lid<m_Lights.getLength()) {
00125 if(fcid >= (int)m_FreeCol.getLength())
00126 setFreeColour(fcid);
00127 m_DesCol[rid][lid].freecolour = fcid;
00128 };
00129 }
00130
00131 void SPalette::setFreeColour(dword fcid,
00132 const vuColourRGBa& lb, const vuColourRGBa& ub)
00133 {
00134 SFreeColour tmp(fcid,lb,ub);
00135 m_FreeCol[fcid] = tmp;
00136 }
00137
00139 void SPalette::matchDesignColours()
00140 {
00141 for(dword r=0;r<getNRefls();r++)
00142 for(dword l=0;l<getNLights();l++)
00143 {
00144 vuColourRGBa rgbw(getRLColour(r,l,m_UseV7));
00145 rgbw[3] = 1;
00146 setDesignRGBW(r,l,rgbw);
00147 }
00148 }
00149
00151 bool SPalette::load(const char* filename)
00152 {
00153 ifstream is(filename);
00154 if(is.good()) {
00155 reset();
00156 try{
00157 while(!is.eof())
00158 {
00159 char rl='x';
00160 is>>ws;
00161 is>>rl;
00162 switch(tolower(rl)) {
00163 case 'r':
00164 {
00165 int id = addReflectance(vuColour31a());
00166 is>>m_Refls[id];
00167 } break;
00168 case 'l':
00169 {
00170 int id = addLight(vuColour31a());
00171 is>>m_Lights[id];
00172 } break;
00173 case 'x':
00174
00175 break;
00176 default:
00177 cout << "Error in palette definition" << endl;
00178 cout << "char: " << rl << endl;
00179 return false;
00180 }
00181 }
00182 } catch (const char* msg) {
00183 cout << "Error loading palette: " << msg <<endl;
00184 return false;
00185 }
00186 } else return false;
00187 return true;
00188 }
00189
00191 bool SPalette::save(const char* filename)
00192 {
00193 ofstream os(filename);
00194 if(os.good()) {
00195 try{
00196 for(int id=0;id<(int)m_Refls.getLength();id++)
00197 os << 'r' << m_Refls[id] << endl;
00198 for(int id=0;id<(int)m_Lights.getLength();id++)
00199 os << 'l' << m_Lights[id] << endl;
00200 } catch (const char* msg) {
00201 cout << "Error saving palette: " << msg <<endl;
00202 return false;
00203 }
00204 } else return false;
00205 return true;
00206 }
00209 bool SPalette::loadSpectrum(int rid, int lid, const char* filename)
00210 {
00211 if(rid<(int)m_Refls.getLength() && lid<(int)m_Lights.getLength())
00212 {
00213 ifstream is(filename);
00214 if(is.good()) {
00215 SSpectrum &specrec = getSpecRec(rid, lid);
00216 try{
00217 is>>specrec;
00218 } catch (const char* msg) {
00219 cerr << "Error loading spectrum: " << msg <<endl;
00220 return false;
00221 }
00222 } else return false;
00223 } else return false;
00224 return true;
00225 }
00226
00229 bool SPalette::saveSpectrum(int rid, int lid, const char* filename)
00230 {
00231 if(rid<(int)m_Refls.getLength() && lid<(int)m_Lights.getLength())
00232 {
00233 ofstream os(filename);
00234 if(os.good()) {
00235 SSpectrum &specrec = getSpecRec(rid, lid);
00236 try{
00237 os<<specrec;
00238 } catch (const char* msg) {
00239 cout << "Error saving spectrum: " << msg <<endl;
00240 return false;
00241 }
00242 } else return false;
00243 } else return false;
00244 return true;
00245 }
00246
00247
00250 bool SPalette::checkSetup()
00251 {
00252 for(dword c=0;c<m_FreeCol.getLength();c++)
00253 m_FreeCol[c].used=0;
00254 for(dword c=0;c<m_Lights.getLength();c++)
00255 m_Lights[c].used=0;
00256 for(dword c=0;c<m_Refls.getLength();c++)
00257 m_Refls[c].used=0;
00258
00259 m_NDesCol=0;
00260 for(dword r=0;r<m_Refls.getLength();r++)
00261 {
00262 for(dword l=0;l<m_Lights.getLength();l++)
00263 {
00264 if(m_DesCol[r][l].getWeight()>0 && m_DesCol[r][l].design)
00265 {
00266
00267
00268 if(m_Lights[l].design && m_Refls[r].design)
00269 return false;
00270 else if(m_Lights[l].design || m_Refls[r].design)
00271 {
00272 m_NDesCol++;
00273 if(m_DesCol[r][l].freecolour>=0)
00274 m_FreeCol[m_DesCol[r][l].freecolour].used++;
00275 if(m_Lights[l].design)
00276 m_Lights[l].used++;
00277 else
00278 m_Refls[r].used++;
00279
00280 }
00281 } else m_DesCol[r][l].design=false;
00282 }
00283 }
00284 if(m_WSmoothness == 0) m_UseSmoothness = false;
00285 if(m_WErrorMin == 0) m_UseErrorMin = false;
00286
00287 return true;
00288 }
00289
00291 void SPalette::setSmoothnessWeight(float weight) {
00292 if(weight <= 0.0) {
00293 m_WSmoothness = 0.0f;
00294 m_UseSmoothness = false;
00295 } else {
00296 m_WSmoothness = weight;
00297 m_UseSmoothness = true;
00298 }
00299 };
00300
00302 void SPalette::setErrorMinWeight(float weight) {
00303 if(weight <= 0.0) {
00304 m_WErrorMin = 0;
00305 m_UseErrorMin = false;
00306 } else {
00307 m_UseErrorMin = true;
00308 m_WErrorMin = weight;
00309 }
00310 };
00311
00313 SMatrix SPalette::makeErrorMat(const vuColour31a& diag)
00314 {
00315 SMatrix res(3,31);
00316 SMatrix dmat31(31,31);
00317 dmat31.makeDiag(SVector(31,diag.getData()));
00318 res = m_CXF31toRGB*dmat31;
00319 SMatrix dmat7(7,7);
00320
00321 dmat7.makeDiag(m_CXF31to7*SVector(31,diag.getData()));
00322
00323
00324 res -= (m_CXF7toRGB*dmat7*m_CXF31to7);
00325 res *= m_WErrorMin;
00326 return res;
00327 }
00328
00330 SMatrix SPalette::makeSmoothingMat()
00331 {
00332 SMatrix res(31,31);
00333
00334
00335
00336
00337
00338 static float vd2data[] = {2,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
00339 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
00340 SVector vd2(31,vd2data);
00341 vd2 *= 1/vd2.norm()/SQRT_31;
00342 res.makeToeplitz(vd2,true);
00343 res *= m_WSmoothness;
00344 return res;
00345 }
00346
00351 SMatrix SPalette::makeCXFMat(const vuColour31a& diag, bool useV7)
00352 {
00353 if(useV7) {
00354 SMatrix dmat7(7,7);
00355 dmat7.makeDiag(m_CXF31to7*SVector(diag));
00356 return m_CXF7toRGB*dmat7*m_CXF31to7;
00357 } else {
00358 SMatrix dmat31(31,31);
00359 dmat31.makeDiag(SVector(diag));
00360 return m_CXF31toRGB*dmat31;
00361 }
00362 }
00363
00367 bool SPalette::createSpectrum()
00368 {
00369 try{
00370
00371 if(!checkSetup()) return false;
00372
00373
00374 dword posfc=0;
00375 for(dword c=0;c<m_FreeCol.getLength();c++)
00376 if(m_FreeCol[c].used) m_FreeCol[c].position = (posfc++);
00377 dword ndesl=0;
00378 for(dword c=0;c<m_Lights.getLength();c++)
00379 if(m_Lights[c].used) ndesl++;
00380 dword ndesr=0;
00381 for(dword c=0;c<m_Refls.getLength();c++)
00382 if(m_Refls[c].used) ndesr++;
00383
00384 if(ndesr+ndesl == 0) return false;
00385
00386
00387 dword nrows = ( 3*m_NDesCol + (m_UseErrorMin?3:0)*m_NDesCol +
00388 (m_UseSmoothness?31:0)*(ndesr+ndesl));
00389 dword ncols = 3*posfc + 31*ndesl + 31*ndesr;
00390 SMatrix M(nrows,ncols,0.0f);
00391 SVector y(nrows,0.0f);
00392
00393
00394 SVector lb(ncols,-SPAL_RANGE),ub(ncols,SPAL_RANGE);
00395 bool useBounds = false;
00396 posfc=0;
00397 for(dword c=0;c<m_FreeCol.getLength();c++)
00398 if(m_FreeCol[c].used) {
00399 useBounds=true;
00400 ub.insert(SVector(3,m_FreeCol[c].ub.getData()), 3*posfc);
00401 lb.insert(SVector(3,m_FreeCol[c].lb.getData()), 3*posfc);
00402 posfc++;
00403 }
00404
00405
00406 SMatrix id33(3,3);
00407 id33.makeIdentity();
00408 dword curcol=3*posfc;
00409 dword currow=0;
00410
00411 for(dword l=0;l<m_Lights.getLength();l++)
00412 if(m_Lights[l].used)
00413 {
00414 SSpectrum &speclig = m_Lights[l];
00415 if(speclig.useBounds) {
00416 useBounds=true;
00417 ub.insert(SVector(speclig.ub),curcol);
00418 lb.insert(SVector(speclig.lb),curcol);
00419 }
00420 for(dword r=0;r<m_Refls.getLength();r++)
00421 if(m_DesCol[r][l].design)
00422 {
00423 float weight = m_DesCol[r][l].getWeight();
00424 SSpectrum &specrec = m_Refls[r];
00425 M.insert(weight * makeCXFMat(specrec.spec,m_UseV7),
00426 currow,curcol);
00427 if(m_DesCol[r][l].freecolour>=0)
00428 {
00429 SFreeColour& fc = m_FreeCol[m_DesCol[r][l].freecolour];
00430 M.insert(-weight*id33,currow,
00431 3*fc.position);
00432 } else
00433 y.insert(weight*m_DesCol[r][l].getCol(),currow);
00434
00435 currow+=3;
00436 if(m_UseErrorMin)
00437 {
00438 #ifdef USE_ERRORMAT
00439 M.insert(weight*makeErrorMat(specrec.spec),
00440 currow,curcol);
00441 #else
00442 M.insert((m_WErrorMin*weight) *
00443 makeCXFMat(specrec.spec,!m_UseV7),
00444 currow,curcol);
00445 y.insert((m_WErrorMin*weight)*
00446 m_DesCol[r][l].getCol(),currow);
00447 #endif // of USE_ERRORMAT
00448 currow+=3;
00449 }
00450 }
00451 if(m_UseSmoothness)
00452 {
00453 M.insert(makeSmoothingMat(),currow,curcol);
00454 currow+=31;
00455 }
00456 curcol+=31;
00457 }
00458
00459
00460 for(dword r=0;r<m_Refls.getLength();r++)
00461 if(m_Refls[r].used)
00462 {
00463 SSpectrum &specref = m_Refls[r];
00464 if(specref.useBounds) {
00465 useBounds=true;
00466 ub.insert(SVector(specref.ub),curcol);
00467 lb.insert(SVector(specref.lb),curcol);
00468 }
00469 for(dword l=0;l<m_Lights.getLength();l++)
00470 if(m_DesCol[r][l].design)
00471 {
00472 float weight = m_DesCol[r][l].getWeight();
00473 SSpectrum &specrec = m_Lights[l];
00474 M.insert(weight * makeCXFMat(specrec.spec,m_UseV7),
00475 currow,curcol);
00476 if(m_DesCol[r][l].freecolour>=0) {
00477 SFreeColour& fc = m_FreeCol[m_DesCol[r][l].freecolour];
00478 M.insert(-weight*id33,currow,
00479 3*fc.position);
00480 } else
00481 y.insert(weight*m_DesCol[r][l].getCol(),currow);
00482
00483 currow+=3;
00484 if(m_UseErrorMin)
00485 {
00486 #ifdef USE_ERRORMAT
00487 M.insert(weight*makeErrorMat(specrec.spec),
00488 currow, curcol);
00489 #else
00490 M.insert((m_WErrorMin*weight)*
00491 makeCXFMat(specrec.spec,!m_UseV7),
00492 currow, curcol);
00493 y.insert((m_WErrorMin*weight)*
00494 m_DesCol[r][l].getCol(),currow);
00495 #endif
00496 currow+=3;
00497 }
00498 }
00499 if(m_UseSmoothness)
00500 {
00501 M.insert(makeSmoothingMat(),currow,curcol);
00502 currow+=31;
00503 }
00504 curcol+=31;
00505 }
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516 SVector x;
00517 if(useBounds)
00518 x = m_Optimizer.minimize(M,y,lb,ub);
00519 else
00520 x = m_Optimizer.minimize(M,y);
00521
00522
00523 double *res = x.getData() + 3*posfc;
00524 for(dword l=0;l<m_Lights.getLength();l++)
00525 if(m_Lights[l].used)
00526 {
00527 m_Lights[l].spec = vuColour31a (res);
00528 res+=31;
00529 }
00530
00531 for(dword r=0;r<m_Refls.getLength();r++)
00532 if(m_Refls[r].used)
00533 {
00534 m_Refls[r].spec = vuColour31a (res);
00535 res+=31;
00536 }
00537 }catch (const char* msg)
00538 {
00539 cout << "SPalette::createSpectrum() EXCEPTION: " << msg <<endl;
00540 return false;
00541 }
00542
00543 return true;
00544 }
00545
00546 }
00547