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:
authorGeraldine Chua <chua.gsk@gmail.com>2018-07-10 10:49:51 +0300
committerGeraldine Chua <chua.gsk@gmail.com>2018-07-10 10:49:51 +0300
commit7fc47f83fc90edfb5404f468124afee605f81fe0 (patch)
tree58ecf47a54ac68d76e2bbcf5179f331f302230ab /intern/cycles/render
parent028f648753c893639f5c1732360e4cf6eb551e71 (diff)
Initial commit for Cycles OpenVDB import.
Diffstat (limited to 'intern/cycles/render')
-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
7 files changed, 596 insertions, 122 deletions
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__ */