diff options
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) |