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
path: root/intern
diff options
context:
space:
mode:
Diffstat (limited to 'intern')
-rw-r--r--intern/cycles/CMakeLists.txt10
-rw-r--r--intern/cycles/app/cycles_xml.cpp1
-rw-r--r--intern/cycles/blender/CMakeLists.txt1
-rw-r--r--intern/cycles/blender/blender_mesh.cpp2
-rw-r--r--intern/cycles/blender/blender_object.cpp28
-rw-r--r--intern/cycles/blender/blender_sync.cpp6
-rw-r--r--intern/cycles/blender/blender_sync.h5
-rw-r--r--intern/cycles/blender/blender_util.h20
-rw-r--r--intern/cycles/blender/blender_volume.cpp185
-rw-r--r--intern/cycles/device/CMakeLists.txt1
-rw-r--r--intern/cycles/device/device.h5
-rw-r--r--intern/cycles/device/device_cpu.cpp32
-rw-r--r--intern/cycles/kernel/CMakeLists.txt12
-rw-r--r--intern/cycles/kernel/bvh/bvh_volume.h18
-rw-r--r--intern/cycles/kernel/bvh/bvh_volume_all.h27
-rw-r--r--intern/cycles/kernel/bvh/qbvh_volume.h18
-rw-r--r--intern/cycles/kernel/bvh/qbvh_volume_all.h27
-rw-r--r--intern/cycles/kernel/geom/geom_primitive.h4
-rw-r--r--intern/cycles/kernel/geom/geom_volume.h24
-rw-r--r--intern/cycles/kernel/kernel_compat_cpu.h2
-rw-r--r--intern/cycles/kernel/kernel_globals.h13
-rw-r--r--intern/cycles/kernel/kernel_shader.h12
-rw-r--r--intern/cycles/kernel/kernel_textures.h3
-rw-r--r--intern/cycles/kernel/kernel_types.h11
-rw-r--r--intern/cycles/kernel/kernel_volume.h626
-rw-r--r--intern/cycles/kernel/openvdb/vdb_globals.h45
-rw-r--r--intern/cycles/kernel/openvdb/vdb_intern.h48
-rw-r--r--intern/cycles/kernel/openvdb/vdb_thread.cpp228
-rw-r--r--intern/cycles/kernel/openvdb/vdb_thread.h49
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp12
-rw-r--r--intern/cycles/render/CMakeLists.txt8
-rw-r--r--intern/cycles/render/attribute.cpp10
-rw-r--r--intern/cycles/render/scene.cpp13
-rw-r--r--intern/cycles/render/scene.cpp.orig377
-rw-r--r--intern/cycles/render/scene.h8
-rw-r--r--intern/cycles/render/session.cpp9
-rw-r--r--intern/cycles/render/shader.cpp18
-rw-r--r--intern/cycles/render/svm.cpp5
-rw-r--r--intern/cycles/render/svm.h5
-rw-r--r--intern/cycles/render/volume.cpp426
-rw-r--r--intern/cycles/render/volume.h95
-rw-r--r--intern/openvdb/openvdb_capi.cc27
-rw-r--r--intern/openvdb/openvdb_capi.h3
43 files changed, 2260 insertions, 219 deletions
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index 5844c2480d6..8a3e505b4c8 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -163,10 +163,20 @@ if(WITH_CYCLES_OPENSUBDIV)
)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ add_definitions(-DDWREAL_IS_DOUBLE=0)
+ add_definitions(-DTBB_USE_EXCEPTIONS=0)
+ include_directories(
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+endif()
+
set(WITH_CYCLES_DEVICE_OPENCL TRUE)
set(WITH_CYCLES_DEVICE_CUDA TRUE)
set(WITH_CYCLES_DEVICE_MULTI TRUE)
+
if(CYCLES_STANDALONE_REPOSITORY)
TEST_UNORDERED_MAP_SUPPORT()
endif()
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index 04f00ef0e10..c2fd6897a40 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -223,7 +223,6 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
for(pugi::xml_node node = graph_node.first_child(); node; node = node.next_sibling()) {
ustring node_name(node.name());
- if(node_name == "connect") {
/* connect nodes */
vector<string> from_tokens, to_tokens;
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index ae4977aaed0..4a1c3377055 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -28,6 +28,7 @@ set(SRC
blender_shader.cpp
blender_sync.cpp
blender_texture.cpp
+ blender_volume.cpp
CCL_api.h
blender_object_cull.h
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index 5ec3bd7a781..ec0c58666a5 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -1088,7 +1088,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob,
else
create_mesh(scene, mesh, b_mesh, used_shaders, false);
- create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
+ //create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current());
}
if(render_layer.use_hair && mesh->subdivision_type == Mesh::SUBDIVISION_NONE)
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index eb573b75e9e..48e7bebeff8 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -24,6 +24,7 @@
#include "render/nodes.h"
#include "render/particles.h"
#include "render/shader.h"
+#include "volume.h"
#include "blender/blender_object_cull.h"
#include "blender/blender_sync.h"
@@ -92,6 +93,21 @@ bool BlenderSync::object_is_light(BL::Object& b_ob)
return (b_ob_data && b_ob_data.is_a(&RNA_Lamp));
}
+bool BlenderSync::object_has_sparse_volume(BL::Object& b_ob)
+{
+ BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
+ if (b_domain) {
+ BL::PointCache b_ptcache = b_domain.point_cache();
+ if (b_ptcache.is_baked() && b_domain.cache_file_format() == BL::SmokeDomainSettings::cache_file_format_OPENVDB) {
+ char filename[1024];
+ SmokeDomainSettings_cache_filename_get(&b_domain.ptr, filename);
+ return strcmp(filename, "");
+ }
+ }
+
+ return false;
+}
+
static uint object_ray_visibility(BL::Object& b_ob)
{
PointerRNA cvisibility = RNA_pointer_get(&b_ob.ptr, "cycles_visibility");
@@ -349,8 +365,13 @@ Object *BlenderSync::sync_object(BL::Object& b_parent,
if(object_map.sync(&object, b_ob, b_parent, key))
object_updated = true;
- /* mesh sync */
- object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
+ if(object_has_sparse_volume(b_ob)) {
+ sync_volume(b_ob);
+ }
+ /*else*/ {
+ /* mesh sync */
+ object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
+ }
/* special case not tracked by object update flags */
@@ -513,6 +534,7 @@ void BlenderSync::sync_objects(float motion_time)
mesh_map.pre_sync();
object_map.pre_sync();
particle_system_map.pre_sync();
+ volume_map.pre_sync();
motion_times.clear();
}
else {
@@ -633,6 +655,8 @@ void BlenderSync::sync_objects(float motion_time)
scene->object_manager->tag_update(scene);
if(particle_system_map.post_sync())
scene->particle_system_manager->tag_update(scene);
+ if(volume_map.post_sync())
+ scene->volume_manager->tag_update(scene);
}
if(motion)
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index 2e3301c4209..25355e1a973 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -56,6 +56,7 @@ BlenderSync::BlenderSync(BL::RenderEngine& b_engine,
mesh_map(&scene->meshes),
light_map(&scene->lights),
particle_system_map(&scene->particle_systems),
+ volume_map(&scene->volumes),
world_map(NULL),
world_recalc(false),
scene(scene),
@@ -148,6 +149,10 @@ bool BlenderSync::sync_recalc()
for(b_ob->particle_systems.begin(b_psys); b_psys != b_ob->particle_systems.end(); ++b_psys)
particle_system_map.set_recalc(*b_ob);
}
+
+ if(b_ob->is_updated()) {
+ volume_map.set_recalc(*b_ob);
+ }
}
BL::BlendData::meshes_iterator b_mesh;
@@ -182,6 +187,7 @@ bool BlenderSync::sync_recalc()
light_map.has_recalc() ||
mesh_map.has_recalc() ||
particle_system_map.has_recalc() ||
+ volume_map.has_recalc() ||
BlendDataObjects_is_updated_get(&b_data.ptr) ||
world_recalc;
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 11e279b81c4..2334b2a3b20 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -46,6 +46,7 @@ class Scene;
class Shader;
class ShaderGraph;
class ShaderNode;
+class Volume;
class BlenderSync {
public:
@@ -119,6 +120,7 @@ private:
BL::Object& b_ob,
bool motion,
int time_index = 0);
+ Volume *sync_volume(BL::Object& b_ob);
Object *sync_object(BL::Object& b_parent,
int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
BL::DupliObject& b_dupli_ob,
@@ -155,6 +157,7 @@ private:
bool BKE_object_is_modified(BL::Object& b_ob);
bool object_is_mesh(BL::Object& b_ob);
bool object_is_light(BL::Object& b_ob);
+ bool object_has_sparse_volume(BL::Object& b_ob);
/* variables */
BL::RenderEngine b_engine;
@@ -166,8 +169,10 @@ private:
id_map<void*, Mesh> mesh_map;
id_map<ObjectKey, Light> light_map;
id_map<ParticleSystemKey, ParticleSystem> particle_system_map;
+ id_map<VolumeKey, Volume> volume_map;
set<Mesh*> mesh_synced;
set<Mesh*> mesh_motion_synced;
+ set<Volume*> volume_synced;
set<float> motion_times;
void *world_map;
bool world_recalc;
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 363e19f7a20..5b63f986538 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -813,6 +813,26 @@ protected:
set< std::pair<int, int> > edges_;
};
+/* Volume Key */
+
+/* XXX For now we just use Object ID as a volume key;
+ * Volumes may become a true ID block in Blender later,
+ * or the key can be augmented to distinguish multiple volumes inside the same object.
+ */
+struct VolumeKey {
+ void *ob;
+
+ VolumeKey(void *ob_)
+ : ob(ob_)
+ {
+ }
+
+ bool operator<(const VolumeKey& k) const
+ {
+ return ob < k.ob;
+ }
+};
+
CCL_NAMESPACE_END
#endif /* __BLENDER_UTIL_H__ */
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
new file mode 100644
index 00000000000..2e5beff138d
--- /dev/null
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2011-2016 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.
+ */
+
+#include "blender_sync.h"
+
+#include "attribute.h"
+#include "../render/volume.h"
+
+#include "util_foreach.h"
+
+#include <openvdb/openvdb.h>
+
+CCL_NAMESPACE_BEGIN
+
+static Attribute *create_openvdb_attribute(Volume *volume,
+ const string& filename,
+ const ustring& name)
+{
+ Attribute *attr = NULL;
+
+ openvdb::initialize();
+
+ openvdb::io::File file(filename);
+ file.open();
+
+ openvdb::GridBase::ConstPtr grid = file.readGrid(name.string());
+
+ openvdb::Name value_type = grid->valueType();
+
+ if(value_type == "float") {
+ attr = volume->attributes.add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
+ }
+ else if(value_type == "vec3s") {
+ if (grid->getMetadata< openvdb::TypedMetadata<bool> >("is_color")) {
+ attr = volume->attributes.add(name, TypeDesc::TypeColor, ATTR_ELEMENT_VOXEL);
+ }
+ else {
+ attr = volume->attributes.add(name, TypeDesc::TypeVector, ATTR_ELEMENT_VOXEL);
+ }
+ }
+ else {
+ fprintf(stderr, "Skipping volume attribute: %s\n", name.string().c_str());
+ }
+
+ return attr;
+}
+
+static Attribute *create_smoke_attribute(BL::Object& b_ob,
+ Volume *volume,
+ const ustring& name,
+ float /*frame*/,
+ string *filename)
+{
+ BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
+ if(b_domain) {
+ char filename_buf[1024];
+ SmokeDomainSettings_cache_filename_get(&b_domain.ptr, filename_buf);
+ *filename = string(filename_buf);
+
+ return create_openvdb_attribute(volume, *filename, name);
+ }
+
+ return NULL;
+}
+
+static bool is_volume_attribute(AttributeStandard std) {
+ return std == ATTR_STD_VOLUME_DENSITY
+ || std == ATTR_STD_VOLUME_COLOR
+ || std == ATTR_STD_VOLUME_FLAME
+ || std == ATTR_STD_VOLUME_HEAT
+ || std == ATTR_STD_VOLUME_VELOCITY;
+}
+
+static void create_volume_attributes(Scene *scene,
+ BL::Object& b_ob,
+ Volume *volume,
+ float frame)
+{
+ foreach(Shader *shader, volume->used_shaders) {
+ foreach(AttributeRequest req, shader->attributes.requests) {
+ ustring name;
+ if (is_volume_attribute(req.std)) {
+ name = ustring(Attribute::standard_name(req.std));
+ }
+ else {
+ name = req.name;
+ }
+
+ if (!name.empty()) {
+ string filename;
+ Attribute *attr = create_smoke_attribute(b_ob, volume, name, frame, &filename);
+
+ if (attr) {
+ VoxelAttribute *volume_data = attr->data_voxel();
+ assert(volume_data && "Failed to create volume data!\n");
+
+ // TODO(kevin): add volume fields to the Volume*
+ //volume_data->manager = volume_manager;
+ volume_data->slot = scene->volume_manager->add_volume(volume, filename, name.string());
+ }
+ }
+ }
+ }
+}
+
+Volume *BlenderSync::sync_volume(BL::Object &b_ob)
+{
+ BL::ID key = b_ob;
+ BL::Material material_override = render_layer.material_override;
+
+ /* find shader indices */
+ vector<Shader*> used_shaders;
+ BL::ID b_ob_data = b_ob.data();
+
+ BL::Object::material_slots_iterator slot;
+ for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) {
+ if(material_override) {
+ find_shader(material_override, used_shaders, scene->default_volume);
+ }
+ else {
+ BL::ID b_material(slot->material());
+ find_shader(b_material, used_shaders, scene->default_volume);
+ }
+ }
+
+ if(used_shaders.size() == 0) {
+ if(material_override)
+ find_shader(material_override, used_shaders, scene->default_volume);
+ else
+ used_shaders.push_back(scene->default_volume);
+ }
+
+ Volume *volume;
+
+ if(!volume_map.sync(&volume, key)) {
+ /* test if shaders changed, these can be object level so mesh
+ * does not get tagged for recalc */
+ if(volume->used_shaders != used_shaders);
+ else {
+ /* even if not tagged for recalc, we may need to sync anyway
+ * because the shader needs different volume attributes */
+ bool attribute_recalc = false;
+
+ foreach(Shader *shader, volume->used_shaders)
+ if(shader->need_update_attributes)
+ attribute_recalc = true;
+
+ if(!attribute_recalc)
+ return volume;
+ }
+ }
+
+ /* ensure we only sync instanced meshes once */
+ if(volume_synced.find(volume) != volume_synced.end())
+ return volume;
+
+ volume_synced.insert(volume);
+
+ volume->used_shaders = used_shaders;
+ volume->name = ustring(b_ob_data.name().c_str());
+
+ create_volume_attributes(scene, b_ob, volume, b_scene.frame_current());
+
+ /* tag update */
+ bool rebuild = false;
+
+ volume->tag_update(scene, rebuild);
+
+ return volume;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt
index 3c632160fbd..711f81790e5 100644
--- a/intern/cycles/device/CMakeLists.txt
+++ b/intern/cycles/device/CMakeLists.txt
@@ -1,6 +1,7 @@
set(INC
..
+ ../kernel/openvdb
../../glew-mx
)
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index 5cd9cf46769..2cf0310d90d 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -35,6 +35,8 @@ CCL_NAMESPACE_BEGIN
class Progress;
class RenderTile;
+struct OpenVDBGlobals;
+
/* Device Types */
enum DeviceType {
@@ -310,6 +312,9 @@ public:
/* open shading language, only for CPU device */
virtual void *osl_memory() { return NULL; }
+ /* OpenVDB data */
+ virtual OpenVDBGlobals *vdb_memory() { return NULL; }
+
/* load/compile kernels, must be called before adding tasks */
virtual bool load_kernels(
const DeviceRequestedFeatures& /*requested_features*/)
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index a17caabc850..e4830ca77fd 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -40,6 +40,12 @@
#include "kernel/osl/osl_shader.h"
#include "kernel/osl/osl_globals.h"
+#ifdef WITH_OPENVDB
+#include "vdb_globals.h"
+#include "vdb_thread.h"
+#endif
+
+
#include "render/buffers.h"
#include "util/util_debug.h"
@@ -170,6 +176,10 @@ public:
OSLGlobals osl_globals;
#endif
+#ifdef WITH_OPENVDB
+ OpenVDBGlobals vdb_globals;
+#endif
+
bool use_split_kernel;
DeviceRequestedFeatures requested_features;
@@ -413,6 +423,15 @@ public:
#endif
}
+ OpenVDBGlobals *vdb_memory()
+ {
+#ifdef WITH_OPENVDB
+ return &vdb_globals;
+#else
+ return NULL;
+#endif
+ }
+
void thread_run(DeviceTask *task)
{
if(task->type == DeviceTask::RENDER) {
@@ -800,6 +819,10 @@ public:
#ifdef WITH_OSL
OSLShader::thread_init(&kg, &kernel_globals, &osl_globals);
#endif
+#ifdef WITH_OPENVDB
+ vdb_thread_init(&kg, &kernel_globals, &vdb_globals);
+#endif
+
for(int sample = 0; sample < task.num_samples; sample++) {
for(int x = task.shader_x; x < task.shader_x + task.shader_w; x++)
shader_kernel()(&kg,
@@ -821,6 +844,9 @@ public:
#ifdef WITH_OSL
OSLShader::thread_free(&kg);
#endif
+#ifdef WITH_OPENVDB
+ vdb_thread_free(&kg);
+#endif
}
int get_split_task_count(DeviceTask& task)
@@ -872,6 +898,9 @@ protected:
#ifdef WITH_OSL
OSLShader::thread_init(&kg, &kernel_globals, &osl_globals);
#endif
+#ifdef WITH_OPENVDB
+ vdb_thread_init(&kg, &kernel_globals, &vdb_globals);
+#endif
return kg;
}
@@ -894,6 +923,9 @@ protected:
#ifdef WITH_OSL
OSLShader::thread_free(kg);
#endif
+#ifdef WITH_OPENVDB
+ vdb_thread_free(kg);
+#endif
}
virtual bool load_kernels(DeviceRequestedFeatures& requested_features_) {
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index bd51bc4d371..7aab5f4a94a 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -430,6 +430,18 @@ if(WITH_CYCLES_OSL)
add_subdirectory(shaders)
endif()
+# OpenVDB module
+
+list(APPEND SRC
+ openvdb/vdb_thread.cpp
+ )
+
+list(APPEND SRC_HEADERS
+ openvdb/vdb_globals.h
+ openvdb/vdb_thread.h
+ openvdb/vdb_intern.h
+ )
+
# CPU module
include_directories(${INC})
diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h
index 764aaee44a1..42e626c8e19 100644
--- a/intern/cycles/kernel/bvh/bvh_volume.h
+++ b/intern/cycles/kernel/bvh/bvh_volume.h
@@ -97,6 +97,24 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif
+#if 1
+ /* try to intersect with VDB volumes */
+ int num_volumes = kernel_data.tables.num_volumes;
+
+ for(int i = 0; i < num_volumes; i++) {
+ float t;
+
+ if(vdb_volume_intersect(kg->vdb_tdata, i, ray, &t)) {
+ isect->type = PRIMITIVE_VOLUME;
+ isect->prim = i;
+ isect->t = t;
+ isect->u = 1.0f;
+ isect->v = 1.0f;
+ return true;
+ }
+ }
+#endif
+
/* traversal loop */
do {
do {
diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h
index 04ec334e54d..8c94512a0b9 100644
--- a/intern/cycles/kernel/bvh/bvh_volume_all.h
+++ b/intern/cycles/kernel/bvh/bvh_volume_all.h
@@ -101,6 +101,33 @@ uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg,
gen_idirsplat_swap(pn, shuf_identity, shuf_swap, idir, idirsplat, shufflexyz);
#endif /* __KERNEL_SSE2__ */
+#if 1
+ /* try to intersect with VDB volumes */
+ int num_volumes = kernel_data.tables.num_volumes;
+
+ for(int i = 0; i < num_volumes; i++) {
+ float t;
+
+ if(vdb_volume_intersect(kg->vdb_tdata, i, ray, &t)) {
+ isect_array->type = PRIMITIVE_VOLUME;
+ isect_array->prim = i;
+ isect_array->t = t;
+ isect_array->u = 1.0f;
+ isect_array->v = 1.0f;
+ isect_array++;
+ num_hits++;
+
+ if(num_hits == max_hits) {
+ return num_hits;
+ }
+ }
+ }
+
+ if(num_hits > 0) {
+ return num_hits;
+ }
+#endif
+
/* traversal loop */
do {
do {
diff --git a/intern/cycles/kernel/bvh/qbvh_volume.h b/intern/cycles/kernel/bvh/qbvh_volume.h
index 192ce009524..bcda7bbd251 100644
--- a/intern/cycles/kernel/bvh/qbvh_volume.h
+++ b/intern/cycles/kernel/bvh/qbvh_volume.h
@@ -91,6 +91,24 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
&near_x, &near_y, &near_z,
&far_x, &far_y, &far_z);
+#if 1
+ /* try to intersect with VDB volumes */
+ int num_volumes = kernel_data.tables.num_volumes;
+
+ for(int i = 0; i < num_volumes; i++) {
+ float t;
+
+ if(vdb_volume_intersect(kg->vdb_tdata, i, ray, &t)) {
+ isect->type = PRIMITIVE_VOLUME;
+ isect->prim = i;
+ isect->t = t;
+ isect->u = 1.0f;
+ isect->v = 1.0f;
+ return true;
+ }
+ }
+#endif
+
/* Traversal loop. */
do {
do {
diff --git a/intern/cycles/kernel/bvh/qbvh_volume_all.h b/intern/cycles/kernel/bvh/qbvh_volume_all.h
index ac5f58a9a51..26f31c379c3 100644
--- a/intern/cycles/kernel/bvh/qbvh_volume_all.h
+++ b/intern/cycles/kernel/bvh/qbvh_volume_all.h
@@ -95,6 +95,33 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg,
&near_x, &near_y, &near_z,
&far_x, &far_y, &far_z);
+#if 1
+ /* try to intersect with VDB volumes */
+ int num_volumes = kernel_data.tables.num_volumes;
+
+ for(int i = 0; i < num_volumes; i++) {
+ float t;
+
+ if(vdb_volume_intersect(kg->vdb_tdata, i, ray, &t)) {
+ isect_array->type = PRIMITIVE_VOLUME;
+ isect_array->prim = i;
+ isect_array->t = t;
+ isect_array->u = 1.0f;
+ isect_array->v = 1.0f;
+ isect_array++;
+ num_hits++;
+
+ if(num_hits == max_hits) {
+ return num_hits;
+ }
+ }
+ }
+
+ if(num_hits > 0) {
+ return num_hits;
+ }
+#endif
+
/* Traversal loop. */
do {
do {
diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h
index 90a9c2147cc..989f1574e94 100644
--- a/intern/cycles/kernel/geom/geom_primitive.h
+++ b/intern/cycles/kernel/geom/geom_primitive.h
@@ -40,7 +40,7 @@ ccl_device_inline float primitive_attribute_float(KernelGlobals *kg,
}
#endif
#ifdef __VOLUME__
- else if(sd->object != OBJECT_NONE && desc.element == ATTR_ELEMENT_VOXEL) {
+ else if(sd->type & PRIMITIVE_ALL_VOLUME) {
return volume_attribute_float(kg, sd, desc, dx, dy);
}
#endif
@@ -68,7 +68,7 @@ ccl_device_inline float3 primitive_attribute_float3(KernelGlobals *kg,
}
#endif
#ifdef __VOLUME__
- else if(sd->object != OBJECT_NONE && desc.element == ATTR_ELEMENT_VOXEL) {
+ else if(sd->type & PRIMITIVE_ALL_VOLUME) {
return volume_attribute_float3(kg, sd, desc, dx, dy);
}
#endif
diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h
index 6be448c4fa4..9a5b94c1f46 100644
--- a/intern/cycles/kernel/geom/geom_volume.h
+++ b/intern/cycles/kernel/geom/geom_volume.h
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#ifdef WITH_OPENVDB
+#include "../openvdb/vdb_thread.h"
+#endif
+
/* Volume Primitive
*
* Volumes are just regions inside meshes with the mesh surface as boundaries.
@@ -49,26 +53,38 @@ ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg,
ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy)
{
+#ifdef __OPENVDB__
+ float3 P = sd->P;
+ /* XXX OpenVDB does not support cubic interpolation - lukas_t */
+ float r = kernel_tex_voxel_float(desc.offset, P.x, P.y, P.z, OPENVDB_SAMPLE_BOX);
+#else
float3 P = volume_normalized_position(kg, sd, sd->P);
InterpolationType interp = (sd->flag & SD_VOLUME_CUBIC)? INTERPOLATION_CUBIC: INTERPOLATION_NONE;
- float4 r = kernel_tex_image_interp_3d(kg, desc.offset, P.x, P.y, P.z, interp);
+ float r = average(float4_to_float3(kernel_tex_image_interp_3d_float(kg, desc.offset, P.x, P.y, P.z, interp)));
+#endif
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
- return average(float4_to_float3(r));
+ return r;
}
ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy)
{
+#ifdef __OPENVDB__
+ float3 P = sd->P;
+ /* XXX OpenVDB does not support cubic interpolation - lukas_t */
+ float3 r = kernel_tex_voxel_float3(desc.offset, P.x, P.y, P.z, OPENVDB_SAMPLE_BOX);
+#else
float3 P = volume_normalized_position(kg, sd, sd->P);
InterpolationType interp = (sd->flag & SD_VOLUME_CUBIC)? INTERPOLATION_CUBIC: INTERPOLATION_NONE;
- float4 r = kernel_tex_image_interp_3d(kg, desc.offset, P.x, P.y, P.z, interp);
+ float3 r = float4_to_float3(kernel_tex_image_interp_3d(kg, desc.offset, P.x, P.y, P.z, interp));
+#endif
if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
- return float4_to_float3(r);
+ return r;
}
#endif
diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h
index 6f63c8f77a2..4b43209e4aa 100644
--- a/intern/cycles/kernel/kernel_compat_cpu.h
+++ b/intern/cycles/kernel/kernel_compat_cpu.h
@@ -119,6 +119,8 @@ template<typename T> struct texture {
#define kernel_tex_fetch_ssef(tex, index) (kg->tex.fetch_ssef(index))
#define kernel_tex_fetch_ssei(tex, index) (kg->tex.fetch_ssei(index))
#define kernel_tex_lookup(tex, t, offset, size) (kg->tex.lookup(t, offset, size))
+#define kernel_tex_voxel_float(tex, x, y, z, sampling) (vdb_volume_sample_scalar(kg->vdb, kg->vdb_tdata, tex, x, y, z, sampling))
+#define kernel_tex_voxel_float3(tex, x, y, z, sampling) (vdb_volume_sample_vector(kg->vdb, kg->vdb_tdata, tex, x, y, z, sampling))
#define kernel_data (kg->__data)
diff --git a/intern/cycles/kernel/kernel_globals.h b/intern/cycles/kernel/kernel_globals.h
index 97d4726407b..7e2f67bbd63 100644
--- a/intern/cycles/kernel/kernel_globals.h
+++ b/intern/cycles/kernel/kernel_globals.h
@@ -42,9 +42,16 @@ struct OSLThreadData;
struct OSLShadingSystem;
# endif
+# ifdef WITH_OPENVDB
+struct OpenVDBGlobals;
+struct OpenVDBThreadData;
+# endif
+
struct Intersection;
struct VolumeStep;
+# define MAX_VOLUME 1024
+
typedef struct KernelGlobals {
# define KERNEL_TEX(type, name) texture<type> name;
# define KERNEL_IMAGE_TEX(type, ttype, name)
@@ -75,6 +82,12 @@ typedef struct KernelGlobals {
int2 global_size;
int2 global_id;
+
+# ifdef WITH_OPENVDB
+ /* OpenVDB */
+ OpenVDBGlobals *vdb;
+ OpenVDBThreadData *vdb_tdata;
+# endif
} KernelGlobals;
#endif /* __KERNEL_CPU__ */
diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h
index 695d4fc380a..d46da189661 100644
--- a/intern/cycles/kernel/kernel_shader.h
+++ b/intern/cycles/kernel/kernel_shader.h
@@ -69,7 +69,12 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg,
#endif
sd->time = ray->time;
- sd->prim = kernel_tex_fetch(__prim_index, isect->prim);
+ if (sd->type & PRIMITIVE_VOLUME) {
+ sd->prim = isect->prim;
+ }
+ else {
+ sd->prim = kernel_tex_fetch(__prim_index, isect->prim);
+ }
sd->ray_length = isect->t;
#ifdef __UV__
@@ -106,6 +111,9 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg,
triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv);
#endif
}
+ else if(sd->type & PRIMITIVE_VOLUME) {
+ sd->shader = kernel_tex_fetch(__vol_shader, sd->prim);
+ }
else {
/* motion triangle */
motion_triangle_shader_setup(kg, sd, isect, ray, false);
@@ -432,7 +440,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s
sd->object = PRIM_NONE; /* todo: fill this for texture coordinates */
#endif
sd->prim = PRIM_NONE;
- sd->type = PRIMITIVE_NONE;
+ sd->type = PRIMITIVE_VOLUME;
#ifdef __UV__
sd->u = 0.0f;
diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h
index 344b2223573..c8e54954a84 100644
--- a/intern/cycles/kernel/kernel_textures.h
+++ b/intern/cycles/kernel/kernel_textures.h
@@ -78,6 +78,9 @@ KERNEL_TEX(float, __lookup_table)
/* sobol */
KERNEL_TEX(uint, __sobol_directions)
+/* volume */
+KERNEL_TEX(uint, __vol_shader)
+
#if !defined(__KERNEL_CUDA__) || __CUDA_ARCH__ >= 300
/* image textures */
KERNEL_TEX(TextureInfo, __texture_info)
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index f76d6c2e556..6c8e1c4e336 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -87,6 +87,9 @@ CCL_NAMESPACE_BEGIN
# ifdef WITH_OSL
# define __OSL__
# endif
+# ifdef WITH_OPENVDB
+# define __OPENVDB__
+# endif
# define __PRINCIPLED__
# define __SUBSURFACE__
# define __CMJ__
@@ -686,11 +689,13 @@ typedef enum PrimitiveType {
* since it is no real traceable primitive.
*/
PRIMITIVE_LAMP = (1 << 4),
+ PRIMITIVE_VOLUME = (1 << 5),
PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE|PRIMITIVE_MOTION_TRIANGLE),
PRIMITIVE_ALL_CURVE = (PRIMITIVE_CURVE|PRIMITIVE_MOTION_CURVE),
PRIMITIVE_ALL_MOTION = (PRIMITIVE_MOTION_TRIANGLE|PRIMITIVE_MOTION_CURVE),
- PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE|PRIMITIVE_ALL_CURVE),
+ PRIMITIVE_ALL_VOLUME = (PRIMITIVE_VOLUME),
+ PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE|PRIMITIVE_ALL_CURVE|PRIMITIVE_ALL_VOLUME),
/* Total number of different traceable primitives.
* NOTE: This is an actual value, not a bitflag.
@@ -996,6 +1001,7 @@ typedef ccl_addr_space struct ShaderData {
typedef struct VolumeStack {
int object;
int shader;
+ int volume;
} VolumeStack;
#endif
@@ -1326,7 +1332,8 @@ static_assert_align(KernelCurves, 16);
typedef struct KernelTables {
int beckmann_offset;
- int pad1, pad2, pad3;
+ int num_volumes;
+ int density_index, pad2;
} KernelTables;
static_assert_align(KernelTables, 16);
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index 5905fb3bf12..35f58850f56 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "openvdb/vdb_thread.h"
+
CCL_NAMESPACE_BEGIN
/* Events for probalistic scattering */
@@ -21,7 +23,8 @@ CCL_NAMESPACE_BEGIN
typedef enum VolumeIntegrateResult {
VOLUME_PATH_SCATTERED = 0,
VOLUME_PATH_ATTENUATED = 1,
- VOLUME_PATH_MISSED = 2
+ VOLUME_PATH_MISSED = 2,
+ VOLUME_PATH_CONTINUE = 3,
} VolumeIntegrateResult;
/* Volume shader properties
@@ -173,6 +176,38 @@ ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg,
*throughput *= volume_color_transmittance(sigma_t, ray->t);
}
+ccl_device_inline bool kernel_volume_integrate_shadow_ray(
+ KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd,
+ float3 *tp, float t, float new_t, float random_jitter_offset,
+ float3 *sum, float tp_eps, int i)
+{
+ float dt = new_t - t;
+
+ /* use random position inside this segment to sample shader */
+ if(new_t == ray->t)
+ random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+
+ float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+ float3 sigma_t;
+
+ /* compute attenuation over segment */
+ if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) {
+ /* Compute expf() only for every Nth step, to save some calculations
+ * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */
+
+ *sum += (-sigma_t * (new_t - t));
+ if((i & 0x07) == 0) { /* ToDo: Other interval? */
+ *tp = *tp * make_float3(expf(sum->x), expf(sum->y), expf(sum->z));
+
+ /* stop if nearly all light is blocked */
+ if(tp->x < tp_eps && tp->y < tp_eps && tp->z < tp_eps)
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* heterogeneous volume: integrate stepping through the volume until we
* reach the end, get absorbed entirely, or run out of iterations */
ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
@@ -194,42 +229,75 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg,
float3 sum = make_float3(0.0f, 0.0f, 0.0f);
- for(int i = 0; i < max_steps; i++) {
- /* advance to new position */
- float new_t = min(ray->t, (i+1) * step);
- float dt = new_t - t;
-
- /* use random position inside this segment to sample shader */
- if(new_t == ray->t)
- random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
+#ifdef __OPENVDB__
+// int density_index = kernel_data.tables.density_index;
+ int num_volumes = kernel_data.tables.num_volumes;
+ bool has_vdb_volume = num_volumes > 0;
+ float t1 = 0.0f;
+ int v = 0;
- float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
- float3 sigma_t;
-
- /* compute attenuation over segment */
- if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) {
- /* Compute expf() only for every Nth step, to save some calculations
- * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */
+ float isec_t = 0.0f;
+ for(; v < num_volumes; v++) {
+ if(vdb_volume_intersect(kg->vdb_tdata, v, ray, &isec_t)) {
+ break;
+ }
+ }
- sum += (-sigma_t * (new_t - t));
- if((i & 0x07) == 0) { /* ToDo: Other interval? */
- tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z));
+ if(has_vdb_volume && v < num_volumes && vdb_volume_scalar_has_uniform_voxels(kg->vdb, v)) {
+ /* TODO(kevin): this call should be moved out of here, all it does is
+ * checking if we have an intersection with the boundbox of the volumue
+ * which in most cases corresponds to the boundbox of the object that has
+ * this volume. Also it initializes the rays for the ray marching. */
+ //if(!vdb_volume_intersect(kg->vdb_tdata, density_index, ray, NULL)) {
+ // return;
+ //}
+
+ /* t and t1 represent the entry and exit points for each leaf node or tile
+ * containing active voxels. If we don't have any active node in the current
+ * ray path (i.e. empty space) the ray march loop is not executed,
+ * otherwise we loop through all leaves until the end of the volume. */
+ while(vdb_volume_march(kg->vdb_tdata, v, &t, &t1)) {
+ int i = 0;
+
+ /* Perform small steps through the current leaf or tile. */
+ for(float new_t = step * ceilf(t / step); new_t <= t1; new_t += step) {
+ bool ok = kernel_volume_integrate_shadow_ray(
+ kg, state, ray, sd, &tp, t, new_t, random_jitter_offset,
+ &sum, tp_eps, i);
+
+ if (ok) {
+ *throughput = tp;
+ return;
+ }
- /* stop if nearly all light is blocked */
- if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps)
- break;
+ /* stop if at the end of the volume */
+ t = new_t;
+ i++;
}
}
-
- /* stop if at the end of the volume */
- t = new_t;
- if(t == ray->t) {
- /* Update throughput in case we haven't done it above */
- tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z));
- break;
+ }
+ else
+#endif
+ {
+ for(int i = 0; i < max_steps; i++) {
+ /* advance to new position */
+ float new_t = min(ray->t, (i+1) * step);
+
+ bool ok = kernel_volume_integrate_shadow_ray(
+ kg, state, ray, sd, &tp, t, new_t, random_jitter_offset,
+ &sum, tp_eps, i);
+
+ /* stop if at the end of the volume */
+ t = new_t;
+ if(ok || t == ray->t) {
+ break;
+ }
}
}
+ /* Update throughput in case we haven't done it above */
+ tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z));
+
*throughput = tp;
}
@@ -456,94 +524,77 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(
return VOLUME_PATH_ATTENUATED;
}
-/* heterogeneous volume distance sampling: integrate stepping through the
- * volume until we reach the end, get absorbed entirely, or run out of
- * iterations. this does probabilistically scatter or get transmitted through
- * for path tracing where we don't want to branch. */
-ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
- KernelGlobals *kg,
- ccl_addr_space PathState *state,
- Ray *ray,
- ShaderData *sd,
- PathRadiance *L,
- ccl_addr_space float3 *throughput)
+ccl_device_inline VolumeIntegrateResult kernel_volume_integrate_ray(
+ KernelGlobals *kg,
+ PathState *state,
+ Ray *ray,
+ ShaderData *sd,
+ PathRadiance *L,
+ float3 *throughput,
+ float t,
+ float new_t,
+ float random_jitter_offset,
+ bool has_scatter,
+ float3 *accum_transmittance,
+ int channel,
+ const float tp_eps,
+ float *xi)
{
+ float dt = new_t - t;
float3 tp = *throughput;
- const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
- /* prepare for stepping */
- int max_steps = kernel_data.integrator.volume_max_steps;
- float step_size = kernel_data.integrator.volume_step_size;
- float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step_size;
+ /* use random position inside this segment to sample shader */
+ if(new_t == ray->t)
+ random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
- /* compute coefficients at the start */
- float t = 0.0f;
- float3 accum_transmittance = make_float3(1.0f, 1.0f, 1.0f);
-
- /* pick random color channel, we use the Veach one-sample
- * model with balance heuristic for the channels */
- float xi = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE);
- float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL);
- int channel = (int)(rphase*3.0f);
- bool has_scatter = false;
-
- for(int i = 0; i < max_steps; i++) {
- /* advance to new position */
- float new_t = min(ray->t, (i+1) * step_size);
- float dt = new_t - t;
-
- /* use random position inside this segment to sample shader */
- if(new_t == ray->t)
- random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * dt;
-
- float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
- VolumeShaderCoefficients coeff;
+ float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+ VolumeShaderCoefficients coeff;
- /* compute segment */
- if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
- int closure_flag = sd->flag;
- float3 new_tp;
- float3 transmittance;
- bool scatter = false;
+ /* compute segment */
+ if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
+ int closure_flag = sd->flag;
+ float3 new_tp;
+ float3 transmittance = make_float3(1.0f, 1.0f, 1.0f);
+ bool scatter = false;
- /* distance sampling */
+ /* distance sampling */
#ifdef __VOLUME_SCATTER__
- if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) {
- has_scatter = true;
+ if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) {
+ has_scatter = true;
- float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
- float3 sigma_s = coeff.sigma_s;
+ float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
+ float3 sigma_s = coeff.sigma_s;
- /* compute transmittance over full step */
- transmittance = volume_color_transmittance(sigma_t, dt);
+ /* compute transmittance over full step */
+ transmittance = volume_color_transmittance(sigma_t, dt);
- /* decide if we will scatter or continue */
- float sample_transmittance = kernel_volume_channel_get(transmittance, channel);
+ /* decide if we will scatter or continue */
+ float sample_transmittance = kernel_volume_channel_get(transmittance, channel);
- if(1.0f - xi >= sample_transmittance) {
- /* compute sampling distance */
- float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
- float new_dt = -logf(1.0f - xi)/sample_sigma_t;
- new_t = t + new_dt;
+ if(1.0f - *xi >= sample_transmittance) {
+ /* compute sampling distance */
+ float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
+ float new_dt = -logf(1.0f - *xi)/sample_sigma_t;
+ new_t = t + new_dt;
- /* transmittance and pdf */
- float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt);
- float3 pdf = sigma_t * new_transmittance;
+ /* transmittance and pdf */
+ float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt);
+ float3 pdf = sigma_t * new_transmittance;
- /* throughput */
- new_tp = tp * sigma_s * new_transmittance / average(pdf);
- scatter = true;
- }
- else {
- /* throughput */
- float pdf = average(transmittance);
- new_tp = tp * transmittance / pdf;
+ /* throughput */
+ new_tp = tp * sigma_s * new_transmittance / average(pdf);
+ scatter = true;
+ }
+ else {
+ /* throughput */
+ float pdf = average(transmittance);
+ new_tp = tp * transmittance / pdf;
- /* remap xi so we can reuse it and keep thing stratified */
- xi = 1.0f - (1.0f - xi)/sample_transmittance;
- }
+ /* remap xi so we can reuse it and keep thing stratified */
+ *xi = 1.0f - (1.0f - *xi)/sample_transmittance;
}
- else
+ }
+ else
#endif
if(closure_flag & SD_ABSORPTION) {
/* absorption only, no sampling needed */
@@ -553,46 +604,137 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
new_tp = tp * transmittance;
}
- /* integrate emission attenuated by absorption */
- if(L && (closure_flag & SD_EMISSION)) {
- float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
- path_radiance_accum_emission(L, state, tp, emission);
- }
+ /* integrate emission attenuated by absorption */
+ if(L && (closure_flag & SD_EMISSION)) {
+ float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
+ path_radiance_accum_emission(L, state, tp, emission);
+ }
- /* modify throughput */
- if(closure_flag & (SD_ABSORPTION|SD_SCATTER)) {
- tp = new_tp;
+ /* modify throughput */
+ if(closure_flag & (SD_ABSORPTION|SD_SCATTER)) {
+ tp = new_tp;
- /* stop if nearly all light blocked */
- if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) {
- tp = make_float3(0.0f, 0.0f, 0.0f);
- break;
- }
+ /* stop if nearly all light blocked */
+ if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) {
+ tp = make_float3(0.0f, 0.0f, 0.0f);
+ *throughput = tp;
+ return VOLUME_PATH_ATTENUATED;
}
+ }
- /* prepare to scatter to new direction */
- if(scatter) {
- /* adjust throughput and move to new location */
- sd->P = ray->P + new_t*ray->D;
- *throughput = tp;
+ /* prepare to scatter to new direction */
+ if(scatter) {
+ /* adjust throughput and move to new location */
+ sd->P = ray->P + new_t*ray->D;
+ *throughput = tp;
- return VOLUME_PATH_SCATTERED;
- }
- else {
- /* accumulate transmittance */
- accum_transmittance *= transmittance;
- }
+ return VOLUME_PATH_SCATTERED;
+ }
+ else {
+ /* accumulate transmittance */
+ *accum_transmittance *= transmittance;
}
+ }
- /* stop if at the end of the volume */
- t = new_t;
- if(t == ray->t)
+ *throughput = tp;
+ return VOLUME_PATH_CONTINUE;
+}
+
+/* heterogeneous volume distance sampling: integrate stepping through the
+ * volume until we reach the end, get absorbed entirely, or run out of
+ * iterations. this does probabilistically scatter or get transmitted through
+ * for path tracing where we don't want to branch. */
+ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
+ PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput)
+{
+ VolumeIntegrateResult result = VOLUME_PATH_MISSED;
+ const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
+
+ /* prepare for stepping */
+ int max_steps = kernel_data.integrator.volume_max_steps;
+ float step_size = kernel_data.integrator.volume_step_size;
+ float random_jitter_offset = lcg_step_float_addrspace(&state->rng_congruential) * step_size;
+
+ /* compute coefficients at the start */
+ float t = 0.0f;
+ float3 accum_transmittance = make_float3(1.0f, 1.0f, 1.0f);
+
+ /* pick random color channel, we use the Veach one-sample
+ * model with balance heuristic for the channels */
+ float xi = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE);
+ float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL);
+ int channel = (int)(rphase*3.0f);
+ sd->randb_closure = rphase*3.0f - channel;
+ bool has_scatter = false;
+ bool path_missed = true;
+
+#ifdef __OPENVDB__
+// int density_index = kernel_data.tables.density_index;
+ int num_volumes = kernel_data.tables.num_volumes;
+ bool has_vdb_volume = num_volumes > 0;
+ float t1 = 0.0f;
+ int i;
+
+ for(i = 0; i < num_volumes; i++) {
+ float isec_t = 0.0f;
+ if(vdb_volume_intersect(kg->vdb_tdata, i, ray, &isec_t)) {
break;
+ }
}
- *throughput = tp;
+ if(has_vdb_volume /*&& i >= 0*/ && vdb_volume_scalar_has_uniform_voxels(kg->vdb, i)) {
+ /* TODO(kevin): this call should be moved out of here, all it does is
+ * checking if we have an intersection with the boundbox of the volumue
+ * which in most cases corresponds to the boundbox of the object that has
+ * this volume. Also it initializes the rays for the ray marching. */
+ //if(!vdb_volume_intersect(kg->vdb_tdata, density_index, ray, NULL)) {
+ // return VOLUME_PATH_MISSED;
+ //}
+
+ /* t and t1 represent the entry and exit points for each leaf node or tile
+ * containing active voxels. If we don't have any active node in the current
+ * ray path (i.e. empty space) the ray march loop is not executed,
+ * otherwise we loop through all leaves until the end of the volume. */
+ while(vdb_volume_march(kg->vdb_tdata, i, &t, &t1)) {
+ path_missed = false;
+
+ /* Perform small steps through the current leaf or tile. */
+ for(float new_t = step_size * ceilf(t / step_size); new_t <= t1; new_t += step_size) {
+ result = kernel_volume_integrate_ray(kg, state, ray, sd, L, throughput, t, new_t,
+ random_jitter_offset, has_scatter,
+ &accum_transmittance, channel, tp_eps, &xi);
+
+ if(result != VOLUME_PATH_CONTINUE)
+ return result;
+
+ t = new_t;
+ }
+ }
+ }
+ else
+#endif
+ {
+ path_missed = false;
- return VOLUME_PATH_ATTENUATED;
+ for(int i = 0; i < max_steps; i++) {
+ /* advance to new position */
+ float new_t = min(ray->t, (i+1) * step_size);
+
+ result = kernel_volume_integrate_ray(kg, state, ray, sd, L, throughput, t, new_t,
+ random_jitter_offset, has_scatter,
+ &accum_transmittance, channel, tp_eps, &xi);
+
+ if(result != VOLUME_PATH_CONTINUE)
+ return result;
+
+ /* stop if at the end of the volume */
+ t = new_t;
+ if(t == ray->t)
+ break;
+ }
+ }
+
+ return (path_missed) ? VOLUME_PATH_MISSED : VOLUME_PATH_ATTENUATED;
}
/* get the volume attenuation and emission over line segment defined by
@@ -719,78 +861,175 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
bool is_last_step_empty = false;
VolumeStep *step = segment->steps;
+#ifdef __OPENVDB__
+ int density_index = kernel_data.tables.density_index;
+ bool has_vdb_volume = kernel_data.tables.num_volumes > 0;
+ float t1 = 0.0f;
+
+ if(has_vdb_volume && vdb_volume_scalar_has_uniform_voxels(kg->vdb, density_index)) {
+ /* TODO(kevin): this call should be moved out of here, all it does is
+ * checking if we have an intersection with the boundbox of the volumue
+ * which in most cases corresponds to the boundbox of the object that has
+ * this volume. Also it initializes the rays for the ray marching. */
+ float isect_t = 0.0f;
+ if(!vdb_volume_intersect(kg->vdb_tdata, density_index, ray, &isect_t)) {
+ return;
+ }
- for(int i = 0; i < max_steps; i++, step++) {
- /* advance to new position */
- float new_t = min(ray->t, (i+1) * step_size);
- float dt = new_t - t;
+ /* t and t1 represent the entry and exit points for each leaf node or tile
+ * containing active voxels. If we don't have any active node in the current
+ * ray path (i.e. empty space) the ray march loop is not executed,
+ * otherwise we loop through all leaves until the end of the volume. */
+ while(vdb_volume_march(kg->vdb_tdata, density_index, &t, &t1)) {
- /* use random position inside this segment to sample shader */
- if(heterogeneous && new_t == ray->t)
- random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+ /* Perform small steps through the current leaf or tile. */
+ for(float new_t = step_size * ceilf(t / step_size); new_t <= t1; new_t += step_size, step++) {
+ float dt = new_t - t;
- float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
- VolumeShaderCoefficients coeff;
+ /* use random position inside this segment to sample shader */
+ if(heterogeneous && new_t == ray->t)
+ random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
- /* compute segment */
- if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
- int closure_flag = sd->flag;
- float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
+ float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+ VolumeShaderCoefficients coeff;
- /* compute accumulated transmittance */
- float3 transmittance = volume_color_transmittance(sigma_t, dt);
+ /* compute segment */
+ if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
+ int closure_flag = sd->flag;
+ float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
- /* compute emission attenuated by absorption */
- if(closure_flag & SD_EMISSION) {
- float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
- accum_emission += accum_transmittance * emission;
- }
+ /* compute accumulated transmittance */
+ float3 transmittance = volume_color_transmittance(sigma_t, dt);
- accum_transmittance *= transmittance;
+ /* compute emission attenuated by absorption */
+ if(closure_flag & SD_EMISSION) {
+ float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
+ accum_emission += accum_transmittance * emission;
+ }
+
+ accum_transmittance *= transmittance;
+
+ /* compute pdf for distance sampling */
+ float3 pdf_distance = dt * accum_transmittance * coeff.sigma_s;
+ cdf_distance = cdf_distance + pdf_distance;
- /* compute pdf for distance sampling */
- float3 pdf_distance = dt * accum_transmittance * coeff.sigma_s;
- cdf_distance = cdf_distance + pdf_distance;
+ /* write step data */
+ step->sigma_t = sigma_t;
+ step->sigma_s = coeff.sigma_s;
+ step->closure_flag = closure_flag;
- /* write step data */
- step->sigma_t = sigma_t;
- step->sigma_s = coeff.sigma_s;
- step->closure_flag = closure_flag;
+ segment->closure_flag |= closure_flag;
- segment->closure_flag |= closure_flag;
+ is_last_step_empty = false;
+ segment->numsteps++;
+ }
+ else {
+ if(is_last_step_empty) {
+ /* consecutive empty step, merge */
+ step--;
+ }
+ else {
+ /* store empty step */
+ step->sigma_t = make_float3(0.0f, 0.0f, 0.0f);
+ step->sigma_s = make_float3(0.0f, 0.0f, 0.0f);
+ step->closure_flag = 0;
+
+ segment->numsteps++;
+ is_last_step_empty = true;
+ }
+ }
+
+ step->accum_transmittance = accum_transmittance;
+ step->cdf_distance = cdf_distance;
+ step->t = new_t;
+ step->shade_t = t + random_jitter_offset;
+
+ /* stop if at the end of the volume */
+ t = new_t;
+ if(t == ray->t)
+ break;
- is_last_step_empty = false;
- segment->numsteps++;
+ /* stop if nearly all light blocked */
+ if(accum_transmittance.x < tp_eps && accum_transmittance.y < tp_eps && accum_transmittance.z < tp_eps)
+ break;
+ }
}
- else {
- if(is_last_step_empty) {
- /* consecutive empty step, merge */
- step--;
+ }
+ else
+#endif
+ {
+ for(int i = 0; i < max_steps; i++, step++) {
+ /* advance to new position */
+ float new_t = min(ray->t, (i+1) * step_size);
+ float dt = new_t - t;
+
+ /* use random position inside this segment to sample shader */
+ if(heterogeneous && new_t == ray->t)
+ random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+
+ float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+ VolumeShaderCoefficients coeff;
+
+ /* compute segment */
+ if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
+ int closure_flag = sd->flag;
+ float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
+
+ /* compute accumulated transmittance */
+ float3 transmittance = volume_color_transmittance(sigma_t, dt);
+
+ /* compute emission attenuated by absorption */
+ if(closure_flag & SD_EMISSION) {
+ float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
+ accum_emission += accum_transmittance * emission;
+ }
+
+ accum_transmittance *= transmittance;
+
+ /* compute pdf for distance sampling */
+ float3 pdf_distance = dt * accum_transmittance * coeff.sigma_s;
+ cdf_distance = cdf_distance + pdf_distance;
+
+ /* write step data */
+ step->sigma_t = sigma_t;
+ step->sigma_s = coeff.sigma_s;
+ step->closure_flag = closure_flag;
+
+ segment->closure_flag |= closure_flag;
+
+ is_last_step_empty = false;
+ segment->numsteps++;
}
else {
- /* store empty step */
- step->sigma_t = make_float3(0.0f, 0.0f, 0.0f);
- step->sigma_s = make_float3(0.0f, 0.0f, 0.0f);
- step->closure_flag = 0;
+ if(is_last_step_empty) {
+ /* consecutive empty step, merge */
+ step--;
+ }
+ else {
+ /* store empty step */
+ step->sigma_t = make_float3(0.0f, 0.0f, 0.0f);
+ step->sigma_s = make_float3(0.0f, 0.0f, 0.0f);
+ step->closure_flag = 0;
- segment->numsteps++;
- is_last_step_empty = true;
+ segment->numsteps++;
+ is_last_step_empty = true;
+ }
}
- }
- step->accum_transmittance = accum_transmittance;
- step->cdf_distance = cdf_distance;
- step->t = new_t;
- step->shade_t = t + random_jitter_offset;
+ step->accum_transmittance = accum_transmittance;
+ step->cdf_distance = cdf_distance;
+ step->t = new_t;
+ step->shade_t = t + random_jitter_offset;
- /* stop if at the end of the volume */
- t = new_t;
- if(t == ray->t)
- break;
+ /* stop if at the end of the volume */
+ t = new_t;
+ if(t == ray->t)
+ break;
- /* stop if nearly all light blocked */
- if(accum_transmittance.x < tp_eps && accum_transmittance.y < tp_eps && accum_transmittance.z < tp_eps)
- break;
+ /* stop if nearly all light blocked */
+ if(accum_transmittance.x < tp_eps && accum_transmittance.y < tp_eps && accum_transmittance.z < tp_eps)
+ break;
+ }
}
/* store total emission and transmittance */
@@ -798,15 +1037,19 @@ ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, PathState *sta
segment->accum_transmittance = accum_transmittance;
/* normalize cumulative density function for distance sampling */
- VolumeStep *last_step = segment->steps + segment->numsteps - 1;
+ int numsteps = segment->numsteps - 1;
- if(!is_zero(last_step->cdf_distance)) {
- VolumeStep *step = &segment->steps[0];
- int numsteps = segment->numsteps;
- float3 inv_cdf_distance_sum = safe_invert_color(last_step->cdf_distance);
+ if(numsteps >= 0) {
+ VolumeStep *last_step = segment->steps + segment->numsteps - 1;
- for(int i = 0; i < numsteps; i++, step++)
- step->cdf_distance *= inv_cdf_distance_sum;
+ if(!is_zero(last_step->cdf_distance)) {
+ VolumeStep *step = &segment->steps[0];
+ int numsteps = segment->numsteps;
+ float3 inv_cdf_distance_sum = safe_invert_color(last_step->cdf_distance);
+
+ for(int i = 0; i < numsteps; i++, step++)
+ step->cdf_distance *= inv_cdf_distance_sum;
+ }
}
}
@@ -1166,6 +1409,7 @@ ccl_device void kernel_volume_stack_init(KernelGlobals *kg,
if(need_add) {
stack[stack_index].object = stack_sd->object;
stack[stack_index].shader = stack_sd->shader;
+ stack[stack_index].volume = stack_sd->prim;
++stack_index;
}
}
diff --git a/intern/cycles/kernel/openvdb/vdb_globals.h b/intern/cycles/kernel/openvdb/vdb_globals.h
new file mode 100644
index 00000000000..771c7f833e5
--- /dev/null
+++ b/intern/cycles/kernel/openvdb/vdb_globals.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 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 __VDB_GLOBALS_H__
+#define __VDB_GLOBALS_H__
+
+#include "vdb_intern.h"
+
+CCL_NAMESPACE_BEGIN
+
+typedef openvdb::math::Ray<float> vdb_ray_t;
+typedef openvdb::math::Transform vdb_transform_t;
+
+struct OpenVDBGlobals {
+ typedef openvdb::FloatGrid scalar_grid_t;
+ typedef openvdb::Vec3SGrid vector_grid_t;
+ typedef openvdb::tools::VolumeRayIntersector<scalar_grid_t, scalar_grid_t::TreeType::RootNodeType::ChildNodeType::LEVEL, vdb_ray_t> scalar_isector_t;
+ typedef openvdb::tools::VolumeRayIntersector<vector_grid_t, vector_grid_t::TreeType::RootNodeType::ChildNodeType::LEVEL, vdb_ray_t> vector_isector_t;
+
+ vector<const scalar_grid_t *> scalar_grids;
+ vector<const vector_grid_t *> vector_grids;
+ /* Main intersectors, which initialize the voxels' bounding box
+ * so the ones for the various threads do not do this,
+ * rather they are generated from a copy of these
+ */
+ vector<scalar_isector_t *> scalar_main_isectors;
+ vector<vector_isector_t *> vector_main_isectors;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __VDB_GLOBALS_H__ */
diff --git a/intern/cycles/kernel/openvdb/vdb_intern.h b/intern/cycles/kernel/openvdb/vdb_intern.h
new file mode 100644
index 00000000000..0ebb0eed094
--- /dev/null
+++ b/intern/cycles/kernel/openvdb/vdb_intern.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 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 __VDB_INTERN_H__
+#define __VDB_INTERN_H__
+
+/* They are too many implicit float conversions happening in OpenVDB, disabling
+ * errors for now (kevin) */
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-conversion"
+# pragma GCC diagnostic ignored "-Wdouble-promotion"
+#endif
+
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/Interpolation.h>
+#include <openvdb/tools/RayIntersector.h>
+
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+#if defined(HAS_CPP11_FEATURES)
+using std::isfinite;
+#else
+using boost::math::isfinite;
+#endif
+
+CCL_NAMESPACE_END
+
+#endif /* __VDB_INTERN_H__ */
diff --git a/intern/cycles/kernel/openvdb/vdb_thread.cpp b/intern/cycles/kernel/openvdb/vdb_thread.cpp
new file mode 100644
index 00000000000..57c57f96f0f
--- /dev/null
+++ b/intern/cycles/kernel/openvdb/vdb_thread.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2016 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.
+ */
+
+#include "kernel_compat_cpu.h"
+#include "kernel_types.h"
+#include "kernel_globals.h"
+
+#include "vdb_globals.h"
+#include "vdb_intern.h"
+#include "vdb_thread.h"
+
+#include "util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Manage thread-local data associated with volumes */
+
+struct OpenVDBScalarThreadData {
+ typedef openvdb::FloatGrid grid_t;
+ typedef openvdb::FloatGrid::ConstAccessor accessor_t;
+ typedef openvdb::tools::GridSampler<accessor_t, openvdb::tools::PointSampler> point_sampler_t;
+ typedef openvdb::tools::GridSampler<accessor_t, openvdb::tools::BoxSampler> box_sampler_t;
+ typedef openvdb::tools::VolumeRayIntersector<grid_t, grid_t::TreeType::RootNodeType::ChildNodeType::LEVEL, vdb_ray_t> isector_t;
+
+ void init(const grid_t &grid, const isector_t &main_isector)
+ {
+ accessor = new accessor_t(grid.getConstAccessor());
+ point_sampler = new point_sampler_t(*accessor, grid.transform());
+ box_sampler = new box_sampler_t(*accessor, grid.transform());
+ isector = new isector_t(main_isector);
+ }
+
+ void free()
+ {
+ delete accessor;
+ delete point_sampler;
+ delete box_sampler;
+ delete isector;
+ }
+
+ accessor_t *accessor;
+ point_sampler_t *point_sampler;
+ box_sampler_t *box_sampler;
+ isector_t *isector;
+};
+
+struct OpenVDBVectorThreadData {
+ typedef openvdb::Vec3SGrid grid_t;
+ typedef openvdb::Vec3SGrid::ConstAccessor accessor_t;
+ typedef openvdb::tools::GridSampler<accessor_t, openvdb::tools::PointSampler> point_sampler_t;
+ typedef openvdb::tools::GridSampler<accessor_t, openvdb::tools::BoxSampler> box_sampler_t;
+ typedef openvdb::tools::GridSampler<accessor_t, openvdb::tools::StaggeredPointSampler> stag_point_sampler_t;
+ typedef openvdb::tools::GridSampler<accessor_t, openvdb::tools::StaggeredBoxSampler> stag_box_sampler_t;
+
+ typedef openvdb::tools::VolumeRayIntersector<grid_t, grid_t::TreeType::RootNodeType::ChildNodeType::LEVEL, vdb_ray_t> isector_t;
+
+ void init (const grid_t &grid, const isector_t &main_isector)
+ {
+ accessor = new accessor_t(grid.getConstAccessor());
+ point_sampler = new point_sampler_t(*accessor, grid.transform());
+ box_sampler = new box_sampler_t(*accessor, grid.transform());
+ stag_point_sampler = new stag_point_sampler_t(*accessor, grid.transform());
+ stag_box_sampler = new stag_box_sampler_t(*accessor, grid.transform());
+ isector = new isector_t(main_isector);
+ }
+
+ void free()
+ {
+ delete accessor;
+ delete point_sampler;
+ delete box_sampler;
+ delete stag_point_sampler;
+ delete stag_box_sampler;
+ delete isector;
+ }
+
+ accessor_t *accessor;
+ point_sampler_t *point_sampler;
+ box_sampler_t *box_sampler;
+ stag_point_sampler_t *stag_point_sampler;
+ stag_box_sampler_t *stag_box_sampler;
+ isector_t *isector;
+};
+
+struct OpenVDBThreadData {
+ std::vector<OpenVDBScalarThreadData> scalar_data;
+ std::vector<OpenVDBVectorThreadData> vector_data;
+};
+
+void vdb_thread_init(KernelGlobals *kg, const KernelGlobals *kernel_globals, OpenVDBGlobals *vdb_globals)
+{
+ kg->vdb = vdb_globals;
+
+ OpenVDBThreadData *tdata = new OpenVDBThreadData;
+
+ tdata->scalar_data.resize(vdb_globals->scalar_grids.size());
+ tdata->vector_data.resize(vdb_globals->vector_grids.size());
+ for (size_t i = 0; i < vdb_globals->scalar_grids.size(); ++i) {
+ tdata->scalar_data[i].init(*vdb_globals->scalar_grids[i], *vdb_globals->scalar_main_isectors[i]);
+ }
+ for (size_t i = 0; i < vdb_globals->vector_grids.size(); ++i) {
+ tdata->vector_data[i].init(*vdb_globals->vector_grids[i], *vdb_globals->vector_main_isectors[i]);
+ }
+ kg->vdb_tdata = tdata;
+}
+
+void vdb_thread_free(KernelGlobals *kg)
+{
+ OpenVDBThreadData *tdata = kg->vdb_tdata;
+ kg->vdb_tdata = NULL;
+
+ for (size_t i = 0; i < tdata->scalar_data.size(); ++i) {
+ tdata->scalar_data[i].free();
+ }
+ for (size_t i = 0; i < tdata->vector_data.size(); ++i) {
+ tdata->vector_data[i].free();
+ }
+ delete tdata;
+}
+
+bool vdb_volume_scalar_has_uniform_voxels(OpenVDBGlobals *vdb, int vdb_index)
+{
+ return vdb->scalar_grids[vdb_index]->hasUniformVoxels();
+}
+
+bool vdb_volume_vector_has_uniform_voxels(OpenVDBGlobals *vdb, int vdb_index)
+{
+ return vdb->vector_grids[vdb_index]->hasUniformVoxels();
+}
+
+float vdb_volume_sample_scalar(OpenVDBGlobals */*vdb*/, OpenVDBThreadData *vdb_thread, int vdb_index,
+ float x, float y, float z, int sampling)
+{
+ OpenVDBScalarThreadData &data = vdb_thread->scalar_data[vdb_index];
+
+ switch (sampling) {
+ case OPENVDB_SAMPLE_POINT:
+ return data.point_sampler->wsSample(openvdb::Vec3d(x, y, z));
+ case OPENVDB_SAMPLE_BOX:
+ return data.box_sampler->wsSample(openvdb::Vec3d(x, y, z));
+ }
+
+ return 0.0f;
+}
+
+float3 vdb_volume_sample_vector(OpenVDBGlobals *vdb, OpenVDBThreadData *vdb_thread, int vdb_index,
+ float x, float y, float z, int sampling)
+{
+ bool staggered = (vdb->vector_grids[vdb_index]->getGridClass() == openvdb::GRID_STAGGERED);
+ OpenVDBVectorThreadData &data = vdb_thread->vector_data[vdb_index];
+ openvdb::Vec3s r;
+
+ if (staggered) {
+ switch (sampling) {
+ case OPENVDB_SAMPLE_POINT:
+ r = data.stag_point_sampler->wsSample(openvdb::Vec3d(x, y, z));
+ break;
+ case OPENVDB_SAMPLE_BOX:
+ r = data.stag_box_sampler->wsSample(openvdb::Vec3d(x, y, z));
+ break;
+ }
+ }
+ else {
+ switch (sampling) {
+ case OPENVDB_SAMPLE_POINT:
+ r = data.point_sampler->wsSample(openvdb::Vec3d(x, y, z));
+ break;
+ case OPENVDB_SAMPLE_BOX:
+ r = data.box_sampler->wsSample(openvdb::Vec3d(x, y, z));
+ break;
+ }
+ }
+
+ return make_float3(r.x(), r.y(), r.z());
+}
+
+bool vdb_volume_intersect(OpenVDBThreadData *vdb_thread, int vdb_index,
+ const Ray *ray, float *isect)
+{
+ OpenVDBScalarThreadData &data = vdb_thread->scalar_data[vdb_index];
+
+ vdb_ray_t::Vec3Type P(ray->P.x, ray->P.y, ray->P.z);
+ vdb_ray_t::Vec3Type D(ray->D.x, ray->D.y, ray->D.z);
+ D.normalize();
+
+ vdb_ray_t vdb_ray(P, D, 1e-5f, ray->t);
+
+ if(data.isector->setWorldRay(vdb_ray)) {
+ // TODO(kevin): is this correct?
+ *isect = static_cast<float>(vdb_ray.t1());
+
+ return true;
+ }
+
+ return false;
+}
+
+bool vdb_volume_march(OpenVDBThreadData *vdb_thread, int vdb_index,
+ float *t0, float *t1)
+{
+ OpenVDBScalarThreadData &data = vdb_thread->scalar_data[vdb_index];
+
+ float vdb_t0(*t0), vdb_t1(*t1);
+
+ if(data.isector->march(vdb_t0, vdb_t1)) {
+ *t0 = data.isector->getWorldTime(vdb_t0);
+ *t1 = data.isector->getWorldTime(vdb_t1);
+
+ return true;
+ }
+
+ return false;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/kernel/openvdb/vdb_thread.h b/intern/cycles/kernel/openvdb/vdb_thread.h
new file mode 100644
index 00000000000..26ef8f194c5
--- /dev/null
+++ b/intern/cycles/kernel/openvdb/vdb_thread.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 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 __VDB_THREAD_H__
+#define __VDB_THREAD_H__
+
+CCL_NAMESPACE_BEGIN
+
+struct Intersection;
+struct KernelGlobals;
+struct OpenVDBGlobals;
+struct OpenVDBThreadData;
+struct Ray;
+
+void vdb_thread_init(KernelGlobals *kg, const KernelGlobals *kernel_globals, OpenVDBGlobals *vdb_globals);
+void vdb_thread_free(KernelGlobals *kg);
+
+enum OpenVDB_SampleType {
+ OPENVDB_SAMPLE_POINT = 0,
+ OPENVDB_SAMPLE_BOX = 1,
+};
+
+bool vdb_volume_scalar_has_uniform_voxels(OpenVDBGlobals *vdb, int vdb_index);
+bool vdb_volume_vector_has_uniform_voxels(OpenVDBGlobals *vdb, int vdb_index);
+float vdb_volume_sample_scalar(OpenVDBGlobals *vdb, OpenVDBThreadData *vdb_thread, int vdb_index,
+ float x, float y, float z, int sampling);
+float3 vdb_volume_sample_vector(OpenVDBGlobals *vdb, OpenVDBThreadData *vdb_thread, int vdb_index,
+ float x, float y, float z, int sampling);
+bool vdb_volume_intersect(OpenVDBThreadData *vdb_thread, int vdb_index,
+ const Ray *ray, float *isect);
+bool vdb_volume_march(OpenVDBThreadData *vdb_thread, int vdb_index,
+ float *t0, float *t1);
+
+CCL_NAMESPACE_END
+
+#endif /* __VDB_THREAD_H__ */
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 8ae004031e1..c220a5ee3a1 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -34,10 +34,6 @@
#include "kernel/osl/osl_services.h"
#include "kernel/osl/osl_shader.h"
-#include "util/util_foreach.h"
-#include "util/util_logging.h"
-#include "util/util_string.h"
-
#include "kernel/kernel_compat_cpu.h"
#include "kernel/split/kernel_split_data_types.h"
#include "kernel/kernel_globals.h"
@@ -50,6 +46,14 @@
#include "kernel/geom/geom.h"
#include "kernel/bvh/bvh.h"
+/* Note: "util_foreach.h" needs to be included after "kernel_compat_cpu.h", as
+ * for some reason ccl::foreach conflicts with openvdb::tools::foreach, which is
+ * indirectly included through "kernel_compat_cpu.h".
+ */
+#include "util/util_foreach.h"
+#include "util/util_logging.h"
+#include "util/util_string.h"
+
#include "kernel/kernel_projection.h"
#include "kernel/kernel_accumulate.h"
#include "kernel/kernel_shader.h"
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 17ac66644e2..ace1bd33fdf 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -8,6 +8,12 @@ set(INC_SYS
${GLEW_INCLUDE_DIR}
)
+if(WITH_OPENVDB)
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+endif()
+
set(SRC
attribute.cpp
background.cpp
@@ -35,6 +41,7 @@ set(SRC
svm.cpp
tables.cpp
tile.cpp
+ volume.cpp
)
set(SRC_HEADERS
@@ -62,6 +69,7 @@ set(SRC_HEADERS
svm.h
tables.h
tile.h
+ volume.h
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RTTI_DISABLE_FLAGS}")
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index 9dff18691cd..ea685c6f6e1 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -33,7 +33,8 @@ Attribute::~Attribute()
VoxelAttribute *voxel_data = data_voxel();
if(voxel_data && voxel_data->slot != -1) {
- voxel_data->manager->remove_image(voxel_data->slot);
+ if (voxel_data->manager)
+ voxel_data->manager->remove_image(voxel_data->slot);
}
}
}
@@ -344,10 +345,13 @@ Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement eleme
/* this is weak .. */
if(triangle_mesh)
attr->resize(triangle_mesh, ATTR_PRIM_TRIANGLE, false);
- if(curve_mesh)
+ else if(curve_mesh)
attr->resize(curve_mesh, ATTR_PRIM_CURVE, false);
- if(subd_mesh)
+ else if(subd_mesh)
attr->resize(subd_mesh, ATTR_PRIM_SUBD, false);
+ else if(element == ATTR_ELEMENT_VOXEL) {
+ attr->resize(1);
+ }
return attr;
}
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index c59a5d97df5..1940ac22f6f 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -32,6 +32,7 @@
#include "render/shader.h"
#include "render/svm.h"
#include "render/tables.h"
+#include "volume.h"
#include "util/util_foreach.h"
#include "util/util_guarded_allocator.h"
@@ -58,6 +59,7 @@ Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_)
particle_system_manager = new ParticleSystemManager();
curve_system_manager = new CurveSystemManager();
bake_manager = new BakeManager();
+ volume_manager = new VolumeManager();
/* OSL only works on the CPU */
if(device_info_.type == DEVICE_CPU)
@@ -83,12 +85,15 @@ void Scene::free_memory(bool final)
delete l;
foreach(ParticleSystem *p, particle_systems)
delete p;
+ foreach(Volume *v, volumes)
+ delete v;
shaders.clear();
meshes.clear();
objects.clear();
lights.clear();
particle_systems.clear();
+ volumes.clear();
if(device) {
camera->device_free(device, &dscene, this);
@@ -112,6 +117,7 @@ void Scene::free_memory(bool final)
image_manager->device_free_builtin(device, &dscene);
lookup_tables->device_free(device, &dscene);
+ volume_manager->device_free(device, &dscene);
}
if(final) {
@@ -128,6 +134,7 @@ void Scene::free_memory(bool final)
delete curve_system_manager;
delete image_manager;
delete bake_manager;
+ delete volume_manager;
}
}
@@ -234,6 +241,11 @@ void Scene::device_update(Device *device_, Progress& progress)
if(progress.get_cancel() || device->have_error()) return;
+ progress.set_status("Updating OpenVDB Volumes");
+ volume_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
if(device->have_error() == false) {
progress.set_status("Updating Device", "Writing constant memory");
device->const_copy_to("__data", &dscene.data, sizeof(dscene.data));
@@ -306,6 +318,7 @@ bool Scene::need_data_update()
|| particle_system_manager->need_update
|| curve_system_manager->need_update
|| bake_manager->need_update
+ || volume_manager->need_update
|| film->need_update);
}
diff --git a/intern/cycles/render/scene.cpp.orig b/intern/cycles/render/scene.cpp.orig
new file mode 100644
index 00000000000..0297e361f03
--- /dev/null
+++ b/intern/cycles/render/scene.cpp.orig
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+
+<<<<<<< HEAD
+#include "background.h"
+#include "bake.h"
+#include "camera.h"
+#include "curves.h"
+#include "device.h"
+#include "film.h"
+#include "integrator.h"
+#include "light.h"
+#include "mesh.h"
+#include "object.h"
+#include "osl.h"
+#include "particles.h"
+#include "scene.h"
+#include "shader.h"
+#include "svm.h"
+#include "tables.h"
+#include "volume.h"
+
+#include "util_foreach.h"
+#include "util_guarded_allocator.h"
+#include "util_logging.h"
+#include "util_progress.h"
+=======
+#include "render/background.h"
+#include "render/bake.h"
+#include "render/camera.h"
+#include "render/curves.h"
+#include "device/device.h"
+#include "render/film.h"
+#include "render/integrator.h"
+#include "render/light.h"
+#include "render/mesh.h"
+#include "render/object.h"
+#include "render/osl.h"
+#include "render/particles.h"
+#include "render/scene.h"
+#include "render/shader.h"
+#include "render/svm.h"
+#include "render/tables.h"
+
+#include "util/util_foreach.h"
+#include "util/util_guarded_allocator.h"
+#include "util/util_logging.h"
+#include "util/util_progress.h"
+>>>>>>> origin/master
+
+CCL_NAMESPACE_BEGIN
+
+Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_)
+: params(params_)
+{
+ device = NULL;
+ memset(&dscene.data, 0, sizeof(dscene.data));
+
+ camera = new Camera();
+ lookup_tables = new LookupTables();
+ film = new Film();
+ background = new Background();
+ light_manager = new LightManager();
+ mesh_manager = new MeshManager();
+ object_manager = new ObjectManager();
+ integrator = new Integrator();
+ image_manager = new ImageManager(device_info_);
+ particle_system_manager = new ParticleSystemManager();
+ curve_system_manager = new CurveSystemManager();
+ bake_manager = new BakeManager();
+ volume_manager = new VolumeManager();
+
+ /* OSL only works on the CPU */
+ if(device_info_.type == DEVICE_CPU)
+ shader_manager = ShaderManager::create(this, params.shadingsystem);
+ else
+ shader_manager = ShaderManager::create(this, SHADINGSYSTEM_SVM);
+}
+
+Scene::~Scene()
+{
+ free_memory(true);
+}
+
+void Scene::free_memory(bool final)
+{
+ foreach(Shader *s, shaders)
+ delete s;
+ foreach(Mesh *m, meshes)
+ delete m;
+ foreach(Object *o, objects)
+ delete o;
+ foreach(Light *l, lights)
+ delete l;
+ foreach(ParticleSystem *p, particle_systems)
+ delete p;
+ foreach(Volume *v, volumes)
+ delete v;
+
+ shaders.clear();
+ meshes.clear();
+ objects.clear();
+ lights.clear();
+ particle_systems.clear();
+ volumes.clear();
+
+ if(device) {
+ camera->device_free(device, &dscene, this);
+ film->device_free(device, &dscene, this);
+ background->device_free(device, &dscene);
+ integrator->device_free(device, &dscene);
+
+ object_manager->device_free(device, &dscene);
+ mesh_manager->device_free(device, &dscene);
+ shader_manager->device_free(device, &dscene, this);
+ light_manager->device_free(device, &dscene);
+
+ particle_system_manager->device_free(device, &dscene);
+ curve_system_manager->device_free(device, &dscene);
+
+ bake_manager->device_free(device, &dscene);
+
+ if(!params.persistent_data || final)
+ image_manager->device_free(device, &dscene);
+ else
+ image_manager->device_free_builtin(device, &dscene);
+
+ lookup_tables->device_free(device, &dscene);
+ volume_manager->device_free(device, &dscene);
+ }
+
+ if(final) {
+ delete lookup_tables;
+ delete camera;
+ delete film;
+ delete background;
+ delete integrator;
+ delete object_manager;
+ delete mesh_manager;
+ delete shader_manager;
+ delete light_manager;
+ delete particle_system_manager;
+ delete curve_system_manager;
+ delete image_manager;
+ delete bake_manager;
+ delete volume_manager;
+ }
+}
+
+void Scene::device_update(Device *device_, Progress& progress)
+{
+ if(!device)
+ device = device_;
+
+ bool print_stats = need_data_update();
+
+ /* The order of updates is important, because there's dependencies between
+ * the different managers, using data computed by previous managers.
+ *
+ * - Image manager uploads images used by shaders.
+ * - Camera may be used for adaptive subdivision.
+ * - Displacement shader must have all shader data available.
+ * - Light manager needs lookup tables and final mesh data to compute emission CDF.
+ * - Film needs light manager to run for use_light_visibility
+ * - Lookup tables are done a second time to handle film tables
+ */
+
+ progress.set_status("Updating Shaders");
+ shader_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Background");
+ background->device_update(device, &dscene, this);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Camera");
+ camera->device_update(device, &dscene, this);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Meshes Flags");
+ mesh_manager->device_update_flags(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Objects");
+ object_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Meshes");
+ mesh_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Objects Flags");
+ object_manager->device_update_flags(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Images");
+ image_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Camera Volume");
+ camera->device_update_volume(device, &dscene, this);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Hair Systems");
+ curve_system_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Lookup Tables");
+ lookup_tables->device_update(device, &dscene);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Lights");
+ light_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Particle Systems");
+ particle_system_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Integrator");
+ integrator->device_update(device, &dscene, this);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Film");
+ film->device_update(device, &dscene, this);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Lookup Tables");
+ lookup_tables->device_update(device, &dscene);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating Baking");
+ bake_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ progress.set_status("Updating OpenVDB Volumes");
+ volume_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
+ if(device->have_error() == false) {
+ progress.set_status("Updating Device", "Writing constant memory");
+ device->const_copy_to("__data", &dscene.data, sizeof(dscene.data));
+ }
+
+ if(print_stats) {
+ size_t mem_used = util_guarded_get_mem_used();
+ size_t mem_peak = util_guarded_get_mem_peak();
+
+ VLOG(1) << "System memory statistics after full device sync:\n"
+ << " Usage: " << string_human_readable_number(mem_used)
+ << " (" << string_human_readable_size(mem_used) << ")\n"
+ << " Peak: " << string_human_readable_number(mem_peak)
+ << " (" << string_human_readable_size(mem_peak) << ")";
+ }
+}
+
+Scene::MotionType Scene::need_motion(bool advanced_shading)
+{
+ if(integrator->motion_blur)
+ return (advanced_shading)? MOTION_BLUR: MOTION_NONE;
+ else if(Pass::contains(film->passes, PASS_MOTION))
+ return MOTION_PASS;
+ else
+ return MOTION_NONE;
+}
+
+float Scene::motion_shutter_time()
+{
+ if(need_motion() == Scene::MOTION_PASS)
+ return 2.0f;
+ else
+ return camera->shuttertime;
+}
+
+bool Scene::need_global_attribute(AttributeStandard std)
+{
+ if(std == ATTR_STD_UV)
+ return Pass::contains(film->passes, PASS_UV);
+ else if(std == ATTR_STD_MOTION_VERTEX_POSITION)
+ return need_motion() != MOTION_NONE;
+ else if(std == ATTR_STD_MOTION_VERTEX_NORMAL)
+ return need_motion() == MOTION_BLUR;
+
+ return false;
+}
+
+void Scene::need_global_attributes(AttributeRequestSet& attributes)
+{
+ for(int std = ATTR_STD_NONE; std < ATTR_STD_NUM; std++)
+ if(need_global_attribute((AttributeStandard)std))
+ attributes.add((AttributeStandard)std);
+}
+
+bool Scene::need_update()
+{
+ return (need_reset() || film->need_update);
+}
+
+bool Scene::need_data_update()
+{
+ return (background->need_update
+ || image_manager->need_update
+ || object_manager->need_update
+ || mesh_manager->need_update
+ || light_manager->need_update
+ || lookup_tables->need_update
+ || integrator->need_update
+ || shader_manager->need_update
+ || particle_system_manager->need_update
+ || curve_system_manager->need_update
+ || bake_manager->need_update
+ || volume_manager->need_update
+ || film->need_update);
+}
+
+bool Scene::need_reset()
+{
+ return need_data_update() || camera->need_update;
+}
+
+void Scene::reset()
+{
+ shader_manager->reset(this);
+ shader_manager->add_default(this);
+
+ /* ensure all objects are updated */
+ camera->tag_update();
+ film->tag_update(this);
+ background->tag_update(this);
+ integrator->tag_update(this);
+ object_manager->tag_update(this);
+ mesh_manager->tag_update(this);
+ light_manager->tag_update(this);
+ particle_system_manager->tag_update(this);
+ curve_system_manager->tag_update(this);
+}
+
+void Scene::device_free()
+{
+ free_memory(false);
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index a1966afd23b..db7831ac14b 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -54,6 +54,8 @@ class ShaderManager;
class Progress;
class BakeManager;
class BakeData;
+class Volume;
+class VolumeManager;
/* Scene Device Data */
@@ -121,6 +123,9 @@ public:
vector<device_vector<uchar>* > tex_byte_image;
vector<device_vector<half>* > tex_half_image;
+ /* volume */
+ device_vector<uint> vol_shader;
+
KernelData data;
};
@@ -182,6 +187,7 @@ public:
vector<Shader*> shaders;
vector<Light*> lights;
vector<ParticleSystem*> particle_systems;
+ vector<Volume*> volumes;
/* data managers */
ImageManager *image_manager;
@@ -192,12 +198,14 @@ public:
ParticleSystemManager *particle_system_manager;
CurveSystemManager *curve_system_manager;
BakeManager *bake_manager;
+ VolumeManager *volume_manager;
/* default shaders */
Shader *default_surface;
Shader *default_light;
Shader *default_background;
Shader *default_empty;
+ Shader *default_volume;
/* device */
Device *device;
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index f1ff6b49b71..0080b1d138a 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -701,11 +701,12 @@ DeviceRequestedFeatures Session::get_requested_device_features()
requested_features.use_camera_motion = scene->camera->use_motion;
foreach(Object *object, scene->objects) {
Mesh *mesh = object->mesh;
- if(mesh->num_curves()) {
- requested_features.use_hair = true;
+ if(mesh) {
+ if(mesh->num_curves()) {
+ requested_features.use_hair = true;
+ }
}
- requested_features.use_object_motion |= object->use_motion | mesh->use_motion_blur;
- requested_features.use_camera_motion |= mesh->use_motion_blur;
+ requested_features.use_object_motion |= object->use_motion;
#ifdef WITH_OPENSUBDIV
if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE) {
requested_features.use_patch_evaluation = true;
diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp
index 3992ada2e85..efdf49f2720 100644
--- a/intern/cycles/render/shader.cpp
+++ b/intern/cycles/render/shader.cpp
@@ -398,6 +398,7 @@ void ShaderManager::device_update_shaders_used(Scene *scene)
scene->default_light->used = true;
scene->default_background->used = true;
scene->default_empty->used = true;
+ scene->default_volume->used = true;
if(scene->background->shader)
scene->background->shader->used = true;
@@ -570,6 +571,23 @@ void ShaderManager::add_default(Scene *scene)
scene->shaders.push_back(shader);
scene->default_empty = shader;
}
+
+ /* default empty */
+ {
+ ShaderGraph *graph = new ShaderGraph();
+
+ ScatterVolumeNode *scatter = new ScatterVolumeNode();
+ scatter->input("Density")->set(1.0);
+ graph->add(scatter);
+
+ graph->connect(scatter->output("Volume"), graph->output()->input("Volume"));
+
+ Shader *shader = new Shader();
+ shader->name = "default_volume";
+ shader->graph = graph;
+ scene->shaders.push_back(shader);
+ scene->default_volume = shader;
+ }
}
void ShaderManager::get_requested_graph_features(ShaderGraph *graph,
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index 32f89897970..f06be4a0ca2 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -59,7 +59,7 @@ void SVMShaderManager::device_update_shader(Scene *scene,
svm_nodes.push_back(make_int4(NODE_SHADER_JUMP, 0, 0, 0));
SVMCompiler::Summary summary;
- SVMCompiler compiler(scene->shader_manager, scene->image_manager);
+ SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->volume_manager);
compiler.background = (shader == scene->default_background);
compiler.compile(scene, shader, svm_nodes, 0, &summary);
@@ -156,10 +156,11 @@ void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s
/* Graph Compiler */
-SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_)
+SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_, VolumeManager *volume_manager_)
{
shader_manager = shader_manager_;
image_manager = image_manager_;
+ volume_manager = volume_manager_;
max_stack_use = 0;
current_type = SHADER_TYPE_SURFACE;
current_shader = NULL;
diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h
index 98ef5fa05d8..a8f95a48a0f 100644
--- a/intern/cycles/render/svm.h
+++ b/intern/cycles/render/svm.h
@@ -35,6 +35,7 @@ class ShaderGraph;
class ShaderInput;
class ShaderNode;
class ShaderOutput;
+class VolumeManager;
/* Shader Manager */
@@ -95,7 +96,8 @@ public:
string full_report() const;
};
- SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager);
+ SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager, VolumeManager *volume_manager_);
+
void compile(Scene *scene,
Shader *shader,
vector<int4>& svm_nodes,
@@ -123,6 +125,7 @@ public:
ShaderType output_type() { return current_type; }
ImageManager *image_manager;
+ VolumeManager *volume_manager;
ShaderManager *shader_manager;
bool background;
diff --git a/intern/cycles/render/volume.cpp b/intern/cycles/render/volume.cpp
new file mode 100644
index 00000000000..e7d1d7d6595
--- /dev/null
+++ b/intern/cycles/render/volume.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2015 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.
+ */
+
+#include "scene.h"
+#include "volume.h"
+
+#include "util/util_foreach.h"
+#include "util/util_logging.h"
+#include "util/util_progress.h"
+#include "util/util_task.h"
+
+#include "../kernel/openvdb/vdb_globals.h"
+
+CCL_NAMESPACE_BEGIN
+
+#define MAX_VOLUME 1024
+
+void Volume::tag_update(Scene *scene, bool /*rebuild*/)
+{
+ scene->volume_manager->need_update = true;
+}
+
+/* ------------------------------------------------------------------------- */
+
+VolumeManager::VolumeManager()
+{
+#ifdef WITH_OPENVDB
+ openvdb::initialize();
+
+ current_grids.reserve(64);
+#endif
+
+ need_update = true;
+ num_float_volume = 0;
+ num_float3_volume = 0;
+}
+
+VolumeManager::~VolumeManager()
+{
+ current_grids.clear();
+}
+
+static inline void catch_exceptions()
+{
+#ifdef WITH_OPENVDB
+ try {
+ throw;
+ }
+ catch(const openvdb::IoError& e) {
+ std::cerr << e.what() << "\n";
+ }
+#endif
+}
+
+int VolumeManager::add_volume(Volume *volume, const std::string &filename, const std::string &name)
+{
+ size_t slot = -1;
+
+ if((slot = find_existing_slot(volume, filename, name)) != -1) {
+ return slot;
+ }
+
+ if((num_float_volume + num_float3_volume + 1) > MAX_VOLUME) {
+ printf("VolumeManager::add_volume: volume limit reached %d!\n", MAX_VOLUME);
+ return -1;
+ }
+
+ try {
+ if(is_openvdb_file(filename)) {
+ slot = add_openvdb_volume(volume, filename, name);
+ }
+
+ add_grid_description(volume, filename, name, slot);
+
+ volumes.push_back(volume);
+ }
+ catch(...) {
+ catch_exceptions();
+ slot = -1;
+ }
+
+ return slot;
+}
+
+int VolumeManager::find_existing_slot(Volume *volume, const std::string &filename, const std::string &name)
+{
+ for(size_t i = 0; i < current_grids.size(); ++i) {
+ GridDescription grid = current_grids[i];
+
+ if(grid.volume == volume) {
+ if(grid.filename == filename && grid.name == name) {
+ return grid.slot;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int VolumeManager::find_density_slot()
+{
+ /* first try finding a matching grid name */
+ for(size_t i = 0; i < current_grids.size(); ++i) {
+ GridDescription grid = current_grids[i];
+
+ if(string_iequals(grid.name, "density") || string_iequals(grid.name, "density high"))
+ return grid.slot;
+ }
+
+ /* try using the first scalar float grid instead */
+ for (size_t i = 0; i < volumes.size(); ++i) {
+ Volume *volume = volumes[i];
+
+ if (!volume->scalar_grids.empty()) {
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+bool VolumeManager::is_openvdb_file(const string& filename) const
+{
+ return string_endswith(filename, ".vdb");
+}
+
+template <typename Container>
+size_t find_empty_slot(Container container)
+{
+ size_t slot = 0;
+
+ for(; slot < container.size(); ++slot) {
+ if(!container[slot]) {
+ break;
+ }
+ }
+
+ if(slot == container.size()) {
+ if(slot == MAX_VOLUME) {
+ printf("VolumeManager::add_volume: volume limit reached %d!\n",
+ MAX_VOLUME);
+ return -1;
+ }
+
+ container.resize(slot + 1);
+ }
+
+ return slot;
+}
+
+size_t VolumeManager::add_openvdb_volume(Volume *volume, const std::string &filename, const std::string &name)
+{
+ size_t slot = -1;
+
+#ifdef WITH_OPENVDB
+ openvdb::io::File file(filename);
+ file.open();
+
+ if(!file.hasGrid(name)) return -1;
+
+ openvdb::GridBase::Ptr grid = file.readGrid(name);
+ if(grid->getGridClass() == openvdb::GRID_LEVEL_SET) return -1;
+
+ if(grid->isType<openvdb::FloatGrid>()) {
+ openvdb::FloatGrid::Ptr fgrid = openvdb::gridPtrCast<openvdb::FloatGrid>(grid);
+ volume->scalar_grids.push_back(fgrid);
+
+ /* XXX Ray intersectors only support uniform grids.
+ * Can we make this transparent somehow? - lukas
+ */
+ assert(fgrid->hasUniformVoxels());
+
+ slot = num_float_volume++;
+ }
+ else if(grid->isType<openvdb::Vec3SGrid>()) {
+ openvdb::Vec3SGrid::Ptr vgrid = openvdb::gridPtrCast<openvdb::Vec3SGrid>(grid);
+ volume->vector_grids.push_back(vgrid);
+
+ slot = num_float3_volume++;
+ }
+#else
+ (void)volume;
+ (void)filename;
+ (void)name;
+#endif
+
+ return slot;
+}
+
+void VolumeManager::add_grid_description(Volume *volume, const std::string &filename, const std::string &name, int slot)
+{
+ GridDescription descr;
+ descr.filename = filename;
+ descr.name = name;
+ descr.volume = volume;
+ descr.slot = slot;
+
+ current_grids.push_back(descr);
+}
+
+static void update_attribute_element_offset(Attribute *vattr,
+ TypeDesc& type,
+ int& offset,
+ AttributeElement& element)
+{
+ if(vattr) {
+ /* store element and type */
+ element = vattr->element;
+ type = vattr->type;
+
+ /* store slot in offset value */
+ VoxelAttribute *voxel_data = vattr->data_voxel();
+ offset = voxel_data->slot;
+ }
+ else {
+ /* attribute not found */
+ element = ATTR_ELEMENT_NONE;
+ offset = 0;
+ }
+}
+
+void VolumeManager::device_update_attributes(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
+{
+ progress.set_status("Updating Volume", "Computing attributes");
+
+ /* gather per volume requested attributes. as volumes may have multiple
+ * shaders assigned, this merges the requested attributes that have
+ * been set per shader by the shader manager */
+ vector<AttributeRequestSet> volume_attributes(volumes.size());
+
+ for(size_t i = 0; i < volumes.size(); i++) {
+ Volume *volume = volumes[i];
+
+ foreach(Shader *shader, volume->used_shaders) {
+ volume_attributes[i].add(shader->attributes);
+ }
+ }
+
+ for(size_t i = 0; i < volumes.size(); i++) {
+ Volume *volume = volumes[i];
+ AttributeRequestSet& attributes = volume_attributes[i];
+
+ /* todo: we now store std and name attributes from requests even if
+ * they actually refer to the same mesh attributes, optimize */
+ foreach(AttributeRequest& req, attributes.requests) {
+ Attribute *vattr = volume->attributes.find(req);
+
+ update_attribute_element_offset(vattr,
+ req.triangle_type,
+ req.triangle_desc.offset,
+ req.triangle_desc.element);
+
+ if(progress.get_cancel()) return;
+ }
+ }
+
+ update_svm_attributes(device, dscene, scene, volume_attributes);
+}
+
+void VolumeManager::update_svm_attributes(Device *device, DeviceScene *dscene, Scene *scene, vector<AttributeRequestSet>& mesh_attributes)
+{
+ /* compute array stride */
+ int attr_map_stride = 0;
+
+ for(size_t i = 0; i < volumes.size(); i++) {
+ attr_map_stride = max(attr_map_stride, (mesh_attributes[i].size() + 1));
+ }
+
+ if(attr_map_stride == 0) {
+ return;
+ }
+
+ /* create attribute map */
+ uint4 *attr_map = dscene->attributes_map.resize(attr_map_stride*volumes.size());
+ memset(attr_map, 0, dscene->attributes_map.size()*sizeof(uint));
+
+ for(size_t i = 0; i < volumes.size(); i++) {
+ AttributeRequestSet& attributes = mesh_attributes[i];
+
+ /* set object attributes */
+ int index = i*attr_map_stride;
+
+ foreach(AttributeRequest& req, attributes.requests) {
+ uint id = scene->shader_manager->get_attribute_id(req.name);
+
+ attr_map[index].x = id;
+ attr_map[index].y = req.triangle_desc.element;
+ attr_map[index].z = as_uint(req.triangle_desc.offset);
+
+ if(req.triangle_type == TypeDesc::TypeFloat)
+ attr_map[index].w = NODE_ATTR_FLOAT;
+ else
+ attr_map[index].w = NODE_ATTR_FLOAT3;
+
+ index++;
+ }
+
+ /* terminator */
+ attr_map[index].x = ATTR_STD_NONE;
+ attr_map[index].y = 0;
+ attr_map[index].z = 0;
+ attr_map[index].w = 0;
+
+ index++;
+ }
+
+ device->tex_alloc("__attributes_map", dscene->attributes_map);
+}
+
+void VolumeManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
+{
+ if(!need_update) {
+ return;
+ }
+
+ device_free(device, dscene);
+ progress.set_status("Updating OpenVDB volumes", "Sending volumes to device.");
+
+ uint *vol_shader = dscene->vol_shader.resize(num_float_volume + num_float3_volume);
+ int s = 0;
+
+ for (size_t i = 0; i < volumes.size(); ++i) {
+ Volume *volume = volumes[i];
+
+ for(size_t i = 0; i < volume->scalar_grids.size(); ++i) {
+ if(!volume->scalar_grids[i]) {
+ continue;
+ }
+
+ vol_shader[s++] = scene->shader_manager->get_shader_id(volume->used_shaders[0], false);
+ }
+
+ for(size_t i = 0; i < volume->vector_grids.size(); ++i) {
+ if(!volume->vector_grids[i]) {
+ continue;
+ }
+
+ vol_shader[s++] = scene->shader_manager->get_shader_id(volume->used_shaders[0], false);
+ }
+
+ if(progress.get_cancel()) {
+ return;
+ }
+ }
+
+ device->tex_alloc("__vol_shader", dscene->vol_shader);
+
+#ifdef WITH_OPENVDB
+ typedef typename OpenVDBGlobals::scalar_grid_t scalar_grid_t;
+ typedef typename OpenVDBGlobals::vector_grid_t vector_grid_t;
+ typedef typename OpenVDBGlobals::scalar_isector_t scalar_isector_t;
+ typedef typename OpenVDBGlobals::vector_isector_t vector_isector_t;
+ OpenVDBGlobals *vdb = device->vdb_memory();
+
+ vdb->scalar_grids.reserve(num_float_volume);
+ vdb->vector_grids.reserve(num_float3_volume);
+ vdb->scalar_main_isectors.reserve(num_float_volume);
+ vdb->vector_main_isectors.reserve(num_float3_volume);
+ for (size_t i = 0; i < volumes.size(); ++i) {
+ Volume *volume = volumes[i];
+
+ for (size_t k = 0; k < volume->scalar_grids.size(); ++k) {
+ scalar_grid_t *grid = volume->scalar_grids[k].get();
+ vdb->scalar_grids.push_back(grid);
+ vdb->scalar_main_isectors.push_back(new scalar_isector_t(*grid));
+ VLOG(1) << grid->getName().c_str() << " memory usage: " << grid->memUsage() / 1024.0f << " kilobytes.\n";
+ }
+ for (size_t k = 0; k < volume->vector_grids.size(); ++k) {
+ vector_grid_t *grid = volume->vector_grids[k].get();
+ vdb->vector_grids.push_back(grid);
+ vdb->vector_main_isectors.push_back(new vector_isector_t(*grid));
+ VLOG(1) << grid->getName().c_str() << " memory usage: " << grid->memUsage() / 1024.0f << " kilobytes.\n";
+ }
+ }
+#endif
+
+ if(progress.get_cancel()) {
+ return;
+ }
+
+ dscene->data.tables.num_volumes = num_float_volume/* + float3_volumes.size()*/;
+ dscene->data.tables.density_index = 0;
+
+ need_update = false;
+}
+
+void VolumeManager::device_free(Device *device, DeviceScene *dscene)
+{
+#ifdef WITH_OPENVDB
+ OpenVDBGlobals *vdb = device->vdb_memory();
+ for (size_t i = 0; i < vdb->scalar_main_isectors.size(); ++i) {
+ delete vdb->scalar_main_isectors[i];
+ }
+ vdb->scalar_grids.clear();
+ vdb->scalar_main_isectors.clear();
+
+ for (size_t i = 0; i < vdb->vector_main_isectors.size(); ++i) {
+ delete vdb->vector_main_isectors[i];
+ }
+ vdb->vector_grids.clear();
+ vdb->vector_main_isectors.clear();
+#endif
+
+ device->tex_free(dscene->vol_shader);
+ dscene->vol_shader.clear();
+}
+
+void VolumeManager::tag_update(Scene */*scene*/)
+{
+ need_update = true;
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/volume.h b/intern/cycles/render/volume.h
new file mode 100644
index 00000000000..3dcafab6995
--- /dev/null
+++ b/intern/cycles/render/volume.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 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 __VOLUMEMANAGER_H__
+#define __VOLUMEMANAGER_H__
+
+#include "attribute.h"
+
+#include "util/util_string.h"
+#include "util/util_types.h"
+
+#ifdef WITH_OPENVDB
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/Interpolation.h>
+#include <openvdb/tools/RayIntersector.h>
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+class Device;
+class DeviceScene;
+class Progress;
+class Scene;
+class Shader;
+
+class Volume {
+public:
+ vector<Shader*> used_shaders;
+ AttributeSet attributes;
+ string name;
+
+#ifdef WITH_OPENVDB
+ vector<openvdb::FloatGrid::Ptr> scalar_grids;
+ vector<openvdb::Vec3SGrid::Ptr> vector_grids;
+#endif
+
+ void tag_update(Scene *scene, bool rebuild);
+};
+
+class VolumeManager {
+ struct GridDescription {
+ Volume *volume;
+ string filename;
+ string name;
+ int sampling;
+ int slot;
+ };
+
+ vector<GridDescription> current_grids;
+ int num_float_volume;
+ int num_float3_volume;
+
+ void delete_volume(int grid_type, int sampling, size_t slot);
+
+ void add_grid_description(Volume *volume, const string& filename, const string& name, int slot);
+ int find_existing_slot(Volume *volume, const string& filename, const string& name);
+
+ bool is_openvdb_file(const string& filename) const;
+ size_t add_openvdb_volume(Volume *volume, const string& filename, const string& name);
+
+public:
+ VolumeManager();
+ ~VolumeManager();
+
+ int add_volume(Volume *volume, const string& filename, const string& name);
+ int find_density_slot();
+
+ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
+ void device_update_attributes(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
+ void update_svm_attributes(Device *device, DeviceScene *dscene, Scene *scene, vector<AttributeRequestSet>& mesh_attributes);
+ void device_free(Device *device, DeviceScene *dscene);
+
+ void tag_update(Scene *scene);
+
+ bool need_update;
+
+ vector<Volume*> volumes;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __VOLUMEMANAGER_H__ */
diff --git a/intern/openvdb/openvdb_capi.cc b/intern/openvdb/openvdb_capi.cc
index ef4f8c8820f..e7d8b83e6eb 100644
--- a/intern/openvdb/openvdb_capi.cc
+++ b/intern/openvdb/openvdb_capi.cc
@@ -36,6 +36,33 @@ int OpenVDB_getVersionHex()
return openvdb::OPENVDB_LIBRARY_VERSION;
}
+void OpenVDB_get_grid_info(const char *filename, OpenVDBGridInfoCallback cb, void *userdata)
+{
+ Timer(__func__);
+
+ using namespace openvdb;
+
+ initialize();
+
+ io::File file(filename);
+ file.open();
+
+ GridPtrVecPtr grids = file.getGrids();
+ int grid_num = grids->size();
+
+ for (size_t i = 0; i < grid_num; ++i) {
+ GridBase::ConstPtr grid = (*grids)[i];
+
+ Name name = grid->getName();
+ Name value_type = grid->valueType();
+ bool is_color = false;
+ if (grid->getMetadata< TypedMetadata<bool> >("is_color"))
+ is_color = grid->metaValue<bool>("is_color");
+
+ cb(userdata, name.c_str(), value_type.c_str(), is_color);
+ }
+}
+
OpenVDBFloatGrid *OpenVDB_export_grid_fl(
OpenVDBWriter *writer,
const char *name, float *data,
diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h
index 2d2feeadcf1..a42521aef4d 100644
--- a/intern/openvdb/openvdb_capi.h
+++ b/intern/openvdb/openvdb_capi.h
@@ -38,6 +38,9 @@ struct OpenVDBVectorGrid;
int OpenVDB_getVersionHex(void);
+typedef void (*OpenVDBGridInfoCallback)(void *userdata, const char *name, const char *value_type, bool is_color);
+void OpenVDB_get_grid_info(const char *filename, OpenVDBGridInfoCallback cb, void *userdata);
+
enum {
VEC_INVARIANT = 0,
VEC_COVARIANT = 1,