Blender V4.3
MOD_volume_displace.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_geometry_set.hh"
10#include "BKE_lib_query.hh"
11#include "BKE_modifier.hh"
12#include "BKE_texture.h"
13#include "BKE_volume.hh"
14#include "BKE_volume_grid.hh"
15#include "BKE_volume_openvdb.hh"
16
17#include "BLT_translation.hh"
18
19#include "DNA_object_types.h"
20#include "DNA_screen_types.h"
21#include "DNA_texture_types.h"
22
25
26#include "UI_interface.hh"
27#include "UI_resources.hh"
28
29#include "MEM_guardedalloc.h"
30
31#include "MOD_ui_common.hh"
32
33#include "RE_texture.h"
34
35#include "RNA_access.hh"
36#include "RNA_prototypes.hh"
37
38#include "BLI_math_vector.h"
39
40#ifdef WITH_OPENVDB
41# include <openvdb/openvdb.h>
42# include <openvdb/tools/Interpolation.h>
43# include <openvdb/tools/Morphology.h>
44# include <openvdb/tools/Prune.h>
45# include <openvdb/tools/ValueTransformer.h>
46#endif
47
48static void init_data(ModifierData *md)
49{
50 VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
51 vdmd->texture = nullptr;
52 vdmd->strength = 0.5f;
53 copy_v3_fl(vdmd->texture_mid_level, 0.5f);
54 vdmd->texture_sample_radius = 1.0f;
55}
56
58{
59 VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
60 if (vdmd->texture != nullptr) {
61 DEG_add_generic_id_relation(ctx->node, &vdmd->texture->id, "Volume Displace Modifier");
62 }
64 if (vdmd->texture_map_object != nullptr) {
66 ctx->node, vdmd->texture_map_object, DEG_OB_COMP_TRANSFORM, "Volume Displace Modifier");
67 }
68 }
69}
70
71static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
72{
73 VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
74 walk(user_data, ob, (ID **)&vdmd->texture, IDWALK_CB_USER);
75 walk(user_data, ob, (ID **)&vdmd->texture_map_object, IDWALK_CB_USER);
76}
77
78static void foreach_tex_link(ModifierData *md, Object *ob, TexWalkFunc walk, void *user_data)
79{
80 PointerRNA ptr = RNA_pointer_create(&ob->id, &RNA_Modifier, md);
81 PropertyRNA *prop = RNA_struct_find_property(&ptr, "texture");
82 walk(user_data, ob, md, &ptr, prop);
83}
84
85static bool depends_on_time(Scene * /*scene*/, ModifierData *md)
86{
87 VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
88 if (vdmd->texture) {
90 }
91 return false;
92}
93
94static void panel_draw(const bContext *C, Panel *panel)
95{
96 uiLayout *layout = panel->layout;
97
98 PointerRNA ob_ptr;
101
102 uiLayoutSetPropSep(layout, true);
103
104 uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr);
105 uiItemR(layout, ptr, "texture_map_mode", UI_ITEM_NONE, IFACE_("Texture Mapping"), ICON_NONE);
106
108 uiItemR(layout, ptr, "texture_map_object", UI_ITEM_NONE, IFACE_("Object"), ICON_NONE);
109 }
110
111 uiItemR(layout, ptr, "strength", UI_ITEM_NONE, nullptr, ICON_NONE);
112 uiItemR(layout, ptr, "texture_sample_radius", UI_ITEM_NONE, IFACE_("Sample Radius"), ICON_NONE);
113 uiItemR(layout, ptr, "texture_mid_level", UI_ITEM_NONE, IFACE_("Mid Level"), ICON_NONE);
114
115 modifier_panel_end(layout, ptr);
116}
117
118static void panel_register(ARegionType *region_type)
119{
121}
122
123#ifdef WITH_OPENVDB
124
125static openvdb::Mat4s matrix_to_openvdb(const blender::float4x4 &m)
126{
127 /* OpenVDB matrices are transposed Blender matrices, i.e. the translation is in the last row
128 * instead of in the last column. However, the layout in memory is the same, because OpenVDB
129 * matrices are row major (compared to Blender's column major matrices). */
130 openvdb::Mat4s new_matrix{m.base_ptr()};
131 return new_matrix;
132}
133
134template<typename GridType> struct DisplaceOp {
135 /* Has to be copied for each thread. */
136 typename GridType::ConstAccessor accessor;
137 const openvdb::Mat4s index_to_texture;
138
139 Tex *texture;
140 const double strength;
141 const openvdb::Vec3d texture_mid_level;
142
143 void operator()(const typename GridType::ValueOnIter &iter) const
144 {
145 const openvdb::Coord coord = iter.getCoord();
146 const openvdb::Vec3d displace_vector = this->compute_displace_vector(coord);
147 /* Subtract vector because that makes the result more similar to advection and the mesh
148 * displace modifier. */
149 const openvdb::Vec3d sample_coord = coord.asVec3d() - displace_vector;
150 const auto new_value = openvdb::tools::BoxSampler::sample(this->accessor, sample_coord);
151 iter.setValue(new_value);
152 }
153
154 openvdb::Vec3d compute_displace_vector(const openvdb::Coord &coord) const
155 {
156 if (this->texture != nullptr) {
157 const openvdb::Vec3f texture_pos = coord.asVec3s() * this->index_to_texture;
158 const openvdb::Vec3d texture_value = this->evaluate_texture(texture_pos);
159 const openvdb::Vec3d displacement = (texture_value - this->texture_mid_level) *
160 this->strength;
161 return displacement;
162 }
163 return openvdb::Vec3d{0, 0, 0};
164 }
165
166 openvdb::Vec3d evaluate_texture(const openvdb::Vec3f &pos) const
167 {
168 TexResult texture_result = {0};
169 BKE_texture_get_value(this->texture, const_cast<float *>(pos.asV()), &texture_result, false);
170 return {texture_result.trgba[0], texture_result.trgba[1], texture_result.trgba[2]};
171 }
172};
173
174static float get_max_voxel_side_length(const openvdb::GridBase &grid)
175{
176 const openvdb::Vec3d voxel_size = grid.voxelSize();
177 const float max_voxel_side_length = std::max({voxel_size[0], voxel_size[1], voxel_size[2]});
178 return max_voxel_side_length;
179}
180
181struct DisplaceGridOp {
182 /* This is the grid that will be displaced. The output is copied back to the original grid. */
183 openvdb::GridBase &base_grid;
184
186 const ModifierEvalContext &ctx;
187
188 template<typename GridType> void operator()()
189 {
190 if constexpr (blender::
191 is_same_any_v<GridType, openvdb::points::PointDataGrid, openvdb::MaskGrid>)
192 {
193 /* We don't support displacing these grid types yet. */
194 return;
195 }
196 else {
197 this->displace_grid<GridType>();
198 }
199 }
200
201 template<typename GridType> void displace_grid()
202 {
203 GridType &grid = static_cast<GridType &>(base_grid);
204
205 /* Make a copy of the original grid to work on. This will replace the original grid. */
206 typename GridType::Ptr temp_grid = grid.deepCopy();
207
208 /* Dilate grid, because the currently inactive cells might become active during the displace
209 * operation. The quality of the approximation of the has a big impact on performance. */
210 const float max_voxel_side_length = get_max_voxel_side_length(grid);
211 const float sample_radius = vdmd.texture_sample_radius * std::abs(vdmd.strength) /
212 max_voxel_side_length / 2.0f;
213 openvdb::tools::dilateActiveValues(temp_grid->tree(),
214 int(std::ceil(sample_radius)),
215 openvdb::tools::NN_FACE_EDGE,
216 openvdb::tools::EXPAND_TILES);
217
218 const openvdb::Mat4s index_to_texture = this->get_index_to_texture_transform();
219
220 /* Construct the operator that will be executed on every cell of the dilated grid. */
221 DisplaceOp<GridType> displace_op{grid.getConstAccessor(),
222 index_to_texture,
223 vdmd.texture,
224 vdmd.strength / max_voxel_side_length,
225 openvdb::Vec3d{vdmd.texture_mid_level}};
226
227 /* Run the operator. This is multi-threaded. It is important that the operator is not shared
228 * between the threads, because it contains a non-thread-safe accessor for the old grid. */
229 openvdb::tools::foreach (temp_grid->beginValueOn(),
230 displace_op,
231 true,
232 /* Disable sharing of the operator. */
233 false);
234
235 /* It is likely that we produced too many active cells. Those are removed here, to avoid
236 * slowing down subsequent operations. */
237 typename GridType::ValueType prune_tolerance{0};
238 openvdb::tools::deactivate(*temp_grid, temp_grid->background(), prune_tolerance);
239 openvdb::tools::prune(temp_grid->tree());
240
241 /* Overwrite the old volume grid with the new grid. */
242 grid.clear();
243 grid.merge(*temp_grid);
244 }
245
246 openvdb::Mat4s get_index_to_texture_transform() const
247 {
248 const openvdb::Mat4s index_to_object{
249 base_grid.transform().baseMap()->getAffineMap()->getMat4()};
250
251 switch (vdmd.texture_map_mode) {
253 return index_to_object;
254 }
256 const openvdb::Mat4s object_to_world = matrix_to_openvdb(ctx.object->object_to_world());
257 return index_to_object * object_to_world;
258 }
260 if (vdmd.texture_map_object == nullptr) {
261 return index_to_object;
262 }
263 const openvdb::Mat4s object_to_world = matrix_to_openvdb(ctx.object->object_to_world());
264 const openvdb::Mat4s world_to_texture = matrix_to_openvdb(
265 vdmd.texture_map_object->world_to_object());
266 return index_to_object * object_to_world * world_to_texture;
267 }
268 }
269 BLI_assert(false);
270 return {};
271 }
272};
273
274#endif
275
276static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
277{
278#ifdef WITH_OPENVDB
279 VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
280
281 /* Iterate over all grids and displace them one by one. */
283 const int grid_amount = BKE_volume_num_grids(volume);
284 for (int grid_index = 0; grid_index < grid_amount; grid_index++) {
285 blender::bke::VolumeGridData *volume_grid = BKE_volume_grid_get_for_write(volume, grid_index);
286 BLI_assert(volume_grid);
287
288 blender::bke::VolumeTreeAccessToken tree_token;
289 openvdb::GridBase &grid = volume_grid->grid_for_write(tree_token);
290 VolumeGridType grid_type = volume_grid->grid_type();
291
292 DisplaceGridOp displace_grid_op{grid, *vdmd, *ctx};
293 BKE_volume_grid_type_operation(grid_type, displace_grid_op);
294 }
295
296#else
297 UNUSED_VARS(md, volume, ctx);
298 BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB");
299#endif
300}
301
303 const ModifierEvalContext *ctx,
304 blender::bke::GeometrySet *geometry_set)
305{
306 Volume *input_volume = geometry_set->get_volume_for_write();
307 if (input_volume != nullptr) {
308 displace_volume(md, ctx, input_volume);
309 }
310}
311
313 /*idname*/ "Volume Displace",
314 /*name*/ N_("Volume Displace"),
315 /*struct_name*/ "VolumeDisplaceModifierData",
316 /*struct_size*/ sizeof(VolumeDisplaceModifierData),
317 /*srna*/ &RNA_VolumeDisplaceModifier,
319 /*flags*/ static_cast<ModifierTypeFlag>(0),
320 /*icon*/ ICON_VOLUME_DATA, /* TODO: Use correct icon. */
321
322 /*copy_data*/ BKE_modifier_copydata_generic,
323
324 /*deform_verts*/ nullptr,
325 /*deform_matrices*/ nullptr,
326 /*deform_verts_EM*/ nullptr,
327 /*deform_matrices_EM*/ nullptr,
328 /*modify_mesh*/ nullptr,
329 /*modify_geometry_set*/ modify_geometry_set,
330
331 /*init_data*/ init_data,
332 /*required_data_mask*/ nullptr,
333 /*free_data*/ nullptr,
334 /*is_disabled*/ nullptr,
335 /*update_depsgraph*/ update_depsgraph,
336 /*depends_on_time*/ depends_on_time,
337 /*depends_on_normals*/ nullptr,
338 /*foreach_ID_link*/ foreach_ID_link,
339 /*foreach_tex_link*/ foreach_tex_link,
340 /*free_runtime_data*/ nullptr,
341 /*panel_register*/ panel_register,
342 /*blend_write*/ nullptr,
343 /*blend_read*/ nullptr,
344 /*foreach_cache*/ nullptr,
345};
@ IDWALK_CB_USER
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
void(*)(void *user_data, Object *ob, ModifierData *md, const PointerRNA *ptr, PropertyRNA *texture_prop) TexWalkFunc
ModifierTypeFlag
void(*)(void *user_data, Object *ob, ID **idpoin, int cb_flag) IDWalkFunc
void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_texture_get_value(struct Tex *texture, const float *tex_co, struct TexResult *texres, bool use_color_management)
Definition texture.cc:722
bool BKE_texture_dependsOnTime(const struct Tex *texture)
Volume data-block.
int BKE_volume_num_grids(const Volume *volume)
bool BKE_volume_load(const Volume *volume, const Main *bmain)
blender::bke::VolumeGridData * BKE_volume_grid_get_for_write(Volume *volume, int grid_index)
VolumeGridType
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE void copy_v3_fl(float r[3], float f)
#define UNUSED_VARS(...)
#define IFACE_(msgid)
void DEG_add_generic_id_relation(DepsNodeHandle *node_handle, ID *id, const char *description)
void DEG_add_object_relation(DepsNodeHandle *node_handle, Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_TRANSFORM
Main * DEG_get_bmain(const Depsgraph *graph)
struct VolumeDisplaceModifierData VolumeDisplaceModifierData
@ eModifierType_VolumeDisplace
@ MOD_VOLUME_DISPLACE_MAP_GLOBAL
@ MOD_VOLUME_DISPLACE_MAP_LOCAL
@ MOD_VOLUME_DISPLACE_MAP_OBJECT
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
ModifierTypeInfo modifierType_VolumeDisplace
static bool depends_on_time(Scene *, ModifierData *md)
static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
static void foreach_tex_link(ModifierData *md, Object *ob, TexWalkFunc walk, void *user_data)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, blender::bke::GeometrySet *geometry_set)
static void panel_draw(const bContext *C, Panel *panel)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiTemplateID(uiLayout *layout, const bContext *C, PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int filter=UI_TEMPLATE_ID_FILTER_ALL, bool live_icon=false, const char *text=nullptr)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition btTransform.h:90
local_group_size(16, 16) .push_constant(Type texture
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
Definition DNA_ID.h:413
struct uiLayout * layout
void * data
Definition RNA_types.hh:42
float trgba[4]
Definition RE_texture.h:88
const T * base_ptr() const
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4126