00001 /* 00002 File: Cluster.cc 00003 00004 Purpose: Implements volume clustering for wavelet radiosity 00005 00006 Author: Andrew Willmott 00007 00008 Notes: 00009 00010 */ 00011 00012 #include "Cluster.h" 00013 #include "RadMethod.h" 00014 #include "Samples.h" 00015 00016 #include "gcl/VecUtil.h" 00017 #include "gcl/Draw.h" 00018 00019 const Int kMinClusterElems = 32; 00020 const Int kMaxClusterLevels = 16; 00021 00022 // Cluster #defines 00023 #define RAD_NON_ISO_SOURCE 00024 #define RAD_NON_ISO_DEST 00025 // the above should always be defined, except for experiments showing how 00026 // bad the isotropic assumption is =) 00027 #define RAD_SAMPLE_ERROR 00028 // find error by sampling 00029 #define RAD_PTB 00030 // calculate intra-cluster visibility; don't include inter-cluster objects. 00031 00032 #define DBG_COUT if (0) cerr 00033 00034 Cluster::Cluster() : HRElem(), elems() 00035 { 00036 Int i; 00037 00038 centre = vl_0; 00039 00040 for (i = 0; i < 8; i++) 00041 child[i] = 0; 00042 00043 flags.Set(hrCluster); 00044 } 00045 00046 Cluster::~Cluster() 00047 { 00048 Int i; 00049 00050 DBG_COUT << "deleting cluster" << endl; 00051 00052 for (i = 0; i < 8; i++) 00053 delete child[i]; 00054 } 00055 00056 Void Cluster::Reset() 00057 { 00058 Int i; 00059 00060 HRElem::Reset(); 00061 for (i = 0; i < 8; i++) 00062 if (child[i]) 00063 child[i]->Reset(); 00064 } 00065 00066 Void Cluster::CreateBounds() 00067 { 00068 Int i; 00069 00070 min = max = elems[0]->EltCentre(); 00071 for (i = 0; i < elems.NumItems(); i++) 00072 elems[i]->EltUpdateBounds(min, max); 00073 00074 centre = (min + max) / 2.0; 00075 } 00076 00077 Void Cluster::SetElems(HRElemList *elemsIn) 00078 { 00079 elems = *elemsIn; 00080 CreateBounds(); 00081 00082 clusterVol = BoxVol(min, max); 00083 level = 0; 00084 #ifdef RAD_VIS 00085 eltParent = 0; 00086 #endif 00087 } 00088 00089 Void Cluster::PushElems() 00090 { 00091 Vector d1, d2; 00092 Int i, j; 00093 Point emin, emax, eCentroid; 00094 HRElemList newElems; 00095 00096 for (j = 0; j < elems.NumItems(); j++) 00097 { 00098 emin = emax = elems[j]->EltCentre(); 00099 elems[j]->EltUpdateBounds(emin, emax); 00100 d1 = emax - emin; 00101 d2 = (max - min) / 2.0; 00102 00103 DBG_COUT << "size check: " << d1 << " < " << d2 << endl; 00104 00105 // if the element is bigger in size than an octant, add it to this 00106 // level of the cluster hierarchy. 00107 if (d1[0] > d2[0] || d1[1] > d2[1] || d1[2] > d2[2]) 00108 { 00109 newElems.Append(elems[j]); 00110 continue; 00111 } 00112 00113 // find the octant the element's centroid belongs to... 00114 eCentroid = (emin + emax) / 2.0; 00115 d1 = eCentroid - centre; 00116 if (sqrlen(d1) < 1e-8) 00117 { 00118 // if it's close to the centre, add it to this level after all. 00119 newElems.Append(elems[j]); 00120 continue; 00121 } 00122 00123 i = 0; 00124 if (d1[0] >= 0.0) i += 1; 00125 if (d1[1] >= 0.0) i += 2; 00126 if (d1[2] >= 0.0) i += 4; 00127 00128 // add it to the given octant 00129 if (child[i] == 0) 00130 child[i] = new Cluster; 00131 child[i]->elems.Append(elems[j]); 00132 } 00133 00134 for (i = 0; i < 8; i++) 00135 if (child[i] && child[i]->elems.NumItems() <= 1) 00136 { 00137 DBG_COUT << "REDACTING cluster with only 1 item" << endl; 00138 newElems.Append(child[i]->elems[0]); 00139 delete child[i]; 00140 child[i] = 0; 00141 } 00142 00143 elems.Replace(newElems); 00144 } 00145 00146 Cluster *Cluster::CreateHierarchy() 00147 { 00148 Int i, n = 0, lightElems; 00149 Cluster *lastChild = 0, *curChild; 00150 00151 E = vl_0; 00152 clusterArea = 0.0; 00153 maxRho = cBlack; 00154 clusterVol = BoxVol(min, max); 00155 flags.UnSet(hrMixed); 00156 00157 if (elems.NumItems() >= kMinClusterElems && level < kMaxClusterLevels) 00158 { 00159 PushElems(); 00160 00161 for (i = 0; i < 8; i++) 00162 if (child[i]) 00163 { 00164 00165 curChild = child[i]; 00166 00167 child[i]->CreateBounds(); 00168 if (sqrlen(child[i]->max - child[i]->min) < 1e-15) 00169 cerr << "WARNING: zero-volume cluster" << endl; 00170 child[i]->level = level + 1; 00171 #ifdef RAD_VIS 00172 child[i]->eltParent = this; 00173 #endif 00174 child[i] = child[i]->CreateHierarchy(); 00175 if (child[i] != curChild) 00176 delete curChild; 00177 00178 if (child[i]) 00179 { 00180 E += child[i]->E * child[i]->clusterArea; // sum power 00181 clusterArea += child[i]->clusterArea; 00182 FindMaxCmpts(child[i]->EltRho(), maxRho, maxRho); 00183 flags.Set(child[i]->flags & hrMixed); 00184 lastChild = child[i]; 00185 n++; 00186 } 00187 } 00188 } 00189 00190 // if there are no elements at this level, and we only have one child, 00191 // we can replace this node with its child. 00192 if (elems.NumItems() == 0 && n <= 1) 00193 { 00194 DBG_COUT << "REDACTING: cluster node with one child cluster" << endl; 00195 return(lastChild); 00196 } 00197 00198 // Add in emitted power of child elements at this level 00199 00200 lightElems = 0; 00201 for (i = 0; i < elems.NumItems(); i++) 00202 { 00203 if (len(elems[i]->EltE()) > 0.0) 00204 { 00205 E += elems[i]->EltE() * elems[i]->EltArea(); 00206 // NOTE -- E currently unused. Should only be needed at 00207 // leaves. 00208 lightElems++; 00209 } 00210 clusterArea += elems[i]->EltArea(); 00211 FindMaxCmpts(elems[i]->EltRho(), maxRho, maxRho); 00212 #ifdef RAD_VIS 00213 elems[i]->eltParent = this; 00214 #endif 00215 } 00216 00217 if (clusterArea < 1e-15) 00218 { 00219 cerr << "REDACTING: cluster with zero surface area" << endl; 00220 return(0); 00221 } 00222 00223 if (lightElems > 0) 00224 flags.Set(hrHasLights); 00225 if (lightElems < elems.NumItems()) 00226 flags.Set(hrHasNonLights); 00227 00228 // divide by area to give equivalent point src... 00229 if (clusterArea > 0.0) 00230 E /= clusterArea; 00231 else 00232 E = vl_0; 00233 00234 DBG_COUT << "--- Cluster at level " << level << " has E = " << 00235 E << ", " << elems.NumItems() << " elems, " << 00236 n << " sub-clusters." << endl; 00237 00238 return(this); 00239 } 00240 00241 Void Cluster::Print(ostream &s) 00242 { 00243 HRLinkIter i; 00244 00245 s << "level " << level << " cluster: " << endl; 00246 s << elems.NumItems() << " patch children" << endl; 00247 s << "E = " << E << " B = " << B << endl; 00248 s << "links: "; 00249 for (i.Begin(links); !i.AtEnd(); i.Inc()) 00250 s << i.Data(); 00251 00252 s << endl; 00253 } 00254 00255 Void Cluster::DrawNodeElem(Renderer &r) 00256 { 00257 Int i; 00258 00259 DrawLeafElem(r); 00260 for (i = 0; i < 8; i++) 00261 if (child[i]) 00262 child[i]->DrawElem(r); 00263 } 00264 00265 Void Cluster::DrawLeafElem(Renderer &r) 00266 { 00267 #ifdef RAD_VIS 00268 if (eltHighlight != 0) 00269 { 00270 if (eltHighlight == 1) 00271 r.C(Colour4(cRed, 0.3)); 00272 else if (eltHighlight == 2) 00273 r.C(Colour4(cYellow, 0.3)); 00274 else if (eltHighlight == 3) 00275 r.C(Colour4(cGreen, 0.3)); 00276 PaintBox(r, min, max); 00277 00278 r.C(HSVCol(hsvYellow + level * 60, 1, 1)); 00279 FrameBox(r, min, max); 00280 } 00281 else if (gRadControl->outlineClusters) 00282 { 00283 r.C(HSVCol(hsvYellow + level * 60, 1, 1)); 00284 FrameBox(r, min, max); 00285 00286 DrawPatches(r); 00287 } 00288 #endif 00289 } 00290 00291 Void Cluster::DrawPatches(Renderer &r) 00292 { 00293 Int i; 00294 00295 for (i = 0; i < elems.NumItems(); i++) 00296 elems[i]->DrawLeafElem(r); 00297 00298 for (i = 0; i < 8; i++) 00299 if (child[i]) 00300 child[i]->DrawPatches(r); 00301 } 00302 00303 // --- Radiosity transfer ----------------------------------------------------- 00304 00305 Colour Cluster::lastB; 00306 00307 Void Cluster::Add() 00308 { 00309 DBG_COUT << "Cluster add: " << B; 00310 B += R; 00311 DBG_COUT << " -> " << B << endl; 00312 } 00313 00314 Void Cluster::Push() 00315 { 00316 Int i; 00317 00318 for (i = 0; i < 8; i++) 00319 if (child[i]) 00320 child[i]->B = B; 00321 00322 #ifdef RAD_NON_ISO_DEST 00323 for (i = 0; i < elems.NumItems(); i++) 00324 elems[i]->ClearB(); 00325 #else 00326 for (i = 0; i < elems.NumItems(); i++) 00327 elems[i]->B_Coeffs()[0] = B; 00328 #endif 00329 } 00330 00331 Void Cluster::Pull() 00332 { 00333 Int i; 00334 00335 B = vl_0; 00336 for (i = 0; i < 8; i++) 00337 if (child[i]) 00338 B += child[i]->EltBA(); 00339 00340 Assert(vl_is_finite(sqrlen(B)), "bad cluster power"); 00341 00342 for (i = 0; i < elems.NumItems(); i++) 00343 { 00344 HRElem *fuckYouGDB = elems[i]; 00345 B += elems[i]->EltBA(); 00346 Assert(vl_is_finite(sqrlen(B)), "bad cluster power"); 00347 } 00348 00349 Assert(vl_is_finite(sqrlen(B)), "bad cluster power"); 00350 00351 Expect(vl_is_finite(clusterArea) && (clusterArea > 0), "trivial cluster"); 00352 if (clusterArea > 0.0) 00353 B /= clusterArea; 00354 00355 Assert(vl_is_finite(sqrlen(B)), "bad cluster B"); 00356 } 00357 00358 Void Cluster::ClearB() 00359 { 00360 lastB = B; 00361 B = vl_0; 00362 } 00363 00364 Void Cluster::CalcLeafRadiosity() 00365 { 00366 _Error("Cluster can't be a leaf"); 00367 // this might change when/if we handle volume-based radiosity 00368 } 00369 00370 Void Cluster::InitRad() 00371 { 00372 Int i; 00373 00374 for (i = 0; i < elems.NumItems(); i++) 00375 elems[i]->InitRad(); 00376 00377 for (i = 0; i < 8; i++) 00378 if (child[i]) 00379 child[i]->InitRad(); 00380 00381 Pull(); 00382 } 00383 00384 GCLReal Cluster::Error() 00385 { 00386 return(dot(kRadRGBToLum, B - lastB)); 00387 } 00388 00389 Void Cluster::ClearR() 00390 { 00391 R = vl_0; 00392 } 00393 00394 // --- Link handling ---------------------------------------------------------- 00395 00396 00397 Void Cluster::MakeChildLinks(HRElem *other, HRLink *link, 00398 Int which, Int levels) 00399 // Splits a link into sublinks that point to the children of 00400 // either 'from' or 'to'. 00401 { 00402 Int i; 00403 00404 if (which == kSubTo) 00405 { 00406 // add to cluster children 00407 for (i = 0; i < 8; i++) 00408 if (child[i]) 00409 child[i]->RefineLink( 00410 link->CreateSubLink(other, child[i]), 00411 levels); 00412 // add to patch children 00413 for (i = 0; i < elems.NumItems(); i++) 00414 elems[i]->RefineLink( 00415 link->CreateSubLink(other, elems[i]), 00416 levels); 00417 } 00418 else if (which == kSubFrom) 00419 { 00420 // add links to cluster children 00421 for (i = 0; i < 8; i++) 00422 if (child[i]) 00423 other->RefineLink( 00424 link->CreateSubLink(child[i], other), 00425 levels); 00426 00427 // add to patch children 00428 for (i = 0; i < elems.NumItems(); i++) 00429 other->RefineLink( 00430 link->CreateSubLink(elems[i], other), 00431 levels); 00432 } 00433 else if (which == kSubBoth) 00434 { 00435 for (i = 0; i < 8; i++) 00436 if (child[i]) 00437 other->MakeChildLinks(child[i], link, kSubFrom, levels); 00438 for (i = 0; i < elems.NumItems(); i++) 00439 other->MakeChildLinks(elems[i], link, kSubFrom, levels); 00440 } 00441 else 00442 _Error("split type not implemented"); 00443 } 00444 00445 00446 // --- children abstraction --------------------------------------------------- 00447 00448 00449 Bool Cluster::IsLeaf() 00450 { 00451 return(false); 00452 } 00453 00454 Void Cluster::ApplyToChildren(Void (HRElem::*method)(Void*), Void *ref) 00455 { 00456 Int i; 00457 00458 for (i = 0; i < 8; i++) 00459 if (child[i]) 00460 (child[i]->*method)(ref); 00461 00462 for (i = 0; i < elems.NumItems(); i++) 00463 (elems[i]->*method)(ref); 00464 } 00465 00466 00467 // --- transport calculations ------------------------------------------------- 00468 00469 00470 Void Cluster::EltSampleTransport( 00471 Int numSamples, 00472 Point p[], 00473 Vector n[], 00474 Matd &coeffs 00475 ) 00476 { 00477 Int i; 00478 GCLReal result, rLen; 00479 Vector r; 00480 #ifdef RAD_NON_ISO_SOURCE 00481 Vector dir = vl_0; 00482 #endif 00483 00484 for (i = 0; i < numSamples; i++) 00485 { 00486 r = centre; 00487 r -= p[i]; 00488 00489 rLen = len(r); 00490 if (rLen > 0.0) 00491 r /= rLen; 00492 result = dot(n[i], r); 00493 00494 if (result <= 0.0) 00495 { 00496 coeffs[i][0] = 0.0; 00497 continue; 00498 } 00499 00500 result /= (sqr(rLen) * vl_pi); 00501 #ifdef RAD_NON_ISO_SOURCE 00502 // use the sample to weight our average direction normal... 00503 dir += r; 00504 #endif 00505 00506 coeffs[i][0] = result; 00507 Assert(vl_is_finite(coeffs[i][0]), "TS: bad transport coeff!!!"); 00508 } 00509 00510 #ifdef RAD_NON_ISO_SOURCE 00511 // now that we have an average direction normal, multiply 00512 // in projected area. 00513 rLen = len(dir); 00514 if (rLen > 0.0) 00515 result = EltProjArea(-dir) / rLen; 00516 else 00517 result = 0.0; 00518 00519 coeffs *= result; 00520 #else 00521 // if we're doing isotropic, the equivalent projected area 00522 // is the total projected area over 4. (We're assuming a sphere, 00523 // basically.) 00524 coeffs *= clusterArea / 4.0; 00525 #endif 00526 00527 DBG_COUT << "ClusEST: sampled coeffs = " << coeffs << endl; 00528 } 00529 00530 const Int kNumCubeSamples = 16; 00531 00532 static Int gRotPick = 0; 00533 00534 Void Cluster::EltGetSamples(Int numSamples, Point p[]) 00535 { 00536 Vector d, *offsets = (Vector*) tCubeSamples; 00537 Int i; 00538 00539 d = max - min; 00540 00541 for (i = 0; i < numSamples; i++) 00542 p[i] = min + offsets[i] * d; 00543 } 00544 00545 GCLReal Cluster::EltCalcTransport(HRElem *from, Matd &coeffs) 00546 { 00547 Vector r; 00548 GCLReal rLen, error; 00549 00550 if (from == this) 00551 { 00552 coeffs[0][0] = 1.0; 00553 error = 1.0; 00554 } 00555 else 00556 { 00557 r = from->EltCentre(); 00558 r -= centre; 00559 rLen = len(r); 00560 if (rLen > 0.0) 00561 r /= rLen; 00562 00563 #ifndef RAD_NON_ISO_DEST 00564 // Assume isotropic, and thus one quarter of total surface 00565 // area facing the source. 00566 r *= 0.25; 00567 #endif 00568 00569 #ifndef RAD_SAMPLE_ERROR 00570 from->EltSampleTransport(1, ¢re, &r, coeffs); 00571 error = coeffs[0][0]; 00572 #else 00573 Point p[kNumCubeSamples]; 00574 Vector n[kNumCubeSamples]; 00575 static Matd trans; 00576 Int i, samples; 00577 00578 EltGetSamples(kNumCubeSamples, p); 00579 for (i = 0; i < kNumCubeSamples; i++) 00580 n[i] = r; 00581 00582 samples = kNumCubeSamples; 00583 trans.SetSize(samples, from->NumCoeffs()); 00584 00585 // sample transport factors to our set of sample points 00586 from->EltSampleTransport(samples, p, n, trans); 00587 00588 error = VecError(col(trans, 0)); 00589 DBG_COUT << "cluster sample error: " << error << endl; 00590 00591 coeffs[0] = trans[0]; 00592 for (i = 1; i < trans.Rows(); i++) 00593 coeffs[0] += trans[i]; 00594 coeffs[0] /= trans.Rows(); 00595 Assert(vl_is_finite(coeffs[0][0]), "TC: bad transport coeff!!!"); 00596 #endif 00597 } 00598 00599 DBG_COUT << "ClusECT: coeffs = " << coeffs << endl; 00600 00601 return(error); 00602 } 00603 00604 // --- visibility estimation -------------------------------------------------- 00605 00606 00607 Void Cluster::EltSetVisPoints(HRElem *to, Point *pts) 00608 { 00609 Int i; 00610 Point dstPoint[16]; 00611 Vector d, r; 00612 Vector *samples; 00613 00614 EltGetSamples(16, pts); 00615 00616 #ifdef RAD_PTB 00617 // proj samples to sides of volume facing the source/dest 00618 if (to && to != this) 00619 { 00620 r = to->EltCentre() - centre; 00621 for (i = 0; i < 16; i++) 00622 pts[i] += ProjectToBox(min, max, pts[i], r) * r; 00623 } 00624 #endif 00625 00626 if (gRadControl->jitterRot) 00627 if (++gRotPick == kNumSampArrays) 00628 gRotPick = 0; 00629 } 00630 00631 Void Cluster::AddIrradiance(const Colour &srcR, const Vector &r) 00632 { 00633 Int i; 00634 00635 for (i = 0; i < elems.NumItems(); i++) 00636 elems[i]->AddIrradiance(srcR, r); 00637 00638 for (i = 0; i < 8; i++) 00639 if (child[i]) 00640 child[i]->AddIrradiance(srcR, r); 00641 } 00642 00643 Colour Cluster::GetPower(const Vector &m) 00644 { 00645 return(B * EltProjArea(m)); 00646 } 00647 00648 GCLReal Cluster::EltProjArea(const Vector &n) 00650 { 00651 #ifdef RAD_NON_ISO_DEST 00652 Int i; 00653 GCLReal eltArea = 0.0; 00654 00655 for (i = 0; i < elems.NumItems(); i++) 00656 { 00657 eltArea += elems[i]->EltProjArea(n); 00658 Assert(vl_is_finite(eltArea), "FPA: proj area is NAN"); 00659 } 00660 00661 for (i = 0; i < 8; i++) 00662 { 00663 if (child[i]) 00664 eltArea += child[i]->EltProjArea(n); 00665 00666 Assert(vl_is_finite(eltArea), "FPA: proj area is NAN"); 00667 } 00668 00669 #ifdef DEBUG 00670 if (elems.NumItems() > 0 && elems[0]->IsFaceClus()) 00671 { 00672 DBG_COUT << "min eltArea " << n << " = " << eltArea << endl; 00673 } 00674 #endif 00675 00676 return(eltArea); 00677 #else 00678 return(0.25 * clusterArea) 00679 #endif 00680 } 00681 00682 GCLReal Cluster::EltMaxProjArea(const Vector &n) 00684 { 00685 Int i; 00686 GCLReal eltArea = 0.0; 00687 00688 for (i = 0; i < elems.NumItems(); i++) 00689 { 00690 eltArea += elems[i]->EltMaxProjArea(n); 00691 Assert(vl_is_finite(eltArea), "FPA: proj area is NAN"); 00692 } 00693 00694 for (i = 0; i < 8; i++) 00695 { 00696 if (child[i]) 00697 eltArea += child[i]->EltMaxProjArea(n); 00698 00699 Assert(vl_is_finite(eltArea), "FPA: proj area is NAN"); 00700 } 00701 00702 #ifdef DEBUG 00703 if (elems.NumItems() > 0 && elems[0]->IsFaceClus()) 00704 { 00705 DBG_COUT << "max eltArea " << n << " = " << eltArea << endl; 00706 DBG_COUT << "rho = " << EltRho() << endl; 00707 } 00708 #endif 00709 00710 return(eltArea); 00711 } 00712 00713 Void Cluster::DistributeColours() 00714 { 00715 Int i; 00716 00717 for (i = 0; i < elems.NumItems(); i++) 00718 elems[i]->DistributeColours(); 00719 00720 for (i = 0; i < 8; i++) 00721 if (child[i]) 00722 child[i]->DistributeColours(); 00723 } 00724 00725 Void Cluster::DistributeColoursBest(ShadeInfo &shadeInfo) 00726 { 00727 Int i; 00728 00729 if (!links.IsEmpty()) 00730 shadeInfo.linkStack.Push(&links); 00731 00732 for (i = 0; i < elems.NumItems(); i++) 00733 elems[i]->DistributeColoursBest(shadeInfo); 00734 00735 for (i = 0; i < 8; i++) 00736 if (child[i]) 00737 child[i]->DistributeColoursBest(shadeInfo); 00738 00739 if (!links.IsEmpty()) 00740 shadeInfo.linkStack.Pop(); 00741 }