1 /*******************************************************************************
2 * photonsortingtask.cpp
3 *
4 * This module implements Photon Mapping.
5 *
6 * Author: Nathan Kopp
7 *
8 * ---------------------------------------------------------------------------
9 * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
10 * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
11 *
12 * POV-Ray is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Affero General Public License as
14 * published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * POV-Ray is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * ---------------------------------------------------------------------------
25 * POV-Ray is based on the popular DKB raytracer version 2.12.
26 * DKBTrace was originally written by David K. Buck.
27 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
28 * ---------------------------------------------------------------------------
29 * $File: //depot/public/povray/3.x/source/backend/lighting/photonsortingtask.cpp $
30 * $Revision: #1 $
31 * $Change: 6069 $
32 * $DateTime: 2013/11/06 11:59:40 $
33 * $Author: chrisc $
34 *******************************************************************************/
35
36 // frame.h must always be the first POV file included (pulls in platform config)
37 #include "backend/frame.h"
38 #include "base/povms.h"
39 #include "base/povmsgid.h"
40 #include "backend/math/vector.h"
41 #include "backend/math/matrices.h"
42 #include "backend/scene/objects.h"
43 #include "backend/shape/csg.h"
44 #include "backend/support/octree.h"
45 #include "backend/bounding/bbox.h"
46 #include "backend/scene/threaddata.h"
47 #include "backend/scene/scene.h"
48 #include "backend/scene/view.h"
49 #include "backend/support/msgutil.h"
50 #include "backend/lighting/point.h"
51 #include "backend/lighting/photonsortingtask.h"
52 #include "backend/lighting/photonshootingstrategy.h"
53 #include "lightgrp.h"
54
55 #include <algorithm>
56
57 // this must be the last file included
58 #include "base/povdebug.h"
59
60 namespace pov
61 {
62
63 /*
64 If you pass a NULL for the "strategy" parameter, then this will
65 load the photon map from a file.
66 Otherwise, it will:
67 1) merge
68 2) sort
69 3) compute gather options
70 4) clean up memory (delete the non-merged maps and delete the strategy)
71 */
PhotonSortingTask(ViewData * vd,const vector<PhotonMap * > & surfaceMaps,const vector<PhotonMap * > & mediaMaps,PhotonShootingStrategy * strategy)72 PhotonSortingTask::PhotonSortingTask(ViewData *vd, const vector<PhotonMap*>& surfaceMaps, const vector<PhotonMap*>& mediaMaps, PhotonShootingStrategy* strategy) :
73 RenderTask(vd),
74 surfaceMaps(surfaceMaps),
75 mediaMaps(mediaMaps),
76 strategy(strategy),
77 messageFactory(10, 370, "Photon", vd->GetSceneData()->backendAddress, vd->GetSceneData()->frontendAddress, vd->GetSceneData()->sceneId, 0), // TODO FIXME - Values need to come from the correct place!
78 cooperate(*this)
79 {
80 }
81
~PhotonSortingTask()82 PhotonSortingTask::~PhotonSortingTask()
83 {
84 }
85
SendProgress(void)86 void PhotonSortingTask::SendProgress(void)
87 {
88 #if 0
89 // TODO FIXME PHOTONS
90 // for now, we won't send this, as it can be confusing on the front-end due to out-of-order delivery from multiple threads.
91 // we need to create a new progress message for sorting.
92 if (timer.ElapsedRealTime() > 1000)
93 {
94 timer.Reset();
95 POVMS_Object obj(kPOVObjectClass_PhotonProgress);
96 obj.SetInt(kPOVAttrib_CurrentPhotonCount, (GetSceneData()->surfacePhotonMap.numPhotons + GetSceneData()->mediaPhotonMap.numPhotons));
97 RenderBackend::SendViewOutput(GetViewData()->GetViewId(), GetSceneData()->frontendAddress, kPOVMsgIdent_Progress, obj);
98 }
99 #endif
100 }
101
Run()102 void PhotonSortingTask::Run()
103 {
104 // quit right away if photons not enabled
105 if (!GetSceneData()->photonSettings.photonsEnabled) return;
106
107 Cooperate();
108
109 if(strategy!=NULL)
110 {
111 delete strategy;
112 sortPhotonMap();
113 }
114 else
115 {
116 if (!this->load())
117 messageFactory.Error(POV_EXCEPTION_STRING("Failed to load photon map from disk"), "Could not load photon map (%s)",GetSceneData()->photonSettings.fileName);
118
119 // set photon options automatically
120 if (GetSceneData()->surfacePhotonMap.numPhotons>0)
121 GetSceneData()->surfacePhotonMap.setGatherOptions(GetSceneData()->photonSettings,false);
122 if (GetSceneData()->mediaPhotonMap.numPhotons>0)
123 GetSceneData()->mediaPhotonMap.setGatherOptions(GetSceneData()->photonSettings,true);
124 }
125
126 // good idea to make sure all warnings and errors arrive frontend now [trf]
127 SendProgress();
128 Cooperate();
129 }
130
Stopped()131 void PhotonSortingTask::Stopped()
132 {
133 // nothing to do for now [trf]
134 }
135
Finish()136 void PhotonSortingTask::Finish()
137 {
138 GetViewDataPtr()->timeType = SceneThreadData::kPhotonTime;
139 GetViewDataPtr()->realTime = ConsumedRealTime();
140 GetViewDataPtr()->cpuTime = ConsumedCPUTime();
141 }
142
143
sortPhotonMap()144 void PhotonSortingTask::sortPhotonMap()
145 {
146 vector<PhotonMap*>::iterator mapIter;
147 for(mapIter = surfaceMaps.begin(); mapIter != surfaceMaps.end(); mapIter++)
148 {
149 GetSceneData()->surfacePhotonMap.mergeMap(*mapIter);
150 //delete (*mapIter);
151 }
152 for(mapIter = mediaMaps.begin(); mapIter != mediaMaps.end(); mapIter++)
153 {
154 GetSceneData()->mediaPhotonMap.mergeMap(*mapIter);
155 //delete (*mapIter);
156 }
157
158 /* now actually build the kd-tree by sorting the array of photons */
159 if (GetSceneData()->surfacePhotonMap.numPhotons>0)
160 {
161 //povwin::WIN32_DEBUG_FILE_OUTPUT("\n\nsurfacePhotonMap.buildTree about to be called\n");
162
163 GetSceneData()->surfacePhotonMap.buildTree();
164 GetSceneData()->surfacePhotonMap.setGatherOptions(GetSceneData()->photonSettings,false);
165 // povwin::WIN32_DEBUG_FILE_OUTPUT("gatherNumSteps: %d\n",GetSceneData()->surfacePhotonMap.gatherNumSteps);
166 // povwin::WIN32_DEBUG_FILE_OUTPUT("gatherRadStep: %lf\n",GetSceneData()->surfacePhotonMap.gatherRadStep);
167 // povwin::WIN32_DEBUG_FILE_OUTPUT("minGatherRad: %lf\n",GetSceneData()->surfacePhotonMap.minGatherRad);
168 // povwin::WIN32_DEBUG_FILE_OUTPUT("minGatherRadMult: %lf\n",GetSceneData()->surfacePhotonMap.minGatherRadMult);
169 // povwin::WIN32_DEBUG_FILE_OUTPUT("numBlocks: %d\n",GetSceneData()->surfacePhotonMap.numBlocks);
170 // povwin::WIN32_DEBUG_FILE_OUTPUT("numPhotons: %d\n",GetSceneData()->surfacePhotonMap.numPhotons);
171 }
172
173 #ifdef GLOBAL_PHOTONS
174 /* ----------- global photons ------------- */
175 if (globalPhotonMap.numPhotons>0)
176 {
177 globalPhotonMap.buildTree();
178 globalPhotonMap.setGatherOptions(false);
179 }
180 #endif
181
182 /* ----------- media photons ------------- */
183 if (GetSceneData()->mediaPhotonMap.numPhotons>0)
184 {
185 GetSceneData()->mediaPhotonMap.buildTree();
186 GetSceneData()->mediaPhotonMap.setGatherOptions(GetSceneData()->photonSettings,true);
187 }
188
189 if (GetSceneData()->surfacePhotonMap.numPhotons+
190 #ifdef GLOBAL_PHOTONS
191 globalPhotonMap.numPhotons+
192 #endif
193 GetSceneData()->mediaPhotonMap.numPhotons > 0)
194 {
195 /* should we load the photon map now that it is built? */
196 if (GetSceneData()->photonSettings.fileName && !GetSceneData()->photonSettings.loadFile)
197 {
198 /* status bar for user */
199 // Send_Progress("Saving Photon Maps", PROGRESS_SAVING_PHOTON_MAPS);
200 if (!this->save())
201 messageFactory.Warning(0,"Could not save photon map.");
202 }
203 }
204 else
205 {
206 if (GetSceneData()->photonSettings.fileName && !GetSceneData()->photonSettings.loadFile)
207 messageFactory.Warning(0,"Could not save photon map - no photons!");
208 }
209 }
210
211
212 /* savePhotonMap()
213
214 Saves the caustic photon map to a file.
215
216 Preconditions:
217 InitBacktraceEverything was called
218 the photon map has been built and balanced
219 photonSettings.fileName contains the filename to save
220
221 Postconditions:
222 Returns 1 if success, 0 if failure.
223 If success, the photon map has been written to the file.
224 */
save()225 int PhotonSortingTask::save()
226 {
227 Photon *ph;
228 FILE *f;
229 int i;
230 size_t err;
231 int numph;
232
233 f = fopen(GetSceneData()->photonSettings.fileName, "wb");
234 if (!f) return 0;
235
236 /* caustic photons */
237 numph = GetSceneData()->surfacePhotonMap.numPhotons;
238 fwrite(&numph, sizeof(numph),1,f);
239 if (numph>0 && GetSceneData()->surfacePhotonMap.head)
240 {
241 for(i=0; i<numph; i++)
242 {
243 ph = &(PHOTON_AMF(GetSceneData()->surfacePhotonMap.head, i));
244 err = fwrite(ph, sizeof(Photon), 1, f);
245
246 if (err<=0)
247 {
248 /* fwrite returned an error! */
249 fclose(f);
250 return 0;
251 }
252 }
253 }
254 else
255 {
256 messageFactory.PossibleError("Photon map for surface is empty.");
257 }
258
259 #ifdef GLOBAL_PHOTONS
260 /* global photons */
261 numph = globalPhotonMap.numPhotons;
262 fwrite(&numph, sizeof(numph),1,f);
263 if (numph>0 && globalPhotonMap.head)
264 {
265 for(i=0; i<numph; i++)
266 {
267 ph = &(PHOTON_AMF(globalPhotonMap.head, i));
268 err = fwrite(ph, sizeof(Photon), 1, f);
269
270 if (err<=0)
271 {
272 /* fwrite returned an error! */
273 fclose(f);
274 return 0;
275 }
276 }
277 }
278 else
279 {
280 messageFactory.PossibleError("Global photon map is empty.");
281 }
282 #endif
283
284 /* media photons */
285 numph = GetSceneData()->mediaPhotonMap.numPhotons;
286 fwrite(&numph, sizeof(numph),1,f);
287 if (numph>0 && GetSceneData()->mediaPhotonMap.head)
288 {
289 for(i=0; i<numph; i++)
290 {
291 ph = &(PHOTON_AMF(GetSceneData()->mediaPhotonMap.head, i));
292 err = fwrite(ph, sizeof(Photon), 1, f);
293
294 if (err<=0)
295 {
296 /* fwrite returned an error! */
297 fclose(f);
298 return 0;
299 }
300 }
301 }
302 else
303 {
304 messageFactory.PossibleError("Photon map for media is empty.");
305 }
306
307 fclose(f);
308 return true;
309 }
310
311 /* loadPhotonMap()
312
313 Loads the caustic photon map from a file.
314
315 Preconditions:
316 InitBacktraceEverything was called
317 the photon map is empty
318 renderer->sceneData->photonSettings.fileName contains the filename to load
319
320 Postconditions:
321 Returns 1 if success, 0 if failure.
322 If success, the photon map has been loaded from the file.
323 If failure then the render should stop with an error
324 */
load()325 int PhotonSortingTask::load()
326 {
327 int i;
328 size_t err;
329 Photon *ph;
330 FILE *f;
331 int numph;
332
333 if (!GetSceneData()->photonSettings.photonsEnabled) return 0;
334
335 messageFactory.Warning(0,"Starting the load of photon file %s\n",GetSceneData()->photonSettings.fileName);
336
337 f = fopen(GetSceneData()->photonSettings.fileName, "rb");
338 if (!f) return 0;
339
340 fread(&numph, sizeof(numph),1,f);
341
342 for(i=0; i<numph; i++)
343 {
344 ph = GetSceneData()->surfacePhotonMap.AllocatePhoton();
345 err = fread(ph, sizeof(Photon), 1, f);
346
347 if (err<=0)
348 {
349 /* fread returned an error! */
350 fclose(f);
351 return 0;
352 }
353 }
354
355 if (!feof(f)) /* for backwards file format compatibility */
356 {
357
358 #ifdef GLOBAL_PHOTONS
359 /* global photons */
360 fread(&numph, sizeof(numph),1,f);
361 for(i=0; i<numph; i++)
362 {
363 ph = GetSceneData()->globalPhotonMap.AllocatePhoton();
364 err = fread(ph, sizeof(Photon), 1, f);
365
366 if (err<=0)
367 {
368 /* fread returned an error! */
369 fclose(f);
370 return 0;
371 }
372 }
373 #endif
374
375 /* media photons */
376 fread(&numph, sizeof(numph),1,f);
377 for(i=0; i<numph; i++)
378 {
379 ph = GetSceneData()->mediaPhotonMap.AllocatePhoton();
380 err = fread(ph, sizeof(Photon), 1, f);
381
382 if (err<=0)
383 {
384 /* fread returned an error! */
385 fclose(f);
386 return 0;
387 }
388 }
389
390 }
391
392 fclose(f);
393 return true;
394 }
395
396
397 }
398