1 |
rschregle |
1.1 |
/* |
2 |
rschregle |
1.2 |
====================================================================== |
3 |
rschregle |
1.1 |
Photon map interface to out-of-core octree |
4 |
|
|
|
5 |
|
|
Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) |
6 |
|
|
(c) Lucerne University of Applied Sciences and Arts, |
7 |
rschregle |
1.2 |
supported by the Swiss National Science Foundation (SNSF, #147053) |
8 |
|
|
====================================================================== |
9 |
rschregle |
1.1 |
|
10 |
rschregle |
1.6 |
$Id: pmapooc.c,v 1.5 2018/11/08 00:54:07 greg Exp $ |
11 |
rschregle |
1.1 |
*/ |
12 |
|
|
|
13 |
|
|
|
14 |
rschregle |
1.2 |
|
15 |
rschregle |
1.1 |
#include "pmapdata.h" /* Includes pmapooc.h */ |
16 |
|
|
#include "source.h" |
17 |
greg |
1.5 |
#include "otspecial.h" |
18 |
rschregle |
1.1 |
#include "oocsort.h" |
19 |
|
|
#include "oocbuild.h" |
20 |
|
|
|
21 |
|
|
|
22 |
|
|
|
23 |
|
|
/* Returns photon position as sorting key for OOC_Octree & friends (notably |
24 |
|
|
* for Morton code generation). |
25 |
|
|
* !!! Uses type conversion from float via TEMPORARY storage; |
26 |
|
|
* !!! THIS IS NOT THREAD SAFE! |
27 |
|
|
* !!! RETURNED KEY PERSISTS ONLY IF COPIED BEFORE NEXT CALL! */ |
28 |
|
|
RREAL *OOC_PhotonKey (const void *p) |
29 |
|
|
{ |
30 |
|
|
static FVECT photonPos; /* Temp storage for type conversion */ |
31 |
|
|
|
32 |
|
|
VCOPY(photonPos, ((Photon*)p) -> pos); |
33 |
|
|
return photonPos; |
34 |
|
|
} |
35 |
|
|
|
36 |
|
|
|
37 |
|
|
|
38 |
|
|
#ifdef DEBUG_OOC |
39 |
|
|
static int OOC_CheckKeys (FILE *file, const OOC_Octree *oct) |
40 |
|
|
{ |
41 |
|
|
Photon p, lastp; |
42 |
|
|
RREAL *key; |
43 |
|
|
OOC_MortonIdx zIdx, lastzIdx = 0; |
44 |
|
|
|
45 |
|
|
rewind(file); |
46 |
|
|
memset(&lastp, 0, sizeof(lastp)); |
47 |
|
|
|
48 |
|
|
while (fread(&p, sizeof(p), 1, file) > 0) { |
49 |
|
|
key = OOC_PhotonKey(&p); |
50 |
|
|
zIdx = OOC_KEY2MORTON(key, oct); |
51 |
|
|
|
52 |
|
|
if (zIdx < lastzIdx) { |
53 |
|
|
error(INTERNAL, "photons not sorted"); |
54 |
|
|
return -1; |
55 |
|
|
} |
56 |
|
|
|
57 |
|
|
if (zIdx == lastzIdx) { |
58 |
|
|
sprintf(errmsg, "identical key %021ld " |
59 |
|
|
"for [%.9f, %.9f, %.9f]\tand [%.9f, %.9f, %.9f]", |
60 |
|
|
zIdx, lastp.pos [0], lastp.pos [1], lastp.pos [2], |
61 |
|
|
p.pos [0], p.pos [1], p.pos [2]); |
62 |
|
|
error(WARNING, errmsg); |
63 |
|
|
} |
64 |
|
|
|
65 |
|
|
lastzIdx = zIdx; |
66 |
|
|
memcpy(&lastp, &p, sizeof(p)); |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
return 0; |
70 |
|
|
} |
71 |
|
|
#endif |
72 |
|
|
|
73 |
|
|
|
74 |
|
|
|
75 |
|
|
void OOC_BuildPhotonMap (struct PhotonMap *pmap, unsigned numProc) |
76 |
|
|
{ |
77 |
|
|
FILE *leafFile; |
78 |
|
|
char leafFname [1024]; |
79 |
|
|
FVECT d, octOrg; |
80 |
|
|
int i; |
81 |
|
|
RREAL octSize = 0; |
82 |
|
|
|
83 |
|
|
/* Determine octree size and origin from pmap extent and init octree */ |
84 |
|
|
VCOPY(octOrg, pmap -> minPos); |
85 |
|
|
VSUB(d, pmap -> maxPos, pmap -> minPos); |
86 |
|
|
for (i = 0; i < 3; i++) |
87 |
|
|
if (octSize < d [i]) |
88 |
|
|
octSize = d [i]; |
89 |
|
|
|
90 |
|
|
if (octSize < FTINY) |
91 |
|
|
error(INTERNAL, "zero octree size in OOC_BuildPhotonMap"); |
92 |
|
|
|
93 |
|
|
/* Derive leaf filename from photon map and open file */ |
94 |
|
|
strncpy(leafFname, pmap -> fileName, sizeof(leafFname)); |
95 |
|
|
strncat(leafFname, PMAP_OOC_LEAFSUFFIX, |
96 |
|
|
sizeof(leafFname) - strlen(leafFname) - 1); |
97 |
|
|
if (!(leafFile = fopen(leafFname, "w+b"))) |
98 |
|
|
error(SYSTEM, "failed to open leaf file in OOC_BuildPhotonMap"); |
99 |
|
|
|
100 |
|
|
#ifdef DEBUG_OOC |
101 |
|
|
eputs("Sorting photons by Morton code...\n"); |
102 |
|
|
#endif |
103 |
|
|
|
104 |
|
|
/* Sort photons in heapfile by Morton code and write to leaf file */ |
105 |
|
|
if (OOC_Sort(pmap -> heap, leafFile, PMAP_OOC_NUMBLK, PMAP_OOC_BLKSIZE, |
106 |
|
|
numProc, sizeof(Photon), octOrg, octSize, OOC_PhotonKey)) |
107 |
|
|
error(INTERNAL, "failed out-of-core photon sort in OOC_BuildPhotonMap"); |
108 |
|
|
|
109 |
|
|
/* Init and build octree */ |
110 |
|
|
OOC_Init(&pmap -> store, sizeof(Photon), octOrg, octSize, OOC_PhotonKey, |
111 |
|
|
leafFile); |
112 |
|
|
|
113 |
|
|
#ifdef DEBUG_OOC |
114 |
|
|
eputs("Checking leaf file consistency...\n"); |
115 |
|
|
OOC_CheckKeys(leafFile, &pmap -> store); |
116 |
|
|
|
117 |
|
|
eputs("Building out-of-core octree...\n"); |
118 |
|
|
#endif |
119 |
|
|
|
120 |
|
|
if (!OOC_Build(&pmap -> store, PMAP_OOC_LEAFMAX, PMAP_OOC_MAXDEPTH)) |
121 |
|
|
error(INTERNAL, "failed out-of-core octree build in OOC_BuildPhotonMap"); |
122 |
|
|
|
123 |
|
|
#ifdef DEBUG_OOC |
124 |
|
|
eputs("Checking out-of-core octree consistency...\n"); |
125 |
|
|
if (OOC_Check(&pmap -> store, OOC_ROOT(&pmap -> store), |
126 |
|
|
octOrg, octSize, 0)) |
127 |
|
|
error(INTERNAL, "inconsistent out-of-core octree; Time4Harakiri"); |
128 |
|
|
#endif |
129 |
|
|
} |
130 |
|
|
|
131 |
|
|
|
132 |
|
|
|
133 |
|
|
int OOC_SavePhotons (const struct PhotonMap *pmap, FILE *out) |
134 |
|
|
{ |
135 |
|
|
return OOC_SaveOctree(&pmap -> store, out); |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
|
139 |
|
|
|
140 |
|
|
int OOC_LoadPhotons (struct PhotonMap *pmap, FILE *nodeFile) |
141 |
|
|
{ |
142 |
|
|
FILE *leafFile; |
143 |
|
|
char leafFname [1024]; |
144 |
|
|
|
145 |
|
|
/* Derive leaf filename from photon map and open file */ |
146 |
|
|
strncpy(leafFname, pmap -> fileName, sizeof(leafFname)); |
147 |
|
|
strncat(leafFname, PMAP_OOC_LEAFSUFFIX, |
148 |
|
|
sizeof(leafFname) - strlen(leafFname) - 1); |
149 |
|
|
|
150 |
|
|
if (!(leafFile = fopen(leafFname, "r"))) |
151 |
|
|
error(SYSTEM, "failed to open leaf file in OOC_LoadPhotons"); |
152 |
|
|
|
153 |
|
|
if (OOC_LoadOctree(&pmap -> store, nodeFile, OOC_PhotonKey, leafFile)) |
154 |
|
|
return -1; |
155 |
|
|
|
156 |
|
|
return 0; |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
|
160 |
|
|
|
161 |
|
|
void OOC_InitFindPhotons (struct PhotonMap *pmap) |
162 |
|
|
{ |
163 |
|
|
if (OOC_InitNearest(&pmap -> squeue, pmap -> maxGather + 1, |
164 |
|
|
sizeof(Photon))) |
165 |
|
|
error(SYSTEM, "can't allocate photon search queue"); |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
|
169 |
|
|
|
170 |
|
|
static void OOC_InitPhotonCache (struct PhotonMap *pmap) |
171 |
|
|
/* Initialise OOC photon cache */ |
172 |
|
|
{ |
173 |
|
|
static char warn = 1; |
174 |
|
|
|
175 |
|
|
if (!pmap -> store.cache && !pmap -> numDensity) { |
176 |
|
|
if (pmapCacheSize > 0) { |
177 |
|
|
const unsigned pageSize = pmapCachePageSize * pmap -> maxGather, |
178 |
|
|
numPages = pmapCacheSize / pageSize; |
179 |
|
|
/* Allocate & init I/O cache in octree */ |
180 |
|
|
pmap -> store.cache = malloc(sizeof(OOC_Cache)); |
181 |
|
|
|
182 |
|
|
if (!pmap -> store.cache || |
183 |
|
|
OOC_CacheInit(pmap -> store.cache, numPages, pageSize, |
184 |
|
|
sizeof(Photon))) { |
185 |
|
|
error(SYSTEM, "failed OOC photon map cache init"); |
186 |
|
|
} |
187 |
|
|
} |
188 |
|
|
else if (warn) { |
189 |
|
|
error(WARNING, "OOC photon map cache DISABLED"); |
190 |
|
|
warn = 0; |
191 |
|
|
} |
192 |
|
|
} |
193 |
|
|
} |
194 |
|
|
|
195 |
|
|
|
196 |
|
|
|
197 |
|
|
typedef struct { |
198 |
|
|
const PhotonMap *pmap; |
199 |
|
|
const float *norm; |
200 |
|
|
} OOC_FilterData; |
201 |
|
|
|
202 |
|
|
|
203 |
|
|
|
204 |
|
|
int OOC_FilterPhoton (void *p, void *fd) |
205 |
|
|
/* Filter callback for photon kNN search, used by OOC_FindNearest() */ |
206 |
|
|
{ |
207 |
|
|
const Photon *photon = p; |
208 |
|
|
const OOC_FilterData *filtData = fd; |
209 |
|
|
const PhotonMap *pmap = filtData -> pmap; |
210 |
|
|
|
211 |
|
|
/* Reject photon if normal faces away (ignored for volume photons) with |
212 |
|
|
* tolerance to account for perturbation; note photon normal is coded |
213 |
|
|
* in range [-127,127], hence we factor this in */ |
214 |
|
|
if (filtData -> norm && |
215 |
|
|
DOT(filtData->norm, photon->norm) <= PMAP_NORM_TOL * 127 * frandom()) |
216 |
|
|
return 0; |
217 |
|
|
|
218 |
rschregle |
1.3 |
if (isContribPmap(pmap)) { |
219 |
|
|
/* Lookup in contribution photon map; filter according to emitting |
220 |
|
|
* light source if contrib list set, else accept all */ |
221 |
|
|
|
222 |
|
|
if (pmap -> srcContrib) { |
223 |
|
|
OBJREC *srcMod; |
224 |
|
|
const int srcIdx = photonSrcIdx(pmap, photon); |
225 |
rschregle |
1.1 |
|
226 |
rschregle |
1.3 |
if (srcIdx < 0 || srcIdx >= nsources) |
227 |
|
|
error(INTERNAL, "invalid light source index in photon map"); |
228 |
rschregle |
1.1 |
|
229 |
rschregle |
1.3 |
srcMod = findmaterial(source [srcIdx].so); |
230 |
rschregle |
1.1 |
|
231 |
rschregle |
1.3 |
/* Reject photon if contributions from light source which emitted |
232 |
|
|
* it are not sought */ |
233 |
|
|
if (!lu_find(pmap -> srcContrib, srcMod -> oname) -> data) |
234 |
|
|
return 0; |
235 |
|
|
} |
236 |
rschregle |
1.1 |
|
237 |
|
|
/* Reject non-caustic photon if lookup for caustic contribs */ |
238 |
|
|
if (pmap -> lookupCaustic && !photon -> caustic) |
239 |
|
|
return 0; |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
/* Accept photon */ |
243 |
|
|
return 1; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
|
247 |
|
|
|
248 |
rschregle |
1.6 |
int OOC_FindPhotons (struct PhotonMap *pmap, const FVECT pos, const FVECT norm) |
249 |
rschregle |
1.1 |
{ |
250 |
|
|
OOC_SearchFilter filt; |
251 |
|
|
OOC_FilterData filtData; |
252 |
|
|
float n [3]; |
253 |
|
|
|
254 |
|
|
/* Lazily init OOC cache */ |
255 |
|
|
if (!pmap -> store.cache) |
256 |
|
|
OOC_InitPhotonCache(pmap); |
257 |
|
|
|
258 |
|
|
/* Set up filter callback */ |
259 |
|
|
filtData.pmap = pmap; |
260 |
rschregle |
1.4 |
if (norm) |
261 |
|
|
VCOPY(n, norm); |
262 |
|
|
filtData.norm = norm ? n : NULL; |
263 |
rschregle |
1.1 |
filt.data = &filtData; |
264 |
|
|
filt.func = OOC_FilterPhoton; |
265 |
|
|
|
266 |
|
|
pmap -> maxDist2 = OOC_FindNearest(&pmap -> store, |
267 |
|
|
OOC_ROOT(&pmap -> store), 0, |
268 |
|
|
pmap -> store.org, pmap -> store.size, |
269 |
|
|
pos, &filt, &pmap -> squeue, |
270 |
|
|
pmap -> maxDist2); |
271 |
|
|
|
272 |
|
|
if (pmap -> maxDist2 < 0) |
273 |
|
|
error(INTERNAL, "failed k-NN photon lookup in OOC_FindPhotons"); |
274 |
rschregle |
1.6 |
|
275 |
|
|
/* Return success or failure (empty queue => none found) */ |
276 |
|
|
return pmap -> squeue.tail ? 0 : -1; |
277 |
rschregle |
1.1 |
} |
278 |
|
|
|
279 |
|
|
|
280 |
|
|
|
281 |
rschregle |
1.6 |
int OOC_Find1Photon (struct PhotonMap* pmap, const FVECT pos, |
282 |
|
|
const FVECT norm, Photon *photon) |
283 |
rschregle |
1.1 |
{ |
284 |
|
|
OOC_SearchFilter filt; |
285 |
|
|
OOC_FilterData filtData; |
286 |
rschregle |
1.6 |
float n [3], maxDist2; |
287 |
rschregle |
1.1 |
|
288 |
|
|
/* Lazily init OOC cache */ |
289 |
|
|
if (!pmap -> store.cache) |
290 |
|
|
OOC_InitPhotonCache(pmap); |
291 |
|
|
|
292 |
|
|
/* Set up filter callback */ |
293 |
|
|
filtData.pmap = pmap; |
294 |
rschregle |
1.4 |
if (norm) |
295 |
|
|
VCOPY(n, norm); |
296 |
|
|
filtData.norm = norm ? n : NULL; |
297 |
rschregle |
1.1 |
filt.data = &filtData; |
298 |
|
|
filt.func = OOC_FilterPhoton; |
299 |
|
|
|
300 |
rschregle |
1.6 |
maxDist2 = OOC_Find1Nearest(&pmap -> store, |
301 |
|
|
OOC_ROOT(&pmap -> store), 0, |
302 |
|
|
pmap -> store.org, pmap -> store.size, |
303 |
|
|
pos, &filt, photon, pmap -> maxDist2); |
304 |
|
|
|
305 |
|
|
if (maxDist2 < 0) |
306 |
|
|
error(INTERNAL, "failed 1-NN photon lookup in OOC_Find1Photon"); |
307 |
|
|
|
308 |
|
|
if (maxDist2 >= pmap -> maxDist2) |
309 |
|
|
/* No photon found => failed */ |
310 |
|
|
return -1; |
311 |
|
|
else { |
312 |
|
|
/* Set photon distance => success */ |
313 |
|
|
pmap -> maxDist2 = maxDist2; |
314 |
|
|
return 0; |
315 |
|
|
} |
316 |
rschregle |
1.1 |
} |
317 |
|
|
|
318 |
|
|
|
319 |
|
|
|
320 |
|
|
int OOC_GetPhoton (struct PhotonMap *pmap, PhotonIdx idx, |
321 |
|
|
Photon *photon) |
322 |
|
|
{ |
323 |
|
|
return OOC_GetData(&pmap -> store, idx, photon); |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
|
327 |
|
|
|
328 |
|
|
Photon *OOC_GetNearestPhoton (const PhotonSearchQueue *squeue, PhotonIdx idx) |
329 |
|
|
{ |
330 |
|
|
return OOC_GetNearest(squeue, idx); |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
|
334 |
|
|
|
335 |
|
|
PhotonIdx OOC_FirstPhoton (const struct PhotonMap* pmap) |
336 |
|
|
{ |
337 |
|
|
return 0; |
338 |
|
|
} |