Blender  V3.3
MOD_volume_displace.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include "BKE_geometry_set.hh"
8 #include "BKE_lib_query.h"
9 #include "BKE_mesh_runtime.h"
10 #include "BKE_modifier.h"
11 #include "BKE_object.h"
12 #include "BKE_texture.h"
13 #include "BKE_volume.h"
14 
15 #include "BLT_translation.h"
16 
17 #include "DNA_mesh_types.h"
18 #include "DNA_meshdata_types.h"
19 #include "DNA_object_types.h"
20 #include "DNA_screen_types.h"
21 #include "DNA_texture_types.h"
22 #include "DNA_volume_types.h"
23 
24 #include "DEG_depsgraph_build.h"
25 #include "DEG_depsgraph_query.h"
26 
27 #include "UI_interface.h"
28 #include "UI_resources.h"
29 
30 #include "BLO_read_write.h"
31 
32 #include "MEM_guardedalloc.h"
33 
34 #include "MOD_modifiertypes.h"
35 #include "MOD_ui_common.h"
36 
37 #include "RE_texture.h"
38 
39 #include "RNA_access.h"
40 #include "RNA_prototypes.h"
41 
42 #include "BLI_math_vector.h"
43 
44 #ifdef WITH_OPENVDB
45 # include <openvdb/openvdb.h>
46 # include <openvdb/tools/Interpolation.h>
47 # include <openvdb/tools/Morphology.h>
48 # include <openvdb/tools/Prune.h>
49 # include <openvdb/tools/ValueTransformer.h>
50 #endif
51 
52 static void initData(ModifierData *md)
53 {
54  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
55  vdmd->texture = nullptr;
56  vdmd->strength = 0.5f;
57  copy_v3_fl(vdmd->texture_mid_level, 0.5f);
58  vdmd->texture_sample_radius = 1.0f;
59 }
60 
62 {
63  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
64  if (vdmd->texture != nullptr) {
65  DEG_add_generic_id_relation(ctx->node, &vdmd->texture->id, "Volume Displace Modifier");
66  }
68  if (vdmd->texture_map_object != nullptr) {
70  ctx->node, vdmd->texture_map_object, DEG_OB_COMP_TRANSFORM, "Volume Displace Modifier");
71  }
72  }
73 }
74 
75 static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
76 {
77  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
78  walk(userData, ob, (ID **)&vdmd->texture, IDWALK_CB_USER);
79  walk(userData, ob, (ID **)&vdmd->texture_map_object, IDWALK_CB_USER);
80 }
81 
82 static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
83 {
84  walk(userData, ob, md, "texture");
85 }
86 
87 static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *md)
88 {
89  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
90  if (vdmd->texture) {
91  return BKE_texture_dependsOnTime(vdmd->texture);
92  }
93  return false;
94 }
95 
96 static void panel_draw(const bContext *C, Panel *panel)
97 {
98  uiLayout *layout = panel->layout;
99 
100  PointerRNA ob_ptr;
103 
104  uiLayoutSetPropSep(layout, true);
105 
106  uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
107  uiItemR(layout, ptr, "texture_map_mode", 0, "Texture Mapping", ICON_NONE);
108 
110  uiItemR(layout, ptr, "texture_map_object", 0, "Object", ICON_NONE);
111  }
112 
113  uiItemR(layout, ptr, "strength", 0, nullptr, ICON_NONE);
114  uiItemR(layout, ptr, "texture_sample_radius", 0, "Sample Radius", ICON_NONE);
115  uiItemR(layout, ptr, "texture_mid_level", 0, "Mid Level", ICON_NONE);
116 
117  modifier_panel_end(layout, ptr);
118 }
119 
120 static void panelRegister(ARegionType *region_type)
121 {
123 }
124 
125 #ifdef WITH_OPENVDB
126 
127 static openvdb::Mat4s matrix_to_openvdb(const float m[4][4])
128 {
129  /* OpenVDB matrices are transposed Blender matrices, i.e. the translation is in the last row
130  * instead of in the last column. However, the layout in memory is the same, because OpenVDB
131  * matrices are row major (compared to Blender's column major matrices). */
132  openvdb::Mat4s new_matrix{reinterpret_cast<const float *>(m)};
133  return new_matrix;
134 }
135 
136 template<typename GridType> struct DisplaceOp {
137  /* Has to be copied for each thread. */
138  typename GridType::ConstAccessor accessor;
139  const openvdb::Mat4s index_to_texture;
140 
141  Tex *texture;
142  const double strength;
143  const openvdb::Vec3d texture_mid_level;
144 
145  void operator()(const typename GridType::ValueOnIter &iter) const
146  {
147  const openvdb::Coord coord = iter.getCoord();
148  const openvdb::Vec3d displace_vector = this->compute_displace_vector(coord);
149  /* Subtract vector because that makes the result more similar to advection and the mesh
150  * displace modifier. */
151  const openvdb::Vec3d sample_coord = coord.asVec3d() - displace_vector;
152  const auto new_value = openvdb::tools::BoxSampler::sample(this->accessor, sample_coord);
153  iter.setValue(new_value);
154  }
155 
156  openvdb::Vec3d compute_displace_vector(const openvdb::Coord &coord) const
157  {
158  if (this->texture != nullptr) {
159  const openvdb::Vec3f texture_pos = coord.asVec3s() * this->index_to_texture;
160  const openvdb::Vec3d texture_value = this->evaluate_texture(texture_pos);
161  const openvdb::Vec3d displacement = (texture_value - this->texture_mid_level) *
162  this->strength;
163  return displacement;
164  }
165  return openvdb::Vec3d{0, 0, 0};
166  }
167 
168  openvdb::Vec3d evaluate_texture(const openvdb::Vec3f &pos) const
169  {
170  TexResult texture_result = {0};
172  nullptr, this->texture, const_cast<float *>(pos.asV()), &texture_result, false);
173  return {texture_result.trgba[0], texture_result.trgba[1], texture_result.trgba[2]};
174  }
175 };
176 
177 static float get_max_voxel_side_length(const openvdb::GridBase &grid)
178 {
179  const openvdb::Vec3d voxel_size = grid.voxelSize();
180  const float max_voxel_side_length = std::max({voxel_size[0], voxel_size[1], voxel_size[2]});
181  return max_voxel_side_length;
182 }
183 
184 struct DisplaceGridOp {
185  /* This is the grid that will be displaced. The output is copied back to the original grid. */
186  openvdb::GridBase &base_grid;
187 
189  const ModifierEvalContext &ctx;
190 
191  template<typename GridType> void operator()()
192  {
193  if constexpr (blender::
194  is_same_any_v<GridType, openvdb::points::PointDataGrid, openvdb::MaskGrid>) {
195  /* We don't support displacing these grid types yet. */
196  return;
197  }
198  else {
199  this->displace_grid<GridType>();
200  }
201  }
202 
203  template<typename GridType> void displace_grid()
204  {
205  GridType &grid = static_cast<GridType &>(base_grid);
206 
207  /* Make a copy of the original grid to work on. This will replace the original grid. */
208  typename GridType::Ptr temp_grid = grid.deepCopy();
209 
210  /* Dilate grid, because the currently inactive cells might become active during the displace
211  * operation. The quality of the approximation of the has a big impact on performance. */
212  const float max_voxel_side_length = get_max_voxel_side_length(grid);
213  const float sample_radius = vdmd.texture_sample_radius * std::abs(vdmd.strength) /
214  max_voxel_side_length / 2.0f;
215  openvdb::tools::dilateActiveValues(temp_grid->tree(),
216  static_cast<int>(std::ceil(sample_radius)),
217  openvdb::tools::NN_FACE_EDGE,
218  openvdb::tools::EXPAND_TILES);
219 
220  const openvdb::Mat4s index_to_texture = this->get_index_to_texture_transform();
221 
222  /* Construct the operator that will be executed on every cell of the dilated grid. */
223  DisplaceOp<GridType> displace_op{grid.getConstAccessor(),
224  index_to_texture,
225  vdmd.texture,
226  vdmd.strength / max_voxel_side_length,
228 
229  /* Run the operator. This is multi-threaded. It is important that the operator is not shared
230  * between the threads, because it contains a non-thread-safe accessor for the old grid. */
231  openvdb::tools::foreach (temp_grid->beginValueOn(),
232  displace_op,
233  true,
234  /* Disable sharing of the operator. */ false);
235 
236  /* It is likely that we produced too many active cells. Those are removed here, to avoid
237  * slowing down subsequent operations. */
238  typename GridType::ValueType prune_tolerance{0};
239  openvdb::tools::deactivate(*temp_grid, temp_grid->background(), prune_tolerance);
240  openvdb::tools::prune(temp_grid->tree());
241 
242  /* Overwrite the old volume grid with the new grid. */
243  grid.clear();
244  grid.merge(*temp_grid);
245  }
246 
247  openvdb::Mat4s get_index_to_texture_transform() const
248  {
249  const openvdb::Mat4s index_to_object{
250  base_grid.transform().baseMap()->getAffineMap()->getMat4()};
251 
252  switch (vdmd.texture_map_mode) {
254  return index_to_object;
255  }
257  const openvdb::Mat4s object_to_world = matrix_to_openvdb(ctx.object->obmat);
258  return index_to_object * object_to_world;
259  }
261  if (vdmd.texture_map_object == nullptr) {
262  return index_to_object;
263  }
264  const openvdb::Mat4s object_to_world = matrix_to_openvdb(ctx.object->obmat);
265  const openvdb::Mat4s world_to_texture = matrix_to_openvdb(vdmd.texture_map_object->imat);
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 
276 static 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. */
282  BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
283  const int grid_amount = BKE_volume_num_grids(volume);
284  for (int grid_index = 0; grid_index < grid_amount; grid_index++) {
285  VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, grid_index);
286  BLI_assert(volume_grid != nullptr);
287 
288  openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
289  VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
290 
291  DisplaceGridOp displace_grid_op{*grid, *vdmd, *ctx};
292  BKE_volume_grid_type_operation(grid_type, displace_grid_op);
293  }
294 
295 #else
296  UNUSED_VARS(md, volume, ctx);
297  BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB");
298 #endif
299 }
300 
302  const ModifierEvalContext *ctx,
303  GeometrySet *geometry_set)
304 {
305  Volume *input_volume = geometry_set->get_volume_for_write();
306  if (input_volume != nullptr) {
307  displace_volume(md, ctx, input_volume);
308  }
309 }
310 
312  /* name */ N_("Volume Displace"),
313  /* structName */ "VolumeDisplaceModifierData",
314  /* structSize */ sizeof(VolumeDisplaceModifierData),
315  /* srna */ &RNA_VolumeDisplaceModifier,
317  /* flags */ static_cast<ModifierTypeFlag>(0),
318  /* icon */ ICON_VOLUME_DATA, /* TODO: Use correct icon. */
319 
320  /* copyData */ BKE_modifier_copydata_generic,
321 
322  /* deformVerts */ nullptr,
323  /* deformMatrices */ nullptr,
324  /* deformVertsEM */ nullptr,
325  /* deformMatricesEM */ nullptr,
326  /* modifyMesh */ nullptr,
327  /* modifyGeometrySet */ modifyGeometrySet,
328 
329  /* initData */ initData,
330  /* requiredDataMask */ nullptr,
331  /* freeData */ nullptr,
332  /* isDisabled */ nullptr,
333  /* updateDepsgraph */ updateDepsgraph,
334  /* dependsOnTime */ dependsOnTime,
335  /* dependsOnNormals */ nullptr,
336  /* foreachIDLink */ foreachIDLink,
337  /* foreachTexLink */ foreachTexLink,
338  /* freeRuntimeData */ nullptr,
339  /* panelRegister */ panelRegister,
340  /* blendWrite */ nullptr,
341  /* blendRead */ nullptr,
342 };
@ IDWALK_CB_USER
Definition: BKE_lib_query.h:73
void(* IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag)
Definition: BKE_modifier.h:107
ModifierTypeFlag
Definition: BKE_modifier.h:65
void(* TexWalkFunc)(void *userData, struct Object *ob, struct ModifierData *md, const char *propname)
Definition: BKE_modifier.h:108
void BKE_modifier_copydata_generic(const struct ModifierData *md, struct ModifierData *md_dst, int flag)
@ eModifierTypeType_NonGeometrical
Definition: BKE_modifier.h:62
void BKE_modifier_set_error(const struct Object *ob, struct ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
General operations, lookup, etc. for blender objects.
bool BKE_texture_dependsOnTime(const struct Tex *texture)
Definition: texture.c:670
void BKE_texture_get_value(const struct Scene *scene, struct Tex *texture, const float *tex_co, struct TexResult *texres, bool use_color_management)
Volume data-block.
VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid)
VolumeGridType
Definition: BKE_volume.h:90
VolumeGrid * BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index)
Definition: volume.cc:1277
int BKE_volume_num_grids(const struct Volume *volume)
bool BKE_volume_load(const struct Volume *volume, const struct Main *bmain)
#define BLI_assert(a)
Definition: BLI_assert.h:46
MINLINE void copy_v3_fl(float r[3], float f)
#define UNUSED_VARS(...)
#define UNUSED(x)
void DEG_add_object_relation(struct DepsNodeHandle *node_handle, struct Object *object, eDepsObjectComponentType component, const char *description)
void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle, struct ID *id, const char *description)
@ DEG_OB_COMP_TRANSFORM
struct Main * DEG_get_bmain(const Depsgraph *graph)
@ MOD_VOLUME_DISPLACE_MAP_GLOBAL
@ MOD_VOLUME_DISPLACE_MAP_LOCAL
@ MOD_VOLUME_DISPLACE_MAP_OBJECT
struct VolumeDisplaceModifierData VolumeDisplaceModifierData
@ eModifierType_VolumeDisplace
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
Definition: MOD_ui_common.c:91
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *md)
ModifierTypeInfo modifierType_VolumeDisplace
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
static void displace_volume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
static void modifyGeometrySet(ModifierData *md, const ModifierEvalContext *ctx, GeometrySet *geometry_set)
static void initData(ModifierData *md)
static void panelRegister(ARegionType *region_type)
static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
static void panel_draw(const bContext *C, Panel *panel)
#define C
Definition: RandGen.cpp:25
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiTemplateID(uiLayout *layout, const struct bContext *C, struct PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int filter, bool live_icon, const char *text)
SIMD_FORCE_INLINE btVector3 operator()(const btVector3 &x) const
Return the transform of the vector.
Definition: btTransform.h:90
Scene scene
struct Vec3f Vec3f
#define foreach(x, y)
Definition: foreach.h:9
uint pos
ccl_device_inline float3 ceil(const float3 &a)
Definition: math_float3.h:363
VecMat::Vec3< double > Vec3d
Definition: Geom.h:27
std::unique_ptr< T > Ptr
Definition: BLI_any.hh:58
T abs(const T &a)
Volume * get_volume_for_write()
Definition: DNA_ID.h:368
struct Depsgraph * depsgraph
Definition: BKE_modifier.h:140
struct Object * object
Definition: BKE_modifier.h:141
struct DepsNodeHandle * node
Definition: BKE_modifier.h:134
float imat[4][4]
float obmat[4][4]
struct uiLayout * layout
void * data
Definition: RNA_types.h:38
float trgba[4]
Definition: RE_texture.h:87
float max
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480