Blender  V2.93
MOD_volume_displace.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include "BKE_lib_query.h"
22 #include "BKE_mesh_runtime.h"
23 #include "BKE_modifier.h"
24 #include "BKE_object.h"
25 #include "BKE_texture.h"
26 #include "BKE_volume.h"
27 
28 #include "DNA_mesh_types.h"
29 #include "DNA_meshdata_types.h"
30 #include "DNA_object_types.h"
31 #include "DNA_screen_types.h"
32 #include "DNA_texture_types.h"
33 #include "DNA_volume_types.h"
34 
35 #include "DEG_depsgraph_build.h"
36 #include "DEG_depsgraph_query.h"
37 
38 #include "UI_interface.h"
39 #include "UI_resources.h"
40 
41 #include "BLO_read_write.h"
42 
43 #include "MEM_guardedalloc.h"
44 
45 #include "MOD_modifiertypes.h"
46 #include "MOD_ui_common.h"
47 
48 #include "RE_texture.h"
49 
50 #include "RNA_access.h"
51 
52 #include "BLI_math_vector.h"
53 
54 #ifdef WITH_OPENVDB
55 # include <openvdb/openvdb.h>
56 # include <openvdb/tools/Interpolation.h>
57 # include <openvdb/tools/Morphology.h>
58 # include <openvdb/tools/Prune.h>
59 # include <openvdb/tools/ValueTransformer.h>
60 #endif
61 
62 static void initData(ModifierData *md)
63 {
64  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
65  vdmd->texture = nullptr;
66  vdmd->strength = 0.5f;
67  copy_v3_fl(vdmd->texture_mid_level, 0.5f);
68  vdmd->texture_sample_radius = 1.0f;
69 }
70 
72 {
73  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
74  if (vdmd->texture != nullptr) {
75  DEG_add_generic_id_relation(ctx->node, &vdmd->texture->id, "Volume Displace Modifier");
76  }
78  if (vdmd->texture_map_object != nullptr) {
80  ctx->node, vdmd->texture_map_object, DEG_OB_COMP_TRANSFORM, "Volume Displace Modifier");
81  }
82  }
83 }
84 
85 static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
86 {
87  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
88  walk(userData, ob, (ID **)&vdmd->texture, IDWALK_CB_USER);
89  walk(userData, ob, (ID **)&vdmd->texture_map_object, IDWALK_CB_USER);
90 }
91 
92 static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
93 {
94  walk(userData, ob, md, "texture");
95 }
96 
97 static bool dependsOnTime(ModifierData *md)
98 {
99  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
100  if (vdmd->texture) {
101  return BKE_texture_dependsOnTime(vdmd->texture);
102  }
103  return false;
104 }
105 
106 static void panel_draw(const bContext *C, Panel *panel)
107 {
108  uiLayout *layout = panel->layout;
109 
110  PointerRNA ob_ptr;
113 
114  uiLayoutSetPropSep(layout, true);
115 
116  uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
117  uiItemR(layout, ptr, "texture_map_mode", 0, "Texture Mapping", ICON_NONE);
118 
120  uiItemR(layout, ptr, "texture_map_object", 0, "Object", ICON_NONE);
121  }
122 
123  uiItemR(layout, ptr, "strength", 0, nullptr, ICON_NONE);
124  uiItemR(layout, ptr, "texture_sample_radius", 0, "Sample Radius", ICON_NONE);
125  uiItemR(layout, ptr, "texture_mid_level", 0, "Mid Level", ICON_NONE);
126 
127  modifier_panel_end(layout, ptr);
128 }
129 
130 static void panelRegister(ARegionType *region_type)
131 {
133 }
134 
135 #ifdef WITH_OPENVDB
136 
137 static openvdb::Mat4s matrix_to_openvdb(const float m[4][4])
138 {
139  /* OpenVDB matrices are transposed Blender matrices, i.e. the translation is in the last row
140  * instead of in the last column. However, the layout in memory is the same, because OpenVDB
141  * matrices are row major (compared to Blender's column major matrices). */
142  openvdb::Mat4s new_matrix{reinterpret_cast<const float *>(m)};
143  return new_matrix;
144 }
145 
146 template<typename GridType> struct DisplaceOp {
147  /* Has to be copied for each thread. */
148  typename GridType::ConstAccessor accessor;
149  const openvdb::Mat4s index_to_texture;
150 
151  Tex *texture;
152  const double strength;
153  const openvdb::Vec3d texture_mid_level;
154 
155  void operator()(const typename GridType::ValueOnIter &iter) const
156  {
157  const openvdb::Coord coord = iter.getCoord();
158  const openvdb::Vec3d displace_vector = this->compute_displace_vector(coord);
159  /* Subtract vector because that makes the result more similar to advection and the mesh
160  * displace modifier. */
161  const openvdb::Vec3d sample_coord = coord.asVec3d() - displace_vector;
162  const auto new_value = openvdb::tools::BoxSampler::sample(this->accessor, sample_coord);
163  iter.setValue(new_value);
164  }
165 
166  openvdb::Vec3d compute_displace_vector(const openvdb::Coord &coord) const
167  {
168  if (this->texture != nullptr) {
169  const openvdb::Vec3f texture_pos = coord.asVec3s() * this->index_to_texture;
170  const openvdb::Vec3d texture_value = this->evaluate_texture(texture_pos);
171  const openvdb::Vec3d displacement = (texture_value - this->texture_mid_level) *
172  this->strength;
173  return displacement;
174  }
175  return openvdb::Vec3d{0, 0, 0};
176  }
177 
178  openvdb::Vec3d evaluate_texture(const openvdb::Vec3f &pos) const
179  {
180  TexResult texture_result = {0};
182  nullptr, this->texture, const_cast<float *>(pos.asV()), &texture_result, false);
183  return {texture_result.tr, texture_result.tg, texture_result.tb};
184  }
185 };
186 
187 static float get_max_voxel_side_length(const openvdb::GridBase &grid)
188 {
189  const openvdb::Vec3d voxel_size = grid.voxelSize();
190  const float max_voxel_side_length = std::max({voxel_size[0], voxel_size[1], voxel_size[2]});
191  return max_voxel_side_length;
192 }
193 
194 struct DisplaceGridOp {
195  /* This is the grid that will be displaced. The output is copied back to the original grid. */
196  openvdb::GridBase &base_grid;
197 
199  const ModifierEvalContext &ctx;
200 
201  template<typename GridType> void operator()()
202  {
203  if constexpr (std::is_same_v<GridType, openvdb::points::PointDataGrid> ||
204  std::is_same_v<GridType, openvdb::StringGrid> ||
205  std::is_same_v<GridType, openvdb::MaskGrid>) {
206  /* We don't support displacing these grid types yet. */
207  return;
208  }
209  else {
210  this->displace_grid<GridType>();
211  }
212  }
213 
214  template<typename GridType> void displace_grid()
215  {
216  GridType &grid = static_cast<GridType &>(base_grid);
217 
218  /* Make a copy of the original grid to work on. This will replace the original grid. */
219  typename GridType::Ptr temp_grid = grid.deepCopy();
220 
221  /* Dilate grid, because the currently inactive cells might become active during the displace
222  * operation. The quality of the approximation of the has a big impact on performance. */
223  const float max_voxel_side_length = get_max_voxel_side_length(grid);
224  const float sample_radius = vdmd.texture_sample_radius * std::abs(vdmd.strength) /
225  max_voxel_side_length / 2.0f;
226  openvdb::tools::dilateActiveValues(temp_grid->tree(),
227  static_cast<int>(std::ceil(sample_radius)),
228  openvdb::tools::NN_FACE_EDGE,
229  openvdb::tools::EXPAND_TILES);
230 
231  const openvdb::Mat4s index_to_texture = this->get_index_to_texture_transform();
232 
233  /* Construct the operator that will be executed on every cell of the dilated grid. */
234  DisplaceOp<GridType> displace_op{grid.getConstAccessor(),
235  index_to_texture,
236  vdmd.texture,
237  vdmd.strength / max_voxel_side_length,
239 
240  /* Run the operator. This is multi-threaded. It is important that the operator is not shared
241  * between the threads, because it contains a non-thread-safe accessor for the old grid. */
242  openvdb::tools::foreach (temp_grid->beginValueOn(),
243  displace_op,
244  true,
245  /* Disable sharing of the operator. */ false);
246 
247  /* It is likely that we produced too many active cells. Those are removed here, to avoid
248  * slowing down subsequent operations. */
249  typename GridType::ValueType prune_tolerance{0};
250  openvdb::tools::deactivate(*temp_grid, temp_grid->background(), prune_tolerance);
251  openvdb::tools::prune(temp_grid->tree());
252 
253  /* Overwrite the old volume grid with the new grid. */
254  grid.clear();
255  grid.merge(*temp_grid);
256  }
257 
258  openvdb::Mat4s get_index_to_texture_transform() const
259  {
260  const openvdb::Mat4s index_to_object{
261  base_grid.transform().baseMap()->getAffineMap()->getMat4()};
262 
263  switch (vdmd.texture_map_mode) {
265  return index_to_object;
266  }
268  const openvdb::Mat4s object_to_world = matrix_to_openvdb(ctx.object->obmat);
269  return index_to_object * object_to_world;
270  }
272  if (vdmd.texture_map_object == nullptr) {
273  return index_to_object;
274  }
275  const openvdb::Mat4s object_to_world = matrix_to_openvdb(ctx.object->obmat);
276  const openvdb::Mat4s world_to_texture = matrix_to_openvdb(vdmd.texture_map_object->imat);
277  return index_to_object * object_to_world * world_to_texture;
278  }
279  }
280  BLI_assert(false);
281  return {};
282  }
283 };
284 
285 #endif
286 
287 static Volume *modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
288 {
289 #ifdef WITH_OPENVDB
290  VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
291 
292  /* Iterate over all grids and displace them one by one. */
293  BKE_volume_load(volume, DEG_get_bmain(ctx->depsgraph));
294  const int grid_amount = BKE_volume_num_grids(volume);
295  for (int grid_index = 0; grid_index < grid_amount; grid_index++) {
296  VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, grid_index);
297  BLI_assert(volume_grid != nullptr);
298 
299  openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false);
300  VolumeGridType grid_type = BKE_volume_grid_type(volume_grid);
301 
302  DisplaceGridOp displace_grid_op{*grid, *vdmd, *ctx};
303  BKE_volume_grid_type_operation(grid_type, displace_grid_op);
304  }
305 
306  return volume;
307 #else
308  UNUSED_VARS(md, ctx);
309  BKE_modifier_set_error(ctx->object, md, "Compiled without OpenVDB");
310  return volume;
311 #endif
312 }
313 
315  /* name */ "Volume Displace",
316  /* structName */ "VolumeDisplaceModifierData",
317  /* structSize */ sizeof(VolumeDisplaceModifierData),
318  /* srna */ &RNA_VolumeDisplaceModifier,
320  /* flags */ static_cast<ModifierTypeFlag>(0),
321  /* icon */ ICON_VOLUME_DATA, /* TODO: Use correct icon. */
322 
323  /* copyData */ BKE_modifier_copydata_generic,
324 
325  /* deformVerts */ nullptr,
326  /* deformMatrices */ nullptr,
327  /* deformVertsEM */ nullptr,
328  /* deformMatricesEM */ nullptr,
329  /* modifyMesh */ nullptr,
330  /* modifyHair */ nullptr,
331  /* modifyGeometrySet */ nullptr,
332  /* modifyVolume */ modifyVolume,
333 
334  /* initData */ initData,
335  /* requiredDataMask */ nullptr,
336  /* freeData */ nullptr,
337  /* isDisabled */ nullptr,
338  /* updateDepsgraph */ updateDepsgraph,
339  /* dependsOnTime */ dependsOnTime,
340  /* dependsOnNormals */ nullptr,
341  /* foreachIDLink */ foreachIDLink,
342  /* foreachTexLink */ foreachTexLink,
343  /* freeRuntimeData */ nullptr,
344  /* panelRegister */ panelRegister,
345  /* blendWrite */ nullptr,
346  /* blendRead */ nullptr,
347 };
@ IDWALK_CB_USER
Definition: BKE_lib_query.h:87
void(* IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag)
Definition: BKE_modifier.h:120
ModifierTypeFlag
Definition: BKE_modifier.h:79
void(* TexWalkFunc)(void *userData, struct Object *ob, struct ModifierData *md, const char *propname)
Definition: BKE_modifier.h:121
void BKE_modifier_copydata_generic(const struct ModifierData *md, struct ModifierData *md_dst, const int flag)
@ eModifierTypeType_NonGeometrical
Definition: BKE_modifier.h:76
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:697
void BKE_texture_get_value(const struct Scene *scene, struct Tex *texture, const float *tex_co, struct TexResult *texres, bool use_color_management)
Volume datablock.
VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid)
VolumeGridType
Definition: BKE_volume.h:98
VolumeGrid * BKE_volume_grid_get_for_write(struct Volume *volume, int grid_index)
Definition: volume.cc:1183
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:58
MINLINE void copy_v3_fl(float r[3], float f)
#define UNUSED_VARS(...)
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)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
ModifierTypeInfo modifierType_VolumeDisplace
static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
static Volume * modifyVolume(ModifierData *md, const ModifierEvalContext *ctx, Volume *volume)
static void initData(ModifierData *md)
static void panelRegister(ARegionType *region_type)
static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void *userData)
static bool dependsOnTime(ModifierData *md)
static void panel_draw(const bContext *C, Panel *panel)
StructRNA RNA_VolumeDisplaceModifier
#define C
Definition: RandGen.cpp:39
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, const bool live_icon, const char *text)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int 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
struct Vec3f Vec3f
uint pos
VecMat::Vec3< double > Vec3d
Definition: Geom.h:41
static void sample(SocketReader *reader, int x, int y, float color[4])
Definition: DNA_ID.h:273
struct Depsgraph * depsgraph
Definition: BKE_modifier.h:153
struct Object * object
Definition: BKE_modifier.h:154
struct DepsNodeHandle * node
Definition: BKE_modifier.h:147
float imat[4][4]
float obmat[4][4]
struct uiLayout * layout
void * data
Definition: RNA_types.h:52
float tb
Definition: RE_texture.h:84
float tr
Definition: RE_texture.h:84
float tg
Definition: RE_texture.h:84
float max
__forceinline const avxi abs(const avxi &a)
Definition: util_avxi.h:186
#define foreach(x, y)
Definition: util_foreach.h:22
ccl_device_inline float3 ceil(const float3 &a)
PointerRNA * ptr
Definition: wm_files.c:3157