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