00001 /*
00002 File: RadMesh.cc
00003
00004 Function: See header file
00005
00006 Author(s): Andrew Willmott
00007
00008 Copyright: (c) 1997-2000, Andrew Willmott
00009
00010 Notes: A RadElem can represent either a 4-sided parallelogram,
00011 or a 3-sided triangle.
00012
00013 */
00014
00015 #include "RadMesh.h"
00016 #include "RadMethod.h"
00017 #include "cl/String.h"
00018 #include "gcl/VecUtil.h"
00019 #include "Samples.h"
00020
00021 #ifdef RAD_VIS
00022 #include "gcl/Forms.h"
00023 #include "gcl/ScenePane.h"
00024 #endif
00025
00072 // --- PatchStats class -------------------------------------------------------
00073
00074 Void PatchStats::Init()
00075 {
00076 maxArea = 0.0;
00077 minArea = vl_inf;
00078 maxRefl = vl_0;
00079 minRefl.MakeBlock(vl_inf);
00080 maxRefl = vl_0;
00081 maxPower = vl_0;
00082 totalPower = vl_0;
00083 }
00084
00085 Void PatchStats::Update(RadElem *patch)
00086 {
00087 Colour powerClr;
00088
00089 FindMinCmpts(minRefl, patch->props->reflectance, minRefl);
00090 FindMaxCmpts(maxRefl, patch->props->reflectance, maxRefl);
00091 maxArea = Max(patch->area, maxArea);
00092 minArea = Min(patch->area, maxArea);
00093
00094 powerClr = patch->props->emittance * patch->area;
00095 totalPower += powerClr;
00096 FindMaxCmpts(maxPower, powerClr, maxPower);
00097 }
00098
00099 Void PatchStats::Report(ostream &s)
00100 {
00101 s << "Patch statistics" << endl;
00102 s << "----------------" << endl;
00103 s << "Area range: [" << minArea << ", " << maxArea << "]" << endl;
00104 s << "Reflectance range: [" << minRefl << ", " << maxRefl << "]" << endl;
00105 s << "Max. Power: " << maxPower << endl;
00106 s << "Total Power: " << totalPower << endl;
00107 s << endl;
00108 }
00109
00110 // --- RadElem class ----------------------------------------------------------
00111
00112
00113 Int RadElem::sGridMem = 0;
00114 Int RadElem::sGridChildMem = 0;
00115
00116 RadElem::RadElem() : props(0),
00117 #ifdef RAD_VIS
00118 highlight(0),
00119 #endif
00120 colour(cPurple)
00121 {
00122 }
00123
00124 RadElem::~RadElem()
00125 {
00126 }
00127
00128 NbRadElem::NbRadElem() : RadElem()
00129 {
00130 Int i;
00131
00132 for (i = 0; i < 4; i++)
00133 {
00134 nbFace[i] = 0;
00135 nbEdge[i] = 0xFF; // for debugging only
00136 }
00137 }
00138
00139 Void RadElem::DrawHighlight(Renderer &r)
00140 {
00141 #ifdef RAD_VIS
00142 if (highlight == 1)
00143 r.C(cRed);
00144 else if (highlight == 2)
00145 r.C(cYellow);
00146 else if (highlight == 3)
00147 r.C(cGreen);
00148
00149 r.Begin(renPoly);
00150 SendPoints(r);
00151 r.End();
00152 #endif
00153 }
00154
00155 Void RadElem::Draw(Renderer &r)
00156 {
00157 #ifdef RAD_VIS
00158 if (highlight)
00159 {
00160 DrawHighlight(r);
00161 if (!gRadControl->funcView)
00162 return;
00163 }
00164 #endif
00165
00166 if (gRadControl->wire)
00167 {
00168 if (gRadControl->redWire)
00169 r.Begin(renLineLoop).SetColour(0.5 * (cRed + colour));
00170 else
00171 r.Begin(renLineLoop).SetColour(Mix(cWhite, colour, 0.8));
00172
00173 SendPoints(r);
00174 r.End();
00175
00176 return;
00177 }
00178
00179 if (gRadControl->funcView)
00180 {
00181 r.Begin(renLineLoop).C(cWhite);
00182 SendPoints(r);
00183 r.End();
00184 }
00185
00186 r.Begin(renPoly);
00187
00188 if (!gRadControl->funcView)
00189 {
00190 if (props->texCoords && gRadControl->texture)
00191 {
00192 r.T(TexCoord(0)).C(VtxClr(0)).P(Vertex(0));
00193 r.T(TexCoord(1)).C(VtxClr(1)).P(Vertex(1));
00194 r.T(TexCoord(2)).C(VtxClr(2)).P(Vertex(2));
00195
00196 if (IsQuad())
00197 r.T(TexCoord(3)).C(VtxClr(3)).P(Vertex(3));
00198 }
00199 else if (gRadControl->gouraud)
00200 {
00201 r.C(VtxClr(0)).P(Vertex(0)).C(VtxClr(1)).P(Vertex(1))
00202 .C(VtxClr(2)).P(Vertex(2));
00203 if (IsQuad())
00204 r.C(VtxClr(3)).P(Vertex(3));
00205 }
00206 else
00207 {
00208 r.C(colour);
00209 SendPoints(r);
00210 }
00211 }
00212 else
00213 {
00214 RaiseVertex(r, 0);
00215 RaiseVertex(r, 1);
00216 RaiseVertex(r, 2);
00217 if (IsQuad())
00218 RaiseVertex(r, 3);
00219 }
00220
00221 r.End();
00222 }
00223
00224 RadElem *RadElem::FindContainer(Coord &coord)
00225 {
00226 return(this);
00227 }
00228
00229 Colour RadElem::SampleLeaf(Coord c)
00230 {
00231 if (gRadControl->gouraud)
00232 {
00233 if (IsTri())
00234 {
00235 Colour result;
00236 ClrReal s = c[0];
00237 ClrReal t = c[1];
00238 ClrReal u = 1 - s - t;
00239
00240 result = VtxClr(0) * c[1] + VtxClr(1) * u + VtxClr(2) * c[0];
00241 return(result);
00242 }
00243 else
00244 {
00245 Colour c1, c2, result;
00246 ClrReal s = c[0];
00247 ClrReal t = c[1];
00248
00249 c1 = (1 - s) * VtxClr(1) + s * VtxClr(2);
00250 c2 = (1 - s) * VtxClr(0) + s * VtxClr(3);
00251 result = (1 - t) * c1 + t * c2;
00252 return(result);
00253 }
00254 }
00255 else
00256 return(colour);
00257 }
00258
00259 Colour RadElem::Sample(Coord c)
00260 {
00261 RadElem *leaf = FindContainer(c);
00262
00263 if (leaf)
00264 return(leaf->SampleLeaf(c));
00265 else
00266 return(cBlack);
00267 }
00268
00269 Void RadElem::Compare(RadElem *to, GCLReal edgeLen, CompareStats &stats)
00270 {
00271 // Sample density is controlled by edgelen: sample every edgeLen
00272 // units in either dimension. Same deal as with patchSubdivs.
00273
00274 Colour err, c1, c2;
00275 Coord b,c;
00276 Colour pcSum, pcSqrSum, pRefSum;
00277 Int nx, ny, i, j;
00278 GCLReal xlen, ylen, weight;
00279
00280 // calculate # samples per axis
00281
00282 xlen = len(Vertex(2) - Vertex(1));
00283 ylen = len(Vertex(0) - Vertex(1));
00284
00285 nx = (Int) (edgeLen * xlen);
00286 ny = (Int) (edgeLen * ylen);
00287
00288 if (nx < 1) nx = 1;
00289 if (ny < 1) ny = 1;
00290
00291 pcSum = vl_0;
00292 pRefSum = vl_0;
00293 pcSqrSum = vl_0;
00294
00295 // Sample away!!!!
00296
00297 for (i = 0; i < nx; i++)
00298 for (j = 0; j < ny; j++)
00299 {
00300
00301 c[0] = (0.5 + i) / GCLReal(nx);
00302 c[1] = (0.5 + j) / GCLReal(ny);
00303 if (IsTri())
00304 ; // XXX need to resample, handle aniso for tri
00305
00306 c1 = Sample(c);
00307 c2 = to->Sample(c);
00308
00309 err = c1 - c2; // colour difference
00310 pcSum += err; // avg. c diff
00311 pcSqrSum += sqr(err); // c variance
00312 pRefSum += (c1 - Emittance()); // reference: c1 - E.
00313 }
00314
00315 weight = area / GCLReal(nx * ny);
00316 pcSum *= weight;
00317 pRefSum *= weight;
00318 pcSqrSum *= weight;
00319
00320 // update stats, weighted by area-per-sample.
00321
00322 stats.areaSum += area;
00323 stats.cSum += pcSum;
00324 stats.cSqrSum += pcSqrSum;
00325 stats.refSum += pRefSum;
00326 }
00327
00328 Void RadElem::SetHighlight(Int h)
00329 {
00330 #ifdef RAD_VIS
00331 highlight = h;
00332 #endif
00333 }
00334
00335 StrConst RadElem::Name()
00336 {
00337 return("rad");
00338 }
00339
00340 Void RadElem::Print(ostream &s)
00341 {
00342 s << index[0] << ' ' << index[1] << ' ' << index[2] << ' '
00343 << index[3] << ' ';
00344 s << clrIdx[0] << ' ' << clrIdx[1] << ' ' << clrIdx[2] << ' '
00345 << clrIdx[3] << ' ';
00346 s << colour << ' ';
00347 }
00348
00349 Void RadElem::Parse(istream &s)
00350 {
00351 s >> index[0] >> index[1] >> index[2] >> index[3];
00352 s >> clrIdx[0] >> clrIdx[1] >> clrIdx[2] >> clrIdx[3];
00353 s >> colour;
00354 }
00355
00356
00357 // --- Form factor routines ---------------------------------------------------
00358
00359
00360 GCLReal RadElem::EstPatchFactor(const Point &p, const Vector &np)
00361 // Elemental factor from this patch to p.
00362 // essentially cos theta * cos phi * Ap / (pi * r^2)
00363 {
00364 GCLReal result, rad4;
00365 Vector temp;
00366
00367 temp = p - Centre();
00368
00369 result = dot(Normal(), temp);
00370
00371 if (result <= 0)
00372 result = 0;
00373 else
00374 {
00375 result *= -dot(np, temp);
00376
00377 if (result <= 0)
00378 result = 0;
00379 else
00380 {
00381 rad4 = sqr(sqrlen(temp));
00382 result *= area;
00383 result /= (rad4 * vl_pi);
00384 }
00385 }
00386
00387 return(result);
00388 }
00389
00390
00391 GCLReal RadElem::EdgeArea(const Vector &p, const Vector &q, const Vector &n)
00392 {
00393 // Numerically stable edge-area calculator. (Including degenerate cases.)
00394 // Andrew J. Willmott, 1992
00395
00396 // 4 dots, 1 cross, 1 atan per edge.
00397
00398 const GCLReal epsilon = 1e-6;
00399 GCLReal sinFactor, qdotp, projArea;
00400
00401 qdotp = dot(p, q);
00402 sinFactor = sqrlen(p) * sqrlen(q) - sqr(qdotp);
00403
00404 projArea = dot(-cross(p, q), n);
00405
00406 if (sinFactor > 0)
00407 // If SinFactor > 0 we may apply the formula without problem
00408 {
00409 sinFactor = sqrt(sinFactor);
00410 return(projArea * (vl_halfPi - atan(qdotp / sinFactor)) /
00411 (2 * vl_pi * sinFactor));
00412 }
00413 // If not, we have three remaining cases...
00414 else if (qdotp > epsilon)
00415 // CASE 1: p and q point in the same direction }
00416 return(0);
00417 else if (qdotp < -epsilon)
00418 // CASE 2: Viewpoint lies on the edge }
00419 return(0.5);
00420 else
00421 // CASE 3: Viewpoint lies at either end of the edge }
00422 return(0.125);
00423 }
00424
00425 GCLReal RadElem::PatchFactor(const Point &p, const Vector &np)
00426 // Common terminology for this is the polygon-to-point form factor.
00427 // The 'form-factor' mentioned in radiosity papers is the average
00428 // patch factor across a patch.
00429 {
00430 Vector x1, x2, x3;
00431 GCLReal sum;
00432
00433 x1 = Vertex(0);
00434 x1 -= p;
00435 x3 = x1;
00436 x2 = Vertex(1);
00437 x2 -= p;
00438
00439 sum = EdgeArea(x1, x2, np);
00440 x1 = Vertex(2);
00441 x1 -= p;
00442 sum += EdgeArea(x2, x1, np);
00443
00444 if (IsTri())
00445 sum += EdgeArea(x1, x3, np);
00446 else
00447 {
00448 x2 = Vertex(3);
00449 x2 -= p;
00450 sum += EdgeArea(x1, x2, np);
00451 sum += EdgeArea(x2, x3, np);
00452 }
00453
00454 if (sum > 0.0)
00455 return(sum);
00456 else
00457 return(0.0);
00458 }
00459
00460 GCLReal RadElem::ApproxPatchFactor(const Point &p, const Vector &np)
00461 // Uses point-to-point PF as default, and swaps to poly-to-point PF
00462 // when necessary.
00463 {
00464 GCLReal result, rad4, npt, res2;
00465 Vector temp;
00466
00467 temp = p - Centre();
00468
00469 result = -dot(np, temp);
00470
00471 if (result <= 0)
00472 result = 0;
00473 else
00474 {
00475 npt = dot(Normal(), temp);
00476
00477 if (npt <= 0)
00478 result = 0;
00479 else
00480 {
00481 rad4 = sqr(sqrlen(temp));
00482 result *= area / vl_pi;
00483 res2 = result * Max(npt, 0.25 * sqrt(area));
00484
00485 if (gRadControl->quadLevel > 0 && rad4 * gRadControl->dFError < res2)
00486 {
00487 // result is blowing up!
00488 // let's bail to the patch factor formula
00489 return(PatchFactor(p, np));
00490 }
00491 else
00492 result *= npt / rad4;
00493 }
00494 }
00495
00496 return(result);
00497 }
00498
00499 GCLReal RadElem::EstFormFactor(RadElem *to)
00500 {
00501 GCLReal result, rad4, npt, res2;
00502 Vector temp;
00503
00504 temp = to->Centre() - Centre();
00505
00506 result = -dot(to->Normal(), temp);
00507
00508 if (result <= 0)
00509 result = 0;
00510 else
00511 {
00512 npt = dot(Normal(), temp);
00513
00514 if (npt <= 0)
00515 result = 0;
00516 else
00517 {
00518 rad4 = sqr(sqrlen(temp));
00519 result *= area / vl_pi;
00520 res2 = result * Max(npt, 0.25 * sqrt(area));
00521
00522 if (gRadControl->quadLevel == 0 || rad4 * gRadControl->dFError >= res2)
00523 result *= npt / rad4;
00524 else
00525 {
00526 // result is blowing up!
00527 // let's bail to the patch factor formula
00528
00529 GCLReal sum;
00530
00531 if (gRadControl->quadLevel > 1)
00532 {
00533 // use more-accurate PF estimate:
00534 // sample corners
00535
00536 sum = PatchFactor(to->Vertex(0), to->Normal());
00537 sum += PatchFactor(to->Vertex(1), to->Normal());
00538 sum += PatchFactor(to->Vertex(2), to->Normal());
00539 sum += PatchFactor(to->Vertex(3), to->Normal());
00540
00541 if (gRadControl->quadLevel > 2)
00542 {
00543 // estimate ff from tetrahedron constructed from
00544 // corner samples and centre. this will
00545 // underestimate the true ff slightly.
00546
00547 sum += 2.0 * PatchFactor(to->Centre(), to->Normal());
00548 sum /= 6.0;
00549 }
00550 else
00551 // underestimate curve
00552 sum /= 4.0;
00553 }
00554 else
00555 sum = PatchFactor(to->Centre(), to->Normal());
00556
00557 return(sum);
00558 }
00559 }
00560 }
00561
00562 return(result);
00563 }
00564
00565 GCLReal RadElem::SampledFormFactor(Int n, RadElem *to, GCLReal &error)
00569 {
00570 GCLReal sum, sample, min, max, rn;
00571 Point destPt;
00572 Vector dx = to->Vertex(2) - to->Vertex(1);
00573 Vector dy = to->Vertex(0) - to->Vertex(1);
00574 Int i, j;
00575
00576 // use most-accurate PF estimate:
00577 // sample in a grid
00578
00579 min = vl_inf;
00580 max = -vl_inf;
00581 sum = 0;
00582 rn = n;
00583
00584 for (i = 0; i < n; i++)
00585 for (j = 0; j < n; j++)
00586 {
00587 destPt = to->Vertex(1);
00588 destPt += ((j + 0.5) / rn) * dx;
00589 destPt += ((i + 0.5) / rn) * dy;
00590
00591 sample = ApproxPatchFactor(destPt, to->Normal());
00592 sum += sample;
00593 min = Min(min, sample);
00594 max = Max(max, sample);
00595 }
00596
00597 error = max - min;
00598
00599 return(sum / sqr(n));
00600 }
00601
00602 Int RadElem::OrientInfo(RadElem *to)
00603 // Returns basic orientation info: 0 = centres of
00604 // patches are obscured, -1 = patches close enough
00605 // to warrant good PF approx, 1 = can use standard approx.
00606 {
00607 GCLReal result, rad4, npt, res2;
00608 Vector temp;
00609
00610 temp = to->Centre() - Centre();
00611 result = -dot(to->Normal(), temp);
00612
00613 if (result <= 0)
00614 return(0);
00615 else
00616 {
00617 npt = dot(Normal(), temp);
00618
00619 if (npt <= 0)
00620 return(0);
00621 else
00622 {
00623 rad4 = sqr(sqrlen(temp));
00624 result *= area / vl_pi;
00625 res2 = result * Max(npt, 0.25 * sqrt(area));
00626
00627 if (gRadControl->quadLevel > 0 &&
00628 rad4 * gRadControl->dFError < res2)
00629 return(-1);
00630 else
00631 return(1);
00632 }
00633 }
00634 }
00635
00636 Void RadElem::SetProps(RadProps *parentProps)
00637 {
00638 props = parentProps;
00639 Centre().MakeZero();
00640
00641 centre += Vertex(0);
00642 centre += Vertex(1);
00643 centre += Vertex(2);
00644 if (IsQuad())
00645 {
00646 centre += Vertex(3);
00647 centre /= 4;
00648 }
00649 else
00650 centre /= 3;
00651
00652 CalcTriAreaNormal(Vertex(0), Vertex(1), Vertex(2), normal);
00653 area = len(normal);
00654 if (area > 0)
00655 normal /= area;
00656 if (IsTri())
00657 area *= 0.5;
00658
00659 #ifdef RAD_TEXTURE
00660 if (gRadControl->textureRefl && IsTextured())
00661 localRefl = props->texture->FilterBox(TexCoord(0), TexCoord(2));
00662 else
00663 localRefl = props->reflectance;
00664 #endif
00665 }
00666
00667 Void RadElem::Reanimate(RadElem *parent)
00668 {
00669 if (parent)
00670 SetProps(parent->props);
00671 }
00672
00673 Void RadElem::ColourVertices(Int weights[])
00674 {
00675 Int j;
00676
00677 for (j = 0; j < Sides(); j++)
00678 {
00679 VtxClr(j) += colour;
00680 weights[clrIdx[j]] += 1;
00681 }
00682 }
00683
00684 Void RadElem::CreatePatches(PatchList &patches)
00685 {
00686 Assert(false, "(RadElem::CreatePatches) need grid for that.");
00687 }
00688
00689 Void RadElem::RaiseVertex(Renderer &r, Int i)
00690 {
00691 Colour *c;
00692
00693 if (gRadControl->gouraud)
00694 c = &VtxClr(i);
00695 else
00696 c = &colour;
00697
00698 r.C(*c);
00699
00700 if (HasNormals())
00701 r.P(Vertex(i) + dot(kRadRGBToLum, *c) * Normal(i));
00702 else
00703 r.P(Vertex(i) + dot(kRadRGBToLum, *c) * Normal());
00704 }
00705
00706 ostream &operator << (ostream &s, RadElem &rq)
00707 {
00708 rq.Print(s);
00709 return(s);
00710 }
00711
00712 istream &operator >> (istream &s, RadElem &rq)
00713 {
00714 rq.Parse(s);
00715 return(s);
00716 }
00717
00718
00719 // --- Inter-elem visibility --------------------------------------------------
00720
00721
00722 // utility routines for patches only
00723
00724 GCLReal RadElem::Visibility16(const Point &p, const Vector &n)
00725 // Returns the fractional visibility of this patch from p.
00726 {
00727 Vector temp;
00728 GCLReal d;
00729
00730 // Is 'p' completely behind this patch?
00731
00732 d = dot(Normal(), Centre());
00733
00734 if (dot(p, Normal()) < d)
00735 return(0.0);
00736
00737 // Is this patch completely behind p's plane?
00738
00739 d = dot(n, p);
00740
00741 if ((dot(Vertex(0), n) <= d)
00742 && (dot(Vertex(1), n) <= d)
00743 && (dot(Vertex(2), n) <= d)
00744 && (IsTri() || dot(Vertex(3), n) <= d))
00745 return(0.0);
00746
00747 // If neither is the case, we have at least partial
00748 // visibility, so ray cast to estimate it
00749
00750 return(RadVis16x1(p, n));
00751 }
00752
00753 GCLReal RadElem::CentreVis(const Point &p, const Vector &n)
00754 // Returns visibility of the centre of this patch from p.
00755 {
00756 Vector temp;
00757 Point dstPoint, srcPoint;
00758 GCLReal d;
00759 Bool hit;
00760
00761 // Is 'p' completely behind this patch?
00762
00763 d = dot(Normal(), Centre());
00764
00765 if ((dot(p, Normal())) <= d)
00766 return(0.0);
00767
00768 // Is the centre of this patch behind p's plane?
00769
00770 d = dot(n, p);
00771
00772 if (dot(Centre(), n) <= d)
00773 return(0.0);
00774
00775 // If neither is the case, we have at least partial
00776 // visibility, so ray cast to estimate it
00777
00778 srcPoint = p;
00779 srcPoint += n * kRaySurfEps;
00780 dstPoint = Centre();
00781 dstPoint += Normal() * kRaySurfEps;
00782
00783 hit = gRadControl->radObject->IntersectsWithRay(srcPoint, dstPoint);
00784 gRadControl->rays += 1;
00785
00786 #ifdef RAD_VIS
00787 if (gRadControl->showRays)
00788 {
00789 if (hit)
00790 gRadControl->radObject->display->
00791 Begin(renLines).C(cRed).P(srcPoint).P(dstPoint).End();
00792 else
00793 gRadControl->radObject->display->
00794 Begin(renLines).C(cGreen).P(srcPoint).P(dstPoint).End();
00795 }
00796 #endif
00797
00798 if (hit)
00799 return(0.0);
00800 else
00801 return(1.0);
00802 }
00803
00804 Bool RadElem::PotentiallyVis(RadElem *to)
00805 // Checks to see whether any part of to is potentially visible
00806 // from this patch, and vice versa.
00807 // returns true if there is potential visibility, false if not.
00808 {
00809 Vector temp;
00810 GCLReal dFrom, dTo;
00811
00812 // Is 'to' completely behind this patch?
00813
00814 dFrom = dot(Normal(), Vertex(0));
00815
00816 if ((dot(to->Vertex(0), Normal()) <= dFrom)
00817 && (dot(to->Vertex(1), Normal()) <= dFrom)
00818 && (dot(to->Vertex(2), Normal()) <= dFrom)
00819 && (to->IsTri() || dot(to->Vertex(3), Normal()) <= dFrom))
00820 return(false);
00821
00822 // Is this patch completely behind 'to'?
00823
00824 dTo = dot(to->Normal(), to->Vertex(0));
00825
00826 if ((dot(Vertex(0), to->Normal()) <= dTo)
00827 && (dot(Vertex(1), to->Normal()) <= dTo)
00828 && (dot(Vertex(2), to->Normal()) <= dTo)
00829 && (IsTri() || dot(Vertex(3), to->Normal()) <= dTo))
00830 return(false);
00831
00832 // If neither is the case, we have at least potential
00833 // visibility.
00834
00835 return(true);
00836 }
00837
00838 Bool RadElem::PotentiallyVisAndTouching(RadElem *to, Bool &touching)
00839 // Checks to see whether any part of to is potentially visible
00840 // from this patch, and vice versa.
00841 // returns true if there is potential visibility, false if not.
00842 // Also sets touching to true if the two patches intersect or touch.
00843 {
00844 Vector temp;
00845 GCLReal dFrom, dTo, a;
00846 Bool pvis, touch;
00847 Int n, z, p;
00848
00849 touching = false;
00850 dFrom = dot(Normal(), Vertex(0));
00851 n = p = z = 0;
00852
00853 // we count p=points above plane, z=points on plane, n = points below plane.
00854 // pvis = at least 1 p1 coeff +ve && at least 1 p2 coeff +ve
00855 // touch: p1-touch && p2-touch
00856 // x-touch: x coeffs not all +ve or all -ve.
00857 //
00858
00859 // opt: can do early termination of touch as soon as z>0 or p&n
00860 // can do early termination of pvis as soon as p>0
00861 // can terminate both iff p>0 && (z>0 | n>0)
00862 // currently don't do this 'cause extra testing might
00863 // kill the optimisations, and I'm lazy...
00864 //
00865
00866 a = dot(to->Vertex(0), Normal()) - dFrom;
00867 if (a > 0) p++; else if (a < 0) n++; else z++;
00868 a = dot(to->Vertex(1), Normal()) - dFrom;
00869 if (a > 0) p++; else if (a < 0) n++; else z++;
00870 a = dot(to->Vertex(2), Normal()) - dFrom;
00871 if (a > 0) p++; else if (a < 0) n++; else z++;
00872 if (to->IsQuad())
00873 {
00874 a = dot(to->Vertex(3), Normal()) - dFrom;
00875 if (a > 0) p++; else if (a < 0) n++; else z++;
00876 }
00877
00878 pvis = p;
00879 touch = z || (p && n);
00880
00881 if (!pvis && !touch)
00882 return(false);
00883
00884 dTo = dot(to->Normal(), to->Vertex(0));
00885 n = p = z = 0;
00886
00887 a = dot(Vertex(0), to->Normal()) - dTo;
00888 if (a > 0) p++; else if (a < 0) n++; else z++;
00889 a = dot(Vertex(1), to->Normal()) - dTo;
00890 if (a > 0) p++; else if (a < 0) n++; else z++;
00891 a = dot(Vertex(2), to->Normal()) - dTo;
00892 if (a > 0) p++; else if (a < 0) n++; else z++;
00893 if (IsQuad())
00894 {
00895 a = dot(Vertex(3), to->Normal()) - dTo;
00896 if (a > 0) p++; else if (a < 0) n++; else z++;
00897 }
00898
00899 if (touch && (z || (p && n)))
00900 touching = true;
00901 return(pvis & p);
00902 }
00903
00904 GCLReal RadElem::Visibility44(RadElem *to)
00905 {
00906 if (PotentiallyVis(to))
00907 return(RadVis4x4(to));
00908 else
00909 return(0.0);
00910 }
00911
00912
00913 // --- Generic element->element visibility method -----------------------------
00914
00915 GCLReal RadElem::Visibility(RadElem *to)
00916 {
00917 GCLReal result;
00918
00919 #ifdef RAD_VIS
00920 if (gRadControl->showRays)
00921 RM_DISPLAY_START;
00922 #endif
00923
00924 switch(gRadControl->visibility)
00925 {
00926 case vis_none:
00927 result = 1.0;
00928 break;
00929 case vis_1:
00930 result = CentreVis(to->Centre(), to->Normal());
00931 break;
00932 case vis_16x1:
00933 result = Visibility16(to->Centre(), to->Normal());
00934 break;
00935 case vis_4x4:
00936 result = Visibility44(to);
00937 break;
00938 default:
00939 Assert(false, "Illegal visibility method specified!");
00940 }
00941
00942 #ifdef RAD_VIS
00943 if (gRadControl->showRays)
00944 {
00945 RM_DISPLAY_END;
00946 RM_OUT1("Visibility: " << result);
00947 if (RM_PAUSE) return(0);
00948 }
00949 #endif
00950
00951 return(result);
00952 }
00953
00954
00955 // ----------------------------------------------------------------------------
00956
00957 // Arrays for jittered sampling; 4 x 4 source, 4 x 4 dest.
00958
00959 static Int gRotPick = 0;
00960
00961 /*
00962 Routine: RadVis4x4
00963 Measures visibility between the patch and q.
00964 Returns visibility factor between 0 and 1.
00965 Uses magic-square jittered 4x4 pattern, a la HR paper.
00966 */
00967
00968 Void RadElem::SetVisPoints(Point *pts)
00969 {
00970 Int i;
00971 Vector dstHoriz, dstVert;
00972 Coord *d1;
00973
00974 dstVert = Vertex(0) - Vertex(1);
00975 dstHoriz = Vertex(2) - Vertex(1);
00976
00977 if (IsQuad())
00978 d1 = (Coord *) tSquareSamples16[gRotPick];
00979 else
00980 d1 = (Coord *) tTriangleSamples16[gRotPick];
00981
00982 for (i = 0; i < 16; i++)
00983 pts[i] = Vertex(1);
00984
00985 for (i = 0; i < 16; i++)
00986 pts[i] += d1[i][0] * dstHoriz +
00987 d1[i][1] * dstVert + kRaySurfEps * Normal();
00988
00989 if (gRadControl->jitterRot)
00990 if (++gRotPick == kNumSampArrays)
00991 gRotPick = 0;
00992 }
00993
00994 GCLReal RadElem::RadVis4x4(RadElem *to)
00995 {
00996 Int i, misses;
00997 Point srcPoint[16], dstPoint[16];
00998 Bool hit;
00999
01000 misses = 0;
01001 SetVisPoints(srcPoint);
01002 to->SetVisPoints(dstPoint);
01003
01004 gRadControl->rays += 16;
01005
01006 for (i = 0; i < 16; i++)
01007 {
01008 hit = gRadControl->radObject->IntersectsWithRay(srcPoint[i], dstPoint[i]);
01009
01010 if (hit)
01011 misses++;
01012
01013 #ifdef RAD_VIS
01014 if (gRadControl->showRays)
01015 {
01016 if (hit)
01017 gRadControl->radObject->display->Begin(renLines).C(cRed).P(srcPoint[i]).
01018 P(dstPoint[i]).End();
01019 else
01020 gRadControl->radObject->display->Begin(renLines).C(cGreen).P(srcPoint[i]).
01021 P(dstPoint[i]).End();
01022 }
01023 #endif
01024 }
01025
01026 return((16 - misses) * 1.0 / 16.0);
01027 }
01028
01029 /*
01030 Routine: RadVis16x1
01031 Measures visibility between the patch and point p by
01032 casting 16 rays between the two.
01033 Returns visibility factor between 0 and 1.
01034 Uses magic-square jittered 4x4 pattern
01035 */
01036
01037 GCLReal RadElem::RadVis16x1(const Point &p, const Vector &n)
01038 {
01039 Int i, misses;
01040 Point srcPoint[16], dstPoint;
01041 Bool hit;
01042
01043 SetVisPoints(srcPoint);
01044 dstPoint = p;
01045 dstPoint += kRaySurfEps * n;
01046 misses = 0;
01047
01048 gRadControl->rays += 16;
01049
01050 for (i = 0; i < 16; i++)
01051 {
01052 hit = gRadControl->radObject->IntersectsWithRay(srcPoint[i], dstPoint);
01053
01054 if (hit)
01055 misses++;
01056
01057 #ifdef RAD_VIS
01058 if (gRadControl->showRays)
01059 {
01060 if (hit)
01061 gRadControl->radObject->display->
01062 Begin(renLines).C(cRed).P(srcPoint[i]).P(dstPoint).End();
01063 else
01064 gRadControl->radObject->display->
01065 Begin(renLines).C(cGreen).P(srcPoint[i]).P(dstPoint).End();
01066 }
01067 #endif
01068 }
01069
01070 return(GCLReal(16 - misses) / 16.0);
01071 }
01072
01073 Coord RadElem::FindCoord(Point &p)
01074 {
01075 Point origin;
01076 Vector r;
01077 Coord result;
01078 VecTrans s, t;
01079
01080 // construct a coordinate system about vertex 1 of the triangle/quad
01081 origin = Vertex(1);
01082 t[2] = Normal();
01083 t[1] = Vertex(0) - origin;
01084 t[0] = Vertex(2) - origin;
01085
01086 // find transformation into that coord system...
01087 s = inv(t);
01088
01089 // transform p
01090 r = p - origin;
01091 r *= s;
01092
01093 // drop height and return
01094 return(Coord(r[0], r[1]));
01095 }
01096
01097 Void RadElem::SetIndexes(
01098 Int dstIdx,
01099 RadElem *src,
01100 Int srcIdx
01101 )
01102 {
01103 index[dstIdx] = src->index[srcIdx];
01104 clrIdx[dstIdx] = src->clrIdx[srcIdx];
01105 normIdx[dstIdx] = src->normIdx[srcIdx];
01106 texIdx[dstIdx] = src->texIdx[srcIdx];
01107 }
01108
01109 Void RadElem::SetAllIndexes(
01110 RadElem *s0,
01111 Int i0,
01112 RadElem *s1,
01113 Int i1,
01114 RadElem *s2,
01115 Int i2,
01116 RadElem *s3,
01117 Int i3
01118 )
01119 {
01120 index[0] = s0->index[i0];
01121 index[1] = s1->index[i1];
01122 index[2] = s2->index[i2];
01123 index[3] = s3->index[i3];
01124
01125 clrIdx[0] = s0->clrIdx[i0];
01126 clrIdx[1] = s1->clrIdx[i1];
01127 clrIdx[2] = s2->clrIdx[i2];
01128 clrIdx[3] = s3->clrIdx[i3];
01129
01130 normIdx[0] = s0->normIdx[i0];
01131 normIdx[1] = s1->normIdx[i1];
01132 normIdx[2] = s2->normIdx[i2];
01133 normIdx[3] = s3->normIdx[i3];
01134
01135 texIdx[0] = s0->texIdx[i0];
01136 texIdx[1] = s1->texIdx[i1];
01137 texIdx[2] = s2->texIdx[i2];
01138 texIdx[3] = s3->texIdx[i3];
01139 }
01140
01141 GCLReal RadElem::MemoryUse()
01142 {
01143 return(sizeof(SELF));
01144 }
01145
01146 GCLReal NbRadElem::MemoryUse()
01147 {
01148 return(sizeof(SELF));
01149 }
01150
01151
01152 // --- Grid Subdivision -------------------------------------------------------
01153
01186 Void GridBase::Mesh(GCLReal density)
01187 // Meshes an element into a grid of similar elements of roughly
01188 // the given density.
01189 // XXX still need to re-fix non-lin density stuff
01190 {
01191 Int i, j, in, jn;
01192 RadElem child, child2, rowEnds;
01193 GCLReal w, h, dw, dh;
01194 Int im, jm;
01195 RadElemPtr *lastRow, upperTri;
01196 Vector u, v;
01197
01198 // Setup increments, step sizes, etc.
01199
01200 if (gRadControl->mesh == mesh_random)
01201 {
01202 density = (0.1 + 0.9 * drand48()) * density;
01203 }
01204
01205 u = Vertex(2) - Vertex(1);
01206 v = Vertex(0) - Vertex(1);
01207
01208 in = (Int) ceil(len(v) * density);
01209 jn = (Int) ceil(len(u) * density);
01210
01211 // For triangles we must subdivide equally in both directions.
01212
01213 if (IsTri())
01214 {
01215 if (jn < in)
01216 jn = in;
01217 else
01218 in = jn;
01219 }
01220
01221 if (gRadControl->mesh != mesh_nonlin)
01222 {
01223 im = 0;
01224 jm = 0;
01225 w = 1.0 / jn;
01226 h = 1.0 / in;
01227 child.area = len(w) * len(h);
01228 if (IsTri())
01229 child.area *= 0.5;
01230 child2.area = child.area;
01231 // child2 is always a triangle: it is not used for quad meshing.
01232 }
01233 else
01234 {
01235 if ((jn % 2) == 1)
01236 w = 4.0 / sqr(jn + 1);
01237 else
01238 w = 4.0 / (sqr(jn + 1) - 1);
01239
01240 if ((in % 2) == 1)
01241 h = 4.0 / sqr(in + 1);
01242 else
01243 h = 4.0 / (sqr(in + 1) - 1);
01244
01245 jm = jn;
01246 im = in;
01247 dw = w;
01248 dh = h;
01249 }
01250
01251 // Set up templates for new elements
01252
01253 lastRow = new RadElemPtr[jn];
01254 child.index[3] = -1;
01255 child2.index[3] = -1;
01256
01257 // Add the new quads/tri's to the grid in turn, from left to right,
01258 // and top to bottom. Each new quad or pair of tris will require a
01259 // new bottom-right vertex.
01260
01261 for (i = 0; i < in; i++)
01262 {
01263 // find outermost bottom vertices of the row, so we can interpolate
01264 // between them while filling in the row. rowEnds:0 is the left-most
01265 // bottom vertex, rowEnds:1 is the right-most.
01266 rowEnds.props = props;
01267 if (i == in - 1)
01268 // Special-case original bottom-left and bottom-right vertices.
01269 {
01270 rowEnds.SetIndexes(0, this, 1);
01271 rowEnds.SetIndexes(1, this, 2);
01272 }
01273 else
01274 {
01275 props->InterpolateIndexes(this, 0, 1, &rowEnds, 0,
01276 GCLReal(i + 1.0) / in);
01277 if (IsQuad())
01278 props->InterpolateIndexes(this, 3, 2, &rowEnds, 1,
01279 GCLReal(i + 1.0) / in);
01280 else
01281 props->InterpolateIndexes(this, 0, 2, &rowEnds, 1,
01282 GCLReal(i + 1.0) / in);
01283 }
01284
01285 // copy left-most into first quad/tri in the row
01286 child.SetIndexes(1, &rowEnds, 0);
01287 if (i == 0)
01288 child.SetIndexes(0, this, 0);
01289 else
01290 child.SetIndexes(0, lastRow[0], 1);
01291
01292 if (jm > 0)
01293 w = dw;
01294 if (IsTri())
01295 jn = i + 1;
01296
01297 for (j = 0; j < jn; j++)
01298 // Add one row of quads/tri's
01299 {
01300 // For each quad/tri, we must add the bottom-right corner as a new point.
01301 if (j == jn - 1)
01302 // Special-case right-most quad/tri
01303 child.SetIndexes(2, &rowEnds, 1);
01304 else
01305 props->InterpolateIndexes(&rowEnds, 0, 1, &child, 2,
01306 Double(j + 1.0) / jn);
01307
01308 if (IsTri())
01309 {
01310 // For triangles: we add them in pairs. The first triangle...
01311 child.SetProps(props);
01312 if (jm > 0)
01313 {
01314 child.area = 0.5 * len(w) * len(h);
01315 child2.area = child.area;
01316 }
01317 upperTri = lastRow[j];
01318 lastRow[j] = AddChild(child, i, j, in, jn);
01319
01320 if (j == i) // if we're at the end of a triangle strip
01321 break; // break to the outer loop
01322
01323 // then the second triangle.
01324
01325 // copy upper-right vertex from last row
01326 child2.SetAllIndexes(&child, 2, upperTri, 2, &child, 0, this, 3);
01327 child2.SetProps(props);
01328 AddChild(child2, i, j, in, jn);
01329
01330 // Move on to next quad/tri pair: shift the indices left.
01331 child.SetIndexes(0, &child2, 1);
01332 child.SetIndexes(1, &child, 2);
01333 }
01334 else
01335 {
01336 // For quads: we first find/copy upper-right vertex
01337
01338 if (i == 0)
01339 // first row -- can't borrow from row above
01340 {
01341 if (j == jn - 1)
01342 // Special-case original top-right corner
01343 {
01344 child.SetIndexes(3, this, 3);
01345 }
01346 else
01347 {
01348 // interpolate between upper-left & upper right.
01349 props->InterpolateIndexes(this, 0, 3, &child, 3,
01350 Double(j + 1.0) / jn);
01351 }
01352 }
01353 else
01354 {
01355 // set to bottom-right vertex of quad above us
01356 child.SetIndexes(3, lastRow[j], 2);
01357 }
01358
01359 // And add the new quad...
01360
01361 if (jm > 0)
01362 child.area = len(w) * len(h);
01363 child.SetProps(props);
01364 lastRow[j] = AddChild(child, i, j, in, jn);
01365
01366 // Move on to next quad/tri pair: shift the indices left.
01367 child.SetIndexes(0, &child, 3);
01368 child.SetIndexes(1, &child, 2);
01369 }
01370
01371 if (jm > 0)
01372 {
01373 if (2 * (j + 1) < jm)
01374 w += dw;
01375 else if (2 * (j + 1) > jm)
01376 w -= dw;
01377 }
01378 }
01379
01380 // Move on to next row, resetting necessary variables
01381
01382 if (im > 0)
01383 {
01384 if (2 * (i + 1) < im)
01385 h += dh;
01386 else if (2 * (i + 1) > im)
01387 h -= dh;
01388 }
01389 }
01390
01391 rows = in;
01392 cols = jn;
01393 delete[] lastRow;
01394 }
01395
01396 Int GridBase::FindChildIndex(Coord &coord)
01397 // which of the grid elements does the coord fall inside?
01398 // find its index, and modify coord to be in its local coord. system.
01399 {
01400 Int x, y, xy;
01401 GCLReal rx, ry;
01402
01403 rx = coord[0];
01404 ry = coord[1];
01405
01406 // Do some bounds checking...
01407 if (IsTri())
01408 {
01409 if ((rx - ry) > 1 || rx < 0 || ry < 0)
01410 return(-1);
01411 }
01412 else
01413 if (rx < 0 || ry < 0 || rx > 1 || ry > 1)
01414 return(-1);
01415
01416 // find rectangular grid coords
01417 rx *= cols;
01418 ry *= rows;
01419
01420 x = (Int) rx;
01421 if (x == cols)
01422 x--;
01423 y = (Int) ry;
01424 if (y == rows)
01425 y--;
01426
01427 // find cell coords
01428 // our grid is numbered from the top down, hence the (rows - 1 - y)
01429 // terms below
01430
01431 if (IsTri())
01432 {
01433 xy = (Int) (rows * (rx + ry));
01434 if (xy == rows)
01435 xy--;
01436
01437 if (xy > x + y)
01438 // flipped triangle
01439 {
01440 coord[0] = x + 1 - rx;
01441 coord[1] = y + 1 - ry;
01442 x = 2 * x + 1;
01443 }
01444 else
01445 // normal triangle
01446 {
01447 coord[0] = rx - x;
01448 coord[1] = ry - y;
01449 x = 2 * x;
01450 }
01451
01452 // number of picked cell
01453 return(x + sqr(rows - 1 - y));
01454 }
01455 else
01456 {
01457 // quad case is much easier!
01458 coord[0] = rx - x;
01459 coord[1] = ry - y;
01460
01461 return(x + (rows - 1 - y) * cols);
01462 }
01463 }
01464
01465 // --- RadGrid methods ---------------------------------------------------------
01466
01467 RadElemPtr RadGrid::AddChild(RadElem &elem, Int i, Int j, Int in, Int jn)
01468 {
01469 children.Append(elem);
01470
01471 return(&children.Last());
01472 }
01473
01474 Void RadGrid::Draw(Renderer &r)
01475 {
01476 if (children.NumItems() > 0
01477 #ifdef RAD_VIS
01478 && !highlight
01479 #endif
01480 )
01481 {
01482 Int i;
01483
01484 for (i = 0; i < children.NumItems(); i++)
01485 children[i].Draw(r);
01486 }
01487 else
01488 RadElem::Draw(r);
01489 }
01490
01491 Void RadGrid::CreatePatches(PatchList &patches)
01492 {
01493 Int i;
01494
01495 if (gRadControl->patchSubdivs == 0.0)
01496 {
01497 patches.Append(this);
01498 rows = cols = 1;
01499 return;
01500 }
01501
01502 children.Clear();
01503 Mesh(gRadControl->patchSubdivs);
01504
01505 for (i = 0; i < children.NumItems(); i++)
01506 patches.Append(&(children[i]));
01507 }
01508
01509 RadElem *RadGrid::FindContainer(Coord &coord)
01510 {
01511 Int i = FindChildIndex(coord);
01512
01513 return(children[i].FindContainer(coord));
01514 }
01515
01516 Void RadGrid::ColourVertices(Int weights[])
01517 {
01518 Int i;
01519
01520 for (i = 0; i < children.NumItems(); i++)
01521 children[i].ColourVertices(weights);
01522 }
01523
01524 StrConst RadGrid::Name()
01525 {
01526 return("grid");
01527 }
01528
01529 Void RadGrid::Print(ostream &s)
01530 {
01531 RadElem::Print(s);
01532 s << rows << ' ' << cols << ' ' << children << ' ';
01533 }
01534
01535 Void RadGrid::Parse(istream &s)
01536 {
01537 Char c;
01538 Int i;
01539
01540 RadElem::Parse(s);
01541
01542 s >> rows >> cols;
01543
01544 children.SetSize(rows * cols);
01545
01546 ChompWhiteSpace(s);
01547 s.get(c);
01548 for (i = 0; i < children.NumItems(); i++)
01549 children[i].Parse(s);
01550 ChompWhiteSpace(s);
01551 s.get(c);
01552 }
01553
01554 Void RadGrid::Reanimate(RadElem *parent)
01555 {
01556 Int i;
01557
01558 RadElem::Reanimate(parent);
01559
01560 for (i = 0; i < children.NumItems(); i++)
01561 children[i].Reanimate(this);
01562 }
01563
01564 GCLReal RadGrid::MemoryUse()
01565 {
01566 sGridMem += sizeof(SELF);
01567 sGridChildMem += sizeof(RadElem) * children.NumItems();
01568 return(sizeof(SELF) + sizeof(RadElem) * children.NumItems());
01569 }
01570
01571
01572 // --- RadProps methods -------------------------------------------------------
01573
01574
01575 RadProps::RadProps() : points(0), colours(0), normals(0), texCoords(0),
01576 texture(0)
01577 {
01578 }
01579
01580 Void RadProps::InterpolateIndexes(
01581 RadElem *srcElt,
01582 Int srcVtx1,
01583 Int srcVtx2,
01584 RadElem *dstElt,
01585 Int dstVtx,
01586 GCLReal m
01587 )
01588 {
01589 GCLReal mc = 1.0 - m;
01590
01591 // do interpolation
01592
01593 points->Append(srcElt->Vertex(srcVtx1) * mc + srcElt->Vertex(srcVtx2) * m);
01594 dstElt->index[dstVtx] = points->NumItems() - 1;
01595
01596 colours->Append(srcElt->VtxClr(srcVtx1) * mc + srcElt->VtxClr(srcVtx2) * m);
01597 dstElt->clrIdx[dstVtx] = colours->NumItems() - 1;
01598
01599 if (normals)
01600 {
01601 normals->Append(srcElt->Normal(srcVtx1) * mc + srcElt->Normal(srcVtx2) * m);
01602 dstElt->normIdx[dstVtx] = normals->NumItems() - 1;
01603 }
01604
01605 if (texCoords)
01606 {
01607 texCoords->Append(srcElt->TexCoord(srcVtx1) * mc
01608 + srcElt->TexCoord(srcVtx2) * m);
01609 dstElt->texIdx[dstVtx] = texCoords->NumItems() - 1;
01610 }
01611 }