| 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.h,v 1.22 2015/11/13 17:26:05 taschreg Exp taschreg $
|
| 11 |
*/
|
| 12 |
|
| 13 |
|
| 14 |
#ifndef OOC_OCT_H
|
| 15 |
#define OOC_OCT_H
|
| 16 |
|
| 17 |
#include "oocmorton.h"
|
| 18 |
#include "ooccache.h"
|
| 19 |
#include <stdint.h>
|
| 20 |
#include <stdio.h>
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
/* ===================================================================
|
| 25 |
* CONSTS
|
| 26 |
* =================================================================== */
|
| 27 |
|
| 28 |
/* Octree index & data counter types */
|
| 29 |
typedef uint32_t OOC_NodeIdx;
|
| 30 |
typedef uint32_t OOC_DataIdx;
|
| 31 |
typedef uint8_t OOC_OctCnt;
|
| 32 |
|
| 33 |
/* Octree node field sizes for above */
|
| 34 |
#define OOC_NODEIDX_BITS 31
|
| 35 |
#define OOC_DATAIDX_BITS 32
|
| 36 |
|
| 37 |
/* Limits for above */
|
| 38 |
#define OOC_NODEIDX_MAX ((1L << OOC_NODEIDX_BITS) - 1)
|
| 39 |
#define OOC_DATAIDX_MAX ((1L << OOC_DATAIDX_BITS) - 2)
|
| 40 |
#define OOC_OCTCNT_MAX UINT8_MAX
|
| 41 |
|
| 42 |
/* Reserved values for above */
|
| 43 |
#define OOC_DATAIDX_ERR (OOC_DATAIDX_MAX + 1)
|
| 44 |
|
| 45 |
/* Block size for node allocation (8k) */
|
| 46 |
#define OOC_BLKSIZE (1L << 13)
|
| 47 |
|
| 48 |
|
| 49 |
|
| 50 |
/* ===================================================================
|
| 51 |
* UTILZ
|
| 52 |
* =================================================================== */
|
| 53 |
|
| 54 |
/* Test/set leaf flag for node n */
|
| 55 |
#define OOC_ISLEAF(n) ((n) -> node.type)
|
| 56 |
#define OOC_SETLEAF(n) ((n) -> node.type = 1)
|
| 57 |
|
| 58 |
/* Test/set branch bit for octant k in node n */
|
| 59 |
#define OOC_ISBRANCH(n,k) (!OOC_ISLEAF(n) && (n) -> node.branch & 1 << (k))
|
| 60 |
#define OOC_SETBRANCH(n,k) ((n) -> node.branch |= 1 << (k))
|
| 61 |
|
| 62 |
/* Return index to node's 1st kid */
|
| 63 |
#define OOC_KID1(o,n) (!OOC_ISLEAF(n) ? (o)->nodes + (n)->node.kid \
|
| 64 |
: NULL)
|
| 65 |
|
| 66 |
/* Get index to last node in octree; -1 indicates empty octree */
|
| 67 |
#define OOC_ROOTIDX(o) ((o) -> nodes && (o) -> numNodes > 0 \
|
| 68 |
? (o) -> numNodes - 1 : -1)
|
| 69 |
|
| 70 |
/* Get octree root; this is at the *end* of the node array! */
|
| 71 |
#define OOC_ROOT(o) (OOC_ROOTIDX(o) >= 0 \
|
| 72 |
? (o) -> nodes + OOC_ROOTIDX(o) : NULL)
|
| 73 |
|
| 74 |
/* Copy node to octree root */
|
| 75 |
#define OOC_SETROOT(o,n) (memcpy(OOC_ROOT(o), (n), sizeof(OOC_Node)))
|
| 76 |
|
| 77 |
/* Set all node fields to 0 */
|
| 78 |
#define OOC_CLEARNODE(n) (memset((n), 0, sizeof(OOC_Node)))
|
| 79 |
|
| 80 |
/* Modify origin o for octant k of size s */
|
| 81 |
#define OOC_OCTORIGIN(o,k,s) ((o) [0] += (s) * ((k) & 1), \
|
| 82 |
(o) [1] += (s) * ((k) >> 1 & 1), \
|
| 83 |
(o) [2] += (s) * ((k) >> 2 & 1))
|
| 84 |
|
| 85 |
/* Get num records stored in node (also handles leaves and NULL pointer) */
|
| 86 |
#define OOC_DATACNT(n) \
|
| 87 |
((n) ? OOC_ISLEAF(n) ? (n) -> leaf.num [0] + (n) -> leaf.num [1] + \
|
| 88 |
(n) -> leaf.num [2] + (n) -> leaf.num [3] + \
|
| 89 |
(n) -> leaf.num [4] + (n) -> leaf.num [5] + \
|
| 90 |
(n) -> leaf.num [6] + (n) -> leaf.num [7] \
|
| 91 |
: (n) -> node.num \
|
| 92 |
: 0)
|
| 93 |
|
| 94 |
/* Shortcuts for morton code from key and data record */
|
| 95 |
#define OOC_KEY2MORTON(k, o) OOC_Key2Morton((k), (o) -> org, \
|
| 96 |
(o) -> mortonScale)
|
| 97 |
#define OOC_REC2MORTON(r, o) OOC_Key2Morton((o) -> key(r), (o) -> org, \
|
| 98 |
(o) -> mortonScale)
|
| 99 |
|
| 100 |
|
| 101 |
|
| 102 |
/* ===================================================================
|
| 103 |
* DATA TYPEZ
|
| 104 |
* =================================================================== */
|
| 105 |
|
| 106 |
/* Octree node */
|
| 107 |
typedef struct {
|
| 108 |
union {
|
| 109 |
struct {
|
| 110 |
/* Interior node (node.type = 0) with:
|
| 111 |
node.kid = index to 1st child node in octree array (a.k.a
|
| 112 |
"Number One Son"), immediately followed by its
|
| 113 |
nonzero siblings as indicated by branch
|
| 114 |
node.num = num records stored in this subtree,
|
| 115 |
node.branch = branch bitmask indicating nonzero kid nodes */
|
| 116 |
char type : 1;
|
| 117 |
OOC_NodeIdx kid : OOC_NODEIDX_BITS;
|
| 118 |
OOC_DataIdx num : OOC_DATAIDX_BITS;
|
| 119 |
uint8_t branch;
|
| 120 |
|
| 121 |
/* NOTE that we can't make the kid's index relative to parent
|
| 122 |
* (which would be more compact), since we don't know the
|
| 123 |
* parent's index a priori during the bottom-up construction! */
|
| 124 |
} node;
|
| 125 |
|
| 126 |
struct {
|
| 127 |
/* Leaf node (leaf.type = node.type = 1 with:
|
| 128 |
leaf.num [k] = num records stored in octant k */
|
| 129 |
char type : 1;
|
| 130 |
OOC_OctCnt num [8];
|
| 131 |
} leaf;
|
| 132 |
};
|
| 133 |
} OOC_Node;
|
| 134 |
|
| 135 |
|
| 136 |
|
| 137 |
/* Top level octree container */
|
| 138 |
typedef struct {
|
| 139 |
FVECT org, bound; /* Cube origin (min. XYZ), size, and
|
| 140 |
resulting bounds (max. XYZ) */
|
| 141 |
RREAL size,
|
| 142 |
*(*key)(const void*); /* Callback for data rec coords */
|
| 143 |
RREAL mortonScale; /* Scale factor for generating Morton
|
| 144 |
codes from coords */
|
| 145 |
OOC_Node *nodes; /* Pointer to node array */
|
| 146 |
/* *************************************
|
| 147 |
* **** NODES ARE STORED IN POSTFIX ****
|
| 148 |
* **** ORDER, I.E. ROOT IS LAST! ****
|
| 149 |
* *************************************/
|
| 150 |
OOC_DataIdx numData; /* Num records in leaves */
|
| 151 |
OOC_NodeIdx maxNodes, /* Num currently allocated nodes */
|
| 152 |
numNodes; /* Num used nodes (<= maxNodes) */
|
| 153 |
unsigned recSize, /* Size of data record in bytes */
|
| 154 |
leafMax, /* Max #records per leaf (for build) */
|
| 155 |
maxDepth; /* Max subdivision depth (for build) */
|
| 156 |
FILE *leafFile; /* File containing data in leaves */
|
| 157 |
OOC_Cache *cache; /* I/O cache for leafFile */
|
| 158 |
} OOC_Octree;
|
| 159 |
|
| 160 |
/* DOAN' FORGET: NODES IN ***POSTFIX*** ORDAH!!! */
|
| 161 |
|
| 162 |
|
| 163 |
|
| 164 |
/* ===================================================================
|
| 165 |
* FUNKZ
|
| 166 |
* =================================================================== */
|
| 167 |
|
| 168 |
/* Init empty octree */
|
| 169 |
void OOC_Null (OOC_Octree *oct);
|
| 170 |
|
| 171 |
|
| 172 |
/* Initialises octree for records of size recSize with 3D coordinates,
|
| 173 |
* accessed via key() callback. The octree's bounding box is defined by
|
| 174 |
* its origin org and size per axis, and must contain all keys. The
|
| 175 |
* octree is associated with the specified leaf file containing the
|
| 176 |
* records in Morton order */
|
| 177 |
void OOC_Init (OOC_Octree *oct, unsigned recSize, FVECT org, RREAL size,
|
| 178 |
RREAL *(*key)(const void*), FILE *leafFile);
|
| 179 |
|
| 180 |
|
| 181 |
/* Allocate new octree node, enlarge array if necessary. Returns pointer
|
| 182 |
to new node or NULL if failed. */
|
| 183 |
OOC_Node *NewNode (OOC_Octree *oct);
|
| 184 |
|
| 185 |
|
| 186 |
/* Reads indexed data record from leaf file and copies it to rec, else
|
| 187 |
* returns -1 on failure */
|
| 188 |
int OOC_GetData (OOC_Octree *oct, OOC_DataIdx idx, void *rec);
|
| 189 |
|
| 190 |
|
| 191 |
/* Appends octree nodes to specified file along with metadata. Uses
|
| 192 |
* RADIANCE's portable I/O routines. Returns 0 on success, else -1. */
|
| 193 |
int OOC_SaveOctree (const OOC_Octree *oct, FILE *nodeFile);
|
| 194 |
|
| 195 |
|
| 196 |
/* Load octree nodes and metadata from nodeFile and associate with
|
| 197 |
* records in leafFile. Uses RADIANCE's portable I/O routines. Returns
|
| 198 |
* 0 on success, else -1. */
|
| 199 |
int OOC_LoadOctree (OOC_Octree *oct, FILE *nodeFile,
|
| 200 |
RREAL *(*key)(const void*), FILE *leafFile);
|
| 201 |
|
| 202 |
#ifdef DEBUG_OOC
|
| 203 |
/* Traverse tree & check for valid nodes; oct -> leafFile must be open;
|
| 204 |
* return 0 if ok, otherwise -1 */
|
| 205 |
int OOC_Check (OOC_Octree *oct, const OOC_Node *node,
|
| 206 |
FVECT org, RREAL size, OOC_DataIdx dataIdx);
|
| 207 |
#endif
|
| 208 |
|
| 209 |
/* Check whether key lies within bounding box defined by org and size */
|
| 210 |
int OOC_InBBox (const OOC_Octree *oct, const FVECT org, RREAL size,
|
| 211 |
const FVECT key);
|
| 212 |
|
| 213 |
/* Return index of octant containing key and corresponding origin if
|
| 214 |
* nuOrg = NULL, or -1 if key lies outside all octants. Size is already
|
| 215 |
* assumed to be halved, i.e. corresponding to the length of an octant
|
| 216 |
* axis. */
|
| 217 |
int OOC_Branch (const OOC_Octree *oct, const FVECT org, RREAL size,
|
| 218 |
const FVECT key, FVECT nuOrg);
|
| 219 |
|
| 220 |
/* Optimised version of OOC_Branch() with precomputed key Morton code */
|
| 221 |
int OOC_BranchZ (const OOC_Octree *oct, const FVECT org, RREAL size,
|
| 222 |
OOC_MortonIdx keyZ, FVECT nuOrg);
|
| 223 |
|
| 224 |
/* For a given octree node, return the sum of the data counters for kids
|
| 225 |
* [0..k-1]. On return, the node either points to the k'th kid, or
|
| 226 |
* NULL if the kid is nonexistent or the node is a leaf */
|
| 227 |
OOC_DataIdx OOC_GetKid (const OOC_Octree *oct, OOC_Node **node,
|
| 228 |
unsigned k);
|
| 229 |
|
| 230 |
/* Self-destruct */
|
| 231 |
void OOC_Delete (OOC_Octree *oct);
|
| 232 |
|
| 233 |
/* DID WE MENTION NODES ARE IN POSTFIX ORDAH? */
|
| 234 |
#endif
|