Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

photonmap.cpp

Go to the documentation of this file.
00001 /*
00002     toxic - A Global Illumination Renderer
00003     Copyright (C) 2003-2004 Francois Beaune
00004     Contact: http://toxicengine.sourceforge.net/
00005 
00006     This file is part of toxic.
00007 
00008     toxic is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     toxic is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with toxic; if not, write to the Free Software
00020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 */
00022 
00023 #include "photonmap.h"  // include first
00024 #include "common/misc/binarystream.h"
00025 #include "common/misc/minmax.h"
00026 #include "context.h"
00027 #include "ibdf.h"
00028 #include "isurfaceshader.h"
00029 #include "surfacebasis.h"
00030 
00031 #include <algorithm>    // std::swap<>(), std::min<>()
00032 #include <cmath>
00033 #include <cstdio>
00034 
00035 using namespace sheep;
00036 using namespace std;
00037 using namespace toxic;
00038 
00039 //! Signature string identifying photon map files.
00040 //! Increment version number each time the file structure change.
00041 const string PhotonMap::m_photon_map_sig = "toxic photon map file version 1";
00042 
00043 //! Buffer size for photon map I/O, expressed in bytes.
00044 const int PHOTONMAP_IO_BUFFER_SIZE = 1024 * 1024;
00045 
00046 PhotonMap::PhotonMap() :
00047     m_stored_photons(0),
00048     m_half_stored_photons(0),
00049     m_prev_scale(1)
00050 {
00051     m_photons.push_back(Photon());
00052 
00053     m_bbox_min[0] = m_bbox_min[1] = m_bbox_min[2] = 1.0e8f;
00054     m_bbox_max[0] = m_bbox_max[1] = m_bbox_max[2] = -1.0e8f;
00055 
00056     // Allocate the buffers used by IrradianceEstimate(), FastIrradianceEstimate()
00057     // and BuildPhotonDirectionHistogram() methods.
00058     m_dist2_buf = new float32[BUF_SIZE];
00059     m_index_buf = new const Photon *[BUF_SIZE];
00060 }
00061 
00062 PhotonMap::~PhotonMap() {
00063     delete [] m_index_buf;
00064     delete [] m_dist2_buf;
00065 }
00066 
00067 int PhotonMap::GetSizeInMemory() const {
00068     return
00069         sizeof(PhotonMap) +
00070         sizeof(Photon) * static_cast<int>(m_photons.capacity()) +
00071         sizeof(float32) * BUF_SIZE +
00072         sizeof(Photon *) * BUF_SIZE;
00073 }
00074 
00075 void PhotonMap::StorePhoton(const Point3 &position,
00076                             const Color3 &power,
00077                             const Vector3 &direction
00078 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00079                             , const Vector3 &normal
00080 #endif  // ENABLE_RADIANCES_PRECOMPUTATION
00081                             )
00082 {
00083     Photon photon;
00084 
00085     photon.SetPosition(position);
00086     photon.SetPower(power);
00087     photon.SetIncidentDirection(direction);
00088 
00089 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00090     photon.SetSurfaceNormal(normal);
00091 #endif  // ENABLE_RADIANCES_PRECOMPUTATION
00092 
00093     m_photons.push_back(photon);
00094 
00095     ++m_stored_photons;
00096 
00097     // Update the bounding box.
00098     if(position.m_x < m_bbox_min[0]) m_bbox_min[0] = static_cast<float32>(position.m_x);
00099     if(position.m_x > m_bbox_max[0]) m_bbox_max[0] = static_cast<float32>(position.m_x);
00100     if(position.m_y < m_bbox_min[1]) m_bbox_min[1] = static_cast<float32>(position.m_y);
00101     if(position.m_y > m_bbox_max[1]) m_bbox_max[1] = static_cast<float32>(position.m_y);
00102     if(position.m_z < m_bbox_min[2]) m_bbox_min[2] = static_cast<float32>(position.m_z);
00103     if(position.m_z > m_bbox_max[2]) m_bbox_max[2] = static_cast<float32>(position.m_z);
00104 }
00105 
00106 void PhotonMap::ScalePhotonPower(Real scale) {
00107     for(int i = m_prev_scale; i <= m_stored_photons; ++i)
00108         m_photons[i].ScalePower(scale);
00109 
00110     m_prev_scale = m_stored_photons + 1;
00111 }
00112 
00113 void PhotonMap::Balance() {
00114     if(m_stored_photons > 1) {
00115         // Allocate two temporary arrays for the balancing procedure.
00116         Photon **pa1 = new Photon *[m_stored_photons + 1];
00117         Photon **pa2 = new Photon *[m_stored_photons + 1];
00118 
00119         for(int i = 0; i <= m_stored_photons; ++i)
00120             pa2[i] = &m_photons[i];
00121 
00122         balance_segment(pa1, pa2, 1, 1, m_stored_photons);
00123 
00124         delete [] pa2;
00125 
00126         // Reorganize balanced kd-tree (make a heap).
00127         int j = 1, foo = 1;
00128         Photon foo_photon = m_photons[j];
00129 
00130         for(int i = 1; i <= m_stored_photons; ++i) {
00131             //!\todo Warning: this is not portable.
00132             const int d = pa1[j] - &m_photons[0];
00133 
00134             pa1[j] = 0;
00135 
00136             if(d != foo)
00137                 m_photons[j] = m_photons[d];
00138             else {
00139                 m_photons[j] = foo_photon;
00140 
00141                 if(i < m_stored_photons) {
00142                     for(; foo <= m_stored_photons; ++foo)
00143                         if(pa1[foo] != 0)
00144                             break;
00145                     foo_photon = m_photons[foo];
00146                     j = foo;
00147                 }
00148 
00149                 continue;
00150             }
00151 
00152             j = d;
00153         }
00154 
00155         delete [] pa1;
00156     }
00157 
00158     m_half_stored_photons = m_stored_photons / 2 - 1;
00159 }
00160 
00161 Color3 PhotonMap::ComputeRadiance(const Context &context,
00162                                   const Point3 &point,
00163                                   const Vector3 &normal,
00164                                   const Vector3 &outgoing,
00165                                   const ShadingData &shadingdata,
00166                                   Real max_dist,
00167                                   int max_photons) const
00168 {
00169     assert(max_photons + 1 <= BUF_SIZE);
00170 
00171     nearest_photons np;
00172     np.m_dist2 = m_dist2_buf;
00173     np.m_index = m_index_buf;
00174     np.m_position[0] = static_cast<float32>(point.m_x);
00175     np.m_position[1] = static_cast<float32>(point.m_y);
00176     np.m_position[2] = static_cast<float32>(point.m_z);
00177     np.m_max = max_photons;
00178     np.m_found = 0;
00179     np.m_got_heap = 0;
00180     np.m_dist2[0] = static_cast<float32>(max_dist * max_dist);
00181 
00182     // Locate the nearest photons.
00183     locate_photons(&np, 1);
00184 
00185     // If less than 8 photons return.
00186     if(np.m_found < 8)
00187         return Color3(0.0);
00188 
00189     const SurfaceBasis surfacebasis(normal);
00190     const Vector3 local_outgoing = surfacebasis.TransformToLocal(outgoing);
00191 
00192     Color3 radiant_intensity(0.0);
00193 
00194     for(int i = 1; i <= np.m_found; ++i) {
00195         const Photon *p = np.m_index[i];
00196 
00197 #ifdef CHECK_FOR_THIN_SURFACES
00198         if(p->GetIncidentDirection() * normal <= 0.0)
00199             continue;
00200 #endif  // CHECK_FOR_THIN_SURFACES
00201 
00202 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00203         // In some cases, this test significantly improves the quality of
00204         // the indirect lighting in corners and along edges.
00205         //!\todo Let THRESHOLD be a user setting.
00206         const Real THRESHOLD = 0.9;
00207         if(p->GetSurfaceNormal() * normal < THRESHOLD)
00208             continue;
00209 #endif  // ENABLE_RADIANCES_PRECOMPUTATION
00210 
00211         // Evaluate the local BSDF at this photon's location.
00212         const Real bsdf_value =
00213             shadingdata.m_bdf->Evaluate(
00214                 context,
00215                 surfacebasis.TransformToLocal(p->GetIncidentDirection()),
00216                 local_outgoing);
00217 
00218         // Accumulate radiant intensity.
00219         //!\todo Missing cosine factor?
00220         radiant_intensity += bsdf_value * p->GetPower();
00221     }
00222 
00223     // Compute the area of the disc defined by the intersection of the nearest
00224     // photons bounding sphere and the surface (locally approximated by a plane)
00225     // containing the point at which we evaluate the irradiance.
00226     assert(fnz(np.m_dist2[0]));
00227     const Real inv_disc_area = (1.0 / PI) / np.m_dist2[0];
00228 
00229     // Compute radiance from radiant intensity.
00230     const Color3 radiance = radiant_intensity * inv_disc_area;
00231 
00232     return shadingdata.m_reflectance * radiance;
00233 }
00234 
00235 Color3 PhotonMap::ApproximateRadiance(const Point3 &point,
00236                                       const Vector3 &normal,
00237                                       const ShadingData &shadingdata,
00238                                       Real max_dist,
00239                                       int max_photons) const
00240 {
00241     assert(max_photons + 1 <= BUF_SIZE);
00242 
00243     nearest_photons np;
00244     np.m_dist2 = m_dist2_buf;
00245     np.m_index = m_index_buf;
00246     np.m_position[0] = static_cast<float32>(point.m_x);
00247     np.m_position[1] = static_cast<float32>(point.m_y);
00248     np.m_position[2] = static_cast<float32>(point.m_z);
00249     np.m_max = max_photons;
00250     np.m_found = 0;
00251     np.m_got_heap = 0;
00252     np.m_dist2[0] = static_cast<float32>(max_dist * max_dist);
00253 
00254     // Locate the nearest photons.
00255     locate_photons(&np, 1);
00256 
00257     // If less than 8 photons return.
00258     if(np.m_found < 8)
00259         return Color3(0.0);
00260 
00261     Color3 radiant_intensity(0.0);
00262 
00263     for(int i = 1; i <= np.m_found; ++i) {
00264         const Photon *p = np.m_index[i];
00265 
00266 #ifdef CHECK_FOR_THIN_SURFACES
00267         if(p->GetIncidentDirection() * normal <= 0.0)
00268             continue;
00269 #endif  // CHECK_FOR_THIN_SURFACES
00270 
00271 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00272         // In some cases, this test significantly improves the quality of
00273         // the indirect lighting in corners and along edges.
00274         //!\todo Let THRESHOLD be a user setting.
00275         const Real THRESHOLD = 0.9;
00276         if(p->GetSurfaceNormal() * normal < THRESHOLD)
00277             continue;
00278 #endif  // ENABLE_RADIANCES_PRECOMPUTATION
00279 
00280         // Approximate the local BSDF at this photon's location by the Lambertian BRDF.
00281         //!\todo Move out of the loop.
00282         const Real bsdf_value = (1.0 / PI);
00283 
00284         // Accumulate radiant intensity.
00285         radiant_intensity += bsdf_value * p->GetPower();
00286     }
00287 
00288     // Compute the area of the disc defined by the intersection of the nearest
00289     // photons bounding sphere and the surface (locally approximated by a plane)
00290     // containing the point at which we evaluate the irradiance.
00291     assert(fnz(np.m_dist2[0]));
00292     const Real inv_disc_area = (1.0 / PI) / np.m_dist2[0];
00293 
00294     // Compute radiance from radiant intensity.
00295     const Color3 radiance = radiant_intensity * inv_disc_area;
00296 
00297     return shadingdata.m_reflectance * radiance;
00298 }
00299 
00300 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00301 
00302 void PhotonMap::PrecomputeRadiances(int spacing,
00303                                     Real max_dist,
00304                                     int max_photons,
00305                                     ProgressMonitor *progmon /*= 0*/)
00306 {
00307     assert(spacing >= 1);
00308     assert(max_photons > 0);
00309 
00310     if(progmon && m_stored_photons > 0)
00311         progmon->StartJob(0.0, m_stored_photons);
00312 
00313     ShadingData fakeshadingdata;
00314     fakeshadingdata.m_reflectance = Color3(1.0);
00315 
00316     for(int i = 1; i <= m_stored_photons; i += spacing) {
00317         if(progmon)
00318             progmon->SetJobProgress(i);
00319 
00320         Photon *photon = &m_photons[i];
00321 
00322         const Color3 radiance =
00323             ApproximateRadiance(
00324                 photon->GetPosition(),
00325                 photon->GetSurfaceNormal(),
00326                 fakeshadingdata,
00327                 max_dist,
00328                 max_photons);
00329 
00330         photon->SetPrecomputedRadiance(radiance);
00331     }
00332 
00333     if(progmon)
00334         progmon->Done();
00335 }
00336 
00337 Color3 PhotonMap::GetPrecomputedRadiance(const Point3 &point,
00338                                          const Vector3 &normal,
00339                                          const ShadingData &shadingdata,
00340                                          Real max_dist) const
00341 {
00342     nearest_photons np;
00343     np.m_dist2 = m_dist2_buf;
00344     np.m_index = m_index_buf;
00345     np.m_position[0] = static_cast<float32>(point.m_x);
00346     np.m_position[1] = static_cast<float32>(point.m_y);
00347     np.m_position[2] = static_cast<float32>(point.m_z);
00348     np.m_dist2[0] = static_cast<float32>(max_dist * max_dist);
00349 
00350     np.m_index[0] = 0;  // no photon found yet
00351 
00352     // Locate the nearest photon.
00353     locate_nearest_photon_with_irradiance(normal, &np, 1);
00354 
00355     const Photon *nearest = np.m_index[0];
00356 
00357     if(!nearest)
00358         return Color3(0.0);
00359 
00360     return shadingdata.m_reflectance * nearest->GetPrecomputedRadiance();
00361 }
00362 
00363 #endif  // ENABLE_RADIANCES_PRECOMPUTATION
00364 
00365 void PhotonMap::BuildPhotonDirectionHistogram(const Point3 &position,
00366                                               const SurfaceBasis &surfacebasis,
00367                                               Real max_dist,
00368                                               int max_photons,
00369                                               Real *histogram,
00370                                               int m, int n,
00371                                               Real *total_power) const
00372 {
00373     assert(max_photons + 1 <= BUF_SIZE);
00374     assert(histogram);
00375     assert(total_power);
00376 
00377     for(int i = m * n - 1; i >= 0; --i)
00378         histogram[i] = 0.0;
00379 
00380     *total_power = 0.0;
00381 
00382     nearest_photons np;
00383     np.m_dist2 = m_dist2_buf;
00384     np.m_index = m_index_buf;
00385     np.m_position[0] = static_cast<float32>(position.m_x);
00386     np.m_position[1] = static_cast<float32>(position.m_y);
00387     np.m_position[2] = static_cast<float32>(position.m_z);
00388     np.m_max = max_photons;
00389     np.m_found = 0;
00390     np.m_got_heap = 0;
00391     np.m_dist2[0] = static_cast<float32>(max_dist * max_dist);
00392 
00393     // Locate the nearest photons.
00394     locate_photons(&np, 1);
00395 
00396     for(int i = 1; i <= np.m_found; ++i) {
00397         const Photon *p = np.m_index[i];
00398 
00399 #ifdef CHECK_FOR_THIN_SURFACES
00400         if(p->GetIncidentDirection() * surfacebasis.GetNormal() <= 0.0)
00401             continue;
00402 #endif  // CHECK_FOR_THIN_SURFACES
00403 
00404 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00405         // In some cases, this test significantly improves the quality of
00406         // the indirect lighting in corners and along edges.
00407         const Real THRESHOLD = 0.0; // 0.9?
00408         if(p->GetSurfaceNormal() * surfacebasis.GetNormal() < THRESHOLD)
00409             continue;
00410 #endif  // ENABLE_RADIANCES_PRECOMPUTATION
00411 
00412         // Get photon incident direction back.
00413         const Vector3 photon_dir = p->GetIncidentDirection();
00414 
00415         // Compute photon direction in surface space.
00416         const Vector3 local_photon_dir = surfacebasis.TransformToLocal(photon_dir);
00417         assert(local_photon_dir.IsUnitLength());
00418         assert(local_photon_dir.m_y > 0.0);
00419 
00420         // Compute spherical coordinates of the photon direction.
00421         Real phi, theta;
00422         VectorToSphericalCoords(local_photon_dir, &phi, &theta);
00423         assert(phi >= 0.0 && phi <= 2.0 * PI);
00424         assert(theta >= 0.0 && theta <= PI);
00425 
00426         const Real cos_theta = cos(theta);
00427         const Real u1 = phi / (2.0 * PI);
00428         const Real v1 = cos_theta * cos_theta;
00429 
00430         assert(u1 >= 0.0 && u1 <= 1.0);
00431         assert(v1 >= 0.0 && v1 <= 1.0);
00432 
00433         const int hi = min(m - 1, static_cast<int>(u1 * m));
00434         const int hj = min(n - 1, static_cast<int>(v1 * n));
00435 
00436         assert(hi >= 0 && hi < m);
00437         assert(hj >= 0 && hj < n);
00438 
00439         const Real photon_power = /*1.0*/p->GetPower().Average();
00440 
00441         histogram[hj * m + hi] += photon_power;
00442         *total_power += photon_power;
00443     }
00444 
00445     const Real frac = *total_power / (m * n * 10.0);
00446 
00447     for(int i = m * n - 1; i >= 0; --i) {
00448         if(histogram[i] == 0.0) {
00449             histogram[i] = frac;
00450             *total_power += frac;
00451         }
00452     }
00453 }
00454 
00455 bool PhotonMap::WriteToFile(const string &filename,
00456                             ProgressMonitor *progmon /*= 0*/) const
00457 {
00458     try {
00459         BinaryStream os(
00460             filename.c_str(),
00461             BinaryStream::OutputStream,
00462             BinaryStream::BigEndian // by convention, photon map files are written using big endian convention
00463         );
00464 
00465         if(!os.IsOpen())
00466             return false;
00467 
00468         // Set buffer size.
00469         os.SetBufferSize(PHOTONMAP_IO_BUFFER_SIZE);
00470 
00471         // Write photon map file signature.
00472         os.Write(m_photon_map_sig.c_str(), m_photon_map_sig.size());
00473 
00474         // Write the number of stored photons.
00475         os.Write(m_stored_photons);
00476 
00477         if(progmon && m_stored_photons > 0)
00478             progmon->StartJob(0.0, m_stored_photons);
00479 
00480         // Write each photon individually.
00481         for(int i = 1; i <= m_stored_photons; ++i) {
00482             m_photons[i].WriteToStream(os);
00483 
00484             if(progmon && ((i & 16383) == 0))
00485                 progmon->SetJobProgress(i);
00486         }
00487 
00488         if(progmon)
00489             progmon->Done();
00490 
00491         // Write the bounding box of the photon array.
00492         os.Write(m_bbox_min[0]);
00493         os.Write(m_bbox_min[1]);
00494         os.Write(m_bbox_min[2]);
00495         os.Write(m_bbox_max[0]);
00496         os.Write(m_bbox_max[1]);
00497         os.Write(m_bbox_max[2]);
00498 
00499         // Write remaining fields.
00500         os.Write(m_half_stored_photons);
00501 
00502         // The stream is automatically closed.
00503         return true;
00504     }
00505     catch(BinaryStream::BaseException) {
00506         // I/O error.
00507         return false;
00508     }
00509 }
00510 
00511 PhotonMap *PhotonMap::CreateFromFile(const string &filename,
00512                                      ProgressMonitor *progmon /*= 0*/)
00513 {
00514     PhotonMap *photon_map = 0;
00515 
00516     try {
00517         BinaryStream is(
00518             filename.c_str(),
00519             BinaryStream::InputStream,
00520             BinaryStream::BigEndian // by convention, photon map files are written using big endian convention
00521         );
00522 
00523         if(!is.IsOpen())
00524             return 0;
00525 
00526         // Set buffer size.
00527         is.SetBufferSize(PHOTONMAP_IO_BUFFER_SIZE);
00528 
00529         // Read photon map file signature and make sure it is valid.
00530         char *buf = new char[m_photon_map_sig.size() + 1];
00531         is.Read(buf, m_photon_map_sig.size());
00532         buf[m_photon_map_sig.size()] = '\0';
00533         if(string(buf) != m_photon_map_sig)
00534             return 0;
00535         delete [] buf;
00536 
00537         // Create the photon map.
00538         photon_map = new PhotonMap();
00539 
00540         // Read the number of stored photons.
00541         is.Read(&photon_map->m_stored_photons);
00542 
00543         if(photon_map->m_stored_photons > 0)
00544             photon_map->m_photons.reserve(photon_map->m_stored_photons);
00545 
00546         if(progmon && photon_map->m_stored_photons > 0)
00547             progmon->StartJob(1.0, photon_map->m_stored_photons);
00548 
00549         // Read each photon individually.
00550         for(int i = 1; i <= photon_map->m_stored_photons; ++i) {
00551             Photon photon;
00552             photon.ReadFromStream(is);
00553             photon_map->m_photons.push_back(photon);
00554 
00555             if(progmon && ((i & 32767) == 0))
00556                 progmon->SetJobProgress(i);
00557         }
00558 
00559         if(progmon)
00560             progmon->Done();
00561 
00562         // Read the bounding box of the photon array.
00563         is.Read(&photon_map->m_bbox_min[0]);
00564         is.Read(&photon_map->m_bbox_min[1]);
00565         is.Read(&photon_map->m_bbox_min[2]);
00566         is.Read(&photon_map->m_bbox_max[0]);
00567         is.Read(&photon_map->m_bbox_max[1]);
00568         is.Read(&photon_map->m_bbox_max[2]);
00569 
00570         // Read remaining fields.
00571         is.Read(&photon_map->m_half_stored_photons);
00572 
00573         // The stream is automatically closed.
00574         return photon_map;
00575     }
00576     catch(BinaryStream::BaseException) {
00577         // I/O error.
00578         delete photon_map;
00579         return 0;
00580     }
00581 }
00582 
00583 void PhotonMap::median_split(Photon **p,
00584                              int start, int end,
00585                              int median,
00586                              int axis) const
00587 {
00588     assert(p);
00589 
00590     int left = start;
00591     int right = end;
00592 
00593     while(right > left) {
00594         const float32 v = p[right]->m_position[axis];
00595         int i = left - 1;
00596         int j = right;
00597 
00598         while(true) {
00599             while(p[++i]->m_position[axis] < v) ;
00600             while(p[--j]->m_position[axis] > v && j > left) ;
00601 
00602             if(i >= j)
00603                 break;
00604 
00605             swap(p[i], p[j]);
00606         }
00607 
00608         swap(p[i], p[right]);
00609 
00610         if(i >= median)
00611             right = i - 1;
00612         if(i <= median)
00613             left = i + 1;
00614     }
00615 }
00616 
00617 void PhotonMap::balance_segment(Photon **pbal,
00618                                 Photon **porg,
00619                                 int index,
00620                                 int start, int end)
00621 {
00622     assert(pbal);
00623     assert(porg);
00624 
00625     // Compute new median.
00626 
00627     int median = 1;
00628 
00629     while(4 * median <= end - start + 1)
00630         median += median;
00631 
00632     if(3 * median <= end - start + 1) {
00633         median += median;
00634         median += start - 1;
00635     } else median = end - median + 1;
00636 
00637     // Find axis to split along.
00638 
00639     int axis = 2;
00640 
00641     if( (m_bbox_max[0] - m_bbox_min[0] > m_bbox_max[1] - m_bbox_min[1]) &&
00642         (m_bbox_max[0] - m_bbox_min[0] > m_bbox_max[2] - m_bbox_min[2]))
00643         axis = 0;
00644     else if(m_bbox_max[1] - m_bbox_min[1] > m_bbox_max[2] - m_bbox_min[2])
00645         axis = 1;
00646 
00647     // Partition photon block around the median.
00648 
00649     median_split(porg, start, end, median, axis);
00650 
00651     pbal[index] = porg[median];
00652     pbal[index]->SetSplittingPlaneAxis(axis);
00653 
00654     // Recursively balance the left and right block.
00655 
00656     if(median > start) {
00657         // Balance left segment.
00658         if(start < median - 1) {
00659             const float32 tmp = m_bbox_max[axis];
00660             m_bbox_max[axis] = pbal[index]->m_position[axis];
00661             balance_segment(pbal, porg, 2 * index, start, median - 1);
00662             m_bbox_max[axis] = tmp;
00663         } else pbal[2 * index] = porg[start];
00664     }
00665 
00666     if(median < end) {
00667         // Balance right segment.
00668         if(median + 1 < end) {
00669             const float32 tmp = m_bbox_min[axis];
00670             m_bbox_min[axis] = pbal[index]->m_position[axis];
00671             balance_segment(pbal, porg, 2 * index + 1, median + 1, end);
00672             m_bbox_min[axis] = tmp;
00673         } else pbal[2 * index + 1] = porg[end];
00674     }
00675 }
00676 
00677 void PhotonMap::locate_photons(nearest_photons *np, int index) const {
00678     const Photon *p = &m_photons[index];
00679 
00680     if(index < m_half_stored_photons) {
00681         // We're not in a leaf.
00682 
00683         const int plane = p->GetSplittingPlaneAxis();
00684         const float32 dist1 = np->m_position[plane] - p->m_position[plane];
00685 
00686         if(dist1 > 0.0f) {  // if dist1 is positive, search right plane first
00687             locate_photons(np, 2 * index + 1);
00688             // Also search left plane if updated bounding sphere crosses the splitting plane.
00689             if(dist1 * dist1 < np->m_dist2[0])
00690                 locate_photons(np, 2 * index);
00691         } else {    // dist1 is negative, search left plane first
00692             locate_photons(np, 2 * index);
00693             // Also search right plane if updated bounding sphere crosses the splitting plane.
00694             if(dist1 * dist1 < np->m_dist2[0])
00695                 locate_photons(np, 2 * index + 1);
00696         }
00697     }
00698 
00699     // Compute squared distance between current photon and np->m_position.
00700     float32 dist1;
00701     dist1 = p->m_position[0] - np->m_position[0];
00702     float32 dist2 = dist1 * dist1;
00703     dist1 = p->m_position[1] - np->m_position[1];
00704     dist2 += dist1 * dist1;
00705     dist1 = p->m_position[2] - np->m_position[2];
00706     dist2 += dist1 * dist1;
00707 
00708     if(dist2 < np->m_dist2[0]) {    // np->m_dist2[0] is the farthest photon
00709         // We found a photon. Insert it in the candidate list.
00710 
00711         if(np->m_found < np->m_max) {
00712             // Heap is not full; use array.
00713             ++np->m_found;
00714             np->m_dist2[np->m_found] = dist2;
00715             np->m_index[np->m_found] = p;
00716         } else {
00717             int j, parent;
00718 
00719             if(np->m_got_heap == 0) {   // do we need to build the heap?
00720                 // Build heap.
00721                 
00722                 float32 dst2;
00723                 const Photon *phot;
00724                 const int half_found = np->m_found >> 1;
00725 
00726                 for(int k = half_found; k >= 1; --k) {
00727                     parent = k;
00728                     phot = np->m_index[k];
00729                     dst2 = np->m_dist2[k];
00730 
00731                     while(parent <= half_found) {
00732                         j = parent + parent;
00733 
00734                         if(j < np->m_found && np->m_dist2[j] < np->m_dist2[j + 1])
00735                             ++j;
00736 
00737                         if(dst2 >= np->m_dist2[j])
00738                             break;
00739 
00740                         np->m_dist2[parent] = np->m_dist2[j];
00741                         np->m_index[parent] = np->m_index[j];
00742 
00743                         parent = j;
00744                     }
00745 
00746                     np->m_dist2[parent] = dst2;
00747                     np->m_index[parent] = phot;
00748                 }
00749 
00750                 np->m_got_heap = 1;
00751             }
00752 
00753             // Insert new photon into max heap. Delete largest element,
00754             // insert new, and reorder the heap.
00755 
00756             parent = 1;
00757             j = 2;  // left child of the root
00758 
00759             while(j <= np->m_found) {
00760                 if(j < np->m_found && np->m_dist2[j] < np->m_dist2[j + 1])
00761                     ++j;
00762 
00763                 if(dist2 > np->m_dist2[j])
00764                     break;
00765 
00766                 np->m_dist2[parent] = np->m_dist2[j];
00767                 np->m_index[parent] = np->m_index[j];
00768                 parent = j;
00769 
00770                 j += j;
00771             }
00772 
00773             //!\todo The 'pane' global illumination renderer (http://www.csit.fsu.edu/~beason/pane)
00774             //! does a check here. Check whether or not this is relevant.
00775             np->m_index[parent] = p;
00776             np->m_dist2[parent] = dist2;
00777 
00778             np->m_dist2[0] = np->m_dist2[1];
00779         }
00780     }
00781 }
00782 
00783 #ifdef ENABLE_RADIANCES_PRECOMPUTATION
00784 
00785 void PhotonMap::locate_nearest_photon_with_irradiance(const Vector3 &normal,
00786                                                       nearest_photons *np,
00787                                                       int index) const
00788 {
00789     const Photon *p = &m_photons[index];
00790 
00791     if(index < m_half_stored_photons) {
00792         // We're not in a leaf.
00793 
00794         const int plane = p->GetSplittingPlaneAxis();
00795         const float32 dist1 = np->m_position[plane] - p->m_position[plane];
00796 
00797         if(dist1 > 0.0f) {  // if dist1 is positive, search right plane first
00798             locate_nearest_photon_with_irradiance(normal, np, 2 * index + 1);
00799             // Also search left plane if updated bounding sphere crosses the splitting plane.
00800             if(dist1 * dist1 < np->m_dist2[0])
00801                 locate_nearest_photon_with_irradiance(normal, np, 2 * index);
00802         } else {    // dist1 is negative, search left plane first
00803             locate_nearest_photon_with_irradiance(normal, np, 2 * index);
00804             // Also search right plane if updated bounding sphere crosses the splitting plane.
00805             if(dist1 * dist1 < np->m_dist2[0])
00806                 locate_nearest_photon_with_irradiance(normal, np, 2 * index + 1);
00807         }
00808     }
00809 
00810     if(p->HasPrecomputedRadiance()) {
00811         const Real THRESHOLD = 0.9;
00812 
00813         if(normal * p->GetSurfaceNormal() >= THRESHOLD) {
00814             // Compute squared distance between current photon and np->m_position.
00815             float32 dist1;
00816             dist1 = p->m_position[0] - np->m_position[0];
00817             float32 dist2 = dist1 * dist1;
00818             dist1 = p->m_position[1] - np->m_position[1];
00819             dist2 += dist1 * dist1;
00820             dist1 = p->m_position[2] - np->m_position[2];
00821             dist2 += dist1 * dist1;
00822 
00823             if(dist2 < np->m_dist2[0]) {
00824                 // We found a photon closer than the closest one until now.
00825                 np->m_dist2[0] = dist2;
00826                 np->m_index[0] = p;
00827             }
00828         }
00829     }
00830 }
00831 
00832 #endif  // ENABLE_RADIANCES_PRECOMPUTATION

Generated on Tue May 11 01:31:51 2004 for toxic by doxygen 1.3.6