16# include <openvdb/tools/FindActiveValues.h>
23 return x + resolution_ * (
y +
z * resolution_);
28 const blocked_range3d<int>
range(
29 index_min.
x, index_max.
x, 32, index_min.
y, index_max.
y, 32, index_min.
z, index_max.
z, 32);
33 auto reduction_func = [&](
const blocked_range3d<int> &r, Extrema<float>
init) -> Extrema<float> {
34 for (
int z = r.cols().begin();
z < r.cols().end(); ++
z) {
35 for (
int y = r.rows().begin();
y < r.rows().end(); ++
y) {
36 for (
int x = r.pages().begin();
x < r.pages().end(); ++
x) {
44 auto join_func = [](Extrema<float> a, Extrema<float>
b) -> Extrema<float> {
53 return (p - bbox_min) * position_to_index_scale_;
56int3 Octree::position_to_floor_index(
const float3 p)
const
59 return clamp(
make_int3(
int(index.
x),
int(index.
y),
int(index.
z)), 0, resolution_ - 1);
62int3 Octree::position_to_ceil_index(
const float3 p)
const
64 if (
any_zero(position_to_index_scale_)) {
69 return clamp(
make_int3(
int(index.
x),
int(index.
y),
int(index.
z)), 1, resolution_);
79 return index_to_position_scale_;
82bool Octree::should_split(std::shared_ptr<OctreeNode> &node)
const
84 const int3 index_min = position_to_floor_index(node->bbox.min);
85 const int3 index_max = position_to_ceil_index(node->bbox.max);
86 node->sigma = get_extrema(index_min, index_max);
88 const float3 bbox_size = node->bbox.size();
96 return (node->sigma.range() *
len(bbox_size) * scale_ > 1.442f &&
102static bool vdb_voxel_intersect(
const float3 p_min,
104 openvdb::BoolGrid::ConstPtr &grid,
105 const openvdb::tools::FindActiveValues<openvdb::BoolTree> &find)
112 const openvdb::math::CoordBBox coord_bbox(
113 openvdb::Coord::floor(grid->worldToIndex({p_min.x, p_min.y, p_min.z})),
114 openvdb::Coord::ceil(grid->worldToIndex({p_max.x, p_max.y, p_max.z})));
118 return find.anyActiveValues(coord_bbox,
true);
128 openvdb::BoolGrid::ConstPtr &interior_mask,
130 const int resolution)
132 const int object_id =
object->get_device_index();
133 const uint shader_id = shader->
id;
140 const float3 padded_size = voxel_size +
pad * 2.0f;
142 const blocked_range3d<int> range(0, resolution, 8, 0, resolution, 8, 0, resolution, 8);
143 parallel_for(range, [&](
const blocked_range3d<int> &r) {
146 const auto find = openvdb::tools::FindActiveValues(interior_mask->tree());
149 for (
int z = r.cols().begin();
z < r.cols().end(); ++
z) {
150 for (
int y = r.rows().begin();
y < r.rows().end(); ++
y) {
151 for (
int x = r.pages().begin();
x < r.pages().end(); ++
x) {
157 if (!vdb_voxel_intersect(p, p + voxel_size, interior_mask, find)) {
165 in.object = object_id;
169 d_input_data[offset * 2] =
in;
171 in.object = shader_id;
173 in.u = padded_size.
y;
174 in.v = padded_size.
z;
175 d_input_data[offset * 2 + 1] =
in;
185 const int num_channels,
186 const int resolution,
189 const float *d_output_data = d_output.
data();
190 const blocked_range3d<int> range(0, resolution, 32, 0, resolution, 32, 0, resolution, 32);
192 parallel_for(range, [&](
const blocked_range3d<int> &r) {
193 for (
int z = r.cols().begin();
z < r.cols().end(); ++
z) {
194 for (
int y = r.rows().begin();
y < r.rows().end(); ++
y) {
195 for (
int x = r.pages().begin();
x < r.pages().end(); ++
x) {
197 sigmas[index].min = d_output_data[index * num_channels + 0];
198 sigmas[index].max = d_output_data[index * num_channels + 1];
205void Octree::evaluate_volume_density(
Device *device,
208 openvdb::BoolGrid::ConstPtr &interior_mask,
218 index_to_position_scale_ = root_->bbox.size() /
float(resolution_);
223 const int size = resolution_ * resolution_ * resolution_;
224 sigmas_.resize(
size);
228 const int num_channels = 2;
231 const int num_inputs =
size * 2;
234 ShaderEval shader_eval(device, progress);
239 [&](device_vector<KernelShaderEvalInput> &d_input) {
247 [&](device_vector<float> &d_output) {
252float Octree::volume_scale(
const Object *
object)
const
254 const Geometry *geom =
object->get_geometry();
256 const Volume *volume =
static_cast<const Volume *
>(geom);
257 if (volume->get_object_space()) {
285std::shared_ptr<OctreeInternalNode> Octree::make_internal(std::shared_ptr<OctreeNode> &node)
288 auto internal = std::make_shared<OctreeInternalNode>(*node);
291 const float3 center = internal->bbox.center();
292 for (
int i = 0;
i < 8;
i++) {
294 const BoundBox bbox(
mix(internal->bbox.min, center, t),
mix(center, internal->bbox.max, t));
295 internal->children_[
i] = std::make_shared<OctreeNode>(bbox, internal->depth + 1);
301void Octree::recursive_build(std::shared_ptr<OctreeNode> &octree_node)
303 if (!should_split(octree_node)) {
308 auto internal = make_internal(octree_node);
310 for (
auto &child : internal->children_) {
311 task_pool_.push([&] { recursive_build(child); });
314 octree_node = internal;
318 const int current_index,
319 const std::shared_ptr<OctreeNode> &node,
320 int &child_index)
const
323 knode.
sigma = node->sigma;
325 if (
auto internal_ptr = std::dynamic_pointer_cast<OctreeInternalNode>(node)) {
330 for (
int i = 0;
i < 8;
i++) {
342 is_flattened_ = flattened;
347 return is_flattened_;
353 openvdb::BoolGrid::ConstPtr &interior_mask,
358 const char *
name =
object->get_asset_name().c_str();
362 evaluate_volume_density(device, progress, interior_mask,
object, shader);
364 evaluate_volume_density(device, progress,
object, shader);
372 scale_ = volume_scale(
object);
373 recursive_build(root_);
375 task_pool_.wait_work();
384 root_ = std::make_shared<OctreeNode>(bbox, 0);
386 is_flattened_ =
false;
421 str +=
"(" + mid_x +
"," + mid_y +
"," + min_z +
"), "
422 "(" + mid_x +
"," + mid_y +
"," + max_z +
"), "
423 "(" + mid_x +
"," + max_y +
"," + max_z +
"), "
424 "(" + mid_x +
"," + max_y +
"," + min_z +
"), "
425 "(" + mid_x +
"," + min_y +
"," + min_z +
"), "
426 "(" + mid_x +
"," + min_y +
"," + max_z +
"), ";
427 str +=
"(" + min_x +
"," + mid_y +
"," + mid_z +
"), "
428 "(" + max_x +
"," + mid_y +
"," + mid_z +
"), "
429 "(" + max_x +
"," + mid_y +
"," + max_z +
"), "
430 "(" + min_x +
"," + mid_y +
"," + max_z +
"), "
431 "(" + min_x +
"," + mid_y +
"," + min_z +
"), "
432 "(" + max_x +
"," + mid_y +
"," + min_z +
"), ";
433 str +=
"(" + mid_x +
"," + min_y +
"," + mid_z +
"), "
434 "(" + mid_x +
"," + max_y +
"," + mid_z +
"), "
435 "(" + max_x +
"," + max_y +
"," + mid_z +
"), "
436 "(" + max_x +
"," + min_y +
"," + mid_z +
"), "
437 "(" + min_x +
"," + min_y +
"," + mid_z +
"), "
438 "(" + min_x +
"," + max_y +
"," + mid_z +
"), ";
440 for (
const auto &child : internal->children_) {
441 child->visualize(
str);
447 std::string
str =
"vertices = [";
448 root_->visualize(
str);
450 "]\nr = range(len(vertices))\n"
451 "edges = [(i, i+1 if i%6<5 else i-4) for i in r]\n"
452 "mesh = bpy.data.meshes.new('Octree')\n"
453 "mesh.from_pydata(vertices, edges, [])\n"
455 "obj = bpy.data.objects.new('" +
458 "octree.objects.link(obj)\n"
459 "bpy.context.view_layer.objects.active = obj\n"
460 "bpy.ops.object.mode_set(mode='EDIT')\n";
463 const float3 center = root_->bbox.center();
464 const float3 size = root_->bbox.size() * 0.5f;
465 file <<
"bpy.ops.mesh.primitive_cube_add(location = " << center <<
", scale = " <<
size <<
")\n";
466 file <<
"bpy.ops.mesh.delete(type='ONLY_FACE')\n"
467 "bpy.ops.object.mode_set(mode='OBJECT')\n"
468 "obj.select_set(True)\n";
MINLINE float power_of_2(float f)
MINLINE float safe_divide(float a, float b)
int pad[32 - sizeof(int)]
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
void visualize(std::ofstream &file, const std::string object_name) const
int get_num_nodes() const
float3 voxel_size() const
int flatten_index(int x, int y, int z) const
void flatten(KernelOctreeNode *, const int, const std::shared_ptr< OctreeNode > &, int &) const
void set_flattened(const bool=true)
float3 index_to_position(int x, int y, int z) const
std::shared_ptr< OctreeNode > get_root() const
void build(Device *, Progress &, const Object *, const Shader *)
bool is_flattened() const
Octree(const BoundBox &bbox)
void set_substatus(const string &substatus_)
static bool is_homogeneous_volume(const Object *, const Shader *)
static void fill_shader_input(device_vector< KernelShaderEvalInput > &d_input, const Octree *octree, const Object *object, const Shader *shader, const int resolution)
static void read_shader_output(const device_vector< float > &d_output, const Octree *octree, const int num_channels, const int resolution, vector< Extrema< float > > &sigmas)
#define VOLUME_OCTREE_MAX_DEPTH
#define CCL_NAMESPACE_END
static const char * to_string(const Interpolation &interp)
VecBase< float, D > normalize(VecOp< float, D >) RET
constexpr T clamp(T, U, U) RET
VecBase< float, 3 > float3
OrientationBounds merge(const OrientationBounds &cone_a, const OrientationBounds &cone_b)
ccl_device_inline float3 one_float3()
ccl_device_inline bool any_zero(const float3 a)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
@ SHADER_EVAL_VOLUME_DENSITY
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
void visualize(std::string &str) const
static Value parallel_reduce(const int range, const Value &identity, const Function &function, const Reduction &reduction)