tornavis/source/blender/blenkernel/intern/volume_render.cc

370 lines
12 KiB
C++

/*
* 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 <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
}
/* 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;
}
}