165 lines
5.4 KiB
C
165 lines
5.4 KiB
C
#ifndef OCTREE_H
|
|
#define OCTREE_H
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
//This was vibecoded #FML
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
typedef struct {
|
|
float x, y, z;
|
|
} oct_vec3;
|
|
|
|
typedef struct {
|
|
oct_vec3 min;
|
|
oct_vec3 max;
|
|
} oct_box;
|
|
|
|
typedef struct {
|
|
uint32_t first_child; // Index of first child in the pool, 0 if leaf
|
|
uint16_t count; // Number of items in this node
|
|
uint16_t data_offset; // Index into the data array
|
|
} oct_node;
|
|
|
|
typedef struct {
|
|
oct_node* nodes;
|
|
uint32_t* data; // Indices of your objects
|
|
uint32_t node_count;
|
|
uint32_t node_capacity;
|
|
uint32_t data_count;
|
|
uint32_t data_capacity;
|
|
oct_box bounds;
|
|
int max_depth;
|
|
} octree_t;
|
|
|
|
// --- Public API ---
|
|
void octree_init(octree_t* tree, oct_box bounds, int max_depth, uint32_t initial_cap);
|
|
void octree_free(octree_t* tree);
|
|
bool octree_insert(octree_t* tree, uint32_t item_id, oct_vec3 pos);
|
|
// Note: For thread safety, wrap insert in a mutex. Query can be lock-free if no inserts happen.
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif // OCTREE_H
|
|
|
|
// --------------------------------------------------------------------------
|
|
#ifdef OCTREE_IMPLEMENTATION
|
|
|
|
static bool oct_box_contains(oct_box b, oct_vec3 p) {
|
|
return (p.x >= b.min.x && p.x <= b.max.x &&
|
|
p.y >= b.min.y && p.y <= b.max.y &&
|
|
p.z >= b.min.z && p.z <= b.max.z);
|
|
}
|
|
|
|
void octree_init(octree_t* tree, oct_box bounds, int max_depth, uint32_t initial_cap) {
|
|
tree->bounds = bounds;
|
|
tree->max_depth = max_depth;
|
|
tree->node_capacity = initial_cap;
|
|
tree->nodes = (oct_node*)calloc(initial_cap, sizeof(oct_node));
|
|
tree->node_count = 1; // Root is at index 0
|
|
|
|
tree->data_capacity = initial_cap * 4;
|
|
tree->data = (uint32_t*)malloc(tree->data_capacity * sizeof(uint32_t));
|
|
tree->data_count = 0;
|
|
}
|
|
|
|
void octree_free(octree_t* tree) {
|
|
free(tree->nodes);
|
|
free(tree->data);
|
|
}
|
|
|
|
// Internal: Splits a node into 8 children
|
|
static void oct_node_split(octree_t* tree, uint32_t node_idx, oct_box node_box) {
|
|
if (tree->node_count + 8 > tree->node_capacity) {
|
|
tree->node_capacity *= 2;
|
|
tree->nodes = (oct_node*)realloc(tree->nodes, tree->node_capacity * sizeof(oct_node));
|
|
}
|
|
|
|
tree->nodes[node_idx].first_child = tree->node_count;
|
|
tree->node_count += 8;
|
|
|
|
// Initialize children...
|
|
for(int i = 0; i < 8; ++i) {
|
|
tree->nodes[tree->nodes[node_idx].first_child + i] = (oct_node){0};
|
|
}
|
|
}
|
|
|
|
// Implementation of octree_insert would go here...
|
|
// It would traverse indices and check oct_box_contains.
|
|
|
|
// Internal helper to get the bounding box of a specific child
|
|
static oct_box oct_get_child_box(oct_box parent, int child_idx) {
|
|
oct_vec3 center = {
|
|
(parent.min.x + parent.max.x) * 0.5f,
|
|
(parent.min.y + parent.max.y) * 0.5f,
|
|
(parent.min.z + parent.max.z) * 0.5f
|
|
};
|
|
oct_box child = parent;
|
|
// Bit 0: X, Bit 1: Y, Bit 2: Z
|
|
if (child_idx & 1) child.min.x = center.x; else child.max.x = center.x;
|
|
if (child_idx & 2) child.min.y = center.y; else child.max.y = center.y;
|
|
if (child_idx & 4) child.min.z = center.z; else child.max.z = center.z;
|
|
return child;
|
|
}
|
|
|
|
// Recursive insert (Internal)
|
|
static bool oct_insert_internal(octree_t* tree, uint32_t node_idx, oct_box node_box, uint32_t item_id, oct_vec3 pos, int depth) {
|
|
// 1. If we are at max depth or it's a leaf with space, store it
|
|
// For simplicity, this version pushes everything to leaves:
|
|
if (depth >= tree->max_depth) {
|
|
// Handle data array resizing
|
|
if (tree->data_count >= tree->data_capacity) {
|
|
tree->data_capacity *= 2;
|
|
tree->data = (uint32_t*)realloc(tree->data, tree->data_capacity * sizeof(uint32_t));
|
|
}
|
|
|
|
// In a real STB-style, you'd link data to the node here
|
|
// For now, we'll just track that the node has data
|
|
tree->nodes[node_idx].count++;
|
|
tree->data[tree->data_count++] = item_id;
|
|
return true;
|
|
}
|
|
|
|
// 2. If leaf, split it
|
|
if (tree->nodes[node_idx].first_child == 0) {
|
|
oct_node_split(tree, node_idx, node_box);
|
|
}
|
|
|
|
// 3. Determine which child the point belongs to
|
|
oct_vec3 center = {
|
|
(node_box.min.x + node_box.max.x) * 0.5f,
|
|
(node_box.min.y + node_box.max.y) * 0.5f,
|
|
(node_box.min.z + node_box.max.z) * 0.5f
|
|
};
|
|
|
|
int child_idx = 0;
|
|
if (pos.x >= center.x) child_idx |= 1;
|
|
if (pos.y >= center.y) child_idx |= 2;
|
|
if (pos.z >= center.z) child_idx |= 4;
|
|
|
|
uint32_t next_node = tree->nodes[node_idx].first_child + child_idx;
|
|
oct_box next_box = oct_get_child_box(node_box, child_idx);
|
|
|
|
return oct_insert_internal(tree, next_node, next_box, item_id, pos, depth + 1);
|
|
}
|
|
|
|
bool octree_insert(octree_t* tree, uint32_t item_id, oct_vec3 pos) {
|
|
if (!oct_box_contains(tree->bounds, pos)) return false;
|
|
return oct_insert_internal(tree, 0, tree->bounds, item_id, pos, 0);
|
|
}
|
|
|
|
// Thread-safe query (No writes/allocations)
|
|
void octree_query_sphere(const octree_t* tree, oct_vec3 center, float radius, void (*callback)(uint32_t)) {
|
|
// This would implement a standard stack-based or recursive search
|
|
// checking if the sphere overlaps the node_box.
|
|
}
|
|
|
|
#endif // OCTREE_IMPLEMENTATION
|