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:
authorKévin Dietrich <kevin.dietrich@mailoo.org>2022-04-19 17:28:14 +0300
committerKévin Dietrich <kevin.dietrich@mailoo.org>2022-04-19 18:07:53 +0300
commit2890c11cd7b008c3c261b6aa833c826c85cac504 (patch)
tree5bc3432767fc6dd260e6fec18bd1817766572abd
parent56cfd60d432bf387c21bf5bde08fece044fac99c (diff)
Cycles: add support for volume motion blur
This adds support for rendering motion blur for volumes, using their velocity field. This works for fluid simulations and imported VDB volumes. For the latter, the name of the velocity field can be set per volume object, with automatic detection of velocity fields that are split into 3 scalar grids. A new parameter is also added to scale velocity for more artistic control. Like for Alembic and USD caches, a parameter to set the unit of time in which the velocity vectors are expressed is also added. For Blender gas simulations, the velocity unit should always be in seconds, so this is only exposed for volume objects which may come from external OpenVDB files. These parameters are available under the `Render` panels for the fluid domain and the volume object data properties respectively. Credits: kernel advection code from Tangent Animation's Blackbird based on earlier work by Geraldine Chua Differential Revision: https://developer.blender.org/D14629
-rw-r--r--intern/cycles/blender/addon/ui.py2
-rw-r--r--intern/cycles/blender/camera.cpp10
-rw-r--r--intern/cycles/blender/object.cpp7
-rw-r--r--intern/cycles/blender/sync.cpp2
-rw-r--r--intern/cycles/blender/volume.cpp49
-rw-r--r--intern/cycles/kernel/integrator/shader_eval.h59
-rw-r--r--intern/cycles/kernel/types.h25
-rw-r--r--intern/cycles/scene/attribute.cpp9
-rw-r--r--intern/cycles/scene/camera.cpp1
-rw-r--r--intern/cycles/scene/camera.h12
-rw-r--r--intern/cycles/scene/geometry.cpp2
-rw-r--r--intern/cycles/scene/geometry.h2
-rw-r--r--intern/cycles/scene/image_vdb.cpp5
-rw-r--r--intern/cycles/scene/image_vdb.h1
-rw-r--r--intern/cycles/scene/object.cpp10
-rw-r--r--intern/cycles/scene/scene.cpp6
-rw-r--r--intern/cycles/scene/scene.h2
-rw-r--r--intern/cycles/scene/volume.cpp149
-rw-r--r--intern/cycles/scene/volume.h1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_volume.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_fluid.py22
-rw-r--r--source/blender/blenkernel/BKE_volume.h5
-rw-r--r--source/blender/blenkernel/intern/fluid.c3
-rw-r--r--source/blender/blenkernel/intern/volume.cc62
-rw-r--r--source/blender/makesdna/DNA_fluid_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_fluid_types.h5
-rw-r--r--source/blender/makesdna/DNA_volume_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_volume_types.h17
-rw-r--r--source/blender/makesrna/RNA_enum_items.h2
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c14
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c6
-rw-r--r--source/blender/makesrna/intern/rna_volume.c59
32 files changed, 511 insertions, 48 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 739a555f037..8a7b0635ed7 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -1034,7 +1034,7 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
def poll(cls, context):
ob = context.object
if CyclesButtonsPanel.poll(context) and ob:
- if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'CURVES', 'POINTCLOUD'}:
+ if ob.type in {'MESH', 'CURVE', 'CURVE', 'SURFACE', 'FONT', 'META', 'CAMERA', 'CURVES', 'POINTCLOUD', 'VOLUME'}:
return True
if ob.instance_type == 'COLLECTION' and ob.instance_collection:
return True
diff --git a/intern/cycles/blender/camera.cpp b/intern/cycles/blender/camera.cpp
index cdd19341534..402fd7c4ec6 100644
--- a/intern/cycles/blender/camera.cpp
+++ b/intern/cycles/blender/camera.cpp
@@ -23,7 +23,7 @@ struct BlenderCamera {
float lens;
float shuttertime;
- Camera::MotionPosition motion_position;
+ MotionPosition motion_position;
array<float> shutter_curve;
Camera::RollingShutterType rolling_shutter_type;
@@ -114,7 +114,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende
bcam->sensor_width = 36.0f;
bcam->sensor_height = 24.0f;
bcam->sensor_fit = BlenderCamera::AUTO;
- bcam->motion_position = Camera::MOTION_POSITION_CENTER;
+ bcam->motion_position = MOTION_POSITION_CENTER;
bcam->border.right = 1.0f;
bcam->border.top = 1.0f;
bcam->viewport_camera_border.right = 1.0f;
@@ -555,10 +555,8 @@ void BlenderSync::sync_camera(BL::RenderSettings &b_render,
curvemapping_to_array(b_shutter_curve, bcam.shutter_curve, RAMP_TABLE_SIZE);
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
- bcam.motion_position = (Camera::MotionPosition)get_enum(cscene,
- "motion_blur_position",
- Camera::MOTION_NUM_POSITIONS,
- Camera::MOTION_POSITION_CENTER);
+ bcam.motion_position = (MotionPosition)get_enum(
+ cscene, "motion_blur_position", MOTION_NUM_POSITIONS, MOTION_POSITION_CENTER);
bcam.rolling_shutter_type = (Camera::RollingShutterType)get_enum(
cscene,
"rolling_shutter_type",
diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp
index f77cbdf847d..9b08b564b25 100644
--- a/intern/cycles/blender/object.cpp
+++ b/intern/cycles/blender/object.cpp
@@ -16,6 +16,7 @@
#include "scene/shader.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
+#include "scene/volume.h"
#include "util/foreach.h"
#include "util/hash.h"
@@ -715,13 +716,13 @@ void BlenderSync::sync_motion(BL::RenderSettings &b_render,
float frame_center_delta = 0.0f;
if (scene->need_motion() != Scene::MOTION_PASS &&
- scene->camera->get_motion_position() != Camera::MOTION_POSITION_CENTER) {
+ scene->camera->get_motion_position() != MOTION_POSITION_CENTER) {
float shuttertime = scene->camera->get_shuttertime();
- if (scene->camera->get_motion_position() == Camera::MOTION_POSITION_END) {
+ if (scene->camera->get_motion_position() == MOTION_POSITION_END) {
frame_center_delta = -shuttertime * 0.5f;
}
else {
- assert(scene->camera->get_motion_position() == Camera::MOTION_POSITION_START);
+ assert(scene->camera->get_motion_position() == MOTION_POSITION_START);
frame_center_delta = shuttertime * 0.5f;
}
diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp
index bd6bfafedeb..1028c940772 100644
--- a/intern/cycles/blender/sync.cpp
+++ b/intern/cycles/blender/sync.cpp
@@ -272,7 +272,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render,
geometry_synced.clear(); /* use for objects and motion sync */
if (scene->need_motion() == Scene::MOTION_PASS || scene->need_motion() == Scene::MOTION_NONE ||
- scene->camera->get_motion_position() == Camera::MOTION_POSITION_CENTER) {
+ scene->camera->get_motion_position() == MOTION_POSITION_CENTER) {
sync_objects(b_depsgraph, b_v3d);
}
sync_motion(b_render, b_depsgraph, b_v3d, b_override, width, height, python_thread_state);
diff --git a/intern/cycles/blender/volume.cpp b/intern/cycles/blender/volume.cpp
index 381b3385a5a..8dd2d45c0b6 100644
--- a/intern/cycles/blender/volume.cpp
+++ b/intern/cycles/blender/volume.cpp
@@ -168,7 +168,8 @@ class BlenderSmokeLoader : public ImageLoader {
AttributeStandard attribute;
};
-static void sync_smoke_volume(Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame)
+static void sync_smoke_volume(
+ BL::Scene &b_scene, Scene *scene, BObjectInfo &b_ob_info, Volume *volume, float frame)
{
if (!b_ob_info.is_real_object_data()) {
return;
@@ -178,6 +179,18 @@ static void sync_smoke_volume(Scene *scene, BObjectInfo &b_ob_info, Volume *volu
return;
}
+ float velocity_scale = b_domain.velocity_scale();
+ /* Motion blur attribute is relative to seconds, we need it relative to frames. */
+ const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
+ const float motion_scale = (need_motion) ?
+ scene->motion_shutter_time() /
+ (b_scene.render().fps() / b_scene.render().fps_base()) :
+ 0.0f;
+
+ velocity_scale *= motion_scale;
+
+ volume->set_velocity_scale(velocity_scale);
+
AttributeStandard attributes[] = {ATTR_STD_VOLUME_DENSITY,
ATTR_STD_VOLUME_COLOR,
ATTR_STD_VOLUME_FLAME,
@@ -234,6 +247,7 @@ class BlenderVolumeLoader : public VDBImageLoader {
};
static void sync_volume_object(BL::BlendData &b_data,
+ BL::Scene &b_scene,
BObjectInfo &b_ob_info,
Scene *scene,
Volume *volume)
@@ -247,6 +261,20 @@ static void sync_volume_object(BL::BlendData &b_data,
volume->set_step_size(b_render.step_size());
volume->set_object_space((b_render.space() == BL::VolumeRender::space_OBJECT));
+ float velocity_scale = b_volume.velocity_scale();
+ if (b_volume.velocity_unit() == BL::Volume::velocity_unit_SECOND) {
+ /* Motion blur attribute is relative to seconds, we need it relative to frames. */
+ const bool need_motion = object_need_motion_attribute(b_ob_info, scene);
+ const float motion_scale = (need_motion) ?
+ scene->motion_shutter_time() /
+ (b_scene.render().fps() / b_scene.render().fps_base()) :
+ 0.0f;
+
+ velocity_scale *= motion_scale;
+ }
+
+ volume->set_velocity_scale(velocity_scale);
+
/* Find grid with matching name. */
for (BL::VolumeGrid &b_grid : b_volume.grids) {
ustring name = ustring(b_grid.name());
@@ -267,9 +295,22 @@ static void sync_volume_object(BL::BlendData &b_data,
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
std = ATTR_STD_VOLUME_TEMPERATURE;
}
- else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) {
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY) ||
+ name == b_volume.velocity_grid()) {
std = ATTR_STD_VOLUME_VELOCITY;
}
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_X) ||
+ name == b_volume.velocity_x_grid()) {
+ std = ATTR_STD_VOLUME_VELOCITY_X;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_Y) ||
+ name == b_volume.velocity_y_grid()) {
+ std = ATTR_STD_VOLUME_VELOCITY_Y;
+ }
+ else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY_Z) ||
+ name == b_volume.velocity_z_grid()) {
+ std = ATTR_STD_VOLUME_VELOCITY_Z;
+ }
if ((std != ATTR_STD_NONE && volume->need_attribute(scene, std)) ||
volume->need_attribute(scene, name)) {
@@ -294,11 +335,11 @@ void BlenderSync::sync_volume(BObjectInfo &b_ob_info, Volume *volume)
if (b_ob_info.object_data.is_a(&RNA_Volume)) {
/* Volume object. Create only attributes, bounding mesh will then
* be automatically generated later. */
- sync_volume_object(b_data, b_ob_info, scene, volume);
+ sync_volume_object(b_data, b_scene, b_ob_info, scene, volume);
}
else {
/* Smoke domain. */
- sync_smoke_volume(scene, b_ob_info, volume, b_scene.frame_current());
+ sync_smoke_volume(b_scene, scene, b_ob_info, volume, b_scene.frame_current());
}
}
diff --git a/intern/cycles/kernel/integrator/shader_eval.h b/intern/cycles/kernel/integrator/shader_eval.h
index 3066fb661a1..3ea53b3e647 100644
--- a/intern/cycles/kernel/integrator/shader_eval.h
+++ b/intern/cycles/kernel/integrator/shader_eval.h
@@ -831,6 +831,65 @@ ccl_device_inline void shader_eval_volume(KernelGlobals kg,
/* todo: this is inefficient for motion blur, we should be
* caching matrices instead of recomputing them each step */
shader_setup_object_transforms(kg, sd, sd->time);
+
+ if ((sd->object_flag & SD_OBJECT_HAS_VOLUME_MOTION) != 0) {
+ AttributeDescriptor v_desc = find_attribute(kg, sd, ATTR_STD_VOLUME_VELOCITY);
+ kernel_assert(v_desc.offset != ATTR_STD_NOT_FOUND);
+
+ const float3 P = sd->P;
+ const float velocity_scale = kernel_tex_fetch(__objects, sd->object).velocity_scale;
+ const float time_offset = kernel_data.cam.motion_position == MOTION_POSITION_CENTER ?
+ 0.5f :
+ 0.0f;
+ const float time = kernel_data.cam.motion_position == MOTION_POSITION_END ?
+ (1.0f - kernel_data.cam.shuttertime) + sd->time :
+ sd->time;
+
+ /* Use a 1st order semi-lagrangian advection scheme to estimate what volume quantity
+ * existed, or will exist, at the given time:
+ *
+ * `phi(x, T) = phi(x - (T - t) * u(x, T), t)`
+ *
+ * where
+ *
+ * x : position
+ * T : super-sampled time (or ray time)
+ * t : current time of the simulation (in rendering we assume this is center frame with
+ * relative time = 0)
+ * phi : the volume quantity
+ * u : the velocity field
+ *
+ * But first we need to determine the velocity field `u(x, T)`, which we can estimate also
+ * using semi-lagrangian advection.
+ *
+ * `u(x, T) = u(x - (T - t) * u(x, T), t)`
+ *
+ * This is the typical way to model self-advection in fluid dynamics, however, we do not
+ * account for other forces affecting the velocity during simulation (pressure, buyoancy,
+ * etc.): this gives a linear interpolation when fluid are mostly "curvy". For better
+ * results, a higher order interpolation scheme can be used (at the cost of more lookups),
+ * or an interpolation of the velocity fields for the previous and next frames could also
+ * be used to estimate `u(x, T)` (which will cost more memory and lookups).
+ *
+ * References:
+ * "Eulerian Motion Blur", Kim and Ko, 2007
+ * "Production Volume Rendering", Wreninge et al., 2012
+ */
+
+ /* Find velocity. */
+ float3 velocity = primitive_volume_attribute_float3(kg, sd, v_desc);
+ object_dir_transform(kg, sd, &velocity);
+
+ /* Find advected P. */
+ sd->P = P - (time - time_offset) * velocity_scale * velocity;
+
+ /* Find advected velocity. */
+ velocity = primitive_volume_attribute_float3(kg, sd, v_desc);
+ object_dir_transform(kg, sd, &velocity);
+
+ /* Find advected P. */
+ sd->P = P - (time - time_offset) * velocity_scale * velocity;
+ }
# endif
}
diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h
index 422285cd346..01df7948241 100644
--- a/intern/cycles/kernel/types.h
+++ b/intern/cycles/kernel/types.h
@@ -489,6 +489,18 @@ enum PanoramaType {
PANORAMA_NUM_TYPES,
};
+/* Specifies an offset for the shutter's time interval. */
+enum MotionPosition {
+ /* Shutter opens at the current frame. */
+ MOTION_POSITION_START = 0,
+ /* Shutter is fully open at the current frame. */
+ MOTION_POSITION_CENTER = 1,
+ /* Shutter closes at the current frame. */
+ MOTION_POSITION_END = 2,
+
+ MOTION_NUM_POSITIONS,
+};
+
/* Direct Light Sampling */
enum DirectLightSamplingType {
@@ -635,6 +647,9 @@ typedef enum AttributeStandard {
ATTR_STD_VOLUME_HEAT,
ATTR_STD_VOLUME_TEMPERATURE,
ATTR_STD_VOLUME_VELOCITY,
+ ATTR_STD_VOLUME_VELOCITY_X,
+ ATTR_STD_VOLUME_VELOCITY_Y,
+ ATTR_STD_VOLUME_VELOCITY_Z,
ATTR_STD_POINTINESS,
ATTR_STD_RANDOM_PER_ISLAND,
ATTR_STD_SHADOW_TRANSPARENCY,
@@ -808,6 +823,8 @@ enum ShaderDataObjectFlag {
SD_OBJECT_CAUSTICS_CASTER = (1 << 9),
/* object is caustics receiver */
SD_OBJECT_CAUSTICS_RECEIVER = (1 << 10),
+ /* object has attribute for volume motion */
+ SD_OBJECT_HAS_VOLUME_MOTION = (1 << 11),
/* object is using caustics */
SD_OBJECT_CAUSTICS = (SD_OBJECT_CAUSTICS_CASTER | SD_OBJECT_CAUSTICS_RECEIVER),
@@ -815,7 +832,8 @@ enum ShaderDataObjectFlag {
SD_OBJECT_FLAGS = (SD_OBJECT_HOLDOUT_MASK | SD_OBJECT_MOTION | SD_OBJECT_TRANSFORM_APPLIED |
SD_OBJECT_NEGATIVE_SCALE_APPLIED | SD_OBJECT_HAS_VOLUME |
SD_OBJECT_INTERSECTS_VOLUME | SD_OBJECT_SHADOW_CATCHER |
- SD_OBJECT_HAS_VOLUME_ATTRIBUTES | SD_OBJECT_CAUSTICS)
+ SD_OBJECT_HAS_VOLUME_ATTRIBUTES | SD_OBJECT_CAUSTICS |
+ SD_OBJECT_HAS_VOLUME_MOTION)
};
typedef struct ccl_align(16) ShaderData
@@ -1040,7 +1058,7 @@ typedef struct KernelCamera {
int rolling_shutter_type;
float rolling_shutter_duration;
- int pad;
+ int motion_position;
} KernelCamera;
static_assert_align(KernelCamera, 16);
@@ -1386,7 +1404,8 @@ typedef struct KernelObject {
uint visibility;
int primitive_type;
- int pad1;
+ /* Volume velocity scale. */
+ float velocity_scale;
} KernelObject;
static_assert_align(KernelObject, 16);
diff --git a/intern/cycles/scene/attribute.cpp b/intern/cycles/scene/attribute.cpp
index 0ca602362bc..df01189a54b 100644
--- a/intern/cycles/scene/attribute.cpp
+++ b/intern/cycles/scene/attribute.cpp
@@ -360,6 +360,12 @@ const char *Attribute::standard_name(AttributeStandard std)
return "temperature";
case ATTR_STD_VOLUME_VELOCITY:
return "velocity";
+ case ATTR_STD_VOLUME_VELOCITY_X:
+ return "velocity_x";
+ case ATTR_STD_VOLUME_VELOCITY_Y:
+ return "velocity_y";
+ case ATTR_STD_VOLUME_VELOCITY_Z:
+ return "velocity_z";
case ATTR_STD_POINTINESS:
return "pointiness";
case ATTR_STD_RANDOM_PER_ISLAND:
@@ -587,6 +593,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_VOLUME_FLAME:
case ATTR_STD_VOLUME_HEAT:
case ATTR_STD_VOLUME_TEMPERATURE:
+ case ATTR_STD_VOLUME_VELOCITY_X:
+ case ATTR_STD_VOLUME_VELOCITY_Y:
+ case ATTR_STD_VOLUME_VELOCITY_Z:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
break;
case ATTR_STD_VOLUME_COLOR:
diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp
index 6aca2fcbb81..710f1c5ee90 100644
--- a/intern/cycles/scene/camera.cpp
+++ b/intern/cycles/scene/camera.cpp
@@ -397,6 +397,7 @@ void Camera::update(Scene *scene)
/* motion blur */
kcam->shuttertime = (need_motion == Scene::MOTION_BLUR) ? shuttertime : -1.0f;
+ kcam->motion_position = motion_position;
/* type */
kcam->type = camera_type;
diff --git a/intern/cycles/scene/camera.h b/intern/cycles/scene/camera.h
index 97bee430588..c150405acc2 100644
--- a/intern/cycles/scene/camera.h
+++ b/intern/cycles/scene/camera.h
@@ -30,18 +30,6 @@ class Camera : public Node {
public:
NODE_DECLARE
- /* Specifies an offset for the shutter's time interval. */
- enum MotionPosition {
- /* Shutter opens at the current frame. */
- MOTION_POSITION_START = 0,
- /* Shutter is fully open at the current frame. */
- MOTION_POSITION_CENTER = 1,
- /* Shutter closes at the current frame. */
- MOTION_POSITION_END = 2,
-
- MOTION_NUM_POSITIONS,
- };
-
/* Specifies rolling shutter effect. */
enum RollingShutterType {
/* No rolling shutter effect. */
diff --git a/intern/cycles/scene/geometry.cpp b/intern/cycles/scene/geometry.cpp
index 351ec4f09ae..349d8ad39c7 100644
--- a/intern/cycles/scene/geometry.cpp
+++ b/intern/cycles/scene/geometry.cpp
@@ -1541,7 +1541,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro
}
Volume *volume = static_cast<Volume *>(geom);
- create_volume_mesh(volume, progress);
+ create_volume_mesh(scene, volume, progress);
/* always reallocate when we have a volume, as we need to rebuild the BVH */
device_update_flags |= DEVICE_MESH_DATA_NEEDS_REALLOC;
diff --git a/intern/cycles/scene/geometry.h b/intern/cycles/scene/geometry.h
index 0c2e70d483d..6210a64509a 100644
--- a/intern/cycles/scene/geometry.h
+++ b/intern/cycles/scene/geometry.h
@@ -216,7 +216,7 @@ class GeometryManager {
protected:
bool displace(Device *device, Scene *scene, Mesh *mesh, Progress &progress);
- void create_volume_mesh(Volume *volume, Progress &progress);
+ void create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress);
/* Attributes */
void update_osl_attributes(Device *device,
diff --git a/intern/cycles/scene/image_vdb.cpp b/intern/cycles/scene/image_vdb.cpp
index 9906606a959..8f30433c229 100644
--- a/intern/cycles/scene/image_vdb.cpp
+++ b/intern/cycles/scene/image_vdb.cpp
@@ -66,6 +66,11 @@ struct ToNanoOp {
# endif
#endif
+VDBImageLoader::VDBImageLoader(openvdb::GridBase::ConstPtr grid_, const string &grid_name)
+ : grid_name(grid_name), grid(grid_)
+{
+}
+
VDBImageLoader::VDBImageLoader(const string &grid_name) : grid_name(grid_name)
{
}
diff --git a/intern/cycles/scene/image_vdb.h b/intern/cycles/scene/image_vdb.h
index c851ef6250d..4c1aba80af8 100644
--- a/intern/cycles/scene/image_vdb.h
+++ b/intern/cycles/scene/image_vdb.h
@@ -17,6 +17,7 @@ CCL_NAMESPACE_BEGIN
class VDBImageLoader : public ImageLoader {
public:
+ VDBImageLoader(openvdb::GridBase::ConstPtr grid_, const string &grid_name);
VDBImageLoader(const string &grid_name);
~VDBImageLoader();
diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp
index 55d89fc3673..c57d26464ed 100644
--- a/intern/cycles/scene/object.cpp
+++ b/intern/cycles/scene/object.cpp
@@ -439,6 +439,14 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
flag |= SD_OBJECT_HAS_VERTEX_MOTION;
}
}
+ else if (geom->is_volume()) {
+ Volume *volume = static_cast<Volume *>(geom);
+ if (volume->attributes.find(ATTR_STD_VOLUME_VELOCITY) &&
+ volume->get_velocity_scale() != 0.0f) {
+ flag |= SD_OBJECT_HAS_VOLUME_MOTION;
+ kobject.velocity_scale = volume->get_velocity_scale();
+ }
+ }
if (state->need_motion == Scene::MOTION_PASS) {
/* Clear motion array if there is no actual motion. */
@@ -488,7 +496,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
kobject.dupli_generated[2] = ob->dupli_generated[2];
kobject.numkeys = (geom->geometry_type == Geometry::HAIR) ?
static_cast<Hair *>(geom)->get_curve_keys().size() :
- (geom->geometry_type == Geometry::POINTCLOUD) ?
+ (geom->geometry_type == Geometry::POINTCLOUD) ?
static_cast<PointCloud *>(geom)->num_points() :
0;
kobject.dupli_uv[0] = ob->dupli_uv[0];
diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp
index b6b53004816..b35242139ea 100644
--- a/intern/cycles/scene/scene.cpp
+++ b/intern/cycles/scene/scene.cpp
@@ -381,7 +381,7 @@ void Scene::device_update(Device *device_, Progress &progress)
}
}
-Scene::MotionType Scene::need_motion()
+Scene::MotionType Scene::need_motion() const
{
if (integrator->get_motion_blur())
return MOTION_BLUR;
@@ -407,6 +407,10 @@ bool Scene::need_global_attribute(AttributeStandard std)
return need_motion() != MOTION_NONE;
else if (std == ATTR_STD_MOTION_VERTEX_NORMAL)
return need_motion() == MOTION_BLUR;
+ else if (std == ATTR_STD_VOLUME_VELOCITY || std == ATTR_STD_VOLUME_VELOCITY_X ||
+ std == ATTR_STD_VOLUME_VELOCITY_Y || std == ATTR_STD_VOLUME_VELOCITY_Z) {
+ return need_motion() != MOTION_NONE;
+ }
return false;
}
diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h
index 54c7d9d93ea..a0d2f4a6c06 100644
--- a/intern/cycles/scene/scene.h
+++ b/intern/cycles/scene/scene.h
@@ -257,7 +257,7 @@ class Scene : public NodeOwner {
void need_global_attributes(AttributeRequestSet &attributes);
enum MotionType { MOTION_NONE = 0, MOTION_PASS, MOTION_BLUR };
- MotionType need_motion();
+ MotionType need_motion() const;
float motion_shutter_time();
bool need_update();
diff --git a/intern/cycles/scene/volume.cpp b/intern/cycles/scene/volume.cpp
index 0555fdd2fad..1357d5ed762 100644
--- a/intern/cycles/scene/volume.cpp
+++ b/intern/cycles/scene/volume.cpp
@@ -10,9 +10,9 @@
# include <openvdb/tools/Dense.h>
# include <openvdb/tools/GridTransformer.h>
# include <openvdb/tools/Morphology.h>
+# include <openvdb/tools/Statistics.h>
#endif
-#include "util/foreach.h"
#include "util/hash.h"
#include "util/log.h"
#include "util/openvdb.h"
@@ -28,6 +28,7 @@ NODE_DEFINE(Volume)
SOCKET_FLOAT(clipping, "Clipping", 0.001f);
SOCKET_FLOAT(step_size, "Step Size", 0.0f);
SOCKET_BOOLEAN(object_space, "Object Space", false);
+ SOCKET_FLOAT(velocity_scale, "Velocity Scale", 1.0f);
return type;
}
@@ -482,11 +483,141 @@ static openvdb::GridBase::ConstPtr openvdb_grid_from_device_texture(device_textu
return sparse;
}
+
+static int estimate_required_velocity_padding(openvdb::GridBase::ConstPtr grid,
+ float velocity_scale)
+{
+ /* TODO: we may need to also find outliers and clamp them to avoid adding too much padding. */
+ openvdb::math::Extrema extrema;
+ openvdb::Vec3d voxel_size;
+
+ /* External .vdb files have a vec3 type for velocity, but the Blender exporter creates a vec4. */
+ if (grid->isType<openvdb::Vec3fGrid>()) {
+ openvdb::Vec3fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec3fGrid>(grid);
+ extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
+ voxel_size = vel_grid->voxelSize();
+ }
+ else if (grid->isType<openvdb::Vec4fGrid>()) {
+ openvdb::Vec4fGrid::ConstPtr vel_grid = openvdb::gridConstPtrCast<openvdb::Vec4fGrid>(grid);
+ extrema = openvdb::tools::extrema(vel_grid->cbeginValueOn());
+ voxel_size = vel_grid->voxelSize();
+ }
+ else {
+ assert(0);
+ return 0;
+ }
+
+ /* We should only have uniform grids, so x = y = z, but we never know. */
+ const double max_voxel_size = openvdb::math::Max(voxel_size.x(), voxel_size.y(), voxel_size.z());
+ if (max_voxel_size == 0.0) {
+ return 0;
+ }
+
+ const double estimated_padding = extrema.max() * static_cast<double>(velocity_scale) /
+ max_voxel_size;
+
+ return static_cast<int>(std::ceil(estimated_padding));
+}
+
+static openvdb::FloatGrid::ConstPtr get_vdb_for_attribute(Volume *volume, AttributeStandard std)
+{
+ Attribute *attr = volume->attributes.find(std);
+ if (!attr) {
+ return nullptr;
+ }
+
+ ImageHandle &handle = attr->data_voxel();
+ VDBImageLoader *vdb_loader = handle.vdb_loader();
+ if (!vdb_loader) {
+ return nullptr;
+ }
+
+ openvdb::GridBase::ConstPtr grid = vdb_loader->get_grid();
+ if (!grid) {
+ return nullptr;
+ }
+
+ if (!grid->isType<openvdb::FloatGrid>()) {
+ return nullptr;
+ }
+
+ return openvdb::gridConstPtrCast<openvdb::FloatGrid>(grid);
+}
+
+class MergeScalarGrids {
+ typedef openvdb::FloatTree ScalarTree;
+
+ openvdb::tree::ValueAccessor<const ScalarTree> m_acc_x, m_acc_y, m_acc_z;
+
+ public:
+ MergeScalarGrids(const ScalarTree *x_tree, const ScalarTree *y_tree, const ScalarTree *z_tree)
+ : m_acc_x(*x_tree), m_acc_y(*y_tree), m_acc_z(*z_tree)
+ {
+ }
+
+ MergeScalarGrids(const MergeScalarGrids &other)
+ : m_acc_x(other.m_acc_x), m_acc_y(other.m_acc_y), m_acc_z(other.m_acc_z)
+ {
+ }
+
+ void operator()(const openvdb::Vec3STree::ValueOnIter &it) const
+ {
+ using namespace openvdb;
+
+ const math::Coord xyz = it.getCoord();
+ float x = m_acc_x.getValue(xyz);
+ float y = m_acc_y.getValue(xyz);
+ float z = m_acc_z.getValue(xyz);
+
+ it.setValue(math::Vec3s(x, y, z));
+ }
+};
+
+static void merge_scalar_grids_for_velocity(const Scene *scene, Volume *volume)
+{
+ if (volume->attributes.find(ATTR_STD_VOLUME_VELOCITY)) {
+ /* A vector grid for velocity is already available. */
+ return;
+ }
+
+ openvdb::FloatGrid::ConstPtr vel_x_grid = get_vdb_for_attribute(volume,
+ ATTR_STD_VOLUME_VELOCITY_X);
+ openvdb::FloatGrid::ConstPtr vel_y_grid = get_vdb_for_attribute(volume,
+ ATTR_STD_VOLUME_VELOCITY_Y);
+ openvdb::FloatGrid::ConstPtr vel_z_grid = get_vdb_for_attribute(volume,
+ ATTR_STD_VOLUME_VELOCITY_Z);
+
+ if (!(vel_x_grid && vel_y_grid && vel_z_grid)) {
+ return;
+ }
+
+ openvdb::Vec3fGrid::Ptr vecgrid = openvdb::Vec3SGrid::create(openvdb::Vec3s(0.0f));
+
+ /* Activate voxels in the vector grid based on the scalar grids to ensure thread safety during
+ * the merge. */
+ vecgrid->tree().topologyUnion(vel_x_grid->tree());
+ vecgrid->tree().topologyUnion(vel_y_grid->tree());
+ vecgrid->tree().topologyUnion(vel_z_grid->tree());
+
+ MergeScalarGrids op(&vel_x_grid->tree(), &vel_y_grid->tree(), &vel_z_grid->tree());
+ openvdb::tools::foreach (vecgrid->beginValueOn(), op, true, false);
+
+ /* Assume all grids have the same transformation. */
+ openvdb::math::Transform::Ptr transform = openvdb::ConstPtrCast<openvdb::math::Transform>(
+ vel_x_grid->transformPtr());
+ vecgrid->setTransform(transform);
+
+ /* Make an attribute for it. */
+ Attribute *attr = volume->attributes.add(ATTR_STD_VOLUME_VELOCITY);
+ ImageLoader *loader = new VDBImageLoader(vecgrid, "merged_velocity");
+ ImageParams params;
+ attr->data_voxel() = scene->image_manager->add_image(loader, params);
+}
#endif
/* ************************************************************************** */
-void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress)
+void GeometryManager::create_volume_mesh(const Scene *scene, Volume *volume, Progress &progress)
{
string msg = string_printf("Computing Volume Mesh %s", volume->name.c_str());
progress.set_status("Updating Mesh", msg);
@@ -495,7 +626,7 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress)
Shader *volume_shader = NULL;
int pad_size = 0;
- foreach (Node *node, volume->get_used_shaders()) {
+ for (Node *node : volume->get_used_shaders()) {
Shader *shader = static_cast<Shader *>(node);
if (!shader->has_volume) {
@@ -529,7 +660,9 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress)
VolumeMeshBuilder builder;
#ifdef WITH_OPENVDB
- foreach (Attribute &attr, volume->attributes.attributes) {
+ merge_scalar_grids_for_velocity(scene, volume);
+
+ for (Attribute &attr : volume->attributes.attributes) {
if (attr.element != ATTR_ELEMENT_VOXEL) {
continue;
}
@@ -567,9 +700,17 @@ void GeometryManager::create_volume_mesh(Volume *volume, Progress &progress)
}
if (grid) {
+ /* Add padding based on the maximum velocity vector. */
+ if (attr.std == ATTR_STD_VOLUME_VELOCITY && scene->need_motion() != Scene::MOTION_NONE) {
+ pad_size = max(pad_size,
+ estimate_required_velocity_padding(grid, volume->get_velocity_scale()));
+ }
+
builder.add_grid(grid, do_clipping, volume->get_clipping());
}
}
+#else
+ (void)scene;
#endif
/* If nothing to build, early out. */
diff --git a/intern/cycles/scene/volume.h b/intern/cycles/scene/volume.h
index cae0f5f5bce..2b94aaf5253 100644
--- a/intern/cycles/scene/volume.h
+++ b/intern/cycles/scene/volume.h
@@ -18,6 +18,7 @@ class Volume : public Mesh {
NODE_SOCKET_API(float, clipping)
NODE_SOCKET_API(float, step_size)
NODE_SOCKET_API(bool, object_space)
+ NODE_SOCKET_API(float, velocity_scale)
virtual void clear(bool preserve_shaders = false) override;
};
diff --git a/release/scripts/startup/bl_ui/properties_data_volume.py b/release/scripts/startup/bl_ui/properties_data_volume.py
index 9fdcd4c1faa..678972a677c 100644
--- a/release/scripts/startup/bl_ui/properties_data_volume.py
+++ b/release/scripts/startup/bl_ui/properties_data_volume.py
@@ -115,6 +115,12 @@ class DATA_PT_volume_render(DataButtonsPanel, Panel):
col = layout.column(align=True)
col.prop(render, "clipping")
+ col = layout.column(align=False)
+ col.prop(volume, "velocity_grid")
+
+ col.prop(volume, "velocity_unit")
+ col.prop(volume, "velocity_scale")
+
class DATA_PT_volume_viewport_display(DataButtonsPanel, Panel):
bl_label = "Viewport Display"
diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py
index 1b03da490b9..88f8658035b 100644
--- a/release/scripts/startup/bl_ui/properties_physics_fluid.py
+++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py
@@ -1485,6 +1485,27 @@ class PHYSICS_PT_viewport_display_advanced(PhysicButtonsPanel, Panel):
note.label(icon='INFO', text="Range highlighting for flags is not available!")
+class PHYSICS_PT_fluid_domain_render(PhysicButtonsPanel, Panel):
+ bl_label = "Render"
+ bl_parent_id = 'PHYSICS_PT_fluid'
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ @classmethod
+ def poll(cls, context):
+ if not PhysicButtonsPanel.poll_gas_domain(context):
+ return False
+
+ return (context.engine in cls.COMPAT_ENGINES)
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+
+ domain = context.fluid.domain_settings
+ layout.prop(domain, "velocity_scale")
+
+
classes = (
FLUID_PT_presets,
PHYSICS_PT_fluid,
@@ -1513,6 +1534,7 @@ classes = (
PHYSICS_PT_viewport_display_color,
PHYSICS_PT_viewport_display_debug,
PHYSICS_PT_viewport_display_advanced,
+ PHYSICS_PT_fluid_domain_render,
)
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index 77f01e7919d..8791469360a 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -77,6 +77,11 @@ const VolumeGrid *BKE_volume_grid_active_get_for_read(const struct Volume *volum
/* Tries to find a grid with the given name. Make sure that the volume has been loaded. */
const VolumeGrid *BKE_volume_grid_find_for_read(const struct Volume *volume, const char *name);
+/* Tries to set the name of the velocity field. If no such grid exists with the given base name,
+ * this will try common postfixes in order to detect velocity fields split into multiple grids.
+ * Return false if neither finding with the base name nor with the postfixes succeeded. */
+bool BKE_volume_set_velocity_grid_by_name(struct Volume *volume, const char *base_name);
+
/* Grid
*
* By default only grid metadata is loaded, for access to the tree and voxels
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 81e73b6cf2c..efb33294efd 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -5071,6 +5071,9 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd,
tfds->openvdb_compression = fds->openvdb_compression;
tfds->clipping = fds->clipping;
tfds->openvdb_data_depth = fds->openvdb_data_depth;
+
+ /* Render options. */
+ tfds->velocity_scale = fds->velocity_scale;
}
else if (tfmd->flow) {
FluidFlowSettings *tffs = tfmd->flow;
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 0c131863edd..307466d7dc9 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -60,6 +60,7 @@ using blender::float3;
using blender::float4x4;
using blender::IndexRange;
using blender::StringRef;
+using blender::StringRefNull;
#ifdef WITH_OPENVDB
# include <atomic>
@@ -517,6 +518,8 @@ static void volume_init_data(ID *id)
MEMCPY_STRUCT_AFTER(volume, DNA_struct_default_get(Volume), id);
BKE_volume_init_grids(volume);
+
+ BLI_strncpy(volume->velocity_grid, "velocity", sizeof(volume->velocity_grid));
}
static void volume_copy_data(Main *UNUSED(bmain),
@@ -794,6 +797,57 @@ bool BKE_volume_is_loaded(const Volume *volume)
#endif
}
+bool BKE_volume_set_velocity_grid_by_name(Volume *volume, const char *base_name)
+{
+ const StringRefNull ref_base_name = base_name;
+
+ if (BKE_volume_grid_find_for_read(volume, base_name)) {
+ BLI_strncpy(volume->velocity_grid, base_name, sizeof(volume->velocity_grid));
+ volume->runtime.velocity_x_grid[0] = '\0';
+ volume->runtime.velocity_y_grid[0] = '\0';
+ volume->runtime.velocity_z_grid[0] = '\0';
+ return true;
+ }
+
+ /* It could be that the velocity grid is split in multiple grids, try with known postfixes. */
+ const StringRefNull postfixes[][3] = {{"x", "y", "z"}, {".x", ".y", ".z"}, {"_x", "_y", "_z"}};
+
+ for (const StringRefNull *postfix : postfixes) {
+ bool found = true;
+ for (int i = 0; i < 3; i++) {
+ std::string post_fixed_name = ref_base_name + postfix[i];
+ if (!BKE_volume_grid_find_for_read(volume, post_fixed_name.c_str())) {
+ found = false;
+ break;
+ }
+ }
+
+ if (!found) {
+ continue;
+ }
+
+ /* Save the base name as well. */
+ BLI_strncpy(volume->velocity_grid, base_name, sizeof(volume->velocity_grid));
+ BLI_strncpy(volume->runtime.velocity_x_grid,
+ (ref_base_name + postfix[0]).c_str(),
+ sizeof(volume->runtime.velocity_x_grid));
+ BLI_strncpy(volume->runtime.velocity_y_grid,
+ (ref_base_name + postfix[1]).c_str(),
+ sizeof(volume->runtime.velocity_y_grid));
+ BLI_strncpy(volume->runtime.velocity_z_grid,
+ (ref_base_name + postfix[2]).c_str(),
+ sizeof(volume->runtime.velocity_z_grid));
+ return true;
+ }
+
+ /* Reset to avoid potential issues. */
+ volume->velocity_grid[0] = '\0';
+ volume->runtime.velocity_x_grid[0] = '\0';
+ volume->runtime.velocity_y_grid[0] = '\0';
+ volume->runtime.velocity_z_grid[0] = '\0';
+ return false;
+}
+
bool BKE_volume_load(const Volume *volume, const Main *bmain)
{
#ifdef WITH_OPENVDB
@@ -857,6 +911,14 @@ bool BKE_volume_load(const Volume *volume, const Main *bmain)
}
}
+ /* Try to detect the velocity grid. */
+ const char *common_velocity_names[] = {"velocity", "vel", "v"};
+ for (const char *common_velocity_name : common_velocity_names) {
+ if (BKE_volume_set_velocity_grid_by_name(const_cast<Volume *>(volume), common_velocity_name)) {
+ break;
+ }
+ }
+
BLI_strncpy(grids.filepath, filepath, FILE_MAX);
return grids.error_msg.empty();
diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h
index fd48585792f..90a91c6c995 100644
--- a/source/blender/makesdna/DNA_fluid_defaults.h
+++ b/source/blender/makesdna/DNA_fluid_defaults.h
@@ -187,6 +187,7 @@
.cache_comp = SM_CACHE_LIGHT, \
.cache_high_comp = SM_CACHE_LIGHT, \
.cache_file_format = 0, \
+ .velocity_scale = 1.0f, \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h
index 11780d99af8..5a1636879bb 100644
--- a/source/blender/makesdna/DNA_fluid_types.h
+++ b/source/blender/makesdna/DNA_fluid_types.h
@@ -670,7 +670,10 @@ typedef struct FluidDomainSettings {
char interp_method;
char gridlines_color_field; /* Simulation field used to color map onto gridlines. */
char gridlines_cell_filter;
- char _pad10[7]; /* Unused. */
+ char _pad10[3]; /* Unused. */
+
+ /* Velocity factor for motion blur rendering. */
+ float velocity_scale;
/* OpenVDB cache options. */
int openvdb_compression;
diff --git a/source/blender/makesdna/DNA_volume_defaults.h b/source/blender/makesdna/DNA_volume_defaults.h
index 2025d664d40..ee98f0ea4fd 100644
--- a/source/blender/makesdna/DNA_volume_defaults.h
+++ b/source/blender/makesdna/DNA_volume_defaults.h
@@ -36,7 +36,8 @@
.frame_duration = 0, \
.display = _DNA_DEFAULT_VolumeDisplay, \
.render = _DNA_DEFAULT_VolumeRender, \
- }
+ .velocity_scale = 1.0f, \
+}
/** \} */
diff --git a/source/blender/makesdna/DNA_volume_types.h b/source/blender/makesdna/DNA_volume_types.h
index f2f53bc910f..a2e558aa790 100644
--- a/source/blender/makesdna/DNA_volume_types.h
+++ b/source/blender/makesdna/DNA_volume_types.h
@@ -24,6 +24,11 @@ typedef struct Volume_Runtime {
/** Default simplify level for volume grids loaded from files. */
int default_simplify_level;
+
+ /* Names for scalar grids which would need to be merged to recompose the velocity grid. */
+ char velocity_x_grid[64];
+ char velocity_y_grid[64];
+ char velocity_z_grid[64];
} Volume_Runtime;
typedef struct VolumeDisplay {
@@ -75,6 +80,18 @@ typedef struct Volume {
VolumeRender render;
VolumeDisplay display;
+ /* Velocity field name. */
+ char velocity_grid[64];
+
+ char _pad3[3];
+
+ /* Unit of time the velocity vectors are expressed in.
+ * This uses the same enumeration values as #CacheFile.velocity_unit. */
+ char velocity_unit;
+
+ /* Factor for velocity vector for artistic control. */
+ float velocity_scale;
+
/* Draw Cache */
void *batch_cache;
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
index 6e2c898d691..6539d636697 100644
--- a/source/blender/makesrna/RNA_enum_items.h
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -212,6 +212,8 @@ DEF_ENUM(rna_enum_subdivision_boundary_smooth_items)
DEF_ENUM(rna_enum_transform_orientation_items)
+DEF_ENUM(rna_enum_velocity_unit_items)
+
/* Not available to RNA pre-processing (`makesrna`).
* Defined in editors for example. */
#ifndef RNA_MAKESRNA
diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c
index 62b08ebb281..c8b154b9b04 100644
--- a/source/blender/makesrna/intern/rna_cachefile.c
+++ b/source/blender/makesrna/intern/rna_cachefile.c
@@ -14,6 +14,12 @@
#include "rna_internal.h"
+const EnumPropertyItem rna_enum_velocity_unit_items[] = {
+ {CACHEFILE_VELOCITY_UNIT_SECOND, "SECOND", 0, "Second", ""},
+ {CACHEFILE_VELOCITY_UNIT_FRAME, "FRAME", 0, "Frame", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "BLI_math.h"
@@ -350,15 +356,9 @@ static void rna_def_cachefile(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_CacheFile_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- static const EnumPropertyItem velocity_unit_items[] = {
- {CACHEFILE_VELOCITY_UNIT_SECOND, "SECOND", 0, "Second", ""},
- {CACHEFILE_VELOCITY_UNIT_FRAME, "FRAME", 0, "Frame", ""},
- {0, NULL, 0, NULL, NULL},
- };
-
prop = RNA_def_property(srna, "velocity_unit", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "velocity_unit");
- RNA_def_property_enum_items(prop, velocity_unit_items);
+ RNA_def_property_enum_items(prop, rna_enum_velocity_unit_items);
RNA_def_property_ui_text(
prop,
"Velocity Unit",
diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c
index dab3cd68d4c..e0ec146a248 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -2644,6 +2644,12 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna)
RNA_def_property_enum_items(prop, gridlines_cell_filter_items);
RNA_def_property_ui_text(prop, "Cell Type", "Cell type to be highlighted");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "velocity_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "velocity_scale");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_text(prop, "Velocity Scale", "Factor to control the amount of motion blur");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_update");
}
static void rna_def_fluid_flow_settings(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
index 5b323629a80..12cb35b239d 100644
--- a/source/blender/makesrna/intern/rna_volume.c
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -78,6 +78,15 @@ static void rna_Volume_update_is_sequence(Main *bmain, Scene *scene, PointerRNA
DEG_relations_tag_update(bmain);
}
+static void rna_Volume_velocity_grid_set(PointerRNA *ptr, const char *value)
+{
+ Volume *volume = (Volume *)ptr->data;
+ if (!BKE_volume_set_velocity_grid_by_name(volume, value)) {
+ WM_reportf(RPT_ERROR, "Could not find grid with name %s", value);
+ }
+ WM_main_add_notifier(NC_GEOM | ND_DATA, volume);
+}
+
/* Grid */
static void rna_VolumeGrid_name_get(PointerRNA *ptr, char *value)
@@ -248,6 +257,7 @@ static void rna_def_volume_grid(BlenderRNA *brna)
RNA_def_property_string_funcs(
prop, "rna_VolumeGrid_name_get", "rna_VolumeGrid_name_length", NULL);
RNA_def_property_ui_text(prop, "Name", "Volume grid name");
+ RNA_def_struct_name_property(srna, prop);
prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -619,6 +629,55 @@ static void rna_def_volume(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "VolumeRender");
RNA_def_property_ui_text(prop, "Render", "Volume render settings for 3D viewport");
+ /* Velocity. */
+ prop = RNA_def_property(srna, "velocity_grid", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "velocity_grid");
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Volume_velocity_grid_set");
+ RNA_def_property_ui_text(
+ prop,
+ "Velocity Grid",
+ "Name of the velocity field, or the base name if the velocity is split into multiple grids");
+
+ prop = RNA_def_property(srna, "velocity_unit", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "velocity_unit");
+ RNA_def_property_enum_items(prop, rna_enum_velocity_unit_items);
+ RNA_def_property_ui_text(
+ prop,
+ "Velocity Unit",
+ "Define how the velocity vectors are interpreted with regard to time, 'frame' means "
+ "the delta time is 1 frame, 'second' means the delta time is 1 / FPS");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ prop = RNA_def_property(srna, "velocity_scale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "velocity_scale");
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_text(prop, "Velocity Scale", "Factor to control the amount of motion blur");
+
+ /* Scalar grids for velocity. */
+ prop = RNA_def_property(srna, "velocity_x_grid", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "runtime.velocity_x_grid");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Velocity X Grid",
+ "Name of the grid for the X axis component of the velocity field if it "
+ "was split into multiple grids");
+
+ prop = RNA_def_property(srna, "velocity_y_grid", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "runtime.velocity_y_grid");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Velocity Y Grid",
+ "Name of the grid for the Y axis component of the velocity field if it "
+ "was split into multiple grids");
+
+ prop = RNA_def_property(srna, "velocity_z_grid", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "runtime.velocity_z_grid");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Velocity Z Grid",
+ "Name of the grid for the Z axis component of the velocity field if it "
+ "was split into multiple grids");
+
/* Common */
rna_def_animdata_common(srna);
}