Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/CMakeLists.txt22
-rw-r--r--intern/cycles/blender/blender_session.cpp5
-rw-r--r--intern/cycles/device/CMakeLists.txt4
-rw-r--r--intern/cycles/device/device_cpu.cpp27
-rw-r--r--intern/cycles/device/device_memory.h8
-rw-r--r--intern/cycles/device/device_memory_openvdb.h62
-rw-r--r--intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h190
-rw-r--r--intern/cycles/render/CMakeLists.txt6
-rw-r--r--intern/cycles/render/image.cpp144
-rw-r--r--intern/cycles/render/image.h14
-rw-r--r--intern/cycles/render/mesh_volume.cpp174
-rw-r--r--intern/cycles/render/mesh_volume.h96
-rw-r--r--intern/cycles/render/openvdb.cpp235
-rw-r--r--intern/cycles/render/openvdb.h49
-rw-r--r--intern/cycles/util/util_texture.h17
-rw-r--r--intern/openvdb/intern/openvdb_reader.cc10
-rw-r--r--intern/openvdb/intern/openvdb_reader.h5
-rw-r--r--intern/openvdb/openvdb_capi.cc123
-rw-r--r--intern/openvdb/openvdb_capi.h20
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py24
-rw-r--r--source/blender/makesdna/DNA_smoke_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c29
22 files changed, 1049 insertions, 218 deletions
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index 100a52625d1..13538caf54c 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -261,6 +261,28 @@ if(WITH_CYCLES_LOGGING)
)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+
+ if(WITH_OPENVDB_3_ABI_COMPATIBLE)
+ add_definitions(
+ -DOPENVDB_3_ABI_COMPATIBLE
+ )
+ endif()
+
+ if(WITH_OPENVDB_BLOSC)
+ add_definitions(
+ -DWITH_OPENVDB_BLOSC
+ )
+ endif()
+
+ include_directories(
+ SYSTEM
+ ${OPENVDB_INCLUDE_DIRS}
+ ../openvdb
+ )
+endif()
+
# Debugging capabilities (debug passes etc).
if(WITH_CYCLES_DEBUG)
add_definitions(-DWITH_CYCLES_DEBUG)
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 00d23b9095e..481057e308c 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -1082,6 +1082,11 @@ void BlenderSession::builtin_image_info(const string &builtin_name,
metadata.width = resolution.x * amplify;
metadata.height = resolution.y * amplify;
metadata.depth = resolution.z * amplify;
+
+ if(b_domain.is_openvdb()) {
+ metadata.openvdb_filepath = blender_absolute_path(b_data, b_id,
+ b_domain.openvdb_filepath());
+ }
}
else {
/* TODO(sergey): Check we're indeed in shader node tree. */
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index 75e78e038ea..8e3494a814c 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -60,6 +60,10 @@ set(SRC_HEADERS
device_task.h
)
+if(WITH_OPENVDB)
+ list(APPEND SRC_HEADERS device_memory_openvdb.h)
+endif()
+
add_definitions(${GL_DEFINITIONS})
if(WITH_CYCLES_NETWORK)
add_definitions(-DWITH_NETWORK)
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index 04578eec954..f38225f867a 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -415,16 +415,28 @@ public:
info.cl_buffer = 0;
info.interpolation = mem.interpolation;
info.extension = mem.extension;
+ info.grid_type = mem.grid_type;
info.width = mem.real_width;
info.height = mem.real_height;
info.depth = mem.real_depth;
- info.grid_info = 0;
- if(grid_info) {
- info.grid_info = (uint64_t)grid_info->host_pointer;
- info.tiled_width = get_tile_res(info.width);
- info.tiled_height = get_tile_res(info.height);
- info.last_tile_width = info.width % TILE_SIZE;
- info.last_tile_height = info.height % TILE_SIZE;
+
+ /* For OpenVDB textures, the kernel will retrieve the accessor from
+ * util, but there must be some value stored in data or the texture
+ * will not be used. As a stopgap, the accessor pointer will just
+ * be stored in both data and util. */
+ switch(mem.grid_type) {
+ case IMAGE_GRID_TYPE_OPENVDB:
+ info.util = info.data;
+ break;
+ case IMAGE_GRID_TYPE_SPARSE:
+ info.util = (uint64_t)grid_info->host_pointer;
+ info.tiled_width = get_tile_res(info.width);
+ info.tiled_height = get_tile_res(info.height);
+ info.last_tile_width = info.width % TILE_SIZE;
+ info.last_tile_height = info.height % TILE_SIZE;
+ break;
+ default:
+ info.util = 0;
}
need_texture_info = true;
}
@@ -438,7 +450,6 @@ public:
grid_info->device_size = grid_info->memory_size();
stats.mem_alloc(grid_info->device_size);
}
-
}
void tex_free(device_memory& mem)
diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h
index 0f3843b90e5..e21636009ee 100644
--- a/intern/cycles/device/device_memory.h
+++ b/intern/cycles/device/device_memory.h
@@ -174,9 +174,12 @@ template<> struct device_type_traits<uint64_t> {
class device_memory
{
public:
- size_t memory_size() { return data_size*data_elements*datatype_size(data_type); }
+ size_t memory_size() {
+ /* Not including grid_info. */
+ return data_size * data_elements * datatype_size(data_type);
+ }
size_t memory_elements_size(int elements) {
- return elements*data_elements*datatype_size(data_type);
+ return elements * data_elements * datatype_size(data_type);
}
/* Data information. */
@@ -197,6 +200,7 @@ public:
const char *name;
InterpolationType interpolation;
ExtensionType extension;
+ ImageGridType grid_type;
/* Pointers. */
Device *device;
diff --git a/intern/cycles/device/device_memory_openvdb.h b/intern/cycles/device/device_memory_openvdb.h
new file mode 100644
index 00000000000..a387612b3dd
--- /dev/null
+++ b/intern/cycles/device/device_memory_openvdb.h
@@ -0,0 +1,62 @@
+#ifndef __DEVICE_MEMORY_OPENVDB_H__
+#define __DEVICE_MEMORY_OPENVDB_H__
+
+/* OpenVDB Memory
+ *
+ *
+ */
+#include <openvdb/openvdb.h>
+#include "device/device_memory.h"
+
+CCL_NAMESPACE_BEGIN
+
+template<typename GridType, typename DataType>
+class device_openvdb : public device_memory
+{
+public:
+ device_openvdb(Device *device,
+ const char *name,
+ MemoryType type,
+ typename GridType::Ptr grid,
+ typename GridType::ConstAccessor accessor,
+ int3 resolution)
+ : device_memory(device, name, type),
+ vdb_grid(grid),
+ vdb_acc(accessor)
+ {
+ using namespace openvdb;
+
+ data_type = device_type_traits<DataType>::data_type;
+ data_elements = device_type_traits<DataType>::num_elements;
+
+ assert(data_elements > 0);
+
+ host_pointer = static_cast<void*>(&vdb_acc);
+
+ data_width = real_width = resolution.x;
+ data_height = real_height = resolution.y;
+ data_depth = real_depth = resolution.z;
+
+ assert((vdb_grid->memUsage() % (data_elements * datatype_size(data_type))) == 0);
+
+ data_size = vdb_grid->memUsage() / (data_elements * datatype_size(data_type));
+ }
+
+ typename GridType::Ptr vdb_grid;
+ typename GridType::ConstAccessor vdb_acc;
+
+ void copy_to_device()
+ {
+ device_copy_to();
+ }
+
+protected:
+ size_t size(size_t width, size_t height, size_t depth)
+ {
+ return width * ((height == 0)? 1: height) * ((depth == 0)? 1: depth);
+ }
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __DEVICE_MEMORY_OPENVDB_H__ */
diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h
index a0d6b54245b..22efcff344c 100644
--- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h
+++ b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h
@@ -19,9 +19,14 @@
#include "util/util_sparse_grid.h"
+#ifdef WITH_OPENVDB
+#include <openvdb/openvdb.h>
+#endif
+
CCL_NAMESPACE_BEGIN
-template<typename T> struct TextureInterpolator {
+template<typename T, typename UtilType>
+struct TextureInterpolator {
#define SET_CUBIC_SPLINE_WEIGHTS(u, t) \
{ \
u[0] = (((-1.0f/6.0f)* t + 0.5f) * t - 0.5f) * t + (1.0f/6.0f); \
@@ -30,11 +35,6 @@ template<typename T> struct TextureInterpolator {
u[3] = (1.0f / 6.0f) * t * t * t; \
} (void)0
- static ccl_always_inline int flatten(int x, int y, int z, int width, int height)
- {
- return x + width * (y + z * height);
- }
-
static ccl_always_inline float4 read(float4 r)
{
return r;
@@ -80,14 +80,29 @@ template<typename T> struct TextureInterpolator {
return read(data[y * width + x]);
}
- static ccl_always_inline float4 read(const T *data, const int *grid_info,
- int x, int y, int z,
- int tiw, int tih, int ltw, int lth)
+ /* Default grid voxel access. */
+ static ccl_always_inline float4 read_data(const T *data,
+ const void */*util*/,
+ int x, int y, int z,
+ int width, int height,
+ int /*tiw*/, int /*tih*/,
+ int /*ltw*/, int /*lth*/)
+ {
+ return read(data[x + width * (y + z * height)]);
+ }
+
+ /* Sparse grid voxel access. */
+ static ccl_always_inline float4 read_data(const T *data,
+ const int *grid_info,
+ int x, int y, int z,
+ int /*width*/, int /*height*/,
+ int tiw, int tih,
+ int ltw, int lth)
{
int tix = x / TILE_SIZE, itix = x % TILE_SIZE,
tiy = y / TILE_SIZE, itiy = y % TILE_SIZE,
tiz = z / TILE_SIZE, itiz = z % TILE_SIZE;
- int dense_index = flatten(tix, tiy, tiz, tiw, tih) * 2;
+ int dense_index = (tix + tiw * (tiy + tiz * tih)) * 2;
int sparse_index = grid_info[dense_index];
int dims = grid_info[dense_index + 1];
if(sparse_index < 0) {
@@ -95,20 +110,36 @@ template<typename T> struct TextureInterpolator {
}
int itiw = dims & (1 << ST_SHIFT_TRUNCATE_WIDTH) ? ltw : TILE_SIZE;
int itih = dims & (1 << ST_SHIFT_TRUNCATE_HEIGHT) ? lth : TILE_SIZE;
- int in_tile_index = flatten(itix, itiy, itiz, itiw, itih);
+ int in_tile_index = itix + itiw * (itiy + itiz * itih);
return read(data[sparse_index + in_tile_index]);
}
- static ccl_always_inline float4 read(const T *data, const int *grid_info,
- int index, int width, int height, int /*depth*/,
- int tiw, int tih, int ltw, int lth)
+#ifdef WITH_OPENVDB
+ /* OpenVDB grid voxel access. */
+ static ccl_always_inline float4 read_data(const T */*data*/,
+ openvdb::FloatGrid::ConstAccessor *accessor,
+ int x, int y, int z,
+ int /*width*/, int /*height*/,
+ int /*tiw*/, int /*tih*/,
+ int /*ltw*/, int /*lth*/)
{
- int x = index % width;
- int y = (index / width) % height;
- int z = index / (width * height);
- return read(data, grid_info, x, y, z, tiw, tih, ltw, lth);
+ const openvdb::math::Coord xyz(x, y, z);
+ return read(accessor->getValue(xyz));
}
+ static ccl_always_inline float4 read_data(const T */*data*/,
+ openvdb::Vec3SGrid::ConstAccessor *accessor,
+ int x, int y, int z,
+ int /*width*/, int /*height*/,
+ int /*tiw*/, int /*tih*/,
+ int /*ltw*/, int /*lth*/)
+ {
+ const openvdb::math::Coord xyz(x, y, z);
+ openvdb::math::Vec3s r = accessor->getValue(xyz);
+ return make_float4(r.x(), r.y(), r.z(), 1.0f);
+ }
+#endif
+
static ccl_always_inline int wrap_periodic(int x, int width)
{
x %= width;
@@ -316,14 +347,11 @@ template<typename T> struct TextureInterpolator {
}
const T *data = (const T*)info.data;
- const int *grid_info = (const int*)info.grid_info;
+ UtilType *util = (UtilType*)info.util;
- if(grid_info) {
- return read(data, grid_info, ix, iy, iz,
- info.tiled_width, info.tiled_height,
- info.last_tile_width, info.last_tile_height);
- }
- return read(data[flatten(ix, iy, iz, width, height)]);
+ return read_data(data, util, ix, iy, iz, width, height,
+ info.tiled_width, info.tiled_height,
+ info.last_tile_width, info.last_tile_height);
}
static ccl_always_inline float4 interp_3d_linear(const TextureInfo& info,
@@ -372,32 +400,20 @@ template<typename T> struct TextureInterpolator {
float4 r;
const T *data = (const T*)info.data;
- const int *gi = (const int*)info.grid_info;
-
- if(gi) {
- int tiw = info.tiled_width;
- int tih = info.tiled_height;
- int ltw = info.last_tile_width;
- int lth = info.last_tile_height;
- r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx) * read(data, gi, ix, iy, iz, tiw, tih, ltw, lth);
- r += (1.0f - tz)*(1.0f - ty)*tx * read(data, gi, nix, iy, iz, tiw, tih, ltw, lth);
- r += (1.0f - tz)*ty*(1.0f - tx) * read(data, gi, ix, niy, iz, tiw, tih, ltw, lth);
- r += (1.0f - tz)*ty*tx * read(data, gi, nix, niy, iz, tiw, tih, ltw, lth);
- r += tz*(1.0f - ty)*(1.0f - tx) * read(data, gi, ix, iy, niz, tiw, tih, ltw, lth);
- r += tz*(1.0f - ty)*tx * read(data, gi, nix, iy, niz, tiw, tih, ltw, lth);
- r += tz*ty*(1.0f - tx) * read(data, gi, ix, niy, niz, tiw, tih, ltw, lth);
- r += tz*ty*tx * read(data, gi, nix, niy, niz, tiw, tih, ltw, lth);
- }
- else {
- r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx) * read(data[flatten(ix, iy, iz, width, height)]);
- r += (1.0f - tz)*(1.0f - ty)*tx * read(data[flatten(nix, iy, iz, width, height)]);
- r += (1.0f - tz)*ty*(1.0f - tx) * read(data[flatten(ix, niy, iz, width, height)]);
- r += (1.0f - tz)*ty*tx * read(data[flatten(nix, niy, iz, width, height)]);
- r += tz*(1.0f - ty)*(1.0f - tx) * read(data[flatten(ix, iy, niz, width, height)]);
- r += tz*(1.0f - ty)*tx * read(data[flatten(nix, iy, niz, width, height)]);
- r += tz*ty*(1.0f - tx) * read(data[flatten(ix, niy, niz, width, height)]);
- r += tz*ty*tx * read(data[flatten(nix, niy, niz, width, height)]);
- }
+ UtilType *util = (UtilType*)info.util;
+ int tiw = info.tiled_width;
+ int tih = info.tiled_height;
+ int ltw = info.last_tile_width;
+ int lth = info.last_tile_height;
+
+ r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx) * read_data(data, util, ix, iy, iz, width, height, tiw, tih, ltw, lth);
+ r += (1.0f - tz)*(1.0f - ty)*tx * read_data(data, util, nix, iy, iz, width, height, tiw, tih, ltw, lth);
+ r += (1.0f - tz)*ty*(1.0f - tx) * read_data(data, util, ix, niy, iz, width, height, tiw, tih, ltw, lth);
+ r += (1.0f - tz)*ty*tx * read_data(data, util, nix, niy, iz, width, height, tiw, tih, ltw, lth);
+ r += tz*(1.0f - ty)*(1.0f - tx) * read_data(data, util, ix, iy, niz, width, height, tiw, tih, ltw, lth);
+ r += tz*(1.0f - ty)*tx * read_data(data, util, nix, iy, niz, width, height, tiw, tih, ltw, lth);
+ r += tz*ty*(1.0f - tx) * read_data(data, util, ix, niy, niz, width, height, tiw, tih, ltw, lth);
+ r += tz*ty*tx * read_data(data, util, nix, niy, niz, width, height, tiw, tih, ltw, lth);
return r;
}
@@ -418,10 +434,6 @@ template<typename T> struct TextureInterpolator {
int width = info.width;
int height = info.height;
int depth = info.depth;
- int tiw = info.tiled_width;
- int tih = info.tiled_height;
- int ltw = info.last_tile_width;
- int lth = info.last_tile_height;
int ix, iy, iz;
int nix, niy, niz;
/* Tricubic b-spline interpolation. */
@@ -491,9 +503,8 @@ template<typename T> struct TextureInterpolator {
/* Some helper macro to keep code reasonable size,
* let compiler to inline all the matrix multiplications.
*/
-#define DATA(x, y, z) (gi ? \
- read(data, gi, xc[x] + yc[y] + zc[z], width, height, depth, tiw, tih, ltw, lth) : \
- read(data[xc[x] + yc[y] + zc[z]]))
+ /* To-do (gschua): fix voxel access for non-default grid types. */
+#define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]]))
#define COL_TERM(col, row) \
(v[col] * (u[0] * DATA(0, col, row) + \
u[1] * DATA(1, col, row) + \
@@ -511,7 +522,6 @@ template<typename T> struct TextureInterpolator {
/* Actual interpolation. */
const T *data = (const T*)info.data;
- const int *gi = (const int*)info.grid_info;
return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
#undef COL_TERM
@@ -527,12 +537,12 @@ template<typename T> struct TextureInterpolator {
return make_float4(0.0f);
switch((interp == INTERPOLATION_NONE)? info.interpolation: interp) {
- case INTERPOLATION_CLOSEST:
- return interp_3d_closest(info, x, y, z);
+ case INTERPOLATION_CUBIC:
+ return interp_3d_tricubic(info, x, y, z);
case INTERPOLATION_LINEAR:
return interp_3d_linear(info, x, y, z);
default:
- return interp_3d_tricubic(info, x, y, z);
+ return interp_3d_closest(info, x, y, z);
}
}
#undef SET_CUBIC_SPLINE_WEIGHTS
@@ -544,18 +554,18 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl
switch(kernel_tex_type(id)) {
case IMAGE_DATA_TYPE_HALF:
- return TextureInterpolator<half>::interp(info, x, y);
+ return TextureInterpolator<half, void>::interp(info, x, y);
case IMAGE_DATA_TYPE_BYTE:
- return TextureInterpolator<uchar>::interp(info, x, y);
+ return TextureInterpolator<uchar, void>::interp(info, x, y);
case IMAGE_DATA_TYPE_FLOAT:
- return TextureInterpolator<float>::interp(info, x, y);
+ return TextureInterpolator<float, void>::interp(info, x, y);
case IMAGE_DATA_TYPE_HALF4:
- return TextureInterpolator<half4>::interp(info, x, y);
+ return TextureInterpolator<half4, void>::interp(info, x, y);
case IMAGE_DATA_TYPE_BYTE4:
- return TextureInterpolator<uchar4>::interp(info, x, y);
+ return TextureInterpolator<uchar4, void>::interp(info, x, y);
case IMAGE_DATA_TYPE_FLOAT4:
default:
- return TextureInterpolator<float4>::interp(info, x, y);
+ return TextureInterpolator<float4, void>::interp(info, x, y);
}
}
@@ -563,20 +573,52 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, int id, float x,
{
const TextureInfo& info = kernel_tex_fetch(__texture_info, id);
+ /* UNLIKELY(info.util) is a quicker way to check if grid type is not
+ * default, since only default type will have util == 0. */
+
switch(kernel_tex_type(id)) {
case IMAGE_DATA_TYPE_HALF:
- return TextureInterpolator<half>::interp_3d(info, x, y, z, interp);
+ if(UNLIKELY(info.util))
+ return TextureInterpolator<half, int>::interp_3d(info, x, y, z, interp);
+ else
+ return TextureInterpolator<half, void>::interp_3d(info, x, y, z, interp);
case IMAGE_DATA_TYPE_BYTE:
- return TextureInterpolator<uchar>::interp_3d(info, x, y, z, interp);
+ if(UNLIKELY(info.util))
+ return TextureInterpolator<uchar, int>::interp_3d(info, x, y, z, interp);
+ else
+ return TextureInterpolator<uchar, void>::interp_3d(info, x, y, z, interp);
case IMAGE_DATA_TYPE_FLOAT:
- return TextureInterpolator<float>::interp_3d(info, x, y, z, interp);
+ if(UNLIKELY(info.util))
+#ifdef WITH_OPENVDB
+ if(info.grid_type == IMAGE_GRID_TYPE_OPENVDB)
+ return TextureInterpolator<float, openvdb::FloatGrid::ConstAccessor>::interp_3d(info, x, y, z, interp);
+ else
+#endif
+ return TextureInterpolator<float, int>::interp_3d(info, x, y, z, interp);
+ else
+ return TextureInterpolator<float, void>::interp_3d(info, x, y, z, interp);
case IMAGE_DATA_TYPE_HALF4:
- return TextureInterpolator<half4>::interp_3d(info, x, y, z, interp);
+ if(UNLIKELY(info.util))
+ return TextureInterpolator<half4, int>::interp_3d(info, x, y, z, interp);
+ else
+ return TextureInterpolator<half4, void>::interp_3d(info, x, y, z, interp);
case IMAGE_DATA_TYPE_BYTE4:
- return TextureInterpolator<uchar4>::interp_3d(info, x, y, z, interp);
+ if(UNLIKELY(info.util))
+ return TextureInterpolator<uchar4, int>::interp_3d(info, x, y, z, interp);
+ else
+ return TextureInterpolator<half4, void>::interp_3d(info, x, y, z, interp);
case IMAGE_DATA_TYPE_FLOAT4:
+ if(UNLIKELY(info.util))
+#ifdef WITH_OPENVDB
+ if(info.grid_type == IMAGE_GRID_TYPE_OPENVDB)
+ return TextureInterpolator<float4, openvdb::Vec3SGrid::ConstAccessor>::interp_3d(info, x, y, z, interp);
+ else
+#endif
+ return TextureInterpolator<float4, int>::interp_3d(info, x, y, z, interp);
+ else
+ return TextureInterpolator<float4, void>::interp_3d(info, x, y, z, interp);
default:
- return TextureInterpolator<float4>::interp_3d(info, x, y, z, interp);
+ return make_float4(0.0f);
}
}
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index b7248354abd..601e6431123 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -51,6 +51,7 @@ set(SRC_HEADERS
integrator.h
light.h
mesh.h
+ mesh_volume.h
nodes.h
object.h
osl.h
@@ -65,6 +66,11 @@ set(SRC_HEADERS
tile.h
)
+if(WITH_OPENVDB)
+ list(APPEND SRC openvdb.cpp)
+ list(APPEND SRC_HEADERS openvdb.h)
+endif()
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RTTI_DISABLE_FLAGS}")
include_directories(${INC})
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index e0337bfab42..70345ca543a 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -29,6 +29,10 @@
#include <OSL/oslexec.h>
#endif
+#ifdef WITH_OPENVDB
+#include "render/openvdb.h"
+#endif
+
CCL_NAMESPACE_BEGIN
/* Some helpers to silence warning in templated function. */
@@ -104,6 +108,26 @@ bool ImageManager::get_image_metadata(const string& filename,
if(builtin_data) {
if(builtin_image_info_cb) {
builtin_image_info_cb(filename, builtin_data, metadata);
+
+#ifdef WITH_OPENVDB
+ /* Metadata for external OpenVDB volumes. */
+ if(!metadata.openvdb_filepath.empty()) {
+ if(!path_exists(metadata.openvdb_filepath)) {
+ VLOG(1) << "File '" << filename << "' does not exist.";
+ return false;
+ }
+ if(path_is_directory(metadata.openvdb_filepath)) {
+ VLOG(1) << "File '" << filename << "' is a directory, can't use as OpenVDB.";
+ return false;
+ }
+ int3 res = get_openvdb_resolution(metadata.openvdb_filepath);
+ metadata.width = res.x;
+ metadata.height = res.y;
+ metadata.depth = res.z;
+ metadata.is_float = true;
+ metadata.is_half = false;
+ }
+#endif
}
else {
return false;
@@ -343,6 +367,7 @@ int ImageManager::add_image(const string& filename,
img->make_sparse = make_sparse;
img->isovalue = isovalue;
img->mem = NULL;
+ img->openvdb_filepath = metadata.openvdb_filepath;
images[type][slot] = img;
@@ -730,7 +755,7 @@ void ImageManager::file_load_failed(device_vector<DeviceType> *tex_img,
}
template<typename DeviceType>
-void ImageManager::file_make_image_sparse(Device *device,
+bool ImageManager::file_make_image_sparse(Device *device,
Image *img,
device_vector<DeviceType> *tex_img)
{
@@ -764,7 +789,7 @@ void ImageManager::file_make_image_sparse(Device *device,
VLOG(1) << "Could not make sparse grid for "
<< path_filename(img->filename) << " (" << img->mem_name << ")"
<< ", no active tiles";
- return;
+ return false;
}
size_t memory_usage = grid_info.size() * sizeof(int) + voxel_count * sizeof(DeviceType);
@@ -775,7 +800,7 @@ void ImageManager::file_make_image_sparse(Device *device,
<< string_human_readable_size(tex_img->memory_size()) << " to "
<< string_human_readable_size(memory_usage)
<< ", not using sparse grid";
- return;
+ return false;
}
VLOG(1) << "Memory usage of '"
@@ -808,6 +833,8 @@ void ImageManager::file_make_image_sparse(Device *device,
tex_img->real_width = real_width;
tex_img->real_height = real_height;
tex_img->real_depth = real_depth;
+
+ return true;
}
template<TypeDesc::BASETYPE FileFormat,
@@ -823,6 +850,8 @@ void ImageManager::load_image(Device *device,
img->mem_name.c_str(),
MEM_TEXTURE);
+ bool is_sparse = false;
+
if(!file_load_image<FileFormat, StorageType, DeviceType>(img,
type,
texture_limit,
@@ -832,17 +861,110 @@ void ImageManager::load_image(Device *device,
file_load_failed<StorageType, DeviceType>(tex_img, type);
}
else if(img->make_sparse) {
- file_make_image_sparse<DeviceType>(device, img, tex_img);
+ is_sparse = file_make_image_sparse<DeviceType>(device, img, tex_img);
}
img->mem = tex_img;
img->mem->interpolation = img->interpolation;
img->mem->extension = img->extension;
+ img->mem->grid_type = is_sparse ? IMAGE_GRID_TYPE_SPARSE : IMAGE_GRID_TYPE_DEFAULT;
thread_scoped_lock device_lock(device_mutex);
tex_img->copy_to_device();
}
+#ifdef WITH_OPENVDB
+template<typename StorageType,
+ typename DeviceType>
+void ImageManager::load_openvdb_image(Device *device,
+ Image *img,
+ ImageDataType type,
+ int texture_limit)
+{
+ VLOG(1) << "Loading " << img->openvdb_filepath << ", Grid: " << img->filename;
+
+ int3 resolution = get_openvdb_resolution(img->openvdb_filepath);
+
+ if(device->info.type == DEVICE_CPU && 0) {
+ device_memory *tex_vdb = NULL;
+ {
+ thread_scoped_lock device_lock(device_mutex);
+ tex_vdb = file_load_openvdb(device,
+ img->openvdb_filepath,
+ img->filename,
+ resolution,
+ img->mem_name,
+ img->interpolation,
+ img->extension,
+ type == IMAGE_DATA_TYPE_FLOAT4,
+ texture_limit);
+ }
+
+ if(tex_vdb) {
+ img->mem = tex_vdb;
+ }
+ else {
+ VLOG(1) << "Failed to load "
+ << path_filename(img->filename) << " (" << img->mem_name << ")";
+
+ device_vector<DeviceType> *tex_img =
+ new device_vector<DeviceType>(device,
+ img->mem_name.c_str(),
+ MEM_TEXTURE);
+
+ file_load_failed<StorageType, DeviceType>(tex_img, type);
+
+ img->mem = tex_img;
+ img->mem->interpolation = img->interpolation;
+ img->mem->extension = img->extension;
+ img->mem->grid_type = IMAGE_GRID_TYPE_DEFAULT;
+
+ thread_scoped_lock device_lock(device_mutex);
+ tex_img->copy_to_device();
+ }
+ }
+ else {
+ device_vector<DeviceType> *tex_img =
+ new device_vector<DeviceType>(device,
+ img->mem_name.c_str(),
+ MEM_TEXTURE);
+ bool is_sparse = false;
+
+ /* All pre-checks should happen BEFORE alloc. */
+ if(!openvdb_has_grid(img->openvdb_filepath, img->filename)) {
+ VLOG(1) << "Failed to load "
+ << path_filename(img->filename) << " (" << img->mem_name << ")";
+
+ file_load_failed<StorageType, DeviceType>(tex_img, type);
+ }
+ else {
+ DeviceType *texture_pixels;
+ {
+ thread_scoped_lock device_lock(device_mutex);
+ texture_pixels = (DeviceType*)tex_img->alloc(resolution.x,
+ resolution.y,
+ resolution.z);
+ }
+
+ file_load_openvdb_dense(img->openvdb_filepath, img->filename,
+ resolution, texture_limit, texture_pixels);
+
+ if(img->make_sparse) {
+ is_sparse = file_make_image_sparse<DeviceType>(device, img, tex_img);
+ }
+ }
+
+ img->mem = tex_img;
+ img->mem->interpolation = img->interpolation;
+ img->mem->extension = img->extension;
+ img->mem->grid_type = is_sparse ? IMAGE_GRID_TYPE_SPARSE : IMAGE_GRID_TYPE_DEFAULT;
+
+ thread_scoped_lock device_lock(device_mutex);
+ tex_img->copy_to_device();
+ }
+}
+#endif
+
void ImageManager::device_load_image(Device *device,
Scene *scene,
ImageDataType type,
@@ -880,10 +1002,20 @@ void ImageManager::device_load_image(Device *device,
/* Create new texture. */
switch(type) {
case IMAGE_DATA_TYPE_FLOAT4:
- load_image<TypeDesc::FLOAT, float, float4>(device, img, type, texture_limit);
+#ifdef WITH_OPENVDB
+ if(!img->openvdb_filepath.empty())
+ load_openvdb_image<float, float4>(device, img, type, texture_limit);
+ else
+#endif
+ load_image<TypeDesc::FLOAT, float, float4>(device, img, type, texture_limit);
break;
case IMAGE_DATA_TYPE_FLOAT:
- load_image<TypeDesc::FLOAT, float, float>(device, img, type, texture_limit);
+#ifdef WITH_OPENVDB
+ if(!img->openvdb_filepath.empty())
+ load_openvdb_image<float, float>(device, img, type, texture_limit);
+ else
+#endif
+ load_image<TypeDesc::FLOAT, float, float>(device, img, type, texture_limit);
break;
case IMAGE_DATA_TYPE_BYTE4:
load_image<TypeDesc::UINT8, uchar, uchar4>(device, img, type, texture_limit);
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 8739c8f7793..b21428cf27b 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -38,6 +38,7 @@ public:
int channels;
size_t width, height, depth;
bool builtin_free_cache;
+ string openvdb_filepath;
/* Automatically set. */
ImageDataType type;
@@ -127,6 +128,8 @@ public:
device_memory *mem;
int users;
+
+ string openvdb_filepath;
};
private:
@@ -161,7 +164,7 @@ private:
ImageDataType type);
template<typename DeviceType>
- void file_make_image_sparse(Device *device,
+ bool file_make_image_sparse(Device *device,
Image *img,
device_vector<DeviceType> *tex_dense);
@@ -173,6 +176,15 @@ private:
ImageDataType type,
int texture_limit);
+#ifdef WITH_OPENVDB
+ template<typename StorageType,
+ typename DeviceType>
+ void load_openvdb_image(Device *device,
+ Image *img,
+ ImageDataType type,
+ int texture_limit);
+#endif
+
int max_flattened_slot(ImageDataType type);
int type_index_to_flattened_slot(int slot, ImageDataType type);
int flattened_slot_to_type_index(int flat_slot, ImageDataType *type);
diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp
index 5ce8198b9d6..51132431411 100644
--- a/intern/cycles/render/mesh_volume.cpp
+++ b/intern/cycles/render/mesh_volume.cpp
@@ -15,23 +15,20 @@
*/
#include "render/mesh.h"
+#include "render/mesh_volume.h"
#include "render/attribute.h"
+#ifdef WITH_OPENVDB
+#include "render/openvdb.h"
+#endif
#include "render/scene.h"
#include "util/util_foreach.h"
#include "util/util_logging.h"
#include "util/util_progress.h"
#include "util/util_sparse_grid.h"
-#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
-struct QuadData {
- int v0, v1, v2, v3;
-
- float3 normal;
-};
-
enum {
QUAD_X_MIN = 0,
QUAD_X_MAX = 1,
@@ -90,70 +87,8 @@ static void create_quad(int3 corners[8], vector<int3> &vertices, vector<QuadData
vertices.push_back(corners[quads_indices[face_index][3]]);
}
-struct VolumeParams {
- int3 resolution;
- float3 cell_size;
- float3 start_point;
- int pad_size;
-};
-
static const int CUBE_SIZE = 8;
-/* Create a mesh from a volume.
- *
- * The way the algorithm works is as follows:
- *
- * - the coordinates of active voxels from a dense volume (or 3d image) are
- * gathered inside an auxialliary volume.
- * - each set of coordinates of an CUBE_SIZE cube are mapped to the same
- * coordinate of the auxilliary volume.
- * - quads are created between active and non-active voxels in the auxialliary
- * volume to generate a tight mesh around the volume.
- */
-class VolumeMeshBuilder {
- /* Auxilliary volume that is used to check if a node already added. */
- vector<char> grid;
-
- /* The resolution of the auxilliary volume, set to be equal to 1/CUBE_SIZE
- * of the original volume on each axis. */
- int3 res;
-
- size_t number_of_nodes;
-
- /* Offset due to padding in the original grid. Padding will transform the
- * coordinates of the original grid from 0...res to -padding...res+padding,
- * so some coordinates are negative, and we need to properly account for
- * them. */
- int3 pad_offset;
-
- VolumeParams *params;
-
-public:
- VolumeMeshBuilder(VolumeParams *volume_params);
-
- void add_node(int x, int y, int z);
-
- void add_node_with_padding(int x, int y, int z);
-
- void create_mesh(vector<float3> &vertices,
- vector<int> &indices,
- vector<float3> &face_normals);
-
-private:
- void generate_vertices_and_quads(vector<int3> &vertices_is,
- vector<QuadData> &quads);
-
- void deduplicate_vertices(vector<int3> &vertices,
- vector<QuadData> &quads);
-
- void convert_object_space(const vector<int3> &vertices,
- vector<float3> &out_vertices);
-
- void convert_quads_to_tris(const vector<QuadData> &quads,
- vector<int> &tris,
- vector<float3> &face_normals);
-};
-
VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params)
{
params = volume_params;
@@ -378,8 +313,8 @@ void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads,
/* ************************************************************************** */
struct VoxelAttributeGrid {
- float *data;
- int *grid_info = NULL;
+ void *data;
+ int *grid_info;
int channels;
};
@@ -395,6 +330,8 @@ void MeshManager::create_volume_mesh(Scene *scene,
/* Compute volume parameters. */
VolumeParams volume_params;
volume_params.resolution = make_int3(0, 0, 0);
+ bool is_openvdb = false;
+ int grid_mem = -1;
foreach(Attribute& attr, mesh->attributes.attributes) {
if(attr.element != ATTR_ELEMENT_VOXEL) {
@@ -409,19 +346,21 @@ void MeshManager::create_volume_mesh(Scene *scene,
image_memory->real_depth);
if(volume_params.resolution == make_int3(0, 0, 0)) {
+ /* First volume grid. */
volume_params.resolution = resolution;
+ grid_mem = image_memory->memory_size();
}
else if(volume_params.resolution != resolution) {
VLOG(1) << "Can't create volume mesh, all voxel grid resolutions must be equal\n";
return;
}
+ is_openvdb = (image_memory->grid_type == IMAGE_GRID_TYPE_OPENVDB);
+
VoxelAttributeGrid voxel_grid;
- voxel_grid.data = static_cast<float*>(image_memory->host_pointer);
+ voxel_grid.data = image_memory->host_pointer;
voxel_grid.channels = image_memory->data_elements;
- if(grid_info) {
- voxel_grid.grid_info = static_cast<int*>(grid_info->host_pointer);
- }
+ voxel_grid.grid_info = grid_info ? static_cast<int*>(grid_info->host_pointer) : NULL;
voxel_grids.push_back(voxel_grid);
}
@@ -484,47 +423,54 @@ void MeshManager::create_volume_mesh(Scene *scene,
VolumeMeshBuilder builder(&volume_params);
const float isovalue = mesh->volume_isovalue;
- for(int z = 0; z < resolution.z; ++z) {
- for(int y = 0; y < resolution.y; ++y) {
- for(int x = 0; x < resolution.x; ++x) {
- for(size_t i = 0; i < voxel_grids.size(); ++i) {
- const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
- const int channels = voxel_grid.channels;
- const int *grid_info = voxel_grid.grid_info;
- int voxel_index;
-
- if(grid_info) {
- if(!using_cuda) {
- voxel_index = compute_index(grid_info, x, y, z,
- tiled_res.x,
- tiled_res.y,
- tiled_res.z,
- last_tile_res.x,
- last_tile_res.y);
+ for(size_t i = 0; i < voxel_grids.size(); ++i) {
+ const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
+ const int channels = voxel_grid.channels;
+
+ if(is_openvdb) {
+#ifdef WITH_OPENVDB
+ if(channels > 1) {
+ build_openvdb_mesh_vec(&builder, voxel_grid.data, resolution, isovalue);
+ }
+ else {
+ build_openvdb_mesh_fl(&builder, voxel_grid.data, resolution, isovalue);
+ }
+#else
+ assert(0);
+#endif
+ }
+ else {
+ const int *grid_info = voxel_grid.grid_info;
+ float *data = static_cast<float*>(voxel_grid.data);
+
+ for(int z = 0; z < resolution.z; ++z) {
+ for(int y = 0; y < resolution.y; ++y) {
+ for(int x = 0; x < resolution.x; ++x) {
+ int voxel_index;
+
+ if(grid_info) {
+ voxel_index = using_cuda ?
+ compute_index_cuda(grid_info, x, y, z,
+ resolution.x, resolution.y, resolution.z,
+ tiled_res.x, tiled_res.y, tiled_res.z) :
+ compute_index(grid_info, x, y, z,
+ tiled_res.x, tiled_res.y, tiled_res.z,
+ last_tile_res.x, last_tile_res.y);
+
+ if(voxel_index < 0) {
+ continue;
+ }
}
else {
- voxel_index = compute_index_cuda(grid_info,
- x, y, z,
- resolution.x,
- resolution.y,
- resolution.z,
- tiled_res.x,
- tiled_res.y,
- tiled_res.z);
+ voxel_index = compute_index(x, y, z, resolution);
}
- if(voxel_index < 0) {
- continue;
- }
- }
- else {
- voxel_index = compute_index(x, y, z, resolution);
- }
- voxel_index *= channels;
- for(int c = 0; c < channels; c++) {
- if(voxel_grid.data[voxel_index + c] >= isovalue) {
- builder.add_node_with_padding(x, y, z);
- break;
+ voxel_index *= channels;
+ for(int c = 0; c < channels; c++) {
+ if(data[voxel_index + c] >= isovalue) {
+ builder.add_node_with_padding(x, y, z);
+ break;
+ }
}
}
}
@@ -562,9 +508,7 @@ void MeshManager::create_volume_mesh(Scene *scene,
<< ((vertices.size() + face_normals.size())*sizeof(float3) + indices.size()*sizeof(int))/(1024.0*1024.0)
<< "Mb.";
- VLOG(1) << "Memory usage volume grid: "
- << (resolution.x*resolution.y*resolution.z*sizeof(float))/(1024.0*1024.0)
- << "Mb.";
+ VLOG(1) << "Memory usage volume grid: " << grid_mem / (1024.0 * 1024.0) << "Mb.";
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/mesh_volume.h b/intern/cycles/render/mesh_volume.h
new file mode 100644
index 00000000000..77ed03b5303
--- /dev/null
+++ b/intern/cycles/render/mesh_volume.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2011-2013 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __MESH_VOLUME_H__
+#define __MESH_VOLUME_H__
+
+#include "util/util_types.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+struct QuadData {
+ int v0, v1, v2, v3;
+
+ float3 normal;
+};
+
+struct VolumeParams {
+ int3 resolution;
+ float3 cell_size;
+ float3 start_point;
+ int pad_size;
+};
+
+/* Create a mesh from a volume.
+ *
+ * The way the algorithm works is as follows:
+ *
+ * - the coordinates of active voxels from a dense volume (or 3d image) are
+ * gathered inside an auxialliary volume.
+ * - each set of coordinates of an CUBE_SIZE cube are mapped to the same
+ * coordinate of the auxilliary volume.
+ * - quads are created between active and non-active voxels in the auxialliary
+ * volume to generate a tight mesh around the volume.
+ */
+class VolumeMeshBuilder {
+ /* Auxilliary volume that is used to check if a node already added. */
+ vector<char> grid;
+
+ /* The resolution of the auxilliary volume, set to be equal to 1/CUBE_SIZE
+ * of the original volume on each axis. */
+ int3 res;
+
+ size_t number_of_nodes;
+
+ /* Offset due to padding in the original grid. Padding will transform the
+ * coordinates of the original grid from 0...res to -padding...res+padding,
+ * so some coordinates are negative, and we need to properly account for
+ * them. */
+ int3 pad_offset;
+
+ VolumeParams *params;
+
+public:
+ VolumeMeshBuilder(VolumeParams *volume_params);
+
+ void add_node(int x, int y, int z);
+
+ void add_node_with_padding(int x, int y, int z);
+
+ void create_mesh(vector<float3> &vertices,
+ vector<int> &indices,
+ vector<float3> &face_normals);
+
+private:
+ void generate_vertices_and_quads(vector<int3> &vertices_is,
+ vector<QuadData> &quads);
+
+ void deduplicate_vertices(vector<int3> &vertices,
+ vector<QuadData> &quads);
+
+ void convert_object_space(const vector<int3> &vertices,
+ vector<float3> &out_vertices);
+
+ void convert_quads_to_tris(const vector<QuadData> &quads,
+ vector<int> &tris,
+ vector<float3> &face_normals);
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __MESH_VOLUME_H__ */
+
diff --git a/intern/cycles/render/openvdb.cpp b/intern/cycles/render/openvdb.cpp
new file mode 100644
index 00000000000..39992520e94
--- /dev/null
+++ b/intern/cycles/render/openvdb.cpp
@@ -0,0 +1,235 @@
+
+#include <openvdb/openvdb.h>
+#include "intern/openvdb_reader.h"
+#include "openvdb_capi.h"
+#include "render/openvdb.h"
+
+#include "util/util_logging.h"
+#include "util/util_path.h"
+
+#include "device/device_memory_openvdb.h"
+
+/* Functions that directly use the OpenVDB library throughout render. */
+
+struct OpenVDBReader;
+
+CCL_NAMESPACE_BEGIN
+
+static struct OpenVDBReader *get_reader(const string& filepath,
+ const string& grid_name)
+{
+ /* Logging is done when metadata is retrieved. */
+ if(!path_exists(filepath) || path_is_directory(filepath)) {
+ return NULL;
+ }
+
+ struct OpenVDBReader *reader = OpenVDBReader_create();
+ OpenVDBReader_open(reader, filepath.c_str());
+
+ /* If grid name is provided, we also check it's validity here. */
+ if(!grid_name.empty()) {
+ if (!OpenVDBReader_has_grid(reader, grid_name.c_str())) {
+ VLOG(1) << filepath << " does not have grid " << grid_name;
+ OpenVDBReader_free(reader);
+ return NULL;
+ }
+ }
+
+ return reader;
+}
+
+static void OpenVDB_import_grid_vector_fl4(OpenVDBReader *reader,
+ const openvdb::Name &name,
+ float4 *data,
+ const int3& resolution)
+
+{
+ using namespace openvdb;
+
+ Vec3SGrid::Ptr vgrid = gridPtrCast<Vec3SGrid>(reader->getGrid(name));
+ Vec3SGrid::ConstAccessor acc = vgrid->getConstAccessor();
+ math::Coord xyz;
+ int &x = xyz[0], &y = xyz[1], &z = xyz[2];
+
+ size_t index = 0;
+ for (z = 0; z < resolution.z; ++z) {
+ for (y = 0; y < resolution.y; ++y) {
+ for (x = 0; x < resolution.x; ++x, index += 4) {
+ math::Vec3s value = acc.getValue(xyz);
+ (*data)[index + 0] = value.x();
+ (*data)[index + 1] = value.y();
+ (*data)[index + 2] = value.z();
+ (*data)[index + 3] = 1.0f;
+ }
+ }
+ }
+}
+
+bool openvdb_has_grid(const string& filepath, const string& grid_name)
+{
+ return get_reader(filepath, grid_name);
+}
+
+int3 get_openvdb_resolution(const string& filepath)
+{
+ struct OpenVDBReader *reader = get_reader(filepath, string());
+ if(!reader) {
+ return make_int3(0, 0, 0);
+ }
+
+ int res[3];
+ OpenVDBReader_get_simple_bounds(reader, res);
+
+ OpenVDBReader_free(reader);
+
+ return make_int3(res[0], res[1], res[2]);
+}
+
+/* For now, since there is no official OpenVDB interpolation implementations
+ * for CUDA or OpenCL, OpenVDB grids can only be saved for CPU rendering.
+ * Otherwise, we convert the OpenVDB grids to dense arrays. */
+
+/* to-do (gschua): handle texture limits. */
+
+/* Thread must be locked before file_load_openvdb_cpu() is called. */
+device_memory *file_load_openvdb(Device *device,
+ const string& filepath,
+ const string& grid_name,
+ const int3& resolution,
+ const string& mem_name,
+ const InterpolationType& interpolation,
+ const ExtensionType& extension,
+ const bool& is_vec,
+ const int& /*texture_limit*/)
+{
+ using namespace openvdb;
+
+ struct OpenVDBReader *reader = get_reader(filepath, grid_name);
+ if(!reader) {
+ return NULL;
+ }
+
+ if(is_vec) {
+ Vec3SGrid::Ptr grid = gridPtrCast<Vec3SGrid>(reader->getGrid(grid_name));
+ Vec3SGrid::ConstAccessor accessor = grid->getConstAccessor();
+
+ device_openvdb<Vec3SGrid, float4> *tex_img =
+ new device_openvdb<Vec3SGrid, float4>(device,
+ mem_name.c_str(),
+ MEM_TEXTURE,
+ grid,
+ accessor,
+ resolution);
+ tex_img->interpolation = interpolation;
+ tex_img->extension = extension;
+ tex_img->grid_type = IMAGE_GRID_TYPE_OPENVDB;
+ tex_img->copy_to_device();
+
+ OpenVDBReader_free(reader);
+ return tex_img;
+ }
+ else {
+ FloatGrid::Ptr grid = gridPtrCast<FloatGrid>(reader->getGrid(grid_name));
+ FloatGrid::ConstAccessor accessor = grid->getConstAccessor();
+
+ device_openvdb<FloatGrid, float> *tex_img =
+ new device_openvdb<FloatGrid, float>(device,
+ mem_name.c_str(),
+ MEM_TEXTURE,
+ grid,
+ accessor,
+ resolution);
+
+ tex_img->interpolation = interpolation;
+ tex_img->extension = extension;
+ tex_img->grid_type = IMAGE_GRID_TYPE_OPENVDB;
+ tex_img->copy_to_device();
+
+ OpenVDBReader_free(reader);
+ return tex_img;
+ }
+}
+
+bool file_load_openvdb_dense(const string& filepath,
+ const string& grid_name,
+ const int3& resolution,
+ const int& /*texture_limit*/,
+ float *data)
+{
+ struct OpenVDBReader *reader = get_reader(filepath, grid_name);
+ if(!reader) {
+ return false;
+ }
+
+ int res[3] = {resolution.x, resolution.y, resolution.z};
+ OpenVDB_import_grid_fl(reader, grid_name.c_str(), &data, res);
+
+ OpenVDBReader_free(reader);
+
+ return true;
+}
+
+bool file_load_openvdb_dense(const string& filepath,
+ const string& grid_name,
+ const int3& resolution,
+ const int& /*texture_limit*/,
+ float4 *data)
+{
+ struct OpenVDBReader *reader = get_reader(filepath, grid_name);
+ if(!reader) {
+ return false;
+ }
+
+ OpenVDB_import_grid_vector_fl4(reader, grid_name, data, resolution);
+
+ OpenVDBReader_free(reader);
+
+ return true;
+}
+
+void build_openvdb_mesh_fl(VolumeMeshBuilder *builder,
+ void *v_accessor,
+ const int3 resolution,
+ const float isovalue)
+{
+ using namespace openvdb;
+
+ FloatGrid::ConstAccessor *acc = static_cast<FloatGrid::ConstAccessor*>(v_accessor);
+ math::Coord xyz;
+ int &x = xyz[0], &y = xyz[1], &z = xyz[2];
+
+ for (z = 0; z < resolution.z; ++z) {
+ for (y = 0; y < resolution.y; ++y) {
+ for (x = 0; x < resolution.x; ++x) {
+ if(acc->getValue(xyz) >= isovalue) {
+ builder->add_node_with_padding(x, y, z);
+ }
+ }
+ }
+ }
+}
+
+void build_openvdb_mesh_vec(VolumeMeshBuilder *builder,
+ void *v_accessor,
+ const int3 resolution,
+ const float isovalue)
+{
+ using namespace openvdb;
+
+ Vec3SGrid::ConstAccessor *acc = static_cast<Vec3SGrid::ConstAccessor*>(v_accessor);
+ math::Coord xyz;
+ int &x = xyz[0], &y = xyz[1], &z = xyz[2];
+
+ for (z = 0; z < resolution.z; ++z) {
+ for (y = 0; y < resolution.y; ++y) {
+ for (x = 0; x < resolution.x; ++x) {
+ math::Vec3s val = acc->getValue(xyz);
+ if(val.x() >= isovalue || val.y() >= isovalue || val.z() >= isovalue) {
+ builder->add_node_with_padding(x, y, z);
+ }
+ }
+ }
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/openvdb.h b/intern/cycles/render/openvdb.h
new file mode 100644
index 00000000000..b3d2ad2412f
--- /dev/null
+++ b/intern/cycles/render/openvdb.h
@@ -0,0 +1,49 @@
+#ifndef __IMAGE_OPENVDB_H__
+#define __IMAGE_OPENVDB_H__
+
+#include "device/device.h"
+#include "render/mesh_volume.h"
+
+#include "util/util_types.h"
+#include "util/util_string.h"
+
+CCL_NAMESPACE_BEGIN
+
+bool openvdb_has_grid(const string& filepath, const string& grid_name);
+int3 get_openvdb_resolution(const string& filepath);
+
+device_memory *file_load_openvdb(Device *device,
+ const string& filepath,
+ const string& grid_name,
+ const int3& resolution,
+ const string& mem_name,
+ const InterpolationType& interpolation,
+ const ExtensionType& extension,
+ const bool& is_vec,
+ const int& /*texture_limit*/);
+
+bool file_load_openvdb_dense(const string& filepath,
+ const string& grid_name,
+ const int3& resolution,
+ const int& /*texture_limit*/,
+ float *data);
+
+bool file_load_openvdb_dense(const string& filepath,
+ const string& grid_name,
+ const int3& resolution,
+ const int& /*texture_limit*/,
+ float4 *data);
+
+void build_openvdb_mesh_fl(VolumeMeshBuilder *builder,
+ void *v_accessor,
+ const int3 resolution,
+ const float isovalue);
+
+void build_openvdb_mesh_vec(VolumeMeshBuilder *builder,
+ void *v_accessor,
+ const int3 resolution,
+ const float isovalue);
+
+CCL_NAMESPACE_END
+
+#endif /* __IMAGE_OPENVDB_H__ */
diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h
index 7f86ccca658..1d945c56b3e 100644
--- a/intern/cycles/util/util_texture.h
+++ b/intern/cycles/util/util_texture.h
@@ -60,6 +60,13 @@ typedef enum ImageDataType {
#define IMAGE_DATA_TYPE_SHIFT 3
#define IMAGE_DATA_TYPE_MASK 0x7
+/* Texture Grid Types */
+typedef enum ImageGridType {
+ IMAGE_GRID_TYPE_DEFAULT,
+ IMAGE_GRID_TYPE_SPARSE,
+ IMAGE_GRID_TYPE_OPENVDB,
+} ImageGridType;
+
/* Extension types for textures.
*
* Defines how the image is extrapolated past its original bounds. */
@@ -78,18 +85,18 @@ typedef struct TextureInfo {
/* Pointer, offset or texture depending on device. */
uint64_t data;
/* References the offsets for tiles in sparse volumes. */
- uint64_t grid_info;
+ uint64_t util;
/* Buffer number for OpenCL. */
uint cl_buffer;
- /* Interpolation and extension type. */
- uint interpolation, extension;
+ /* Interpolation, extension, and grid type. */
+ uint interpolation, extension, grid_type;
/* Dimensions. */
uint width, height, depth;
/* Tiled dimensions for sparse grid index calculations,
* and length of the last tile's dimensions. */
uint tiled_width, tiled_height, last_tile_width, last_tile_height;
- /* Dummy variable to keep TextureInfo the correct size. */
- uint64_t dummy;
+ /* Dummy variables to keep TextureInfo the correct size. */
+ uint dummy;
} TextureInfo;
CCL_NAMESPACE_END
diff --git a/intern/openvdb/intern/openvdb_reader.cc b/intern/openvdb/intern/openvdb_reader.cc
index 8b15b81710d..7c730f56183 100644
--- a/intern/openvdb/intern/openvdb_reader.cc
+++ b/intern/openvdb/intern/openvdb_reader.cc
@@ -122,6 +122,16 @@ openvdb::GridBase::Ptr OpenVDBReader::getGrid(const openvdb::Name &name) const
return m_file->readGrid(name);
}
+openvdb::math::CoordBBox OpenVDBReader::getGridBounds(const openvdb::Name &name) const
+{
+ return getGrid(name)->evalActiveVoxelBoundingBox();
+}
+
+openvdb::math::Transform::Ptr OpenVDBReader::getGridTranform(const openvdb::Name &name) const
+{
+ return getGrid(name)->transformPtr();
+}
+
size_t OpenVDBReader::numGrids() const
{
return m_file->getGrids()->size();
diff --git a/intern/openvdb/intern/openvdb_reader.h b/intern/openvdb/intern/openvdb_reader.h
index 07f77130ff9..a9f90ec1770 100644
--- a/intern/openvdb/intern/openvdb_reader.h
+++ b/intern/openvdb/intern/openvdb_reader.h
@@ -48,7 +48,12 @@ public:
void mat4sMeta(const openvdb::Name &name, float value[4][4]) const;
bool hasGrid(const openvdb::Name &name) const;
+
+ /* Don't run these functions without checking if grid exists first! */
openvdb::GridBase::Ptr getGrid(const openvdb::Name &name) const;
+ openvdb::math::CoordBBox getGridBounds(const openvdb::Name &name) const;
+ openvdb::math::Transform::Ptr getGridTranform(const openvdb::Name &name) const;
+
size_t numGrids() const;
};
diff --git a/intern/openvdb/openvdb_capi.cc b/intern/openvdb/openvdb_capi.cc
index 1c8b51a23c4..a213cf3b534 100644
--- a/intern/openvdb/openvdb_capi.cc
+++ b/intern/openvdb/openvdb_capi.cc
@@ -36,6 +36,26 @@ int OpenVDB_getVersionHex()
return openvdb::OPENVDB_LIBRARY_VERSION;
}
+const char *vdb_grid_name(const int grid)
+{
+ switch(grid) {
+ case VDB_SMOKE_DENSITY:
+ return "density";
+ case VDB_SMOKE_COLOR:
+ return "color";
+ case VDB_SMOKE_FLAME:
+ return "flame";
+ case VDB_SMOKE_HEAT:
+ return "heat";
+ case VDB_SMOKE_TEMPERATURE:
+ return "temperature";
+ case VDB_SMOKE_VELOCITY:
+ return "velocity";
+ default:
+ return "";
+ }
+}
+
OpenVDBFloatGrid *OpenVDB_export_grid_fl(
OpenVDBWriter *writer,
const char *name, float *data,
@@ -216,6 +236,16 @@ void OpenVDBReader_open(OpenVDBReader *reader, const char *filename)
reader->open(filename);
}
+bool OpenVDBReader_has_grid(OpenVDBReader *reader, const char *name)
+{
+ return reader->hasGrid(name);
+}
+
+bool OpenVDBReader_has_smoke_grid(OpenVDBReader *reader, const int grid)
+{
+ return reader->hasGrid(vdb_grid_name(grid));
+}
+
void OpenVDBReader_get_meta_fl(OpenVDBReader *reader, const char *name, float *value)
{
reader->floatMeta(name, *value);
@@ -240,3 +270,96 @@ void OpenVDBReader_get_meta_mat4(OpenVDBReader *reader, const char *name, float
{
reader->mat4sMeta(name, value);
}
+
+static bool OpenVDBReader_get_bbox(struct OpenVDBReader *reader,
+ openvdb::math::CoordBBox *bbox,
+ openvdb::BBoxd *bbox_world,
+ openvdb::math::Vec3d *v_size)
+{
+ openvdb::math::Transform::Ptr tfm;
+ bool is_valid = true; /* file is valid if all grids have the same tranform */
+
+ for(int type = 0; type < VDB_SMOKE_GRID_NUM; type++) {
+ const char *grid_name = vdb_grid_name(type);
+
+ if(reader->hasGrid(grid_name)) {
+ bbox->expand(reader->getGridBounds(grid_name));
+
+ if(!tfm) {
+ tfm = reader->getGridTranform(grid_name);
+ }
+ else {
+ is_valid = (*tfm == *(reader->getGridTranform(grid_name)));
+ }
+ }
+ }
+
+ if(bbox_world) {
+ *bbox_world = tfm->indexToWorld(*bbox);
+ }
+ if(v_size) {
+ *v_size = tfm->voxelSize();
+ }
+
+ return is_valid;
+}
+
+bool OpenVDBReader_get_simple_bounds(struct OpenVDBReader *reader, int res[3])
+{
+ using namespace openvdb;
+
+ math::CoordBBox bbox;
+ math::Coord coord;
+ bool is_valid = OpenVDBReader_get_bbox(reader, &bbox, NULL, NULL);
+
+ coord = bbox.dim();
+ res[0] = coord[0];
+ res[1] = coord[1];
+ res[2] = coord[2];
+
+ return is_valid;
+}
+
+bool OpenVDBReader_get_detailed_bounds(struct OpenVDBReader *reader,
+ int res_min[3], int res_max[3], int res[3],
+ float bbox_min[3], float bbox_max[3], float voxel_size[3])
+{
+ using namespace openvdb;
+
+ math::CoordBBox bbox;
+ BBoxd bbox_world;
+ math::Coord coord;
+ math::Vec3d vec3d;
+ bool is_valid = OpenVDBReader_get_bbox(reader, &bbox, &bbox_world, &vec3d);
+
+ voxel_size[0] = vec3d[0];
+ voxel_size[1] = vec3d[1];
+ voxel_size[2] = vec3d[2];
+
+ vec3d = bbox_world.min();
+ bbox_min[0] = vec3d[0];
+ bbox_min[1] = vec3d[1];
+ bbox_min[2] = vec3d[2];
+
+ vec3d = bbox_world.max();
+ bbox_max[0] = vec3d[0];
+ bbox_max[1] = vec3d[1];
+ bbox_max[2] = vec3d[2];
+
+ coord = bbox.getStart();
+ res_min[0] = coord[0];
+ res_min[1] = coord[1];
+ res_min[2] = coord[2];
+
+ coord = bbox.getEnd();
+ res_max[0] = coord[0];
+ res_max[1] = coord[1];
+ res_max[2] = coord[2];
+
+ coord = bbox.dim();
+ res[0] = coord[0];
+ res[1] = coord[1];
+ res[2] = coord[2];
+
+ return is_valid;
+}
diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h
index fe7af82769b..3f89a264d1d 100644
--- a/intern/openvdb/openvdb_capi.h
+++ b/intern/openvdb/openvdb_capi.h
@@ -46,6 +46,19 @@ enum {
VEC_CONTRAVARIANT_ABSOLUTE = 4,
};
+enum {
+ VDB_SMOKE_DENSITY,
+ VDB_SMOKE_COLOR,
+ VDB_SMOKE_FLAME,
+ VDB_SMOKE_HEAT,
+ VDB_SMOKE_TEMPERATURE,
+ VDB_SMOKE_VELOCITY,
+
+ VDB_SMOKE_GRID_NUM
+};
+
+const char *vdb_grid_name(const int grid);
+
struct OpenVDBFloatGrid *OpenVDB_export_grid_fl(
struct OpenVDBWriter *writer,
const char *name, float *data,
@@ -93,12 +106,19 @@ void OpenVDBWriter_write(struct OpenVDBWriter *writer, const char *filename);
struct OpenVDBReader *OpenVDBReader_create(void);
void OpenVDBReader_free(struct OpenVDBReader *reader);
void OpenVDBReader_open(struct OpenVDBReader *reader, const char *filename);
+bool OpenVDBReader_has_grid(struct OpenVDBReader *reader, const char *name);
+bool OpenVDBReader_has_smoke_grid(struct OpenVDBReader *reader, const int grid);
void OpenVDBReader_get_meta_fl(struct OpenVDBReader *reader, const char *name, float *value);
void OpenVDBReader_get_meta_int(struct OpenVDBReader *reader, const char *name, int *value);
void OpenVDBReader_get_meta_v3(struct OpenVDBReader *reader, const char *name, float value[3]);
void OpenVDBReader_get_meta_v3_int(struct OpenVDBReader *reader, const char *name, int value[3]);
void OpenVDBReader_get_meta_mat4(struct OpenVDBReader *reader, const char *name, float value[4][4]);
+bool OpenVDBReader_get_simple_bounds(struct OpenVDBReader *reader, int res[3]);
+bool OpenVDBReader_get_detailed_bounds(struct OpenVDBReader *reader,
+ int res_min[3], int res_max[3], int res[3],
+ float bbox_min[3], float bbox_max[3], float voxel_size[3]);
+
#ifdef __cplusplus
}
#endif
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index 9489fb71e15..42ff60da6f1 100644
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -82,6 +82,16 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
sub.prop(domain, "dissolve_speed", text="Time")
sub.prop(domain, "use_dissolve_smoke_log", text="Slow")
+ if(bpy.app.build_options.openvdb):
+ layout.prop(domain, "is_openvdb", text="OpenVDB Import")
+
+ if(domain.is_openvdb):
+ split = layout.split(percentage=0.35)
+ col = split.column()
+ col.label(text="File Path:")
+ col = split.column()
+ col.prop(domain, "openvdb_filepath", text="")
+
elif md.smoke_type == 'FLOW':
flow = md.flow_settings
@@ -178,7 +188,7 @@ class PHYSICS_PT_smoke_fire(PhysicButtonsPanel, Panel):
@classmethod
def poll(cls, context):
md = context.smoke
- return md and (md.smoke_type == 'DOMAIN')
+ return md and (md.smoke_type == 'DOMAIN') and (not md.domain_settings.is_openvdb)
def draw(self, context):
layout = self.layout
@@ -208,7 +218,7 @@ class PHYSICS_PT_smoke_adaptive_domain(PhysicButtonsPanel, Panel):
@classmethod
def poll(cls, context):
md = context.smoke
- return md and (md.smoke_type == 'DOMAIN')
+ return md and (md.smoke_type == 'DOMAIN') and (not md.domain_settings.is_openvdb)
def draw_header(self, context):
md = context.smoke.domain_settings
@@ -243,7 +253,7 @@ class PHYSICS_PT_smoke_highres(PhysicButtonsPanel, Panel):
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES) and (not md.domain_settings.is_openvdb)
def draw_header(self, context):
md = context.smoke.domain_settings
@@ -283,7 +293,7 @@ class PHYSICS_PT_smoke_groups(PhysicButtonsPanel, Panel):
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES) and (not md.domain_settings.is_openvdb)
def draw(self, context):
layout = self.layout
@@ -312,7 +322,7 @@ class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES) and (not md.domain_settings.is_openvdb)
def draw(self, context):
layout = self.layout
@@ -349,7 +359,7 @@ class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES)
+ return md and (md.smoke_type == 'DOMAIN') and (rd.engine in cls.COMPAT_ENGINES) and (not md.domain_settings.is_openvdb)
def draw(self, context):
domain = context.smoke.domain_settings
@@ -364,7 +374,7 @@ class PHYSICS_PT_smoke_display_settings(PhysicButtonsPanel, Panel):
def poll(cls, context):
md = context.smoke
rd = context.scene.render
- return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+ return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine) and (not md.domain_settings.is_openvdb)
def draw(self, context):
domain = context.smoke.domain_settings
diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h
index c1565bde882..6d729e9b98a 100644
--- a/source/blender/makesdna/DNA_smoke_types.h
+++ b/source/blender/makesdna/DNA_smoke_types.h
@@ -43,6 +43,7 @@ enum {
#endif
MOD_SMOKE_FILE_LOAD = (1 << 6), /* flag for file load */
MOD_SMOKE_ADAPTIVE_DOMAIN = (1 << 7),
+ MOD_SMOKE_OPENVDB_EXTERN = (1 << 8),
};
/* noise */
@@ -220,6 +221,8 @@ typedef struct SmokeDomainSettings {
float clipping;
float pad3;
+
+ char openvdb_filepath[1024];
} SmokeDomainSettings;
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index b860ad303f0..7d2ad0852b5 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -440,6 +440,23 @@ static void rna_Smoke_use_color_ramp_set(PointerRNA *ptr, int value)
}
}
+static void rna_SmokeDomain_openvdb_filepath_get(PointerRNA *ptr, char *value)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ BLI_strncpy(value, sds->openvdb_filepath, sizeof(sds->openvdb_filepath));
+}
+
+static int rna_SmokeDomain_openvdb_filepath_length(PointerRNA *ptr)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ return strlen(sds->openvdb_filepath);
+}
+
+static void rna_SmokeDomain_openvdb_filepath_set(PointerRNA *ptr, const char *value)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ BLI_strncpy(sds->openvdb_filepath, value, sizeof(sds->openvdb_filepath));
+}
#else
static void rna_def_smoke_domain_settings(BlenderRNA *brna)
@@ -914,6 +931,18 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Clipping",
"Value under which voxels are considered empty space to optimize caching or rendering");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
+
+ /* OpenVDB */
+
+ prop = RNA_def_property(srna, "is_openvdb", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_OPENVDB_EXTERN);
+ RNA_def_property_ui_text(prop, "OpenVDB Import", "Set domain for external OpenVDB smoke files");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+
+ prop = RNA_def_property(srna, "openvdb_filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_funcs(prop, "rna_SmokeDomain_openvdb_filepath_get", "rna_SmokeDomain_openvdb_filepath_length", "rna_SmokeDomain_openvdb_filepath_set");
+ RNA_def_property_ui_text(prop, "OpenVDB File Path", "Path to the .vdb file");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
}
static void rna_def_smoke_flow_settings(BlenderRNA *brna)