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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Kottler <dev@samkottler.net>2020-08-13 17:58:33 +0300
committerSam Kottler <dev@samkottler.net>2020-08-13 17:58:33 +0300
commit7c38d008de5ebdcb96ebdd67b8dc459d5551702f (patch)
tree8fb22f60bc31019e3fe8f5b2bf35159c2e32cda9 /intern/cycles
parentcc86e03fd52cd68e66b817590aac80ec2a358d3e (diff)
parent4e103101f7a20eaa2a61306a1070a04b63a69958 (diff)
Merge branch 'blender-v2.90-release' into soc-2020-production-ready-light-tree-2
Diffstat (limited to 'intern/cycles')
-rw-r--r--intern/cycles/blender/blender_camera.cpp13
-rw-r--r--intern/cycles/blender/blender_session.cpp29
-rw-r--r--intern/cycles/blender/blender_shader.cpp4
-rw-r--r--intern/cycles/blender/blender_sync.cpp10
-rw-r--r--intern/cycles/blender/blender_util.h6
-rw-r--r--intern/cycles/blender/blender_volume.cpp75
-rw-r--r--intern/cycles/kernel/CMakeLists.txt4
-rw-r--r--intern/cycles/kernel/kernel_path.h2
-rw-r--r--intern/cycles/render/geometry.cpp10
-rw-r--r--intern/cycles/render/image.cpp37
-rw-r--r--intern/cycles/render/image.h7
-rw-r--r--intern/cycles/render/image_vdb.cpp12
-rw-r--r--intern/cycles/render/image_vdb.h6
-rw-r--r--intern/cycles/render/mesh_volume.cpp514
-rw-r--r--intern/cycles/render/session.cpp19
-rw-r--r--intern/cycles/test/CMakeLists.txt1
-rw-r--r--intern/cycles/test/util_transform_test.cpp53
-rw-r--r--intern/cycles/util/CMakeLists.txt1
-rw-r--r--intern/cycles/util/util_math_fast.h20
-rw-r--r--intern/cycles/util/util_math_float4.h18
-rw-r--r--intern/cycles/util/util_openvdb.h32
-rw-r--r--intern/cycles/util/util_transform.cpp28
-rw-r--r--intern/cycles/util/util_transform.h11
23 files changed, 564 insertions, 348 deletions
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp
index 592a69585de..e1ab3b3fbc1 100644
--- a/intern/cycles/blender/blender_camera.cpp
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -76,6 +76,9 @@ struct BlenderCamera {
int full_width;
int full_height;
+ int render_width;
+ int render_height;
+
BoundBox2D border;
BoundBox2D pano_viewplane;
BoundBox2D viewport_camera_border;
@@ -126,8 +129,10 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende
bcam->matrix = transform_identity();
/* render resolution */
- bcam->full_width = render_resolution_x(b_render);
- bcam->full_height = render_resolution_y(b_render);
+ bcam->render_width = render_resolution_x(b_render);
+ bcam->render_height = render_resolution_y(b_render);
+ bcam->full_width = bcam->render_width;
+ bcam->full_height = bcam->render_height;
}
static float blender_camera_focal_distance(BL::RenderEngine &b_engine,
@@ -398,8 +403,8 @@ static void blender_camera_sync(Camera *cam,
/* panorama sensor */
if (bcam->type == CAMERA_PANORAMA && bcam->panorama_type == PANORAMA_FISHEYE_EQUISOLID) {
- float fit_xratio = (float)bcam->full_width * bcam->pixelaspect.x;
- float fit_yratio = (float)bcam->full_height * bcam->pixelaspect.y;
+ float fit_xratio = (float)bcam->render_width * bcam->pixelaspect.x;
+ float fit_yratio = (float)bcam->render_height * bcam->pixelaspect.y;
bool horizontal_fit;
float sensor_size;
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 391a1b8f473..a06030c8b7d 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -59,6 +59,7 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
BL::BlendData &b_data,
bool preview_osl)
: session(NULL),
+ scene(NULL),
sync(NULL),
b_engine(b_engine),
b_userpref(b_userpref),
@@ -88,6 +89,7 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine,
int width,
int height)
: session(NULL),
+ scene(NULL),
sync(NULL),
b_engine(b_engine),
b_userpref(b_userpref),
@@ -492,27 +494,15 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_)
/* Update denoising parameters. */
session->set_denoising(session_params.denoising);
- bool use_denoising = session_params.denoising.use;
- bool store_denoising_passes = session_params.denoising.store_passes;
-
- buffer_params.denoising_data_pass = use_denoising || store_denoising_passes;
- buffer_params.denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
- buffer_params.denoising_prefiltered_pass = store_denoising_passes &&
- session_params.denoising.type == DENOISER_NLM;
-
- scene->film->denoising_data_pass = buffer_params.denoising_data_pass;
- scene->film->denoising_clean_pass = buffer_params.denoising_clean_pass;
- scene->film->denoising_prefiltered_pass = buffer_params.denoising_prefiltered_pass;
-
- /* Add passes */
+ /* Compute render passes and film settings. */
vector<Pass> passes = sync->sync_render_passes(
b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising);
- buffer_params.passes = passes;
- scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
- scene->film->tag_passes_update(scene, passes);
- scene->film->tag_update(scene);
- scene->integrator->tag_update(scene);
+ /* Set buffer params, using film settings from sync_render_passes. */
+ buffer_params.passes = passes;
+ buffer_params.denoising_data_pass = scene->film->denoising_data_pass;
+ buffer_params.denoising_clean_pass = scene->film->denoising_clean_pass;
+ buffer_params.denoising_prefiltered_pass = scene->film->denoising_prefiltered_pass;
BL::RenderResult::views_iterator b_view_iter;
@@ -982,7 +972,8 @@ void BlenderSession::update_status_progress()
remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
if (background) {
- scene_status += " | " + scene->name;
+ if (scene)
+ scene_status += " | " + scene->name;
if (b_rlay_name != "")
scene_status += ", " + b_rlay_name;
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 33e73b5a4b9..ae681432a43 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -678,7 +678,7 @@ static ShaderNode *add_node(Scene *scene,
* builtin names for packed images and movies
*/
int scene_frame = b_scene.frame_current();
- int image_frame = image_user_frame_number(b_image_user, scene_frame);
+ int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame);
image->handle = scene->image_manager->add_image(
new BlenderImageLoader(b_image, image_frame), image->image_params());
}
@@ -713,7 +713,7 @@ static ShaderNode *add_node(Scene *scene,
if (is_builtin) {
int scene_frame = b_scene.frame_current();
- int image_frame = image_user_frame_number(b_image_user, scene_frame);
+ int image_frame = image_user_frame_number(b_image_user, b_image, scene_frame);
env->handle = scene->image_manager->add_image(new BlenderImageLoader(b_image, image_frame),
env->image_params());
}
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index ee90b4dfbfe..f806569c389 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -709,6 +709,16 @@ vector<Pass> BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay,
}
RNA_END;
+ scene->film->denoising_data_pass = denoising.use || denoising.store_passes;
+ scene->film->denoising_clean_pass = (scene->film->denoising_flags & DENOISING_CLEAN_ALL_PASSES);
+ scene->film->denoising_prefiltered_pass = denoising.store_passes &&
+ denoising.type == DENOISER_NLM;
+
+ scene->film->pass_alpha_threshold = b_view_layer.pass_alpha_threshold();
+ scene->film->tag_passes_update(scene, passes);
+ scene->film->tag_update(scene);
+ scene->integrator->tag_update(scene);
+
return passes;
}
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index ad90a5f8d52..1ea34b41aa2 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -238,7 +238,7 @@ static inline string image_user_file_path(BL::ImageUser &iuser,
{
char filepath[1024];
iuser.tile(0);
- BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra);
+ BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra);
BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath);
string filepath_str = string(filepath);
@@ -248,9 +248,9 @@ static inline string image_user_file_path(BL::ImageUser &iuser,
return filepath_str;
}
-static inline int image_user_frame_number(BL::ImageUser &iuser, int cfra)
+static inline int image_user_frame_number(BL::ImageUser &iuser, BL::Image &ima, int cfra)
{
- BKE_image_user_frame_calc(NULL, iuser.ptr.data, cfra);
+ BKE_image_user_frame_calc(ima.ptr.data, iuser.ptr.data, cfra);
return iuser.frame_current();
}
diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/blender_volume.cpp
index 80591e0eec8..d0e1e4d6131 100644
--- a/intern/cycles/blender/blender_volume.cpp
+++ b/intern/cycles/blender/blender_volume.cpp
@@ -217,43 +217,29 @@ static void sync_smoke_volume(Scene *scene, BL::Object &b_ob, Mesh *mesh, float
class BlenderVolumeLoader : public VDBImageLoader {
public:
BlenderVolumeLoader(BL::BlendData &b_data, BL::Volume &b_volume, const string &grid_name)
- : VDBImageLoader(grid_name), b_data(b_data), b_volume(b_volume), unload(false)
- {
- }
-
- bool load_metadata(ImageMetaData &metadata) override
+ : VDBImageLoader(grid_name), b_volume(b_volume)
{
b_volume.grids.load(b_data.ptr.data);
- BL::VolumeGrid b_volume_grid = find_grid();
-
- if (!b_volume_grid) {
- return false;
- }
-
- unload = !b_volume_grid.is_loaded();
#ifdef WITH_OPENVDB
- Volume *volume = (Volume *)b_volume.ptr.data;
- VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
- grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
-#endif
+ BL::Volume::grids_iterator b_grid_iter;
+ for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
+ BL::VolumeGrid b_volume_grid(*b_grid_iter);
+ if (b_volume_grid.name() == grid_name) {
+ const bool unload = !b_volume_grid.is_loaded();
- return VDBImageLoader::load_metadata(metadata);
- }
+ Volume *volume = (Volume *)b_volume.ptr.data;
+ VolumeGrid *volume_grid = (VolumeGrid *)b_volume_grid.ptr.data;
+ grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid);
- bool load_pixels(const ImageMetaData &metadata,
- void *pixels,
- const size_t pixel_size,
- const bool associate_alpha) override
- {
- b_volume.grids.load(b_data.ptr.data);
- BL::VolumeGrid b_volume_grid = find_grid();
+ if (unload) {
+ b_volume_grid.unload();
+ }
- if (!b_volume_grid) {
- return false;
+ break;
+ }
}
-
- return VDBImageLoader::load_pixels(metadata, pixels, pixel_size, associate_alpha);
+#endif
}
bool equals(const ImageLoader &other) const override
@@ -263,36 +249,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
return b_volume == other_loader.b_volume && grid_name == other_loader.grid_name;
}
- void cleanup() override
- {
- VDBImageLoader::cleanup();
-
- BL::VolumeGrid b_volume_grid = find_grid();
- if (b_volume_grid && unload) {
- b_volume_grid.unload();
- }
- }
-
- /* Find grid with matching name. Grid point not stored in the class since
- * grids may be unloaded before we load the pixels, for example for motion
- * blur where we move between frames. */
- BL::VolumeGrid find_grid()
- {
-#ifdef WITH_OPENVDB
- BL::Volume::grids_iterator b_grid_iter;
- for (b_volume.grids.begin(b_grid_iter); b_grid_iter != b_volume.grids.end(); ++b_grid_iter) {
- if (b_grid_iter->name() == grid_name) {
- return *b_grid_iter;
- }
- }
-#endif
-
- return BL::VolumeGrid(PointerRNA_NULL);
- }
-
- BL::BlendData b_data;
BL::Volume b_volume;
- bool unload;
};
static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *scene, Mesh *mesh)
@@ -342,7 +299,7 @@ static void sync_volume_object(BL::BlendData &b_data, BL::Object &b_ob, Scene *s
ImageParams params;
params.frame = b_volume.grids.frame();
- attr->data_voxel() = scene->image_manager->add_image(loader, params);
+ attr->data_voxel() = scene->image_manager->add_image(loader, params, false);
}
}
}
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index 5533eeb006d..db146226dc7 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -539,7 +539,7 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
${SRC_UTIL_HEADERS}
COMMAND ${CUBIN_CC_ENV}
"$<TARGET_FILE:cycles_cubin_cc>"
- -target 52
+ -target 50
-ptx
-i ${CMAKE_CURRENT_SOURCE_DIR}/${input}
${cuda_flags}
@@ -563,7 +563,7 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES)
COMMAND
${CUDA_NVCC_EXECUTABLE}
--ptx
- -arch=sm_52
+ -arch=sm_50
${cuda_flags}
${input}
WORKING_DIRECTORY
diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h
index 15cf2e02fdd..2ba3b1c4fdc 100644
--- a/intern/cycles/kernel/kernel_path.h
+++ b/intern/cycles/kernel/kernel_path.h
@@ -695,11 +695,9 @@ ccl_device void kernel_path_trace(
kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray);
-# ifndef __KERNEL_OPTIX__
if (ray.t == 0.0f) {
return;
}
-# endif
/* Initialize state. */
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp
index 3d1b6e1d865..145b1fa492c 100644
--- a/intern/cycles/render/geometry.cpp
+++ b/intern/cycles/render/geometry.cpp
@@ -1201,9 +1201,13 @@ void GeometryManager::device_update_volume_images(Device *device, Scene *scene,
}
ImageHandle &handle = attr.data_voxel();
- const int slot = handle.svm_slot();
- if (slot != -1) {
- volume_images.insert(slot);
+ /* We can build directly from OpenVDB data structures, no need to
+ * load such images early. */
+ if (!handle.vdb_loader()) {
+ const int slot = handle.svm_slot();
+ if (slot != -1) {
+ volume_images.insert(slot);
+ }
}
}
}
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 8d187814d64..a5dfcf60d61 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -18,6 +18,7 @@
#include "device/device.h"
#include "render/colorspace.h"
#include "render/image_oiio.h"
+#include "render/image_vdb.h"
#include "render/scene.h"
#include "render/stats.h"
@@ -172,6 +173,31 @@ device_texture *ImageHandle::image_memory(const int tile_index) const
return img ? img->mem : NULL;
}
+VDBImageLoader *ImageHandle::vdb_loader(const int tile_index) const
+{
+ if (tile_index >= tile_slots.size()) {
+ return NULL;
+ }
+
+ ImageManager::Image *img = manager->images[tile_slots[tile_index]];
+
+ if (img == NULL) {
+ return NULL;
+ }
+
+ ImageLoader *loader = img->loader;
+
+ if (loader == NULL) {
+ return NULL;
+ }
+
+ if (loader->is_vdb_loader()) {
+ return dynamic_cast<VDBImageLoader *>(loader);
+ }
+
+ return NULL;
+}
+
bool ImageHandle::operator==(const ImageHandle &other) const
{
return manager == other.manager && tile_slots == other.tile_slots;
@@ -258,6 +284,11 @@ bool ImageLoader::equals(const ImageLoader *a, const ImageLoader *b)
}
}
+bool ImageLoader::is_vdb_loader() const
+{
+ return false;
+}
+
/* Image Manager */
ImageManager::ImageManager(const DeviceInfo &info)
@@ -362,9 +393,11 @@ ImageHandle ImageManager::add_image(const string &filename,
return handle;
}
-ImageHandle ImageManager::add_image(ImageLoader *loader, const ImageParams &params)
+ImageHandle ImageManager::add_image(ImageLoader *loader,
+ const ImageParams &params,
+ const bool builtin)
{
- const int slot = add_image_slot(loader, params, true);
+ const int slot = add_image_slot(loader, params, builtin);
ImageHandle handle;
handle.tile_slots.push_back(slot);
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index fffe7c5152a..cb059256ce3 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -39,6 +39,7 @@ class Progress;
class RenderStats;
class Scene;
class ColorSpaceProcessor;
+class VDBImageLoader;
/* Image Parameters */
class ImageParams {
@@ -124,6 +125,8 @@ class ImageLoader {
virtual bool equals(const ImageLoader &other) const = 0;
static bool equals(const ImageLoader *a, const ImageLoader *b);
+ virtual bool is_vdb_loader() const;
+
/* Work around for no RTTI. */
};
@@ -149,6 +152,8 @@ class ImageHandle {
int svm_slot(const int tile_index = 0) const;
device_texture *image_memory(const int tile_index = 0) const;
+ VDBImageLoader *vdb_loader(const int tile_index = 0) const;
+
protected:
vector<int> tile_slots;
ImageManager *manager;
@@ -169,7 +174,7 @@ class ImageManager {
ImageHandle add_image(const string &filename,
const ImageParams &params,
const vector<int> &tiles);
- ImageHandle add_image(ImageLoader *loader, const ImageParams &params);
+ ImageHandle add_image(ImageLoader *loader, const ImageParams &params, const bool builtin = true);
void device_update(Device *device, Scene *scene, Progress &progress);
void device_update_slot(Device *device, Scene *scene, int slot, Progress *progress);
diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/render/image_vdb.cpp
index 500131c2d84..3f7dd45ee88 100644
--- a/intern/cycles/render/image_vdb.cpp
+++ b/intern/cycles/render/image_vdb.cpp
@@ -185,4 +185,16 @@ void VDBImageLoader::cleanup()
#endif
}
+bool VDBImageLoader::is_vdb_loader() const
+{
+ return true;
+}
+
+#ifdef WITH_OPENVDB
+openvdb::GridBase::ConstPtr VDBImageLoader::get_grid()
+{
+ return grid;
+}
+#endif
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/render/image_vdb.h
index 7dec63b11e6..4500cfbfb88 100644
--- a/intern/cycles/render/image_vdb.h
+++ b/intern/cycles/render/image_vdb.h
@@ -43,6 +43,12 @@ class VDBImageLoader : public ImageLoader {
virtual void cleanup() override;
+ virtual bool is_vdb_loader() const override;
+
+#ifdef WITH_OPENVDB
+ openvdb::GridBase::ConstPtr get_grid();
+#endif
+
protected:
string grid_name;
#ifdef WITH_OPENVDB
diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp
index 607363d01c6..70189ea4812 100644
--- a/intern/cycles/render/mesh_volume.cpp
+++ b/intern/cycles/render/mesh_volume.cpp
@@ -15,34 +15,25 @@
*/
#include "render/attribute.h"
+#include "render/image_vdb.h"
#include "render/mesh.h"
#include "render/scene.h"
+#ifdef WITH_OPENVDB
+# include <openvdb/tools/Dense.h>
+# include <openvdb/tools/GridTransformer.h>
+# include <openvdb/tools/Morphology.h>
+#endif
+
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_logging.h"
+#include "util/util_openvdb.h"
#include "util/util_progress.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
-const int64_t VOXEL_INDEX_NONE = -1;
-
-static int64_t compute_voxel_index(const int3 &resolution, int64_t x, int64_t y, int64_t z)
-{
- if (x < 0 || x >= resolution.x) {
- return VOXEL_INDEX_NONE;
- }
- else if (y < 0 || y >= resolution.y) {
- return VOXEL_INDEX_NONE;
- }
- else if (z < 0 || z >= resolution.z) {
- return VOXEL_INDEX_NONE;
- }
-
- return x + y * resolution.x + z * resolution.x * resolution.y;
-}
-
struct QuadData {
int v0, v1, v2, v3;
@@ -123,122 +114,146 @@ static void create_quad(int3 corners[8],
quads.push_back(quad);
}
-struct VolumeParams {
- int3 resolution;
- float3 cell_size;
- float3 start_point;
- int pad_size;
-};
-
-static const int CUBE_SIZE = 8;
-
/* Create a mesh from a volume.
*
* The way the algorithm works is as follows:
*
- * - The coordinates of active voxels from a dense volume (or 3d image) are
- * gathered inside an auxiliary volume.
- * - Each set of coordinates of an CUBE_SIZE cube are mapped to the same
- * coordinate of the auxiliary volume.
- * - Quads are created between active and non-active voxels in the auxiliary
- * volume to generate a tight mesh around the volume.
+ * - The topologies of input OpenVDB grids are merged into a temporary grid.
+ * - Voxels of the temporary grid are dilated to account for the padding necessary for volume
+ * sampling.
+ * - Quads are created on the boundary between active and inactive leaf nodes of the temporary
+ * grid.
*/
class VolumeMeshBuilder {
- /* Auxiliary volume that is used to check if a node already added. */
- vector<char> grid;
-
- /* The resolution of the auxiliary volume, set to be equal to 1/CUBE_SIZE
- * of the original volume on each axis. */
- int3 res;
-
- size_t number_of_nodes;
-
- /* Offset due to padding in the original grid. Padding will transform the
- * coordinates of the original grid from 0...res to -padding...res+padding,
- * so some coordinates are negative, and we need to properly account for
- * them. */
- int3 pad_offset;
-
- VolumeParams *params;
-
public:
- VolumeMeshBuilder(VolumeParams *volume_params);
+#ifdef WITH_OPENVDB
+ /* use a MaskGrid to store the topology to save memory */
+ openvdb::MaskGrid::Ptr topology_grid;
+ openvdb::CoordBBox bbox;
+#endif
+ bool first_grid;
- void add_node(int x, int y, int z);
+ VolumeMeshBuilder();
- void add_node_with_padding(int x, int y, int z);
+#ifdef WITH_OPENVDB
+ void add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping);
+#endif
- void create_mesh(vector<float3> &vertices, vector<int> &indices, vector<float3> &face_normals);
+ void add_padding(int pad_size);
+
+ void create_mesh(vector<float3> &vertices,
+ vector<int> &indices,
+ vector<float3> &face_normals,
+ const float face_overlap_avoidance);
- private:
void generate_vertices_and_quads(vector<int3> &vertices_is, vector<QuadData> &quads);
- void convert_object_space(const vector<int3> &vertices, vector<float3> &out_vertices);
+ void convert_object_space(const vector<int3> &vertices,
+ vector<float3> &out_vertices,
+ const float face_overlap_avoidance);
void convert_quads_to_tris(const vector<QuadData> &quads,
vector<int> &tris,
vector<float3> &face_normals);
-};
-VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params)
-{
- params = volume_params;
- number_of_nodes = 0;
+ bool empty_grid() const;
- const int64_t x = divide_up(params->resolution.x, CUBE_SIZE);
- const int64_t y = divide_up(params->resolution.y, CUBE_SIZE);
- const int64_t z = divide_up(params->resolution.z, CUBE_SIZE);
+#ifdef WITH_OPENVDB
+ template <typename GridType>
+ void merge_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping)
+ {
+ typename GridType::ConstPtr typed_grid = openvdb::gridConstPtrCast<GridType>(grid);
- /* Adding 2*pad_size since we pad in both positive and negative directions
- * along the axis. */
- const int64_t px = divide_up(params->resolution.x + 2 * params->pad_size, CUBE_SIZE);
- const int64_t py = divide_up(params->resolution.y + 2 * params->pad_size, CUBE_SIZE);
- const int64_t pz = divide_up(params->resolution.z + 2 * params->pad_size, CUBE_SIZE);
+ if (do_clipping) {
+ using ValueType = typename GridType::ValueType;
+ typename GridType::Ptr copy = typed_grid->deepCopy();
+ typename GridType::ValueOnIter iter = copy->beginValueOn();
- res = make_int3(px, py, pz);
- pad_offset = make_int3(px - x, py - y, pz - z);
+ for (; iter; ++iter) {
+ if (iter.getValue() < ValueType(volume_clipping)) {
+ iter.setValueOff();
+ }
+ }
- grid.resize(px * py * pz, 0);
-}
+ typed_grid = copy;
+ }
-void VolumeMeshBuilder::add_node(int x, int y, int z)
-{
- /* Map coordinates to index space. */
- const int index_x = (x / CUBE_SIZE) + pad_offset.x;
- const int index_y = (y / CUBE_SIZE) + pad_offset.y;
- const int index_z = (z / CUBE_SIZE) + pad_offset.z;
+ topology_grid->topologyUnion(*typed_grid);
+ }
+#endif
+};
- assert((index_x >= 0) && (index_y >= 0) && (index_z >= 0));
+VolumeMeshBuilder::VolumeMeshBuilder()
+{
+ first_grid = true;
+}
- const int64_t index = compute_voxel_index(res, index_x, index_y, index_z);
- if (index == VOXEL_INDEX_NONE) {
- return;
+#ifdef WITH_OPENVDB
+void VolumeMeshBuilder::add_grid(openvdb::GridBase::ConstPtr grid, bool do_clipping, float volume_clipping)
+{
+ /* set the transform of our grid from the first one */
+ if (first_grid) {
+ topology_grid = openvdb::MaskGrid::create();
+ topology_grid->setTransform(grid->transform().copy());
+ first_grid = false;
}
-
- /* We already have a node here. */
- if (grid[index] == 1) {
- return;
+ /* if the transforms do not match, we need to resample one of the grids so that
+ * its index space registers with that of the other, here we resample our mask
+ * grid so memory usage is kept low */
+ else if (topology_grid->transform() != grid->transform()) {
+ openvdb::MaskGrid::Ptr temp_grid = topology_grid->copyWithNewTree();
+ temp_grid->setTransform(grid->transform().copy());
+ openvdb::tools::resampleToMatch<openvdb::tools::BoxSampler>(*topology_grid, *temp_grid);
+ topology_grid = temp_grid;
+ topology_grid->setTransform(grid->transform().copy());
}
- ++number_of_nodes;
-
- grid[index] = 1;
+ if (grid->isType<openvdb::FloatGrid>()) {
+ merge_grid<openvdb::FloatGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::Vec3fGrid>()) {
+ merge_grid<openvdb::Vec3fGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::Vec4fGrid>()) {
+ merge_grid<openvdb::Vec4fGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::BoolGrid>()) {
+ merge_grid<openvdb::BoolGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::DoubleGrid>()) {
+ merge_grid<openvdb::DoubleGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::Int32Grid>()) {
+ merge_grid<openvdb::Int32Grid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::Int64Grid>()) {
+ merge_grid<openvdb::Int64Grid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::Vec3IGrid>()) {
+ merge_grid<openvdb::Vec3IGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::Vec3dGrid>()) {
+ merge_grid<openvdb::Vec3dGrid>(grid, do_clipping, volume_clipping);
+ }
+ else if (grid->isType<openvdb::MaskGrid>()) {
+ topology_grid->topologyUnion(*openvdb::gridConstPtrCast<openvdb::MaskGrid>(grid));
+ }
}
+#endif
-void VolumeMeshBuilder::add_node_with_padding(int x, int y, int z)
+void VolumeMeshBuilder::add_padding(int pad_size)
{
- for (int px = x - params->pad_size; px < x + params->pad_size; ++px) {
- for (int py = y - params->pad_size; py < y + params->pad_size; ++py) {
- for (int pz = z - params->pad_size; pz < z + params->pad_size; ++pz) {
- add_node(px, py, pz);
- }
- }
- }
+#ifdef WITH_OPENVDB
+ openvdb::tools::dilateVoxels(topology_grid->tree(), pad_size);
+#else
+ (void)pad_size;
+#endif
}
void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
vector<int> &indices,
- vector<float3> &face_normals)
+ vector<float3> &face_normals,
+ const float face_overlap_avoidance)
{
/* We create vertices in index space (is), and only convert them to object
* space when done. */
@@ -247,7 +262,7 @@ void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
generate_vertices_and_quads(vertices_is, quads);
- convert_object_space(vertices_is, vertices);
+ convert_object_space(vertices_is, vertices, face_overlap_avoidance);
convert_quads_to_tris(quads, indices, face_normals);
}
@@ -255,85 +270,97 @@ void VolumeMeshBuilder::create_mesh(vector<float3> &vertices,
void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_is,
vector<QuadData> &quads)
{
- unordered_map<size_t, int> used_verts;
+#ifdef WITH_OPENVDB
+ const openvdb::MaskGrid::TreeType &tree = topology_grid->tree();
+ tree.evalLeafBoundingBox(bbox);
- for (int z = 0; z < res.z; ++z) {
- for (int y = 0; y < res.y; ++y) {
- for (int x = 0; x < res.x; ++x) {
- int64_t voxel_index = compute_voxel_index(res, x, y, z);
- if (grid[voxel_index] == 0) {
- continue;
- }
+ const int3 resolution = make_int3(bbox.dim().x(), bbox.dim().y(), bbox.dim().z());
- /* Compute min and max coords of the node in index space. */
- int3 min = make_int3((x - pad_offset.x) * CUBE_SIZE,
- (y - pad_offset.y) * CUBE_SIZE,
- (z - pad_offset.z) * CUBE_SIZE);
-
- /* Maximum is just CUBE_SIZE voxels away from minimum on each axis. */
- int3 max = make_int3(min.x + CUBE_SIZE, min.y + CUBE_SIZE, min.z + CUBE_SIZE);
-
- int3 corners[8] = {
- make_int3(min[0], min[1], min[2]),
- make_int3(max[0], min[1], min[2]),
- make_int3(max[0], max[1], min[2]),
- make_int3(min[0], max[1], min[2]),
- make_int3(min[0], min[1], max[2]),
- make_int3(max[0], min[1], max[2]),
- make_int3(max[0], max[1], max[2]),
- make_int3(min[0], max[1], max[2]),
- };
-
- /* Only create a quad if on the border between an active and
- * an inactive node.
- */
-
- voxel_index = compute_voxel_index(res, x - 1, y, z);
- if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
- create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MIN);
- }
+ unordered_map<size_t, int> used_verts;
- voxel_index = compute_voxel_index(res, x + 1, y, z);
- if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
- create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MAX);
- }
+ for (auto iter = tree.cbeginLeaf(); iter; ++iter) {
+ openvdb::CoordBBox leaf_bbox = iter->getNodeBoundingBox();
+ /* +1 to convert from exclusive to include bounds. */
+ leaf_bbox.max() = leaf_bbox.max().offsetBy(1);
+
+ int3 min = make_int3(leaf_bbox.min().x(), leaf_bbox.min().y(), leaf_bbox.min().z());
+ int3 max = make_int3(leaf_bbox.max().x(), leaf_bbox.max().y(), leaf_bbox.max().z());
+
+ int3 corners[8] = {
+ make_int3(min[0], min[1], min[2]),
+ make_int3(max[0], min[1], min[2]),
+ make_int3(max[0], max[1], min[2]),
+ make_int3(min[0], max[1], min[2]),
+ make_int3(min[0], min[1], max[2]),
+ make_int3(max[0], min[1], max[2]),
+ make_int3(max[0], max[1], max[2]),
+ make_int3(min[0], max[1], max[2]),
+ };
+
+ /* Only create a quad if on the border between an active and an inactive leaf.
+ *
+ * We verify that a leaf exists by probing a coordinate that is at its center,
+ * to do so we compute the center of the current leaf and offset this coordinate
+ * by the size of a leaf in each direction.
+ */
+ static const int LEAF_DIM = openvdb::MaskGrid::TreeType::LeafNodeType::DIM;
+ auto center = leaf_bbox.min() + openvdb::Coord(LEAF_DIM / 2);
+
+ if (!tree.probeLeaf(openvdb::Coord(center.x() - LEAF_DIM, center.y(), center.z()))) {
+ create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MIN);
+ }
- voxel_index = compute_voxel_index(res, x, y - 1, z);
- if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
- create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MIN);
- }
+ if (!tree.probeLeaf(openvdb::Coord(center.x() + LEAF_DIM, center.y(), center.z()))) {
+ create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_X_MAX);
+ }
- voxel_index = compute_voxel_index(res, x, y + 1, z);
- if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
- create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MAX);
- }
+ if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() - LEAF_DIM, center.z()))) {
+ create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MIN);
+ }
- voxel_index = compute_voxel_index(res, x, y, z - 1);
- if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
- create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MIN);
- }
+ if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y() + LEAF_DIM, center.z()))) {
+ create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Y_MAX);
+ }
- voxel_index = compute_voxel_index(res, x, y, z + 1);
- if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
- create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MAX);
- }
- }
+ if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() - LEAF_DIM))) {
+ create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MIN);
+ }
+
+ if (!tree.probeLeaf(openvdb::Coord(center.x(), center.y(), center.z() + LEAF_DIM))) {
+ create_quad(corners, vertices_is, quads, resolution, used_verts, QUAD_Z_MAX);
}
}
+#else
+ (void)vertices_is;
+ (void)quads;
+#endif
}
void VolumeMeshBuilder::convert_object_space(const vector<int3> &vertices,
- vector<float3> &out_vertices)
+ vector<float3> &out_vertices,
+ const float face_overlap_avoidance)
{
+#ifdef WITH_OPENVDB
+ /* compute the offset for the face overlap avoidance */
+ bbox = topology_grid->evalActiveVoxelBoundingBox();
+ openvdb::Coord dim = bbox.dim();
+
+ float3 cell_size = make_float3(1.0f / dim.x(), 1.0f / dim.y(), 1.0f / dim.z());
+ float3 point_offset = cell_size * face_overlap_avoidance;
+
out_vertices.reserve(vertices.size());
for (size_t i = 0; i < vertices.size(); ++i) {
- float3 vertex = make_float3(vertices[i].x, vertices[i].y, vertices[i].z);
- vertex *= params->cell_size;
- vertex += params->start_point;
-
- out_vertices.push_back(vertex);
+ openvdb::math::Vec3d p = topology_grid->indexToWorld(
+ openvdb::math::Vec3d(vertices[i].x, vertices[i].y, vertices[i].z));
+ float3 vertex = make_float3((float)p.x(), (float)p.y(), (float)p.z());
+ out_vertices.push_back(vertex + point_offset);
}
+#else
+ (void)vertices;
+ (void)out_vertices;
+ (void)face_overlap_avoidance;
+#endif
}
void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads,
@@ -359,57 +386,115 @@ void VolumeMeshBuilder::convert_quads_to_tris(const vector<QuadData> &quads,
}
}
-/* ************************************************************************** */
+bool VolumeMeshBuilder::empty_grid() const
+{
+#ifdef WITH_OPENVDB
+ return !topology_grid || topology_grid->tree().leafCount() == 0;
+#else
+ return true;
+#endif
+}
-struct VoxelAttributeGrid {
- float *data;
- int channels;
-};
+#ifdef WITH_OPENVDB
+template<typename GridType>
+static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_texture *image_memory,
+ float volume_clipping,
+ Transform transform_3d)
+{
+ using ValueType = typename GridType::ValueType;
+
+ openvdb::CoordBBox dense_bbox(0,
+ 0,
+ 0,
+ image_memory->data_width - 1,
+ image_memory->data_height - 1,
+ image_memory->data_depth - 1);
+ openvdb::tools::Dense<ValueType, openvdb::tools::MemoryLayout::LayoutXYZ> dense(
+ dense_bbox, static_cast<ValueType *>(image_memory->host_pointer));
+
+ typename GridType::Ptr sparse = GridType::create(ValueType(0.0f));
+ openvdb::tools::copyFromDense(dense, *sparse, ValueType(volume_clipping));
+
+ /* copyFromDense will remove any leaf node that contains constant data and replace it with a tile,
+ * however, we need to preserve the leaves in order to generate the mesh, so revoxelize the leaves
+ * that were pruned. This should not affect areas that were skipped due to the volume_clipping parameter. */
+ sparse->tree().voxelizeActiveTiles();
+
+ /* Compute index to world matrix. */
+ float3 voxel_size = make_float3(1.0f / image_memory->data_width, 1.0f / image_memory->data_height, 1.0f / image_memory->data_depth);
+
+ transform_3d = transform_inverse(transform_3d);
+
+ openvdb::Mat4R index_to_world_mat((double)(voxel_size.x * transform_3d[0][0]), 0.0, 0.0, 0.0,
+ 0.0, (double)(voxel_size.y * transform_3d[1][1]), 0.0, 0.0,
+ 0.0, 0.0, (double)(voxel_size.z * transform_3d[2][2]), 0.0,
+ (double)transform_3d[0][3], (double)transform_3d[1][3], (double)transform_3d[2][3], 1.0);
+
+ openvdb::math::Transform::Ptr index_to_world_tfm = openvdb::math::Transform::createLinearTransform(index_to_world_mat);
+
+ sparse->setTransform(index_to_world_tfm);
+
+ return sparse;
+}
+#endif
+
+/* ************************************************************************** */
void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
{
string msg = string_printf("Computing Volume Mesh %s", mesh->name.c_str());
progress.set_status("Updating Mesh", msg);
- vector<VoxelAttributeGrid> voxel_grids;
-
- /* Compute volume parameters. */
- VolumeParams volume_params;
- volume_params.resolution = make_int3(0, 0, 0);
-
- Transform transform = transform_identity();
+ VolumeMeshBuilder builder;
+#ifdef WITH_OPENVDB
foreach (Attribute &attr, mesh->attributes.attributes) {
if (attr.element != ATTR_ELEMENT_VOXEL) {
continue;
}
+ bool do_clipping = false;
+
ImageHandle &handle = attr.data_voxel();
- device_texture *image_memory = handle.image_memory();
- int3 resolution = make_int3(
- image_memory->data_width, image_memory->data_height, image_memory->data_depth);
- if (volume_params.resolution == make_int3(0, 0, 0)) {
- volume_params.resolution = resolution;
- }
- else if (volume_params.resolution != resolution) {
- /* TODO: support this as it's common for OpenVDB. */
- VLOG(1) << "Can't create accurate volume mesh, all voxel grid resolutions must be equal\n";
- continue;
+ /* Try building from OpenVDB grid directly. */
+ VDBImageLoader *vdb_loader = handle.vdb_loader();
+ openvdb::GridBase::ConstPtr grid;
+ if (vdb_loader) {
+ grid = vdb_loader->get_grid();
+
+ /* If building from an OpenVDB grid, we need to manually clip the values. */
+ do_clipping = true;
}
- VoxelAttributeGrid voxel_grid;
- voxel_grid.data = static_cast<float *>(image_memory->host_pointer);
- voxel_grid.channels = image_memory->data_elements;
- voxel_grids.push_back(voxel_grid);
+ /* Else fall back to creating an OpenVDB grid from the dense volume data. */
+ if (!grid) {
+ device_texture *image_memory = handle.image_memory();
- /* TODO: support multiple transforms. */
- if (image_memory->info.use_transform_3d) {
- transform = image_memory->info.transform_3d;
+ if (image_memory->data_elements == 1) {
+ grid = openvdb_grid_from_device_texture<openvdb::FloatGrid>(image_memory,
+ mesh->volume_clipping,
+ handle.metadata().transform_3d);
+ }
+ else if (image_memory->data_elements == 3) {
+ grid = openvdb_grid_from_device_texture<openvdb::Vec3fGrid>(image_memory,
+ mesh->volume_clipping,
+ handle.metadata().transform_3d);
+ }
+ else if (image_memory->data_elements == 4) {
+ grid = openvdb_grid_from_device_texture<openvdb::Vec4fGrid>(image_memory,
+ mesh->volume_clipping,
+ handle.metadata().transform_3d);
+ }
+ }
+
+ if (grid) {
+ builder.add_grid(grid, do_clipping, mesh->volume_clipping);
}
}
+#endif
- if (voxel_grids.empty()) {
+ if (builder.empty_grid()) {
return;
}
@@ -438,56 +523,19 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
return;
}
- /* Compute start point and cell size from transform. */
- const int3 resolution = volume_params.resolution;
- float3 start_point = make_float3(0.0f, 0.0f, 0.0f);
- float3 cell_size = make_float3(1.0f / resolution.x, 1.0f / resolution.y, 1.0f / resolution.z);
-
- /* TODO: support arbitrary transforms, not just scale + translate. */
- const Transform itfm = transform_inverse(transform);
- start_point = transform_point(&itfm, start_point);
- cell_size = transform_direction(&itfm, cell_size);
+ builder.add_padding(pad_size);
/* Slightly offset vertex coordinates to avoid overlapping faces with other
* volumes or meshes. The proper solution would be to improve intersection in
* the kernel to support robust handling of multiple overlapping faces or use
* an all-hit intersection similar to shadows. */
- const float3 face_overlap_avoidance = cell_size * 0.1f *
- hash_uint_to_float(hash_string(mesh->name.c_str()));
-
- volume_params.start_point = start_point + face_overlap_avoidance;
- volume_params.cell_size = cell_size;
- volume_params.pad_size = pad_size;
-
- /* Build bounding mesh around non-empty volume cells. */
- VolumeMeshBuilder builder(&volume_params);
- const float clipping = mesh->volume_clipping;
-
- for (int z = 0; z < resolution.z; ++z) {
- for (int y = 0; y < resolution.y; ++y) {
- for (int x = 0; x < resolution.x; ++x) {
- int64_t voxel_index = compute_voxel_index(resolution, x, y, z);
-
- for (size_t i = 0; i < voxel_grids.size(); ++i) {
- const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
- const int channels = voxel_grid.channels;
-
- for (int c = 0; c < channels; c++) {
- if (voxel_grid.data[voxel_index * channels + c] >= clipping) {
- builder.add_node_with_padding(x, y, z);
- break;
- }
- }
- }
- }
- }
- }
+ const float face_overlap_avoidance = 0.1f * hash_uint_to_float(hash_string(mesh->name.c_str()));
/* Create mesh. */
vector<float3> vertices;
vector<int> indices;
vector<float3> face_normals;
- builder.create_mesh(vertices, indices, face_normals);
+ builder.create_mesh(vertices, indices, face_normals, face_overlap_avoidance);
mesh->clear(true);
mesh->reserve_mesh(vertices.size(), indices.size() / 3);
@@ -514,10 +562,6 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
indices.size() * sizeof(int)) /
(1024.0 * 1024.0)
<< "Mb.";
-
- VLOG(1) << "Memory usage volume grid: "
- << (resolution.x * resolution.y * resolution.z * sizeof(float)) / (1024.0 * 1024.0)
- << "Mb.";
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index c5033359c6b..70c4214c684 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -945,8 +945,14 @@ void Session::set_pause(bool pause_)
}
}
- if (notify)
- pause_cond.notify_all();
+ if (session_thread) {
+ if (notify) {
+ pause_cond.notify_all();
+ }
+ }
+ else if (pause_) {
+ update_status_time(pause_);
+ }
}
void Session::set_denoising(const DenoiseParams &denoising)
@@ -1150,8 +1156,15 @@ bool Session::render_need_denoise(bool &delayed)
return false;
}
+ /* Immediately denoise when we reach the start sample or last sample. */
+ const int num_samples_finished = tile_manager.state.sample + 1;
+ if (num_samples_finished == params.denoising.start_sample ||
+ num_samples_finished == params.samples) {
+ return true;
+ }
+
/* Do not denoise until the sample at which denoising should start is reached. */
- if (tile_manager.state.sample < min(params.denoising.start_sample, params.samples - 1)) {
+ if (num_samples_finished < params.denoising.start_sample) {
return false;
}
diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt
index 6dcc7f7b3dd..07b345baff8 100644
--- a/intern/cycles/test/CMakeLists.txt
+++ b/intern/cycles/test/CMakeLists.txt
@@ -112,3 +112,4 @@ set_source_files_properties(util_avxf_avx_test.cpp PROPERTIES COMPILE_FLAGS "${C
CYCLES_TEST(util_avxf_avx "cycles_util;bf_intern_numaapi;${OPENIMAGEIO_LIBRARIES};${BOOST_LIBRARIES}")
set_source_files_properties(util_avxf_avx2_test.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}")
CYCLES_TEST(util_avxf_avx2 "cycles_util;bf_intern_numaapi;${OPENIMAGEIO_LIBRARIES};${BOOST_LIBRARIES}")
+CYCLES_TEST(util_transform "cycles_util;${OPENIMAGEIO_LIBRARIES};${BOOST_LIBRARIES}")
diff --git a/intern/cycles/test/util_transform_test.cpp b/intern/cycles/test/util_transform_test.cpp
new file mode 100644
index 00000000000..58ce0fdfee4
--- /dev/null
+++ b/intern/cycles/test/util_transform_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2011-2020 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 "testing/testing.h"
+
+#include "util/util_transform.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+TEST(transform_motion_decompose, Degenerated)
+{
+ // Simple case: single degenerated matrix.
+ {
+ vector<Transform> motion = {transform_scale(0.0f, 0.0f, 0.0f)};
+ vector<DecomposedTransform> decomp(motion.size());
+ transform_motion_decompose(decomp.data(), motion.data(), motion.size());
+ EXPECT_TRUE(transform_decomposed_isfinite_safe(&decomp[0]));
+ }
+
+ // Copy from previous to current.
+ {
+ vector<Transform> motion = {transform_rotate(M_PI_4_F, make_float3(1.0f, 1.0f, 1.0f)),
+ transform_scale(0.0f, 0.0f, 0.0f)};
+ vector<DecomposedTransform> decomp(motion.size());
+ transform_motion_decompose(decomp.data(), motion.data(), motion.size());
+ EXPECT_NEAR(len(decomp[1].x - decomp[0].x), 0.0f, 1e-6f);
+ }
+
+ // Copy from next to current.
+ {
+ vector<Transform> motion = {transform_scale(0.0f, 0.0f, 0.0f),
+ transform_rotate(M_PI_4_F, make_float3(1.0f, 1.0f, 1.0f))};
+ vector<DecomposedTransform> decomp(motion.size());
+ transform_motion_decompose(decomp.data(), motion.data(), motion.size());
+ EXPECT_NEAR(len(decomp[0].x - decomp[1].x), 0.0f, 1e-6f);
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index f5e488d1bd2..a35ec6c7e29 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -88,6 +88,7 @@ set(SRC_HEADERS
util_murmurhash.h
util_openimagedenoise.h
util_opengl.h
+ util_openvdb.h
util_optimization.h
util_param.h
util_path.h
diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h
index e979bd9e0c0..07b0878e3d5 100644
--- a/intern/cycles/util/util_math_fast.h
+++ b/intern/cycles/util/util_math_fast.h
@@ -87,7 +87,7 @@ ccl_device_inline int fast_rint(float x)
/* Single roundps instruction on SSE4.1+ (for gcc/clang at least). */
return float_to_int(rintf(x));
#else
- /* emulate rounding by adding/substracting 0.5. */
+ /* emulate rounding by adding/subtracting 0.5. */
return float_to_int(x + copysignf(0.5f, x));
#endif
}
@@ -445,12 +445,10 @@ ccl_device_inline float fast_expf(float x)
return fast_exp2f(x / M_LN2_F);
}
-#ifndef __KERNEL_GPU__
-/* MSVC seems to have a code-gen bug here in at least SSE41/AVX
- * see T78047 for details. */
-# ifdef _MSC_VER
-# pragma optimize("", off)
-# endif
+#if defined(__KERNEL_CPU__) && !defined(_MSC_VER)
+/* MSVC seems to have a code-gen bug here in at least SSE41/AVX, see
+ * T78047 and T78869 for details. Just disable for now, it only makes
+ * a small difference in denoising performance. */
ccl_device float4 fast_exp2f4(float4 x)
{
const float4 one = make_float4(1.0f);
@@ -466,14 +464,16 @@ ccl_device float4 fast_exp2f4(float4 x)
r = madd4(x, r, make_float4(1.0f));
return __int4_as_float4(__float4_as_int4(r) + (m << 23));
}
-# ifdef _MSC_VER
-# pragma optimize("", on)
-# endif
ccl_device_inline float4 fast_expf4(float4 x)
{
return fast_exp2f4(x / M_LN2_F);
}
+#else
+ccl_device_inline float4 fast_expf4(float4 x)
+{
+ return make_float4(fast_expf(x.x), fast_expf(x.y), fast_expf(x.z), fast_expf(x.w));
+}
#endif
ccl_device_inline float fast_exp10(float x)
diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/util_math_float4.h
index cd4b3e3b74c..ec5328adb31 100644
--- a/intern/cycles/util/util_math_float4.h
+++ b/intern/cycles/util/util_math_float4.h
@@ -477,6 +477,24 @@ ccl_device_inline float4 safe_divide_float4_float(const float4 a, const float b)
return (b != 0.0f) ? a / b : make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
+ccl_device_inline bool isfinite4_safe(float4 v)
+{
+ return isfinite_safe(v.x) && isfinite_safe(v.y) && isfinite_safe(v.z) && isfinite_safe(v.w);
+}
+
+ccl_device_inline float4 ensure_finite4(float4 v)
+{
+ if (!isfinite_safe(v.x))
+ v.x = 0.0f;
+ if (!isfinite_safe(v.y))
+ v.y = 0.0f;
+ if (!isfinite_safe(v.z))
+ v.z = 0.0f;
+ if (!isfinite_safe(v.w))
+ v.w = 0.0f;
+ return v;
+}
+
CCL_NAMESPACE_END
#endif /* __UTIL_MATH_FLOAT4_H__ */
diff --git a/intern/cycles/util/util_openvdb.h b/intern/cycles/util/util_openvdb.h
new file mode 100644
index 00000000000..a3ebb03e5a4
--- /dev/null
+++ b/intern/cycles/util/util_openvdb.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011-2020 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 __UTIL_OPENVDB_H__
+#define __UTIL_OPENVDB_H__
+
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+
+namespace openvdb {
+
+using Vec4fTree = tree::Tree4<Vec4f, 5, 4, 3>::Type;
+using Vec4fGrid = Grid<Vec4fTree>;
+
+}; // namespace openvdb
+
+#endif
+
+#endif /* __UTIL_OPENVDB_H__ */
diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/util_transform.cpp
index 101122740d7..6417752f704 100644
--- a/intern/cycles/util/util_transform.cpp
+++ b/intern/cycles/util/util_transform.cpp
@@ -269,17 +269,17 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf
/* extract scale and shear first */
float3 scale, shear;
scale.x = len(colx);
- colx /= scale.x;
+ colx = safe_divide_float3_float(colx, scale.x);
shear.z = dot(colx, coly);
coly -= shear.z * colx;
scale.y = len(coly);
- coly /= scale.y;
+ coly = safe_divide_float3_float(coly, scale.y);
shear.y = dot(colx, colz);
colz -= shear.y * colx;
shear.x = dot(coly, colz);
colz -= shear.x * coly;
scale.z = len(colz);
- colz /= scale.z;
+ colz = safe_divide_float3_float(colz, scale.z);
transform_set_column(&M, 0, colx);
transform_set_column(&M, 1, coly);
@@ -300,6 +300,7 @@ static void transform_decompose(DecomposedTransform *decomp, const Transform *tf
void transform_motion_decompose(DecomposedTransform *decomp, const Transform *motion, size_t size)
{
+ /* Decompose and correct rotation. */
for (size_t i = 0; i < size; i++) {
transform_decompose(decomp + i, motion + i);
@@ -310,6 +311,27 @@ void transform_motion_decompose(DecomposedTransform *decomp, const Transform *mo
decomp[i].x = -decomp[i].x;
}
}
+
+ /* Copy rotation to decomposed transform where scale is degenerate. This avoids weird object
+ * rotation interpolation when the scale goes to 0 for a time step.
+ *
+ * Note that this is very simple and naive implementation, which only deals with degenerated
+ * scale happening only on one frame. It is possible to improve it further by interpolating
+ * rotation into s degenerated range using rotation from timesteps from adjacent non-degenerated
+ * time steps. */
+ for (size_t i = 0; i < size; i++) {
+ const float3 scale = make_float3(decomp[i].y.w, decomp[i].z.w, decomp[i].w.w);
+ if (!is_zero(scale)) {
+ continue;
+ }
+
+ if (i > 0) {
+ decomp[i].x = decomp[i - 1].x;
+ }
+ else if (i < size - 1) {
+ decomp[i].x = decomp[i + 1].x;
+ }
+ }
}
Transform transform_from_viewplane(BoundBox2D &viewplane)
diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h
index d0a6264d5cf..d8bbd389aa6 100644
--- a/intern/cycles/util/util_transform.h
+++ b/intern/cycles/util/util_transform.h
@@ -466,6 +466,17 @@ ccl_device void transform_motion_array_interpolate(Transform *tfm,
transform_compose(tfm, &decomp);
}
+ccl_device_inline bool transform_isfinite_safe(Transform *tfm)
+{
+ return isfinite4_safe(tfm->x) && isfinite4_safe(tfm->y) && isfinite4_safe(tfm->z);
+}
+
+ccl_device_inline bool transform_decomposed_isfinite_safe(DecomposedTransform *decomp)
+{
+ return isfinite4_safe(decomp->x) && isfinite4_safe(decomp->y) && isfinite4_safe(decomp->z) &&
+ isfinite4_safe(decomp->w);
+}
+
#ifndef __KERNEL_GPU__
class BoundBox2D;