ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/oococt.c
Revision: 2.3
Committed: Tue May 17 17:39:47 2016 UTC (8 years ago) by rschregle
Content type: text/plain
Branch: MAIN
Changes since 2.2: +417 -139 lines
Log Message:
Initial import of ooC photon map

File Contents

# Content
1 /*
2 ======================================================================
3 Out-of-core octree data structure
4
5 Roland Schregle (roland.schregle@{hslu.ch, gmail.com})
6 (c) Lucerne University of Applied Sciences and Arts,
7 supported by the Swiss National Science Foundation (SNSF, #147053)
8 ======================================================================
9
10 $Id: oococt.c,v 1.28 2016/04/25 17:49:50 taschreg Exp taschreg $
11 */
12
13
14
15 #include "oococt.h"
16 #include "rtio.h"
17 #include <stdlib.h>
18 #include <unistd.h>
19
20
21
22 void OOC_Null (OOC_Octree *oct)
23 /* Init empty octree */
24 {
25 oct -> maxNodes = oct -> numNodes = oct -> leafMax = oct -> maxDepth =
26 oct -> numData = oct -> size = oct -> recSize = oct -> mortonScale = 0;
27 oct -> org [0] = oct -> org [1] = oct -> org [2] =
28 oct -> bound [0] = oct -> bound [1] = oct -> bound [2] = 0;
29 oct -> key = NULL;
30 oct -> nodes = NULL;
31 oct -> leafFile = NULL;
32 oct -> cache = NULL;
33 }
34
35
36
37 void OOC_Init (OOC_Octree *oct, unsigned recSize, FVECT org, RREAL size,
38 RREAL *(*key)(const void*), FILE *leafFile)
39 {
40 oct -> maxNodes = oct -> numNodes = oct -> leafMax =
41 oct -> maxDepth = oct -> numData = 0;
42 VCOPY(oct -> org, org);
43 oct -> size = oct -> bound[0] = oct -> bound[1] = oct -> bound[2] = size;
44 VADD(oct -> bound, oct -> bound, org);
45 oct -> mortonScale = size > 0 ? OOC_MORTON_MAX / size : 0;
46 oct -> recSize = recSize;
47 oct -> key = key;
48 oct -> nodes = NULL;
49 oct -> leafFile = leafFile;
50 oct -> cache = NULL; /* Cache currently initialised externally */
51 }
52
53
54
55 OOC_Node *NewNode (OOC_Octree *oct)
56 /* Allocate new octree node, enlarge array if necessary.
57 Return pointer to new node or NULL if failed. */
58 {
59 OOC_Node *n = NULL;
60
61 if (oct -> numNodes >= OOC_NODEIDX_MAX) {
62 /* Node index overflow */
63 fprintf(stderr, "OOC_NewNode: node index overflow (numNodes = %d)\n",
64 oct -> numNodes);
65 return NULL;
66 }
67
68 if (++oct -> numNodes > oct -> maxNodes) {
69 /* Need to allocate root or enlarge array */
70 oct -> maxNodes += OOC_BLKSIZE / sizeof(OOC_Node);
71 oct -> nodes = realloc(oct -> nodes,
72 oct -> maxNodes * sizeof(OOC_Node));
73 if (!oct -> nodes) {
74 perror("OOC_NewNode: couldn't realloc() nodes");
75 return NULL;
76 }
77 }
78
79 n = oct -> nodes + oct -> numNodes - 1;
80 n -> node.num = n -> node.kid = n -> node.branch = n -> node.type = 0;
81
82 return n;
83 }
84
85
86
87 int OOC_GetData (OOC_Octree *oct, OOC_DataIdx idx, void *rec)
88 /* Reads indexed data record from leaf file and copies it to rec, else
89 * returns -1 on failure */
90 {
91 if (!oct || !rec)
92 return -1;
93
94 if (idx >= oct -> numData) {
95 fprintf(stderr, "OOC_GetData: invalid data record index %u\n", idx);
96 return -1;
97 }
98
99 if (oct -> cache) {
100 /* Retrieve record from leaf file via I/O cache */
101 void *cachedRec;
102
103 if (!(cachedRec = OOC_CacheData(oct -> cache, oct -> leafFile, idx)))
104 return -1;
105
106 /* It's safer to copy the record to the caller's local buffer as a
107 * returned pointer may be invalidated by subsequent calls if the
108 * page it points to is swapped out */
109 memcpy(rec, cachedRec, oct -> recSize);
110 }
111 else {
112 /* No I/O caching; do (SLOW!) per-record I/O from leaf file */
113 const unsigned long pos = (unsigned long)oct -> recSize * idx;
114
115 if (pread(fileno(oct -> leafFile), rec, oct -> recSize, pos) !=
116 oct -> recSize) {
117 perror("OOC_GetData: failed seek/read in leaf file");
118 return -1;
119 }
120 }
121
122 return 0;
123 }
124
125
126
127 int OOC_InBBox (const OOC_Octree *oct, const FVECT org, RREAL size,
128 const FVECT key)
129 /* Check whether key lies within bounding box defined by org and size.
130 * Compares Morton codes rather than the coordinates directly to account
131 * for dicretisation error introduced by the former. */
132 {
133 const OOC_MortonIdx keyZ = OOC_KEY2MORTON(key, oct);
134 FVECT bbox = {size, size, size};
135
136 VADD(bbox, org, bbox);
137 return keyZ > OOC_KEY2MORTON(org, oct) &&
138 keyZ < OOC_KEY2MORTON(bbox, oct);
139 }
140
141
142
143 int OOC_Branch (const OOC_Octree *oct, const FVECT org, RREAL size,
144 const FVECT key, FVECT nuOrg)
145 /* Return index of octant containing key and corresponding origin if nuOrg
146 * != NULL, or -1 if key lies outside all octants. Size is already assumed
147 * to be halved, i.e. corresponding to the length of an octant axis.
148 * Compares Morton codes rather than the coordinates directly to account
149 * for dicretisation error introduced by the former. */
150 {
151 int o;
152 FVECT octOrg;
153
154 for (o = 0; o < 8; o++) {
155 VCOPY(octOrg, org);
156 OOC_OCTORIGIN(octOrg, o, size);
157
158 if (OOC_InBBox(oct, octOrg, size, key)) {
159 if (nuOrg)
160 VCOPY(nuOrg, octOrg);
161
162 return o;
163 }
164 }
165
166 return -1;
167 }
168
169
170
171 int OOC_BranchZ (const OOC_Octree *oct, const FVECT org, RREAL size,
172 OOC_MortonIdx keyZ, FVECT nuOrg)
173 /* Optimised version of OOC_Branch() with precomputed key Morton code */
174 {
175 int o;
176 const FVECT cent = {size, size, size};
177 FVECT octOrg, bbox;
178
179 for (o = 0; o < 8; o++) {
180 VCOPY(octOrg, org);
181 OOC_OCTORIGIN(octOrg, o, size);
182 VADD(bbox, octOrg, cent);
183
184 if (keyZ > OOC_KEY2MORTON(octOrg, oct) &&
185 keyZ < OOC_KEY2MORTON(bbox, oct)) {
186 if (nuOrg)
187 VCOPY(nuOrg, octOrg);
188
189 return o;
190 }
191 }
192
193 return -1;
194 }
195
196
197
198 OOC_DataIdx OOC_GetKid (const OOC_Octree *oct, OOC_Node **node, unsigned kid)
199 /* For a given octree node, return the sum of the data counters for kids
200 * [0..k-1]. On return, the node either points to the k'th kid, or
201 * NULL if the kid is nonexistent or the node is a leaf */
202 {
203 unsigned k;
204 OOC_Node *kidNode = OOC_KID1(oct, *node); /* NULL if leaf */
205 OOC_DataIdx dataIdx = 0;
206
207 for (k = 0; k < kid; k++) {
208 if (OOC_ISLEAF(*node))
209 /* Sum data counters of leaf octants */
210 dataIdx += (*node) -> leaf.num [k];
211 else
212 /* Sum data counter of inner node's nonempty kids and advance
213 * pointer to sibling */
214 if (OOC_ISBRANCH(*node, k)) {
215 dataIdx += OOC_DATACNT(kidNode);
216 kidNode++;
217 }
218 }
219
220 /* Update node pointer to selected kid (or NULL if nonexistent or node is
221 * a leaf) */
222 *node = OOC_ISBRANCH(*node, kid) ? kidNode : NULL;
223 return dataIdx;
224 }
225
226
227
228 #ifdef DEBUG_OOC
229 int OOC_Check (OOC_Octree *oct, const OOC_Node *node,
230 FVECT org, RREAL size, OOC_DataIdx dataIdx)
231 /* Traverse tree & check for valid nodes; oct -> leafFile must be open;
232 * return 0 if ok, otherwise -1 */
233 {
234 unsigned k;
235 FVECT kidOrg;
236 const RREAL kidSize = size * 0.5;
237
238 if (!oct || !node)
239 return -1;
240
241 if (OOC_ISLEAF(node)) {
242 /* Node is a leaf; check records in each octant */
243 char rec [oct -> recSize]; /* Violates C89! */
244 OOC_OctCnt d;
245 RREAL *key;
246
247 for (k = 0; k < 8; k++) {
248 VCOPY(kidOrg, org);
249 OOC_OCTORIGIN(kidOrg, k, kidSize);
250
251 for (d = node -> leaf.num [k]; d; d--, dataIdx++) {
252 #ifdef DEBUG_OOC_CHECK
253 fprintf(stderr, "OOC_Check: checking record %lu\n",
254 (unsigned long)dataIdx);
255 #endif
256 if (OOC_GetData(oct, dataIdx, rec)) {
257 fprintf(stderr, "OOC_Check: failed getting record at %lu\n",
258 (unsigned long)dataIdx);
259 return -1;
260 }
261
262 key = oct -> key(rec);
263 if (!OOC_InBBox(oct, kidOrg, kidSize, key)) {
264 fprintf(stderr, "OOC_Check: key [%f, %f, %f] (zIdx %020lu) "
265 "outside bbox [[%f-%f], [%f-%f], [%f-%f]] "
266 "in octant %d (should be %d)\n",
267 key [0], key [1], key [2],
268 OOC_KEY2MORTON(key, oct),
269 kidOrg [0], kidOrg [0] + kidSize,
270 kidOrg [1], kidOrg [1] + kidSize,
271 kidOrg [2], kidOrg [2] + kidSize,
272 k, OOC_Branch(oct, org, kidSize, key, NULL));
273 /* return -1; */
274 }
275 }
276 }
277 }
278 else {
279 /* Internal node; recurse on all kids */
280 const OOC_Node *kid = OOC_KID1(oct, node);
281 OOC_DataIdx numData = 0;
282
283 if (node -> node.kid >= oct -> numNodes) {
284 fprintf(stderr, "OOC_Check: invalid node index %u\n",
285 node -> node.kid);
286 return -1;
287 }
288
289 if (!node -> node.num) {
290 fputs("OOC_Check: empty octree node\n", stderr);
291 return -1;
292 }
293
294 for (k = 0; k < 8; k++)
295 if (OOC_ISBRANCH(node, k)) {
296 VCOPY(kidOrg, org);
297 OOC_OCTORIGIN(kidOrg, k, kidSize);
298
299 if (OOC_Check(oct, kid, kidOrg, kidSize, dataIdx))
300 return -1;
301
302 dataIdx += OOC_DATACNT(kid);
303 numData += OOC_DATACNT(kid);
304 kid++;
305 }
306
307 if (node -> node.num != numData) {
308 fprintf(stderr,
309 "Parent/kid data count mismatch; expected %u, found %u\n",
310 node -> node.num, numData);
311 return -1;
312 }
313 }
314
315 return 0;
316 }
317 #endif
318
319
320
321 int OOC_SaveOctree (const OOC_Octree *oct, FILE *out)
322 /* Appends octree nodes to specified file along with metadata. Uses
323 * RADIANCE's portable I/O routines. Returns 0 on success, else -1. Note
324 * the internal representation of the nodes is platform specific as they
325 * contain unions, therefore we use the portable I/O routines from the
326 * RADIANCE library */
327 {
328 int i;
329 OOC_NodeIdx nc;
330 OOC_Node *np = NULL;
331
332 if (!oct || !oct->nodes || !oct->numData || !oct->numNodes || !out) {
333 fputs("OOC_SaveOctree: empty octree\n", stderr);
334 return -1;
335 }
336
337 /* Write octree origin, size, data record size, number of records, and
338 * number of nodes */
339 for (i = 0; i < 3; i++)
340 putflt(oct -> org [i], out);
341
342 putflt(oct -> size, out);
343
344 #if 0
345 for (i = 0; i < 3; i++)
346 putflt(oct -> bound [i], out);
347
348 putint(oct -> mortonScale, sizeof(oct -> mortonScale), out);
349 #endif
350 putint(oct -> recSize, sizeof(oct -> recSize), out);
351 putint(oct -> numData, sizeof(oct -> numData), out);
352 putint(oct -> numNodes, sizeof(oct -> numNodes), out);
353
354 /* Write nodes to file */
355 for (nc = oct -> numNodes, np = oct -> nodes; nc; nc--, np++) {
356 if (OOC_ISLEAF(np)) {
357 /* Write leaf marker */
358 putint(-1, 1, out);
359
360 /* Write leaf octant counters */
361 for (i = 0; i < 8; i++)
362 putint(np -> leaf.num [i], sizeof(np -> leaf.num [i]), out);
363 }
364 else {
365 /* Write node marker */
366 putint(0, 1, out);
367
368 /* Write node, rounding field size up to nearest number of bytes */
369 putint(np -> node.kid, (OOC_NODEIDX_BITS + 7) >> 3, out);
370 putint(np -> node.num, (OOC_DATAIDX_BITS + 7) >> 3, out);
371 putint(np -> node.branch, 1, out);
372 }
373
374 if (ferror(out)) {
375 fputs("OOC_SaveOctree: failed writing to node file", stderr);
376 return -1;
377 }
378 }
379
380 fflush(out);
381 return 0;
382 }
383
384
385
386 int OOC_LoadOctree (OOC_Octree *oct, FILE *nodeFile,
387 RREAL *(*key)(const void*), FILE *leafFile)
388 /* Load octree nodes and metadata from nodeFile and associate with records
389 * in leafFile. Uses RADIANCE's portable I/O routines. Returns 0 on
390 * success, else -1. */
391 {
392 int i;
393 OOC_NodeIdx nc;
394 OOC_Node *np = NULL;
395
396 if (!oct || !nodeFile)
397 return -1;
398
399 OOC_Null(oct);
400
401 /* Read octree origin, size, record size, #records and #nodes */
402 for (i = 0; i < 3; i++)
403 oct -> org [i] = getflt(nodeFile);
404
405 oct -> size = getflt(nodeFile);
406
407 #if 0
408 for (i = 0; i < 3; i++)
409 oct -> bound [i] = getflt(nodeFile);
410
411 oct -> mortonScale = getint(sizeof(oct -> mortonScale), nodeFile);
412 #else
413 oct -> bound [0] = oct -> bound [1] = oct -> bound [2] = oct -> size;
414 VADD(oct -> bound, oct -> bound, oct -> org);
415
416 oct -> mortonScale = OOC_MORTON_MAX / oct -> size;
417 #endif
418
419 #if 0
420 fprintf(stderr, "OOC_LoadOctree: mortonScale = %lf\n", oct -> mortonScale);
421 #endif
422
423 oct -> recSize = getint(sizeof(oct -> recSize), nodeFile);
424 oct -> numData = getint(sizeof(oct -> numData), nodeFile);
425 oct -> numNodes = getint(sizeof(oct -> numNodes), nodeFile);
426 oct -> key = key;
427 oct -> leafFile = leafFile;
428
429 if (feof(nodeFile) || !oct -> numData || !oct -> numNodes) {
430 fputs("OOC_LoadOctree: empty octree\n", stderr);
431 return -1;
432 }
433
434 /* Allocate octree nodes */
435 if (!(oct -> nodes = calloc(oct -> numNodes, sizeof(OOC_Node)))) {
436 fputs("OOC_LoadOctree: failed octree allocation\n", stderr);
437 return -1;
438 }
439
440 /* Read nodes from file */
441 for (nc = oct -> numNodes, np = oct -> nodes; nc; nc--, np++) {
442 OOC_CLEARNODE(np);
443
444 /* Read node type marker */
445 if (getint(1, nodeFile)) {
446 /* Read leaf octant counters and set node type */
447 for (i = 0; i < 8; i++)
448 np -> leaf.num [i] = getint(sizeof(np -> leaf.num [i]),
449 nodeFile);
450
451 OOC_SETLEAF(np);
452 }
453 else {
454 /* Read node, rounding field size up to nearest number of bytes */
455 np -> node.kid = getint((OOC_NODEIDX_BITS + 7) >> 3, nodeFile);
456 np -> node.num = getint((OOC_DATAIDX_BITS + 7) >> 3, nodeFile);
457 np -> node.branch = getint(1, nodeFile);
458 }
459
460 if (ferror(nodeFile)) {
461 fputs("OOC_LoadOctree: failed reading from node file\n", stderr);
462 return -1;
463 }
464 }
465
466 return 0;
467 }
468
469
470
471 void OOC_Delete (OOC_Octree *oct)
472 /* Self-destruct */
473 {
474 free(oct -> nodes);
475 fclose(oct -> leafFile);
476
477 if (oct -> cache)
478 OOC_DeleteCache(oct -> cache);
479 }