15# include <openvdb/tools/GridTransformer.h>
16# include <openvdb/tools/LevelSetUtil.h>
17# include <openvdb/tools/Morphology.h>
22#include "util/nanovdb.h"
30#include <OpenImageIO/filesystem.h>
71#if defined(WITH_OPENVDB) && defined(WITH_NANOVDB)
72const int quads_indices[6][4] = {
87const float3 quads_normals[6] = {
102static int add_vertex(
const int3 v,
105 unordered_map<size_t, int> &used_verts)
107 const size_t vert_key =
v.x +
v.y * size_t(res.
x + 1) +
108 v.z * size_t(res.
x + 1) * size_t(res.
y + 1);
109 const unordered_map<size_t, int>::iterator it = used_verts.find(vert_key);
111 if (it != used_verts.end()) {
115 const int vertex_offset = vertices.size();
116 used_verts[vert_key] = vertex_offset;
117 vertices.push_back(
v);
118 return vertex_offset;
121static void create_quad(
const int3 corners[8],
125 unordered_map<size_t, int> &used_verts,
126 const int face_index)
129 quad.v0 = add_vertex(corners[quads_indices[face_index][0]], vertices, res, used_verts);
130 quad.v1 = add_vertex(corners[quads_indices[face_index][1]], vertices, res, used_verts);
131 quad.v2 = add_vertex(corners[quads_indices[face_index][2]], vertices, res, used_verts);
132 quad.v3 = add_vertex(corners[quads_indices[face_index][3]], vertices, res, used_verts);
133 quad.normal = quads_normals[face_index];
135 quads.push_back(
quad);
147class VolumeMeshBuilder {
150 openvdb::MaskGrid::Ptr topology_grid;
151 openvdb::CoordBBox bbox;
156 void add_grid(
const nanovdb::GridHandle<> &grid);
158 void add_padding(
const int pad_size);
162 const float face_overlap_avoidance,
163 const bool ray_marching);
165 void generate_vertices_and_quads(vector<int3> &vertices_is,
166 vector<QuadData> &quads,
167 const bool ray_marching);
169 void convert_object_space(
const vector<int3> &vertices,
170 vector<float3> &out_vertices,
171 const float face_overlap_avoidance);
173 void convert_quads_to_tris(
const vector<QuadData> &quads, vector<int> &tris);
175 bool empty_grid()
const;
178VolumeMeshBuilder::VolumeMeshBuilder()
183void VolumeMeshBuilder::add_grid(
const nanovdb::GridHandle<> &nanogrid)
186 openvdb::MaskGrid::Ptr grid = nanovdb_to_openvdb_mask(nanogrid);
189 topology_grid = grid;
197 if (topology_grid->transform() != grid->transform()) {
198 const openvdb::MaskGrid::Ptr temp_grid = topology_grid->copyWithNewTree();
199 temp_grid->setTransform(grid->transform().copy());
200 openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(*topology_grid, *temp_grid);
201 topology_grid = temp_grid;
202 topology_grid->setTransform(grid->transform().copy());
205 topology_grid->topologyUnion(*grid);
208void VolumeMeshBuilder::add_padding(
const int pad_size)
210 openvdb::tools::dilateActiveValues(
211 topology_grid->tree(), pad_size, openvdb::tools::NN_FACE, openvdb::tools::IGNORE_TILES);
216 const float face_overlap_avoidance,
217 const bool ray_marching)
224 generate_vertices_and_quads(vertices_is, quads, ray_marching);
226 convert_object_space(vertices_is, vertices, face_overlap_avoidance);
228 convert_quads_to_tris(quads,
indices);
231static bool is_non_empty_leaf(
const openvdb::MaskGrid::TreeType &
tree,
const openvdb::Coord coord)
233 const auto *leaf_node =
tree.probeLeaf(coord);
234 return (leaf_node && !leaf_node->isEmpty());
237void VolumeMeshBuilder::generate_vertices_and_quads(
vector<ccl::int3> &vertices_is,
239 const bool ray_marching)
243 topology_grid->tree().voxelizeActiveTiles();
245 const openvdb::MaskGrid::TreeType &
tree = topology_grid->tree();
246 tree.evalLeafBoundingBox(bbox);
248 const int3 resolution =
make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
250 unordered_map<size_t, int> used_verts;
251 for (
auto iter =
tree.cbeginLeaf(); iter; ++iter) {
252 if (iter->isEmpty()) {
255 openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox();
257 leaf_bbox.max() = leaf_bbox.max().offsetBy(1);
258 int3 min =
make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z());
259 int3 max =
make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z());
276 static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM;
277 auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2);
278 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z())))
280 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_X_MIN);
282 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z())))
284 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_X_MAX);
286 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z())))
288 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Y_MIN);
290 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z())))
292 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Y_MAX);
294 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM)))
296 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Z_MIN);
298 if (!is_non_empty_leaf(
tree, openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM)))
300 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Z_MAX);
306 bbox = topology_grid->evalActiveVoxelBoundingBox();
308 const int3 resolution =
make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
311 bbox.max() = bbox.max().offsetBy(1);
328 unordered_map<size_t, int> used_verts;
330 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_X_MIN);
331 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_X_MAX);
332 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Y_MIN);
333 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Y_MAX);
334 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Z_MIN);
335 create_quad(corners, vertices_is, quads, resolution, used_verts,
QUAD_Z_MAX);
338void VolumeMeshBuilder::convert_object_space(
const vector<int3> &vertices,
340 const float face_overlap_avoidance)
343 openvdb::Coord dim = bbox.dim();
345 const float3 cell_size =
make_float3(1.0f / dim.x(), 1.0f / dim.y(), 1.0f / dim.z());
346 const float3 point_offset = cell_size * face_overlap_avoidance;
348 out_vertices.reserve(vertices.size());
350 for (
size_t i = 0;
i < vertices.size(); ++
i) {
351 openvdb::math::Vec3d p = topology_grid->indexToWorld(
352 openvdb::math::Vec3d(vertices[
i].
x, vertices[
i].
y, vertices[
i].
z));
354 out_vertices.push_back(vertex + point_offset);
360 int index_offset = 0;
361 tris.resize(quads.size() * 6);
363 for (
size_t i = 0;
i < quads.size(); ++
i) {
364 tris[index_offset++] = quads[
i].v0;
365 tris[index_offset++] = quads[
i].v2;
366 tris[index_offset++] = quads[
i].v1;
368 tris[index_offset++] = quads[
i].v0;
369 tris[index_offset++] = quads[
i].v3;
370 tris[index_offset++] = quads[
i].v2;
374bool VolumeMeshBuilder::empty_grid()
const
376 return !topology_grid ||
377 (!topology_grid->tree().hasActiveTiles() && topology_grid->tree().leafCount() == 0);
380static int estimate_required_velocity_padding(
const nanovdb::GridHandle<> &grid,
381 const float velocity_scale)
383 const auto *typed_grid = grid.template grid<nanovdb::Vec3f>(0);
385 if (typed_grid ==
nullptr) {
389 const nanovdb::Vec3d voxel_size = typed_grid->voxelSize();
392 const double max_voxel_size = openvdb::math::Max(voxel_size[0], voxel_size[1], voxel_size[2]);
393 if (max_voxel_size == 0.0) {
398 const nanovdb::Vec3f mn = typed_grid->tree().root().minimum();
399 const nanovdb::Vec3f mx = typed_grid->tree().root().maximum();
400 float max_value = 0.0f;
401 max_value =
max(max_value,
fabsf(mx[0]));
402 max_value =
max(max_value,
fabsf(mx[1]));
403 max_value =
max(max_value,
fabsf(mx[2]));
404 max_value =
max(max_value,
fabsf(mn[0]));
405 max_value =
max(max_value,
fabsf(mn[1]));
406 max_value =
max(max_value,
fabsf(mn[2]));
408 const double estimated_padding = max_value *
static_cast<double>(velocity_scale) /
411 return static_cast<int>(std::ceil(estimated_padding));
429 const openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid();
434 if (!grid->isType<openvdb::FloatGrid>()) {
438 return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid);
441class MergeScalarGrids {
444 openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z;
447 MergeScalarGrids(
const ScalarTree *x_tree,
const ScalarTree *y_tree,
const ScalarTree *z_tree)
448 : m_acc_x(*x_tree), m_acc_y(*y_tree), m_acc_z(*z_tree)
452 MergeScalarGrids(
const MergeScalarGrids &other)
456 void operator()(
const openvdb::Vec3STree::ValueOnIter &it)
const
458 using namespace openvdb;
460 const math::Coord xyz = it.getCoord();
461 const float x = m_acc_x.getValue(xyz);
462 const float y = m_acc_y.getValue(xyz);
463 const float z = m_acc_z.getValue(xyz);
465 it.setValue(math::Vec3s(
x,
y,
z));
469static void merge_scalar_grids_for_velocity(
const Scene *scene,
Volume *volume)
476 const openvdb::FloatGrid::ConstPtr vel_x_grid = get_vdb_for_attribute(
478 const openvdb::FloatGrid::ConstPtr vel_y_grid = get_vdb_for_attribute(
480 const openvdb::FloatGrid::ConstPtr vel_z_grid = get_vdb_for_attribute(
483 if (!(vel_x_grid && vel_y_grid && vel_z_grid)) {
487 const openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f));
491 vecgrid->tree().topologyUnion(vel_x_grid->tree());
492 vecgrid->tree().topologyUnion(vel_y_grid->tree());
493 vecgrid->tree().topologyUnion(vel_z_grid->tree());
495 MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree());
496 openvdb::tools::foreach(vecgrid->beginValueOn(), op,
true,
false);
499 const openvdb::math::Transform::Ptr
transform = openvdb::ConstPtrCast<openvdb::math::Transform>(
500 vel_x_grid->transformPtr());
515 const string msg =
string_printf(
"Computing Volume Mesh %s", volume->
name.c_str());
519 Shader *volume_shader =
nullptr;
522 for (
Node *node : volume->get_used_shaders()) {
529 volume_shader = shader;
532 pad_size =
max(1, pad_size);
535 pad_size =
max(2, pad_size);
548 if (!volume_shader) {
552#if defined(WITH_OPENVDB) && defined(WITH_NANOVDB)
554 VolumeMeshBuilder builder;
563 if (handle.
empty()) {
576 nanovdb::GridHandle grid(
577 nanovdb::HostBuffer::createFull(
texture->memory_size(),
texture->host_pointer));
581 pad_size =
max(pad_size,
582 estimate_required_velocity_padding(grid, volume->get_velocity_scale()));
585 builder.add_grid(grid);
589 if (builder.empty_grid()) {
590 LOG_DEBUG <<
"Memory usage volume mesh: 0 Mb. (empty grid)";
594 builder.add_padding(pad_size);
600 const float face_overlap_avoidance = 0.1f *
606 const bool ray_marching = scene->
integrator->get_volume_ray_marching();
607 builder.create_mesh(vertices,
indices, face_overlap_avoidance, ray_marching);
610 volume->used_shaders.
clear();
611 volume->used_shaders.push_back_slow(volume_shader);
613 for (
size_t i = 0;
i < vertices.size(); ++
i) {
617 for (
size_t i = 0;
i <
indices.size();
i += 3) {
622 LOG_DEBUG <<
"Memory usage volume mesh: "
623 << (vertices.size() *
sizeof(
float3) +
indices.size() *
sizeof(
int)) /
634 merge_scalar_grids_for_velocity(scene,
this);
642 need_rebuild_ =
true;
647 need_rebuild_ =
true;
653 if (object_octrees_.empty()) {
662 for (
const Node *node : object->get_geometry()->get_used_shaders()) {
663 const Shader *shader =
static_cast<const Shader *
>(node);
667 object_octrees_.erase({object, shader});
673 update_visualization_ =
true;
681 for (
auto it = object_octrees_.begin(); it != object_octrees_.end();) {
682 if (it->first.second == shader) {
683 it = object_octrees_.erase(it);
696 for (
auto it = object_octrees_.begin(); it != object_octrees_.end();) {
697 const Object *
object = it->first.first;
698 if (object->get_geometry() == geometry) {
699 it = object_octrees_.erase(it);
708 for (
auto it = vdb_map_.begin(); it != vdb_map_.end();) {
709 if (it->first.first == geometry) {
710 it = vdb_map_.erase(it);
721 update_root_indices_ =
true;
745static bool mesh_is_closed(
const std::vector<openvdb::Vec3I> &triangles)
747 const size_t num_triangles = triangles.size();
748 if (num_triangles % 2) {
753 std::multiset<std::pair<int, int>> edges;
756 for (
const auto &tri : triangles) {
757 for (
int i = 0;
i < 3;
i++) {
758 const std::pair<int, int>
e = {tri[
i], tri[(
i + 1) % 3]};
759 if (edges.count(
e)) {
765 const auto count = edges.count({
e.second,
e.first});
772 edges.insert({
e.second,
e.first});
784 return num_triangles * 3 == num_edges * 2;
787openvdb::BoolGrid::ConstPtr VolumeManager::mesh_to_sdf_grid(
const Mesh *mesh,
789 const float half_width)
791 const int num_verts = mesh->get_verts().size();
792 std::vector<openvdb::Vec3f> points(num_verts);
794 const float3 &vert = mesh->get_verts()[
i];
795 points[
i] = openvdb::Vec3f(vert.
x, vert.
y, vert.
z);
799 std::vector<openvdb::Vec3I> triangles;
800 triangles.reserve(max_num_triangles);
801 for (
int i = 0;
i < max_num_triangles;
i++) {
803 const int shader_index = mesh->get_shader()[
i];
804 if (
static_cast<const Shader *
>(mesh->get_used_shaders()[shader_index]) == shader) {
805 triangles.emplace_back(mesh->get_triangles()[
i * 3],
806 mesh->get_triangles()[
i * 3 + 1],
807 mesh->get_triangles()[
i * 3 + 2]);
811 if (!mesh_is_closed(triangles)) {
814 return openvdb::BoolGrid::create();
819 const auto vdb_voxel_size = openvdb::Vec3d(mesh_size.
x, mesh_size.
y, mesh_size.
z) /
822 auto xform = openvdb::math::Transform::createLinearTransform(1.0);
823 xform->postScale(vdb_voxel_size);
825 auto sdf_grid = openvdb::tools::meshToLevelSet<openvdb::FloatGrid>(
826 *xform, points, triangles, half_width);
828 return openvdb::tools::sdfInteriorMask(*sdf_grid, 0.5 * vdb_voxel_size.length());
831openvdb::BoolGrid::ConstPtr VolumeManager::get_vdb(
const Geometry *geom,
832 const Shader *shader)
const
835 if (
auto it = vdb_map_.find({geom, shader}); it != vdb_map_.end()) {
840 return openvdb::BoolGrid::create();
844void VolumeManager::initialize_octree(
const Scene *scene,
Progress &progress)
847 std::map<std::pair<const Geometry *, const Shader *>, std::shared_ptr<Octree>> geometry_octrees;
848 for (
const auto &it : object_octrees_) {
849 const Shader *shader = it.first.second;
851 if (
const Object *
object = it.first.first) {
852 geometry_octrees[{
object->get_geometry(), shader}] = it.second;
859 const Geometry *geom =
object->get_geometry();
865 for (
const Node *node : geom->get_used_shaders()) {
866 const Shader *shader =
static_cast<const Shader *
>(node);
871 if (object_octrees_.find({object, shader}) == object_octrees_.end()) {
873 const Light *light =
static_cast<const Light *
>(geom);
877 object_octrees_[{object, shader}] = std::make_shared<Octree>(
BoundBox(-
size,
size));
881 const Mesh *mesh =
static_cast<const Mesh *
>(geom);
887 if (
auto it = geometry_octrees.find({geom, shader}); it != geometry_octrees.end()) {
889 object_octrees_[{object, shader}] = it->second;
892 auto octree = std::make_shared<Octree>(mesh->
bounds);
893 geometry_octrees[{geom, shader}] = octree;
894 object_octrees_[{object, shader}] = octree;
900 object_octrees_[{object, shader}] = std::make_shared<Octree>(mesh->
bounds);
907 vdb_map_.find({geom, shader}) == vdb_map_.end())
909 const Mesh *mesh =
static_cast<const Mesh *
>(geom);
911 if (dim.
x > 0.0f && dim.
y > 0.0f && dim.
z > 0.0f) {
912 const char *
name =
object->get_asset_name().c_str();
914 vdb_map_[{geom, shader}] = mesh_to_sdf_grid(mesh, shader, 1.0f);
924void VolumeManager::update_num_octree_nodes()
926 num_octree_nodes_ = 0;
927 num_octree_roots_ = 0;
929 std::set<const Octree *> unique_octrees;
930 for (
const auto &it : object_octrees_) {
931 const Octree *octree = it.second.get();
932 if (unique_octrees.find(octree) != unique_octrees.end()) {
936 unique_octrees.insert(octree);
943int VolumeManager::num_octree_nodes()
const
945 return num_octree_nodes_;
948int VolumeManager::num_octree_roots()
const
950 return num_octree_roots_;
953void VolumeManager::build_octree(
Device *device,
Progress &progress)
955 const double start_time =
time_dt();
957 for (
auto &it : object_octrees_) {
958 if (it.second->is_built()) {
962 const Object *
object = it.first.first;
963 const Shader *shader = it.first.second;
965 openvdb::BoolGrid::ConstPtr interior_mask = get_vdb(object->get_geometry(), shader);
966 it.second->build(device, progress, interior_mask,
object, shader);
968 it.second->build(device, progress,
object, shader);
972 update_num_octree_nodes();
976 LOG_DEBUG << object_octrees_.size() <<
" volume octree(s) with a total of " << num_octree_nodes()
977 <<
" nodes are built in " <<
build_time <<
" seconds.";
980void VolumeManager::update_root_indices(
DeviceScene *dscene,
const Scene *scene)
const
982 if (object_octrees_.empty()) {
987 std::map<const Octree *, int> octree_root_indices;
992 for (
const auto &it : object_octrees_) {
993 const Object *
object = it.first.first;
995 const Octree *octree = it.second.get();
996 auto entry = octree_root_indices.find(octree);
997 if (entry == octree_root_indices.end()) {
998 roots[object_id] = root_index;
999 octree_root_indices[octree] = root_index;
1005 roots[object_id] = entry->second;
1012void VolumeManager::flatten_octree(
DeviceScene *dscene,
const Scene *scene)
const
1014 if (object_octrees_.empty()) {
1018 update_root_indices(dscene, scene);
1020 for (
const auto &it : object_octrees_) {
1022 it.second->set_flattened(
false);
1030 for (
const auto &it : object_octrees_) {
1031 std::shared_ptr<Octree> octree = it.second;
1032 if (octree->is_flattened()) {
1038 kroots[root_index].
shader = it.first.second->id;
1039 kroots[root_index].
id = node_index;
1042 auto root = octree->get_root();
1050 const uint current_index = node_index++;
1051 knodes[current_index].
parent = -1;
1052 octree->flatten(knodes, current_index, root, node_index);
1053 octree->set_flattened();
1059 LOG_DEBUG <<
"Memory usage of volume octrees: "
1067std::string VolumeManager::visualize_octree(
const char *filename)
const
1069 const std::string filename_full =
path_join(OIIO::Filesystem::current_path(), filename);
1071 std::ofstream file(filename_full);
1072 if (file.is_open()) {
1073 std::ostringstream buffer;
1074 file <<
"# Visualize volume octree.\n\n"
1075 "import bpy\nimport mathutils\n\n"
1076 "if bpy.context.active_object:\n"
1077 " bpy.context.active_object.select_set(False)\n\n"
1078 "octree = bpy.data.collections.new(name='Octree')\n"
1079 "bpy.context.scene.collection.children.link(octree)\n\n";
1081 for (
const auto &it : object_octrees_) {
1083 const auto octree = it.second;
1084 const std::string object_name = it.first.first->get_asset_name().string();
1085 octree->visualize(file, object_name);
1088 const Object *
object = it.first.first;
1089 const Geometry *geom =
object->get_geometry();
1092 file <<
"obj.matrix_world = mathutils.Matrix((" << t.
x <<
", " << t.
y <<
", " << t.
z
1093 <<
", (" << 0 <<
"," << 0 <<
"," << 0 <<
"," << 1 <<
")))\n\n";
1100 return filename_full;
1103void VolumeManager::update_step_size(
const Scene *scene,
DeviceScene *dscene)
const
1120 const Geometry *geom =
object->get_geometry();
1125 volume_step_size[
object->index] = scene->
integrator->get_volume_step_rate() *
1126 object->compute_volume_step_size();
1138 if (scene->
integrator->get_volume_ray_marching()) {
1145 update_step_size(scene, dscene);
1154 initialize_octree(scene, progress);
1155 build_octree(device, progress);
1156 flatten_octree(dscene, scene);
1158 update_visualization_ =
true;
1159 need_rebuild_ =
false;
1160 update_root_indices_ =
false;
1162 else if (update_root_indices_) {
1163 update_root_indices(dscene, scene);
1164 update_root_indices_ =
false;
1167 if (update_visualization_) {
1168 LOG_DEBUG <<
"Octree visualization has been written to " << visualize_octree(
"octree.py");
1169 update_visualization_ =
false;
1189 for (
auto &it : vdb_map_) {
static void create_mesh(Scene *scene, Mesh *mesh, const ::Mesh &b_mesh, const array< Node * > &used_shaders, const bool need_motion, const float motion_scale, const bool subdivision=false)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
list< Attribute > attributes
Attribute * find(ustring name) const
Attribute * add(ustring name, const TypeDesc type, AttributeElement element)
device_vector< int > volume_tree_root_ids
device_vector< KernelOctreeRoot > volume_tree_roots
device_vector< KernelOctreeNode > volume_tree_nodes
device_vector< float > volume_step_size
virtual void const_copy_to(const char *name, void *host, const size_t size)=0
void create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress)
Geometry(const NodeType *node_type, const Type type)
VDBImageLoader * vdb_loader() const
device_texture * image_memory() const
int get_num_nodes() const
void set_substatus(const string &substatus_)
void set_status(const string &status_, const string &substatus_="")
bool has_volume_attribute_dependency
bool has_volume_spatial_varying
void device_update(Device *, DeviceScene *, const Scene *, Progress &)
void device_free(DeviceScene *)
static bool is_homogeneous_volume(const Object *, const Shader *)
void tag_update_indices()
T * alloc(const size_t width, const size_t height=0)
#define VOLUME_OCTREE_MAX_DEPTH
#define CCL_NAMESPACE_END
blender::gpu::Batch * quad
#define assert(assertion)
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
VecBase< float, 3 > float3
static uint hash_string(const char *str)
ccl_device_inline float hash_uint_to_float(const uint kx)
@ ATTR_STD_VOLUME_VELOCITY_Y
@ ATTR_STD_VOLUME_VELOCITY_Z
@ ATTR_STD_VOLUME_VELOCITY
@ ATTR_STD_VOLUME_VELOCITY_X
ccl_device_inline bool is_zero(const float2 a)
std::array< VecBase< T, 3 >, 8 > corners(const Bounds< VecBase< T, 3 > > &bounds)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
#define SOCKET_FLOAT(name, ui_name, default_value,...)
#define NODE_DEFINE(structname)
#define SOCKET_BOOLEAN(name, ui_name, default_value,...)
string path_join(const string &dir, const string &file)
@ VOLUME_INTERPOLATION_LINEAR
@ VOLUME_INTERPOLATION_CUBIC
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
ImageHandle & data_voxel()
__forceinline float3 size() const
packed_float3 translation
void add_triangle(const int v0, const int v1, const int v2, const int shader, bool smooth)
Mesh(const NodeType *node_type_, Type geom_type_)
void clear(bool preserve_shaders=false) override
void reserve_mesh(const int numverts, const int numtris)
size_t num_triangles() const
void add_vertex(const float3 P)
static NodeType * add(const char *name, CreateFunc create, Type type=NONE, const NodeType *base=nullptr)
vector< ParamValue > attributes
int get_device_index() const
MotionType need_motion() const
unique_ptr_vector< Object > objects
unique_ptr< ImageManager > image_manager
void merge_grids(const Scene *scene)
void clear(bool preserve_shaders=false) override
@ IMAGE_DATA_TYPE_NANOVDB_EMPTY
ccl_device_inline bool is_nanovdb_type(int type)
CCL_NAMESPACE_BEGIN double time_dt()