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 }