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 }