1 |
+ |
#ifndef lint |
2 |
+ |
static const char RCSid[] = "$Id$"; |
3 |
+ |
#endif |
4 |
+ |
|
5 |
+ |
|
6 |
|
/* |
7 |
< |
================================================================== |
7 |
> |
====================================================================== |
8 |
|
Out-of-core octree data structure |
9 |
|
|
10 |
|
Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) |
11 |
< |
(c) Fraunhofer Institute for Solar Energy Systems, |
12 |
< |
Lucerne University of Applied Sciences & Arts |
13 |
< |
================================================================== |
11 |
> |
(c) Lucerne University of Applied Sciences and Arts, |
12 |
> |
supported by the Swiss National Science Foundation (SNSF, #147053) |
13 |
> |
====================================================================== |
14 |
|
|
15 |
|
$Id$ |
16 |
|
*/ |
17 |
|
|
18 |
|
|
19 |
< |
#include <stdlib.h> |
19 |
> |
#if !defined(_WIN32) && !defined(_WIN64) || defined(PMAP_OOC) |
20 |
> |
/* No Windoze support for now */ |
21 |
> |
|
22 |
|
#include "oococt.h" |
23 |
< |
#include "oocsort.h" |
23 |
> |
#include "rtio.h" |
24 |
> |
#include <stdlib.h> |
25 |
> |
#include <unistd.h> |
26 |
|
|
27 |
|
|
28 |
< |
static OOC_Sort_Key zInterleave (OOC_Sort_Key k) |
29 |
< |
/* Interleave lower 21 bits of k with 00, resulting in 63 bits. |
30 |
< |
This code taken from |
22 |
< |
http://www.forceflow.be/2013/10/07/ |
23 |
< |
morton-encodingdecoding-through-bit-interleaving-implementations/ */ |
28 |
> |
|
29 |
> |
void OOC_Null (OOC_Octree *oct) |
30 |
> |
/* Init empty octree */ |
31 |
|
{ |
32 |
< |
/* Mask out lower 21 bits, the rest is cryptic */ |
33 |
< |
OOC_Sort_Key i = k & 0x1fffff; |
34 |
< |
|
35 |
< |
i = (i | i << 32) & 0x001f00000000ffff; |
36 |
< |
i = (i | i << 16) & 0x001f0000ff0000ff; |
37 |
< |
i = (i | i << 8) & 0x100f00f00f00f00f; |
38 |
< |
i = (i | i << 4) & 0x10c30c30c30c30c3; |
39 |
< |
i = (i | i << 2) & 0x1249249249249249; |
33 |
< |
|
34 |
< |
return i; |
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 |
< |
static OOC_Sort_Key Key2zOrder (OOC_Octree *oct, FVECT key) |
44 |
< |
/* Compute 63-bit Morton (Z-order) index for 3D key */ |
43 |
> |
|
44 |
> |
void OOC_Init (OOC_Octree *oct, unsigned recSize, FVECT org, RREAL size, |
45 |
> |
RREAL *(*key)(const void*), FILE *leafFile) |
46 |
|
{ |
47 |
< |
int i; |
48 |
< |
OOC_Sort_Key k [3]; |
49 |
< |
|
50 |
< |
/* Normalise key and map to 21-bit int */ |
51 |
< |
for (i = 0; i < 3; i++) |
52 |
< |
k [i] = (((OOC_Sort_Key)1 << 21) - 1) / oct -> size * |
53 |
< |
(key [i] - oct -> org [i]); |
54 |
< |
|
55 |
< |
/* Interleave each dim with zeros and merge */ |
56 |
< |
return zInterleave(k [0]) | zInterleave(k [1]) << 1 | |
57 |
< |
zInterleave(k [2]) << 2; |
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 |
< |
static OOC_Node *NewNode (OOC_Octree *oct) |
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 |
< |
|
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 |
< |
realloc(oct -> root, oct -> maxNodes * sizeof(OOC_Node)); |
79 |
< |
if (!oct -> root) |
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 -> root + oct -> numNodes - 1; |
87 |
< |
n -> num = n -> kid = 0; |
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 |
|
} |
72 |
– |
|
91 |
|
|
92 |
< |
int OOC_Init (OOC_Octree *oct, unsigned recSize, unsigned leafMax, |
93 |
< |
unsigned maxDepth) |
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 |
< |
ooc -> org [0] = oct -> org [1] = oct -> org [2] = FHUGE; |
99 |
< |
ooc -> size = oct -> maxNodes = oct -> lastNode = 0; |
100 |
< |
ooc -> recSize = recSize; |
101 |
< |
ooc -> leafMax = leafMax; |
102 |
< |
ooc -> maxDepth = maxDepth; |
103 |
< |
ooc -> root = NULL; |
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 |
< |
return NewNode(oct) ? 0 : -1; |
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 |
< |
int OOC_Branch (FVECT key, FVECT org, RREAL *size); |
134 |
< |
/* Return index of kid containing key, update origin and size accordingly */ |
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 |
< |
size *= 0.5; |
141 |
< |
int kidIdx = 0; |
140 |
> |
const OOC_MortonIdx keyZ = OOC_KEY2MORTON(key, oct); |
141 |
> |
FVECT bbox = {size, size, size}; |
142 |
|
|
143 |
< |
for (int i = 0; i < 3; i++) { |
144 |
< |
RREAL cent = org [i] + size; |
145 |
< |
if (key [i] > cent) { |
146 |
< |
kidIdx |= 1 << i; |
147 |
< |
org [i] = cent; |
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 kidIdx; |
173 |
> |
return -1; |
174 |
|
} |
175 |
|
|
176 |
|
|
177 |
< |
OOC_Idx OOC_Find (OOC_Octree *oct, FVECT key) |
178 |
< |
/* Iterative search for given key */ |
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 |
< |
FVECT org; |
183 |
< |
RREAL size; |
184 |
< |
OOC_Idx dataIdx = 0; |
112 |
< |
OOC_Node *node = NULL; |
182 |
> |
int o; |
183 |
> |
const FVECT cent = {size, size, size}; |
184 |
> |
FVECT octOrg, bbox; |
185 |
|
|
186 |
< |
if (!oct || !oct -> root) |
187 |
< |
return -1; |
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 |
< |
node = oct -> root; |
201 |
< |
VCOPY(org, oct -> org); |
202 |
< |
size = oct -> size; |
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 |
< |
/* Quit at terminal/empty node or internal leaf */ |
215 |
< |
while (OOC_INTNODE(*node)) { |
216 |
< |
/* Determine kid to branch into and update data index with skipped |
217 |
< |
* kids' data counts */ |
218 |
< |
int kidIdx = OOC_Branch(key, org, size); |
219 |
< |
node = oct -> root + OOC_KID(*node); |
220 |
< |
while (kidIdx--) |
221 |
< |
dataIdx += OOC_NUMDATA(node++); |
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 |
< |
/* Reached empty node -> not found */ |
228 |
< |
if (OOC_EMPTYNODE(*node)) |
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 |
< |
/* Reached terminal node -> find leaf to branch into and update data |
249 |
< |
* index with skipped leaves' data counts */ |
250 |
< |
if (OOC_TERMNODE(*node)) { |
251 |
< |
OOC_Leaf *leaf = ooc -> leaves [OOC_KID(*node)]; |
252 |
< |
int leafIdx = OOC_Branch(key, org, size); |
253 |
< |
while (leafIdx--) |
254 |
< |
dataIdx += *leaf++; |
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 |
< |
/* (Reached internal leaf -> already have data index) */ |
328 |
< |
return dataIdx; |
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 |
< |
#if 0 |
386 |
< |
OOC_Node *FindNode (OOC_Octree *oct, FVECT key, OOC_Idx *dataIdx, int flags) |
387 |
< |
/* ITERATIVE node search for given key */ |
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 |
< |
FVECT org; |
393 |
< |
double size; |
394 |
< |
OOC_Idx dataIdx = 0; |
156 |
< |
OOC_Node *node = NULL; |
392 |
> |
int i; |
393 |
> |
OOC_NodeIdx nc; |
394 |
> |
OOC_Node *np = NULL; |
395 |
|
|
396 |
< |
if (!oct || !oct -> root) |
397 |
< |
return NULL; |
396 |
> |
if (!oct || !nodeFile) |
397 |
> |
return -1; |
398 |
> |
|
399 |
> |
OOC_Null(oct); |
400 |
|
|
401 |
< |
node = oct -> root; |
402 |
< |
VCOPY(org, oct -> org); |
403 |
< |
size = oct -> size; |
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 |
< |
/* Quit at terminal or empty node */ |
416 |
< |
while (!OOC_TERM(*node) && !OOC_EMPTY(*node)) { |
417 |
< |
size *= 0.5; |
418 |
< |
int kidIdx = 0; |
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 |
< |
/* Determine kid to branch into and update origin accordingly */ |
431 |
< |
for (int i = 0; i < 3; i++) { |
432 |
< |
RREAL cent = org [i] + size; |
433 |
< |
if (key [i] > cent) { |
434 |
< |
kidIdx |= 1 << i; |
435 |
< |
org [i] = cent; |
436 |
< |
} |
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 |
< |
|
440 |
< |
/* Increment node's data counter if inserting */ |
441 |
< |
if (flags & OOC_INSERT) |
442 |
< |
node -> numData++; |
443 |
< |
|
444 |
< |
/* Update data index with skipped kids' data counts */ |
445 |
< |
node = oct -> root + node -> kid; |
446 |
< |
while (kidIdx--) |
447 |
< |
dataIdx += node++ -> numData; |
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 node; |
453 |
< |
} |
452 |
> |
return 0; |
453 |
> |
} |
454 |
|
|
455 |
< |
|
456 |
< |
OOC_Idx FindLeaf (OOC_Leaf *leaf, FVECT key, OOC_Idx *dataIdx); |
457 |
< |
/* Return index into data block for leaf containing key */ |
455 |
> |
|
456 |
> |
|
457 |
> |
void OOC_Delete (OOC_Octree *oct) |
458 |
> |
/* Self-destruct */ |
459 |
|
{ |
460 |
< |
int kidIdx = 0; |
461 |
< |
OOC_Idx = |
460 |
> |
free(oct -> nodes); |
461 |
> |
fclose(oct -> leafFile); |
462 |
|
|
463 |
< |
for (int i = 0; i < 3; i++) { |
464 |
< |
if (key [i] |
465 |
< |
#endif |
463 |
> |
if (oct -> cache) |
464 |
> |
OOC_DeleteCache(oct -> cache); |
465 |
> |
} |
466 |
> |
|
467 |
> |
#endif /* NIX / PMAP_OOC */ |