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