1 |
|
/* |
2 |
< |
================================================================== |
2 |
> |
====================================================================== |
3 |
|
Out-of-core octree data structure |
4 |
|
|
5 |
|
Roland Schregle (roland.schregle@{hslu.ch, gmail.com}) |
6 |
< |
(c) Fraunhofer Institute for Solar Energy Systems, |
7 |
< |
Lucerne University of Applied Sciences & Arts |
8 |
< |
================================================================== |
6 |
> |
(c) Lucerne University of Applied Sciences and Arts, |
7 |
> |
supported by the Swiss National Science Foundation (SNSF, #147053) |
8 |
> |
====================================================================== |
9 |
|
|
10 |
|
$Id$ |
11 |
|
*/ |
12 |
|
|
13 |
|
|
14 |
< |
#include <stdlib.h> |
14 |
> |
|
15 |
|
#include "oococt.h" |
16 |
< |
#include "oocsort.h" |
16 |
> |
#include "rtio.h" |
17 |
> |
#include <stdlib.h> |
18 |
> |
#include <unistd.h> |
19 |
|
|
20 |
|
|
21 |
< |
static OOC_Sort_Key zInterleave (OOC_Sort_Key k) |
22 |
< |
/* Interleave lower 21 bits of k with 00, resulting in 63 bits. |
23 |
< |
This code taken from |
22 |
< |
http://www.forceflow.be/2013/10/07/ |
23 |
< |
morton-encodingdecoding-through-bit-interleaving-implementations/ */ |
21 |
> |
|
22 |
> |
void OOC_Null (OOC_Octree *oct) |
23 |
> |
/* Init empty octree */ |
24 |
|
{ |
25 |
< |
/* Mask out lower 21 bits, the rest is cryptic */ |
26 |
< |
OOC_Sort_Key i = k & 0x1fffff; |
27 |
< |
|
28 |
< |
i = (i | i << 32) & 0x001f00000000ffff; |
29 |
< |
i = (i | i << 16) & 0x001f0000ff0000ff; |
30 |
< |
i = (i | i << 8) & 0x100f00f00f00f00f; |
31 |
< |
i = (i | i << 4) & 0x10c30c30c30c30c3; |
32 |
< |
i = (i | i << 2) & 0x1249249249249249; |
33 |
< |
|
34 |
< |
return i; |
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 |
< |
static OOC_Sort_Key Key2zOrder (OOC_Octree *oct, FVECT key) |
37 |
< |
/* Compute 63-bit Morton (Z-order) index for 3D key */ |
36 |
> |
|
37 |
> |
void OOC_Init (OOC_Octree *oct, unsigned recSize, FVECT org, RREAL size, |
38 |
> |
RREAL *(*key)(const void*), FILE *leafFile) |
39 |
|
{ |
40 |
< |
int i; |
41 |
< |
OOC_Sort_Key k [3]; |
42 |
< |
|
43 |
< |
/* Normalise key and map to 21-bit int */ |
44 |
< |
for (i = 0; i < 3; i++) |
45 |
< |
k [i] = (((OOC_Sort_Key)1 << 21) - 1) / oct -> size * |
46 |
< |
(key [i] - oct -> org [i]); |
47 |
< |
|
48 |
< |
/* Interleave each dim with zeros and merge */ |
49 |
< |
return zInterleave(k [0]) | zInterleave(k [1]) << 1 | |
50 |
< |
zInterleave(k [2]) << 2; |
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 |
< |
static OOC_Node *NewNode (OOC_Octree *oct) |
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 |
< |
|
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 |
< |
realloc(oct -> root, oct -> maxNodes * sizeof(OOC_Node)); |
72 |
< |
if (!oct -> root) |
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 -> root + oct -> numNodes - 1; |
80 |
< |
n -> num = n -> kid = 0; |
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 |
|
} |
72 |
– |
|
84 |
|
|
85 |
< |
int OOC_Init (OOC_Octree *oct, unsigned recSize, unsigned leafMax, |
86 |
< |
unsigned maxDepth) |
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 |
< |
ooc -> org [0] = oct -> org [1] = oct -> org [2] = FHUGE; |
92 |
< |
ooc -> size = oct -> maxNodes = oct -> lastNode = 0; |
93 |
< |
ooc -> recSize = recSize; |
94 |
< |
ooc -> leafMax = leafMax; |
95 |
< |
ooc -> maxDepth = maxDepth; |
96 |
< |
ooc -> root = NULL; |
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 |
< |
return NewNode(oct) ? 0 : -1; |
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 |
< |
int OOC_Branch (FVECT key, FVECT org, RREAL *size); |
127 |
< |
/* Return index of kid containing key, update origin and size accordingly */ |
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 |
< |
size *= 0.5; |
134 |
< |
int kidIdx = 0; |
133 |
> |
const OOC_MortonIdx keyZ = OOC_KEY2MORTON(key, oct); |
134 |
> |
FVECT bbox = {size, size, size}; |
135 |
|
|
136 |
< |
for (int i = 0; i < 3; i++) { |
137 |
< |
RREAL cent = org [i] + size; |
138 |
< |
if (key [i] > cent) { |
139 |
< |
kidIdx |= 1 << i; |
140 |
< |
org [i] = cent; |
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 kidIdx; |
166 |
> |
return -1; |
167 |
|
} |
168 |
|
|
169 |
|
|
170 |
< |
OOC_Idx OOC_Find (OOC_Octree *oct, FVECT key) |
171 |
< |
/* Iterative search for given key */ |
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 |
< |
FVECT org; |
176 |
< |
RREAL size; |
177 |
< |
OOC_Idx dataIdx = 0; |
112 |
< |
OOC_Node *node = NULL; |
175 |
> |
int o; |
176 |
> |
const FVECT cent = {size, size, size}; |
177 |
> |
FVECT octOrg, bbox; |
178 |
|
|
179 |
< |
if (!oct || !oct -> root) |
180 |
< |
return -1; |
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 |
< |
node = oct -> root; |
194 |
< |
VCOPY(org, oct -> org); |
195 |
< |
size = oct -> size; |
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 |
< |
/* Quit at terminal/empty node or internal leaf */ |
208 |
< |
while (OOC_INTNODE(*node)) { |
209 |
< |
/* Determine kid to branch into and update data index with skipped |
210 |
< |
* kids' data counts */ |
211 |
< |
int kidIdx = OOC_Branch(key, org, size); |
212 |
< |
node = oct -> root + OOC_KID(*node); |
213 |
< |
while (kidIdx--) |
214 |
< |
dataIdx += OOC_NUMDATA(node++); |
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 |
< |
/* Reached empty node -> not found */ |
221 |
< |
if (OOC_EMPTYNODE(*node)) |
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 |
< |
/* Reached terminal node -> find leaf to branch into and update data |
316 |
< |
* index with skipped leaves' data counts */ |
317 |
< |
if (OOC_TERMNODE(*node)) { |
318 |
< |
OOC_Leaf *leaf = ooc -> leaves [OOC_KID(*node)]; |
319 |
< |
int leafIdx = OOC_Branch(key, org, size); |
320 |
< |
while (leafIdx--) |
321 |
< |
dataIdx += *leaf++; |
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 |
< |
/* (Reached internal leaf -> already have data index) */ |
345 |
< |
return dataIdx; |
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 |
< |
#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 |
|
|
407 |
< |
/* Quit at terminal or empty node */ |
408 |
< |
while (!OOC_TERM(*node) && !OOC_EMPTY(*node)) { |
409 |
< |
size *= 0.5; |
168 |
< |
int kidIdx = 0; |
407 |
> |
#if 0 |
408 |
> |
for (i = 0; i < 3; i++) |
409 |
> |
oct -> bound [i] = getflt(nodeFile); |
410 |
|
|
411 |
< |
/* Determine kid to branch into and update origin accordingly */ |
412 |
< |
for (int i = 0; i < 3; i++) { |
413 |
< |
RREAL cent = org [i] + size; |
414 |
< |
if (key [i] > cent) { |
415 |
< |
kidIdx |= 1 << i; |
416 |
< |
org [i] = cent; |
417 |
< |
} |
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 |
< |
|
454 |
< |
/* Increment node's data counter if inserting */ |
455 |
< |
if (flags & OOC_INSERT) |
456 |
< |
node -> numData++; |
457 |
< |
|
458 |
< |
/* Update data index with skipped kids' data counts */ |
459 |
< |
node = oct -> root + node -> kid; |
460 |
< |
while (kidIdx--) |
461 |
< |
dataIdx += node++ -> numData; |
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 node; |
467 |
< |
} |
466 |
> |
return 0; |
467 |
> |
} |
468 |
|
|
469 |
< |
|
470 |
< |
OOC_Idx FindLeaf (OOC_Leaf *leaf, FVECT key, OOC_Idx *dataIdx); |
471 |
< |
/* Return index into data block for leaf containing key */ |
469 |
> |
|
470 |
> |
|
471 |
> |
void OOC_Delete (OOC_Octree *oct) |
472 |
> |
/* Self-destruct */ |
473 |
|
{ |
474 |
< |
int kidIdx = 0; |
475 |
< |
OOC_Idx = |
474 |
> |
free(oct -> nodes); |
475 |
> |
fclose(oct -> leafFile); |
476 |
|
|
477 |
< |
for (int i = 0; i < 3; i++) { |
478 |
< |
if (key [i] |
479 |
< |
#endif |
477 |
> |
if (oct -> cache) |
478 |
> |
OOC_DeleteCache(oct -> cache); |
479 |
> |
} |