mirror of
https://github.com/Relintai/godot_voxel.git
synced 2024-11-19 02:47:18 +01:00
Use object pool to speed up octree construction
This commit is contained in:
parent
bfbe382a7d
commit
cead529f11
35
dmc/object_pool.h
Normal file
35
dmc/object_pool.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef OBJECT_POOL_H
|
||||||
|
#define OBJECT_POOL_H
|
||||||
|
|
||||||
|
#include "core/os/memory.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class ObjectPool {
|
||||||
|
public:
|
||||||
|
T *create() {
|
||||||
|
if (_objects.empty()) {
|
||||||
|
return memnew(T);
|
||||||
|
} else {
|
||||||
|
T *obj = _objects.back();
|
||||||
|
_objects.pop_back();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recycle(T *obj) {
|
||||||
|
obj->init();
|
||||||
|
_objects.push_back(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ObjectPool() {
|
||||||
|
for (auto it = _objects.begin(); it != _objects.end(); ++it) {
|
||||||
|
memdelete(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<T *> _objects;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OBJECT_POOL_H
|
@ -16,49 +16,7 @@ const float SURFACE_ISO_LEVEL = 0.0;
|
|||||||
const float NEAR_SURFACE_FACTOR = 2.0;
|
const float NEAR_SURFACE_FACTOR = 2.0;
|
||||||
const float SQRT3 = 1.7320508075688772;
|
const float SQRT3 = 1.7320508075688772;
|
||||||
|
|
||||||
// Octree used only for dual grid construction
|
// Helper to modify padded voxel data
|
||||||
struct OctreeNode {
|
|
||||||
|
|
||||||
Vector3i origin;
|
|
||||||
int size; // Nodes are cubic
|
|
||||||
HermiteValue center_value;
|
|
||||||
OctreeNode *children[8];
|
|
||||||
|
|
||||||
OctreeNode() {
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
children[i] = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~OctreeNode() {
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
if (children[i]) {
|
|
||||||
memdelete(children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool has_children() const {
|
|
||||||
return children[0] != nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void split(OctreeNode *node) {
|
|
||||||
|
|
||||||
CRASH_COND(node->has_children());
|
|
||||||
CRASH_COND(node->size == 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
|
|
||||||
OctreeNode *child = memnew(OctreeNode);
|
|
||||||
const int *v = OctreeUtility::g_octant_position[i];
|
|
||||||
child->size = node->size / 2;
|
|
||||||
child->origin = node->origin + Vector3i(v[0], v[1], v[2]) * child->size;
|
|
||||||
|
|
||||||
node->children[i] = child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VoxelAccess {
|
struct VoxelAccess {
|
||||||
|
|
||||||
const VoxelBuffer &buffer;
|
const VoxelBuffer &buffer;
|
||||||
@ -190,28 +148,63 @@ inline Vector3 get_center(const OctreeNode *node) {
|
|||||||
return node->origin.to_vec3() + 0.5 * Vector3(node->size, node->size, node->size);
|
return node->origin.to_vec3() + 0.5 * Vector3(node->size, node->size, node->size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_octree_top_down(OctreeNode *node, const VoxelAccess &voxels, float geometric_error) {
|
class OctreeBuilderTopDown {
|
||||||
|
public:
|
||||||
|
OctreeBuilderTopDown(const VoxelAccess &voxels, float geometry_error, OctreeNodePool &pool) :
|
||||||
|
_voxels(voxels),
|
||||||
|
_geometry_error(geometry_error),
|
||||||
|
_pool(pool) {
|
||||||
|
}
|
||||||
|
|
||||||
if (can_split(node->origin, node->size, voxels, geometric_error)) {
|
OctreeNode *build(Vector3i origin, int size) {
|
||||||
|
OctreeNode *root = _pool.create();
|
||||||
|
root->origin = origin;
|
||||||
|
root->size = size;
|
||||||
|
build(root);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
split(node);
|
private:
|
||||||
|
void build(OctreeNode *node) {
|
||||||
|
if (can_split(node->origin, node->size, _voxels, _geometry_error)) {
|
||||||
|
split(node);
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
build(node->children[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node->center_value = _voxels.get_interpolated_hermite_value(get_center(node));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void split(OctreeNode *node) {
|
||||||
|
|
||||||
|
CRASH_COND(node->has_children());
|
||||||
|
CRASH_COND(node->size == 1);
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
generate_octree_top_down(node->children[i], voxels, geometric_error);
|
|
||||||
|
OctreeNode *child = _pool.create();
|
||||||
|
const int *v = OctreeUtility::g_octant_position[i];
|
||||||
|
child->size = node->size / 2;
|
||||||
|
child->origin = node->origin + Vector3i(v[0], v[1], v[2]) * child->size;
|
||||||
|
|
||||||
|
node->children[i] = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
node->center_value = voxels.get_interpolated_hermite_value(get_center(node));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private:
|
||||||
|
const VoxelAccess &_voxels;
|
||||||
|
const float _geometry_error;
|
||||||
|
OctreeNodePool &_pool;
|
||||||
|
};
|
||||||
|
|
||||||
// Builds the octree bottom-up, to ensure that no detail can be missed by a top-down approach.
|
// Builds the octree bottom-up, to ensure that no detail can be missed by a top-down approach.
|
||||||
class OctreeBuilderBottomUp {
|
class OctreeBuilderBottomUp {
|
||||||
public:
|
public:
|
||||||
OctreeBuilderBottomUp(const VoxelAccess &voxels, float geometry_error) :
|
OctreeBuilderBottomUp(const VoxelAccess &voxels, float geometry_error, OctreeNodePool &pool) :
|
||||||
_voxels(voxels),
|
_voxels(voxels),
|
||||||
_geometry_error(geometry_error) {
|
_geometry_error(geometry_error),
|
||||||
|
_pool(pool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
OctreeNode *build(Vector3i node_origin, int node_size) const {
|
OctreeNode *build(Vector3i node_origin, int node_size) const {
|
||||||
@ -235,7 +228,7 @@ public:
|
|||||||
// No nodes, test if the 8 octants are worth existing (this could be leaves)
|
// No nodes, test if the 8 octants are worth existing (this could be leaves)
|
||||||
if (can_split(node_origin, node_size, _voxels, _geometry_error)) {
|
if (can_split(node_origin, node_size, _voxels, _geometry_error)) {
|
||||||
|
|
||||||
node = memnew(OctreeNode);
|
node = _pool.create();
|
||||||
node->origin = node_origin;
|
node->origin = node_origin;
|
||||||
node->size = node_size;
|
node->size = node_size;
|
||||||
|
|
||||||
@ -252,7 +245,7 @@ public:
|
|||||||
// Some child nodes were deemed worthy of existence,
|
// Some child nodes were deemed worthy of existence,
|
||||||
// create their siblings at the same detail level
|
// create their siblings at the same detail level
|
||||||
|
|
||||||
node = memnew(OctreeNode);
|
node = _pool.create();
|
||||||
node->origin = node_origin;
|
node->origin = node_origin;
|
||||||
node->size = node_size;
|
node->size = node_size;
|
||||||
|
|
||||||
@ -271,7 +264,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
inline OctreeNode *create_child(Vector3i parent_origin, int parent_size, int i) const {
|
inline OctreeNode *create_child(Vector3i parent_origin, int parent_size, int i) const {
|
||||||
const int *dir = OctreeUtility::g_octant_position[i];
|
const int *dir = OctreeUtility::g_octant_position[i];
|
||||||
OctreeNode *child = memnew(OctreeNode);
|
OctreeNode *child = _pool.create();
|
||||||
child->size = parent_size / 2;
|
child->size = parent_size / 2;
|
||||||
child->origin = parent_origin + child->size * Vector3i(dir[0], dir[1], dir[2]);
|
child->origin = parent_origin + child->size * Vector3i(dir[0], dir[1], dir[2]);
|
||||||
child->center_value = _voxels.get_interpolated_hermite_value(get_center(child));
|
child->center_value = _voxels.get_interpolated_hermite_value(get_center(child));
|
||||||
@ -281,6 +274,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
const VoxelAccess &_voxels;
|
const VoxelAccess &_voxels;
|
||||||
const float _geometry_error;
|
const float _geometry_error;
|
||||||
|
OctreeNodePool &_pool;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Action_T>
|
template <typename Action_T>
|
||||||
@ -1285,13 +1279,11 @@ Ref<ArrayMesh> VoxelMesherDMC::build_mesh(const VoxelBuffer &voxels, real_t geom
|
|||||||
// because all voxels are queried.
|
// because all voxels are queried.
|
||||||
//
|
//
|
||||||
#ifdef BUILD_OCTREE_BOTTOM_UP
|
#ifdef BUILD_OCTREE_BOTTOM_UP
|
||||||
dmc::OctreeBuilderBottomUp octree_builder(voxels_access, geometric_error);
|
dmc::OctreeBuilderBottomUp octree_builder(voxels_access, geometric_error, _octree_node_pool);
|
||||||
dmc::OctreeNode *root = octree_builder.build(Vector3i(), chunk_size);
|
dmc::OctreeNode *root = octree_builder.build(Vector3i(), chunk_size);
|
||||||
#else
|
#else
|
||||||
dmc::OctreeNode *root = memnew(dmc::OctreeNode);
|
dmc::OctreeBuilderTopDown octree_builder(voxels_access, geometric_error, _octree_node_pool);
|
||||||
root->origin = Vector3i();
|
dmc::OctreeNode *root = octree_builder.build(Vector3i(), chunk_size);
|
||||||
root->size = chunk_size;
|
|
||||||
dmc::generate_octree_top_down(root, voxels_access, geometric_error);
|
|
||||||
#endif
|
#endif
|
||||||
// TODO OctreeNode pool to stop allocating. Or, flat octree?
|
// TODO OctreeNode pool to stop allocating. Or, flat octree?
|
||||||
|
|
||||||
@ -1331,7 +1323,7 @@ Ref<ArrayMesh> VoxelMesherDMC::build_mesh(const VoxelBuffer &voxels, real_t geom
|
|||||||
_dual_grid.cells.clear();
|
_dual_grid.cells.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
memdelete(root);
|
root->recycle(_octree_node_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Marching squares skirts
|
// TODO Marching squares skirts
|
||||||
|
@ -4,10 +4,45 @@
|
|||||||
#include "../voxel_buffer.h"
|
#include "../voxel_buffer.h"
|
||||||
#include "hermite_value.h"
|
#include "hermite_value.h"
|
||||||
#include "mesh_builder.h"
|
#include "mesh_builder.h"
|
||||||
|
#include "object_pool.h"
|
||||||
#include "scene/resources/mesh.h"
|
#include "scene/resources/mesh.h"
|
||||||
|
|
||||||
namespace dmc {
|
namespace dmc {
|
||||||
|
|
||||||
|
struct OctreeNode;
|
||||||
|
typedef ObjectPool<OctreeNode> OctreeNodePool;
|
||||||
|
|
||||||
|
// Octree used only for dual grid construction
|
||||||
|
struct OctreeNode {
|
||||||
|
|
||||||
|
Vector3i origin;
|
||||||
|
int size; // Nodes are cubic
|
||||||
|
HermiteValue center_value;
|
||||||
|
OctreeNode *children[8];
|
||||||
|
|
||||||
|
OctreeNode() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void init() {
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
children[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recycle(OctreeNodePool &pool) {
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
if (children[i]) {
|
||||||
|
pool.recycle(children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool has_children() const {
|
||||||
|
return children[0] != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct DualCell {
|
struct DualCell {
|
||||||
Vector3 corners[8];
|
Vector3 corners[8];
|
||||||
HermiteValue values[8];
|
HermiteValue values[8];
|
||||||
@ -49,6 +84,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
dmc::MeshBuilder _mesh_builder;
|
dmc::MeshBuilder _mesh_builder;
|
||||||
dmc::DualGrid _dual_grid;
|
dmc::DualGrid _dual_grid;
|
||||||
|
dmc::OctreeNodePool _octree_node_pool;
|
||||||
|
|
||||||
struct Stats {
|
struct Stats {
|
||||||
real_t octree_build_time;
|
real_t octree_build_time;
|
||||||
|
Loading…
Reference in New Issue
Block a user