diff options
Diffstat (limited to 'source/blender/blenkernel/intern/volume_render.cc')
-rw-r--r-- | source/blender/blenkernel/intern/volume_render.cc | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc new file mode 100644 index 00000000000..d8fbbce3bae --- /dev/null +++ b/source/blender/blenkernel/intern/volume_render.cc @@ -0,0 +1,358 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/volume_render.cc + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math_matrix.h" +#include "BLI_math_vector.h" + +#include "DNA_volume_types.h" + +#include "BKE_volume.h" +#include "BKE_volume_render.h" + +#ifdef WITH_OPENVDB +# include <openvdb/openvdb.h> +# include <openvdb/tools/Dense.h> +#endif + +/* Dense Voxels */ + +bool BKE_volume_grid_dense_bounds(const Volume *volume, + VolumeGrid *volume_grid, + int64_t min[3], + int64_t max[3]) +{ +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + + openvdb::CoordBBox bbox = grid->evalActiveVoxelBoundingBox(); + if (!bbox.empty()) { + /* OpenVDB bbox is inclusive, so add 1 to convert. */ + min[0] = bbox.min().x(); + min[1] = bbox.min().y(); + min[2] = bbox.min().z(); + max[0] = bbox.max().x() + 1; + max[1] = bbox.max().y() + 1; + max[2] = bbox.max().z() + 1; + return true; + } +#else + UNUSED_VARS(volume, volume_grid); +#endif + + min[0] = 0; + min[1] = 0; + min[2] = 0; + max[0] = 0; + max[1] = 0; + max[2] = 0; + return false; +} + +/* Transform matrix from unit cube to object space, for 3D texture sampling. */ +void BKE_volume_grid_dense_transform_matrix(const VolumeGrid *volume_grid, + const int64_t min[3], + const int64_t max[3], + float mat[4][4]) +{ +#ifdef WITH_OPENVDB + float index_to_world[4][4]; + BKE_volume_grid_transform_matrix(volume_grid, index_to_world); + + float texture_to_index[4][4]; + float loc[3] = {(float)min[0], (float)min[1], (float)min[2]}; + float size[3] = {(float)(max[0] - min[0]), (float)(max[1] - min[1]), (float)(max[2] - min[2])}; + size_to_mat4(texture_to_index, size); + copy_v3_v3(texture_to_index[3], loc); + + mul_m4_m4m4(mat, index_to_world, texture_to_index); +#else + UNUSED_VARS(volume_grid, min, max); + unit_m4(mat); +#endif +} + +void BKE_volume_grid_dense_voxels(const Volume *volume, + VolumeGrid *volume_grid, + const int64_t min[3], + const int64_t max[3], + float *voxels) +{ +#ifdef WITH_OPENVDB + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + + /* Convert to OpenVDB inclusive bbox with -1. */ + openvdb::CoordBBox bbox(min[0], min[1], min[2], max[0] - 1, max[1] - 1, max[2] - 1); + + switch (BKE_volume_grid_type(volume_grid)) { + case VOLUME_GRID_BOOLEAN: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::BoolGrid>(grid), dense); + break; + } + case VOLUME_GRID_FLOAT: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid), dense); + break; + } + case VOLUME_GRID_DOUBLE: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::DoubleGrid>(grid), dense); + break; + } + case VOLUME_GRID_INT: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int32Grid>(grid), dense); + break; + } + case VOLUME_GRID_INT64: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Int64Grid>(grid), dense); + break; + } + case VOLUME_GRID_MASK: { + openvdb::tools::Dense<float, openvdb::tools::LayoutXYZ> dense(bbox, voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid), dense); + break; + } + case VOLUME_GRID_VECTOR_FLOAT: { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid), dense); + break; + } + case VOLUME_GRID_VECTOR_DOUBLE: { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3dGrid>(grid), dense); + break; + } + case VOLUME_GRID_VECTOR_INT: { + openvdb::tools::Dense<openvdb::Vec3f, openvdb::tools::LayoutXYZ> dense( + bbox, (openvdb::Vec3f *)voxels); + openvdb::tools::copyToDense(*openvdb::gridConstPtrCast<openvdb::Vec3IGrid>(grid), dense); + break; + } + case VOLUME_GRID_STRING: + case VOLUME_GRID_POINTS: + case VOLUME_GRID_UNKNOWN: { + /* Zero channels to copy. */ + break; + } + } +#else + UNUSED_VARS(volume, volume_grid, min, max, voxels); +#endif +} + +/* Wireframe */ + +#ifdef WITH_OPENVDB +struct VolumeWireframe { + std::vector<openvdb::Vec3f> verts; + std::vector<openvdb::Vec2I> edges; + + template<typename GridType> + void add_grid(openvdb::GridBase::ConstPtr gridbase, const bool points, const bool coarse) + { + using TreeType = typename GridType::TreeType; + using Depth2Type = typename TreeType::RootNodeType::ChildNodeType::ChildNodeType; + using NodeCIter = typename TreeType::NodeCIter; + using GridConstPtr = typename GridType::ConstPtr; + + GridConstPtr grid = openvdb::gridConstPtrCast<GridType>(gridbase); + const openvdb::math::Transform &transform = grid->transform(); + const int depth = (coarse) ? 2 : 3; + + NodeCIter iter = grid->tree().cbeginNode(); + iter.setMaxDepth(depth); + + for (; iter; ++iter) { + if (iter.getDepth() == depth) { + openvdb::CoordBBox coordbbox; + + if (depth == 2) { + /* Internal node at depth 2. */ + const Depth2Type *node = nullptr; + iter.getNode(node); + if (node) { + node->evalActiveBoundingBox(coordbbox, false); + } + else { + continue; + } + } + else { + /* Leaf node. */ + if (!iter.getBoundingBox(coordbbox)) { + continue; + } + } + + /* +1 to convert from exclusive to include bounds. */ + coordbbox.max() = coordbbox.max().offsetBy(1); + openvdb::BBoxd bbox = transform.indexToWorld(coordbbox); + + if (points) { + add_point(bbox); + } + else { + add_box(bbox); + } + } + } + } + + void add_point(const openvdb::BBoxd &bbox) + { + verts.push_back(bbox.getCenter()); + } + + void add_box(const openvdb::BBoxd &bbox) + { + /* TODO: deduplicate edges, hide flat edges? */ + openvdb::Vec3f min = bbox.min(); + openvdb::Vec3f max = bbox.max(); + + const int vert_offset = verts.size(); + const int edge_offset = edges.size(); + + /* Create vertices. */ + verts.resize(vert_offset + 8); + verts[vert_offset + 0] = openvdb::Vec3f(min[0], min[1], min[2]); + verts[vert_offset + 1] = openvdb::Vec3f(max[0], min[1], min[2]); + verts[vert_offset + 2] = openvdb::Vec3f(max[0], max[1], min[2]); + verts[vert_offset + 3] = openvdb::Vec3f(min[0], max[1], min[2]); + verts[vert_offset + 4] = openvdb::Vec3f(min[0], min[1], max[2]); + verts[vert_offset + 5] = openvdb::Vec3f(max[0], min[1], max[2]); + verts[vert_offset + 6] = openvdb::Vec3f(max[0], max[1], max[2]); + verts[vert_offset + 7] = openvdb::Vec3f(min[0], max[1], max[2]); + + /* Create edges. */ + const int box_edges[12][2] = {{0, 1}, + {1, 2}, + {2, 3}, + {3, 0}, + {4, 5}, + {5, 6}, + {6, 7}, + {7, 4}, + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7}}; + + edges.resize(edge_offset + 12); + for (int i = 0; i < 12; i++) { + edges[edge_offset + i] = openvdb::Vec2I(vert_offset + box_edges[i][0], + vert_offset + box_edges[i][1]); + } + } +}; +#endif + +void BKE_volume_grid_wireframe(const Volume *volume, + VolumeGrid *volume_grid, + BKE_volume_wireframe_cb cb, + void *cb_userdata) +{ +#ifdef WITH_OPENVDB + VolumeWireframe wireframe; + + if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) { + /* Nothing. */ + } + else if (volume->display.wireframe_type == VOLUME_WIREFRAME_BOUNDS) { + /* Bounding box. */ + float min[3], max[3]; + BKE_volume_grid_bounds(volume_grid, min, max); + + openvdb::BBoxd bbox(min, max); + wireframe.add_box(bbox); + } + else { + /* Tree nodes. */ + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + const bool points = (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS); + const bool coarse = (volume->display.wireframe_detail == VOLUME_WIREFRAME_COARSE); + + switch (BKE_volume_grid_type(volume_grid)) { + case VOLUME_GRID_BOOLEAN: { + wireframe.add_grid<openvdb::BoolGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_FLOAT: { + wireframe.add_grid<openvdb::FloatGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_DOUBLE: { + wireframe.add_grid<openvdb::DoubleGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_INT: { + wireframe.add_grid<openvdb::Int32Grid>(grid, points, coarse); + break; + } + case VOLUME_GRID_INT64: { + wireframe.add_grid<openvdb::Int64Grid>(grid, points, coarse); + break; + } + case VOLUME_GRID_MASK: { + wireframe.add_grid<openvdb::MaskGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_VECTOR_FLOAT: { + wireframe.add_grid<openvdb::Vec3fGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_VECTOR_DOUBLE: { + wireframe.add_grid<openvdb::Vec3dGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_VECTOR_INT: { + wireframe.add_grid<openvdb::Vec3IGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_STRING: { + wireframe.add_grid<openvdb::StringGrid>(grid, points, coarse); + break; + } + case VOLUME_GRID_POINTS: + case VOLUME_GRID_UNKNOWN: { + break; + } + } + } + + cb(cb_userdata, + (float(*)[3])wireframe.verts.data(), + (int(*)[2])wireframe.edges.data(), + wireframe.verts.size(), + wireframe.edges.size()); +#else + UNUSED_VARS(volume, volume_grid); + cb(cb_userdata, NULL, NULL, 0, 0); +#endif +} |