/* * 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. */ /** \file * \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 # include #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 dense(bbox, voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_FLOAT: { openvdb::tools::Dense dense(bbox, voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_DOUBLE: { openvdb::tools::Dense dense(bbox, voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_INT: { openvdb::tools::Dense dense(bbox, voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_INT64: { openvdb::tools::Dense dense(bbox, voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_MASK: { openvdb::tools::Dense dense(bbox, voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_VECTOR_FLOAT: { openvdb::tools::Dense dense( bbox, (openvdb::Vec3f *)voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_VECTOR_DOUBLE: { openvdb::tools::Dense dense( bbox, (openvdb::Vec3f *)voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(grid), dense); break; } case VOLUME_GRID_VECTOR_INT: { openvdb::tools::Dense dense( bbox, (openvdb::Vec3f *)voxels); openvdb::tools::copyToDense(*openvdb::gridConstPtrCast(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 verts; std::vector edges; template 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(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(grid, points, coarse); break; } case VOLUME_GRID_FLOAT: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_DOUBLE: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_INT: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_INT64: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_MASK: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_VECTOR_FLOAT: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_VECTOR_DOUBLE: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_VECTOR_INT: { wireframe.add_grid(grid, points, coarse); break; } case VOLUME_GRID_STRING: { wireframe.add_grid(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 } /* Render */ float BKE_volume_density_scale(const Volume *volume, const float matrix[4][4]) { if (volume->render.space == VOLUME_SPACE_OBJECT) { float unit[3] = {1.0f, 1.0f, 1.0f}; normalize_v3(unit); mul_mat3_m4_v3(matrix, unit); return 1.0f / len_v3(unit); } else { return 1.0f; } }