Blender V5.0
MOD_meshsequencecache.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
8
9#include <cstring>
10
11#include "BLI_math_vector.hh"
12#include "BLI_string.h"
13#include "BLI_utildefines.h"
14
15#include "BLT_translation.hh"
16
17#include "DNA_cachefile_types.h"
18#include "DNA_defaults.h"
19#include "DNA_mesh_types.h"
20#include "DNA_modifier_types.h"
21#include "DNA_object_types.h"
23#include "DNA_scene_types.h"
24#include "DNA_screen_types.h"
25
26#include "MEM_guardedalloc.h"
27
28#include "BKE_cachefile.hh"
29#include "BKE_geometry_set.hh"
30#include "BKE_lib_query.hh"
31#include "BKE_mesh.hh"
32
33#include "UI_interface.hh"
35#include "UI_resources.hh"
36
37#include "RNA_access.hh"
38#include "RNA_prototypes.hh"
39
42
44
45#include "MOD_modifiertypes.hh"
46#include "MOD_ui_common.hh"
47
48#if defined(WITH_USD) || defined(WITH_ALEMBIC)
49# include "BKE_lib_id.hh"
50#endif
51
52#ifdef WITH_ALEMBIC
53# include "ABC_alembic.h"
54#endif
55
56#ifdef WITH_USD
57# include "usd.hh"
58#endif
59
60using namespace blender;
61
62static void init_data(ModifierData *md)
63{
64 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
65
67
68 mcmd->cache_file = nullptr;
69 mcmd->object_path[0] = '\0';
71
73}
74
75static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
76{
77#if 0
78 const MeshSeqCacheModifierData *mcmd = (const MeshSeqCacheModifierData *)md;
79#endif
81
83
84 tmcmd->reader = nullptr;
85 tmcmd->reader_object_path[0] = '\0';
86}
87
88static void free_data(ModifierData *md)
89{
90 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
91
92 if (mcmd->reader) {
93 mcmd->reader_object_path[0] = '\0';
95 }
96}
97
98static bool is_disabled(const Scene * /*scene*/, ModifierData *md, bool /*use_render_params*/)
99{
100 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
101
102 /* leave it up to the modifier to check the file is valid on calculation */
103 return (mcmd->cache_file == nullptr) || (mcmd->object_path[0] == '\0');
104}
105
106#if defined(WITH_USD) || defined(WITH_ALEMBIC)
107
108/* Return true if the modifier evaluation is for the ORCO mesh and the mesh hasn't changed
109 * topology.
110 */
111static bool can_use_mesh_for_orco_evaluation(MeshSeqCacheModifierData *mcmd,
112 const ModifierEvalContext *ctx,
113 const Mesh *mesh,
114 const double frame_offset,
115 const double time_offset,
116 const char **r_err_str)
117{
118 if ((ctx->flag & MOD_APPLY_ORCO) == 0) {
119 return false;
120 }
121
122 CacheFile *cache_file = mcmd->cache_file;
123
124 switch (cache_file->type) {
126# ifdef WITH_ALEMBIC
127 if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time_offset, r_err_str)) {
128 return true;
129 }
130# else
131 UNUSED_VARS(time_offset);
132# endif
133 break;
135# ifdef WITH_USD
137 mcmd->reader, ctx->object, mesh, frame_offset, r_err_str))
138 {
139 return true;
140 }
141# else
142 UNUSED_VARS(frame_offset);
143# endif
144 break;
146 break;
147 }
148
149 return false;
150}
151#endif
152
154 const ModifierEvalContext *ctx,
155 bke::GeometrySet *geometry_set)
156{
157#if defined(WITH_USD) || defined(WITH_ALEMBIC)
158 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
159
161 CacheFile *cache_file = mcmd->cache_file;
162 const double frame = double(DEG_get_ctime(ctx->depsgraph));
163 const double frame_offset = BKE_cachefile_frame_offset(cache_file, frame);
164 const double time_offset = BKE_cachefile_time_offset(
165 cache_file, frame, scene->frames_per_second());
166 const char *err_str = nullptr;
167
168 if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
170 BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
171 if (!mcmd->reader) {
173 ctx->object, md, "Could not create cache reader for file %s", cache_file->filepath);
174 return;
175 }
176 }
177
178 if (geometry_set->has_mesh()) {
179 const Mesh *mesh = geometry_set->get_mesh();
180 if (can_use_mesh_for_orco_evaluation(mcmd, ctx, mesh, frame_offset, time_offset, &err_str)) {
181 return;
182 }
183 }
184
185 /* Time (in frames or seconds) between two velocity samples. Automatically computed to
186 * scale the velocity vectors at render time for generating proper motion blur data. */
187# ifdef WITH_ALEMBIC
188 float velocity_scale = mcmd->velocity_scale;
190 velocity_scale *= scene->frames_per_second();
191 }
192# endif
193
194 switch (cache_file->type) {
196# ifdef WITH_ALEMBIC
198 params.time = time_offset;
199 params.read_flags = mcmd->read_flag;
200 params.velocity_name = mcmd->cache_file->velocity_name;
201 params.velocity_scale = velocity_scale;
202 ABC_read_geometry(mcmd->reader, ctx->object, *geometry_set, &params, &err_str);
203# endif
204 break;
205 }
206 case CACHEFILE_TYPE_USD: {
207# ifdef WITH_USD
209 frame_offset, mcmd->read_flag);
211 mcmd->reader, ctx->object, *geometry_set, params, &err_str);
212# endif
213 break;
214 }
216 break;
217 }
218
219 if (err_str) {
220 BKE_modifier_set_error(ctx->object, md, "%s", err_str);
221 }
222
223#else
224 UNUSED_VARS(ctx, md, geometry_set);
225 return;
226#endif
227}
228
229static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
230{
231#if defined(WITH_USD) || defined(WITH_ALEMBIC)
232 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
233
234 /* Only used to check whether we are operating on org data or not... */
235 Mesh *object_mesh = (ctx->object->type == OB_MESH) ? static_cast<Mesh *>(ctx->object->data) :
236 nullptr;
237 Mesh *org_mesh = mesh;
238
240 CacheFile *cache_file = mcmd->cache_file;
241 const double frame = double(DEG_get_ctime(ctx->depsgraph));
242 const double frame_offset = BKE_cachefile_frame_offset(cache_file, frame);
243 const double time_offset = BKE_cachefile_time_offset(
244 cache_file, frame, scene->frames_per_second());
245 const char *err_str = nullptr;
246
247 if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
249 BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
250 if (!mcmd->reader) {
252 ctx->object, md, "Could not create reader for file %s", cache_file->filepath);
253 return mesh;
254 }
255 }
256
257 /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we
258 * must return the mesh as-is instead of deforming it. */
259 if (can_use_mesh_for_orco_evaluation(mcmd, ctx, mesh, frame_offset, time_offset, &err_str)) {
260 return mesh;
261 }
262
263 if (object_mesh != nullptr) {
264 const Span<float3> mesh_positions = mesh->vert_positions();
265 const Span<blender::int2> mesh_edges = mesh->edges();
266 const blender::OffsetIndices mesh_faces = mesh->faces();
267 const Span<float3> me_positions = object_mesh->vert_positions();
268 const Span<blender::int2> me_edges = object_mesh->edges();
269 const blender::OffsetIndices me_faces = object_mesh->faces();
270
271 /* TODO(sybren+bastien): possibly check relevant custom data layers (UV/color depending on
272 * flags) and duplicate those too.
273 * XXX(Hans): This probably isn't true anymore with various copy-on-eval improvements, etc. */
274 if ((me_positions.data() == mesh_positions.data()) || (me_edges.data() == mesh_edges.data()) ||
275 (me_faces.data() == mesh_faces.data()))
276 {
277 /* We need to duplicate data here, otherwise we'll modify org mesh, see #51701. */
278 mesh = reinterpret_cast<Mesh *>(
279 BKE_id_copy_ex(nullptr,
280 &mesh->id,
281 nullptr,
284 }
285 }
286
289 modify_geometry_set(md, ctx, &geometry_set);
290 Mesh *result = geometry_set.get_component_for_write<bke::MeshComponent>().release();
291
292 if (!ELEM(result, nullptr, mesh) && (mesh != org_mesh)) {
293 BKE_id_free(nullptr, mesh);
294 mesh = org_mesh;
295 }
296
297 return result ? result : mesh;
298#else
299 UNUSED_VARS(ctx, md);
300 return mesh;
301#endif
302}
303
304static bool depends_on_time(Scene * /*scene*/, ModifierData *md)
305{
306#if defined(WITH_USD) || defined(WITH_ALEMBIC)
307 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
308 return (mcmd->cache_file != nullptr);
309#else
310 UNUSED_VARS(md);
311 return false;
312#endif
313}
314
315static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
316{
317 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
318
319 walk(user_data, ob, reinterpret_cast<ID **>(&mcmd->cache_file), IDWALK_CB_USER);
320}
321
323{
324 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
325
326 if (mcmd->cache_file != nullptr) {
328 ctx->node, mcmd->cache_file, DEG_OB_COMP_CACHE, "Mesh Cache File");
329 }
330}
331
332static void panel_draw(const bContext *C, Panel *panel)
333{
334 uiLayout *layout = panel->layout;
335
336 PointerRNA ob_ptr;
338
339 PointerRNA cache_file_ptr = RNA_pointer_get(ptr, "cache_file");
340 bool has_cache_file = !RNA_pointer_is_null(&cache_file_ptr);
341
342 layout->use_property_split_set(true);
343
344 uiTemplateCacheFile(layout, C, ptr, "cache_file");
345
346 if (has_cache_file) {
347 layout->prop_search(
348 ptr, "object_path", &cache_file_ptr, "object_paths", std::nullopt, ICON_NONE);
349 }
350
351 if (RNA_enum_get(&ob_ptr, "type") == OB_MESH) {
352 layout->prop(ptr, "read_data", UI_ITEM_R_EXPAND, std::nullopt, ICON_NONE);
353 layout->prop(ptr, "use_vertex_interpolation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
354 }
355 else if (RNA_enum_get(&ob_ptr, "type") == OB_CURVES) {
356 layout->prop(ptr, "use_vertex_interpolation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
357 }
358
360}
361
362static void velocity_panel_draw(const bContext * /*C*/, Panel *panel)
363{
364 uiLayout *layout = panel->layout;
365
366 PointerRNA ob_ptr;
368
369 PointerRNA fileptr;
370 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
371 return;
372 }
373
374 layout->use_property_split_set(true);
375 uiTemplateCacheFileVelocity(layout, &fileptr);
376 layout->prop(ptr, "velocity_scale", UI_ITEM_NONE, std::nullopt, ICON_NONE);
377}
378
379static void time_panel_draw(const bContext * /*C*/, Panel *panel)
380{
381 uiLayout *layout = panel->layout;
382
383 PointerRNA ob_ptr;
385
386 PointerRNA fileptr;
387 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
388 return;
389 }
390
391 layout->use_property_split_set(true);
392 uiTemplateCacheFileTimeSettings(layout, &fileptr);
393}
394
395static void override_layers_panel_draw(const bContext *C, Panel *panel)
396{
397 uiLayout *layout = panel->layout;
398
399 PointerRNA ob_ptr;
401
402 PointerRNA fileptr;
403 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
404 return;
405 }
406
407 layout->use_property_split_set(true);
408 uiTemplateCacheFileLayers(layout, C, &fileptr);
409}
410
411static void panel_register(ARegionType *region_type)
412{
415 modifier_subpanel_register(region_type, "time", "Time", nullptr, time_panel_draw, panel_type);
417 region_type, "velocity", "Velocity", nullptr, velocity_panel_draw, panel_type);
418 modifier_subpanel_register(region_type,
419 "override_layers",
420 "Override Layers",
421 nullptr,
423 panel_type);
424}
425
426static void blend_read(BlendDataReader * /*reader*/, ModifierData *md)
427{
428 MeshSeqCacheModifierData *msmcd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
429 msmcd->reader = nullptr;
430 msmcd->reader_object_path[0] = '\0';
431}
432
434 /*idname*/ "MeshSequenceCache",
435 /*name*/ N_("MeshSequenceCache"),
436 /*struct_name*/ "MeshSeqCacheModifierData",
437 /*struct_size*/ sizeof(MeshSeqCacheModifierData),
438 /*srna*/ &RNA_MeshSequenceCacheModifier,
440 /*flags*/
442 /*icon*/ ICON_MOD_MESHDEFORM, /* TODO: Use correct icon. */
443
444 /*copy_data*/ copy_data,
445
446 /*deform_verts*/ nullptr,
447 /*deform_matrices*/ nullptr,
448 /*deform_verts_EM*/ nullptr,
449 /*deform_matrices_EM*/ nullptr,
450 /*modify_mesh*/ modify_mesh,
451 /*modify_geometry_set*/ modify_geometry_set,
452
453 /*init_data*/ init_data,
454 /*required_data_mask*/ nullptr,
455 /*free_data*/ free_data,
456 /*is_disabled*/ is_disabled,
457 /*update_depsgraph*/ update_depsgraph,
458 /*depends_on_time*/ depends_on_time,
459 /*depends_on_normals*/ nullptr,
460 /*foreach_ID_link*/ foreach_ID_link,
461 /*foreach_tex_link*/ nullptr,
462 /*free_runtime_data*/ nullptr,
463 /*panel_register*/ panel_register,
464 /*blend_write*/ nullptr,
465 /*blend_read*/ blend_read,
466 /*foreach_cache*/ nullptr,
467 /*foreach_working_space_color*/ nullptr,
468};
bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, const struct Mesh *existing_mesh, double time, const char **r_err_str)
void BKE_cachefile_reader_free(CacheFile *cache_file, CacheReader **reader)
Definition cachefile.cc:210
double BKE_cachefile_time_offset(const CacheFile *cache_file, double time, double fps)
Definition cachefile.cc:404
double BKE_cachefile_frame_offset(const CacheFile *cache_file, double time)
Definition cachefile.cc:411
void BKE_cachefile_reader_open(CacheFile *cache_file, CacheReader **reader, Object *object, const char *object_path)
Definition cachefile.cc:160
void BKE_id_free(Main *bmain, void *idv)
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:777
@ LIB_ID_COPY_NO_PREVIEW
@ LIB_ID_CREATE_NO_USER_REFCOUNT
@ LIB_ID_CREATE_NO_MAIN
@ LIB_ID_CREATE_NO_DEG_TAG
@ IDWALK_CB_USER
void(*)(void *user_data, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag cb_flag) IDWalkFunc
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
@ eModifierTypeFlag_AcceptsCVs
@ eModifierTypeFlag_AcceptsMesh
void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(3
@ MOD_APPLY_ORCO
#define BLI_assert(a)
Definition BLI_assert.h:46
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define UNUSED_VARS(...)
#define ELEM(...)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define STREQ(a, b)
void DEG_add_object_cache_relation(DepsNodeHandle *handle, CacheFile *cache_file, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_CACHE
float DEG_get_ctime(const Depsgraph *graph)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
@ CACHEFILE_VELOCITY_UNIT_FRAME
@ CACHE_FILE_TYPE_INVALID
@ CACHEFILE_TYPE_ALEMBIC
@ CACHEFILE_TYPE_USD
#define DNA_struct_default_get(struct_name)
@ eModifierType_MeshSequenceCache
#define MOD_MESHSEQ_READ_ALL
Object is a sort of wrapper for general info.
@ OB_MESH
@ OB_CURVES
static bool is_disabled
Read Guarded memory(de)allocation.
static void init_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void blend_read(BlendDataReader *, ModifierData *md)
static void panel_draw(const bContext *, Panel *panel)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
Definition MOD_array.cc:862
static void free_data(ModifierData *md)
Definition MOD_bevel.cc:272
static bool depends_on_time(Scene *, ModifierData *)
Definition MOD_build.cc:47
ModifierTypeInfo modifierType_MeshSequenceCache
static void override_layers_panel_draw(const bContext *C, Panel *panel)
static void velocity_panel_draw(const bContext *, Panel *panel)
static Mesh * modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
static void time_panel_draw(const bContext *, Panel *panel)
PanelType * modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
PanelType * modifier_panel_register(ARegionType *region_type, ModifierType type, PanelDrawFn draw)
PointerRNA * modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void modifier_error_message_draw(uiLayout *layout, PointerRNA *ptr)
#define C
Definition RandGen.cpp:29
void uiTemplateCacheFileLayers(uiLayout *layout, const bContext *C, PointerRNA *fileptr)
bool uiTemplateCacheFilePointer(PointerRNA *ptr, blender::StringRefNull propname, PointerRNA *r_file_ptr)
void uiTemplateCacheFileVelocity(uiLayout *layout, PointerRNA *fileptr)
void uiTemplateCacheFileTimeSettings(uiLayout *layout, PointerRNA *fileptr)
void uiTemplateCacheFile(uiLayout *layout, const bContext *C, PointerRNA *ptr, blender::StringRefNull propname)
@ UI_ITEM_R_EXPAND
#define UI_ITEM_NONE
void ABC_read_geometry(CacheReader *reader, Object *ob, blender::bke::GeometrySet &geometry_set, const ABCReadParams *params, const char **r_err_str)
constexpr const T * data() const
Definition BLI_span.hh:215
static void update_depsgraph(tGraphSliderOp *gso)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bool USD_mesh_topology_changed(CacheReader *reader, const Object *ob, const Mesh *existing_mesh, const double time, const char **r_err_str)
USDMeshReadParams create_mesh_read_params(const double motion_sample_time, const int read_flags)
void USD_read_geometry(CacheReader *reader, const Object *ob, blender::bke::GeometrySet &geometry_set, const USDMeshReadParams params, const char **r_err_str)
static bool depends_on_time(Scene *, ModifierData *md)
static void copy_data(const ModifierData *md, ModifierData *target, const int flag)
static void init_data(ModifierData *md)
static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
static void panel_draw(const bContext *C, Panel *panel)
static void modify_geometry_set(ModifierData *md, const ModifierEvalContext *ctx, bke::GeometrySet *geometry_set)
static void free_data(ModifierData *md)
static void panel_register(ARegionType *region_type)
static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
static void blend_read(BlendDataReader *reader, ModifierData *md)
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
bool RNA_pointer_is_null(const PointerRNA *ptr)
int RNA_enum_get(PointerRNA *ptr, const char *name)
char filepath[1024]
char velocity_name[64]
Definition DNA_ID.h:414
struct CacheReader * reader
ModifierApplyFlag flag
struct uiLayout * layout
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
static GeometrySet from_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const Mesh * get_mesh() const
void prop_search(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop, PropertyRNA *item_searchpropname, std::optional< blender::StringRefNull > name, int icon, bool results_are_suggestions)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238
uint8_t flag
Definition wm_window.cc:145