Blender V4.3
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
9#include <cstring>
10#include <limits>
11
12#include "BLI_math_vector.hh"
13#include "BLI_string.h"
14#include "BLI_utildefines.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_cachefile_types.h"
19#include "DNA_defaults.h"
20#include "DNA_mesh_types.h"
21#include "DNA_modifier_types.h"
22#include "DNA_object_types.h"
24#include "DNA_scene_types.h"
25#include "DNA_screen_types.h"
26
27#include "MEM_guardedalloc.h"
28
29#include "BKE_cachefile.hh"
30#include "BKE_geometry_set.hh"
31#include "BKE_lib_query.hh"
32#include "BKE_mesh.hh"
33
34#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 float time,
115 const char **r_err_str)
116{
117 if ((ctx->flag & MOD_APPLY_ORCO) == 0) {
118 return false;
119 }
120
121 CacheFile *cache_file = mcmd->cache_file;
122
123 switch (cache_file->type) {
125# ifdef WITH_ALEMBIC
126 if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, r_err_str)) {
127 return true;
128 }
129# endif
130 break;
132# ifdef WITH_USD
134 mcmd->reader, ctx->object, mesh, time, r_err_str))
135 {
136 return true;
137 }
138# endif
139 break;
141 break;
142 }
143
144 return false;
145}
146
147static Mesh *generate_bounding_box_mesh(const std::optional<Bounds<float3>> &bounds,
148 Material **mat,
149 short totcol)
150{
151 if (!bounds) {
152 return nullptr;
153 }
154
155 Mesh *result = geometry::create_cuboid_mesh(bounds->max - bounds->min, 2, 2, 2);
156 if (mat) {
157 result->mat = static_cast<Material **>(MEM_dupallocN(mat));
158 result->totcol = totcol;
159 }
160
161 BKE_mesh_translate(result, math::midpoint(bounds->min, bounds->max), false);
162
163 return result;
164}
165
166#endif
167
169 const ModifierEvalContext *ctx,
170 bke::GeometrySet *geometry_set)
171{
172#if defined(WITH_USD) || defined(WITH_ALEMBIC)
173 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
174
176 CacheFile *cache_file = mcmd->cache_file;
177 const float frame = DEG_get_ctime(ctx->depsgraph);
178 const double time = BKE_cachefile_time_offset(cache_file, frame, FPS);
179 const char *err_str = nullptr;
180
181 if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
183 BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
184 if (!mcmd->reader) {
186 ctx->object, md, "Could not create cache reader for file %s", cache_file->filepath);
187 return;
188 }
189 }
190
191 if (geometry_set->has_mesh()) {
192 const Mesh *mesh = geometry_set->get_mesh();
193 if (can_use_mesh_for_orco_evaluation(mcmd, ctx, mesh, time, &err_str)) {
194 return;
195 }
196 }
197
198 /* Do not process data if using a render procedural, return a box instead for displaying in the
199 * viewport. */
200 if (BKE_cache_file_uses_render_procedural(cache_file, scene)) {
201 Mesh *bbox = nullptr;
202 if (geometry_set->has_mesh()) {
203 const Mesh *mesh = geometry_set->get_mesh();
204 bbox = generate_bounding_box_mesh(mesh->bounds_min_max(), mesh->mat, mesh->totcol);
205 }
206 else if (geometry_set->has_pointcloud()) {
207 const PointCloud *pointcloud = geometry_set->get_pointcloud();
208 bbox = generate_bounding_box_mesh(
209 pointcloud->bounds_min_max(), pointcloud->mat, pointcloud->totcol);
210 }
211
212 *geometry_set = bke::GeometrySet::from_mesh(bbox, bke::GeometryOwnershipType::Editable);
213 return;
214 }
215
216 /* Time (in frames or seconds) between two velocity samples. Automatically computed to
217 * scale the velocity vectors at render time for generating proper motion blur data. */
218 float velocity_scale = mcmd->velocity_scale;
220 velocity_scale *= FPS;
221 }
222
223 switch (cache_file->type) {
225# ifdef WITH_ALEMBIC
227 params.time = time;
228 params.read_flags = mcmd->read_flag;
229 params.velocity_name = mcmd->cache_file->velocity_name;
230 params.velocity_scale = velocity_scale;
231 ABC_read_geometry(mcmd->reader, ctx->object, *geometry_set, &params, &err_str);
232# endif
233 break;
234 }
235 case CACHEFILE_TYPE_USD: {
236# ifdef WITH_USD
238 time * FPS, mcmd->read_flag);
240 mcmd->reader, ctx->object, *geometry_set, params, &err_str);
241# endif
242 break;
243 }
245 break;
246 }
247
248 if (err_str) {
249 BKE_modifier_set_error(ctx->object, md, "%s", err_str);
250 }
251
252#else
253 UNUSED_VARS(ctx, md, geometry_set);
254 return;
255#endif
256}
257
258static Mesh *modify_mesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
259{
260#if defined(WITH_USD) || defined(WITH_ALEMBIC)
261 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
262
263 /* Only used to check whether we are operating on org data or not... */
264 Mesh *object_mesh = (ctx->object->type == OB_MESH) ? static_cast<Mesh *>(ctx->object->data) :
265 nullptr;
266 Mesh *org_mesh = mesh;
267
269 CacheFile *cache_file = mcmd->cache_file;
270 const float frame = DEG_get_ctime(ctx->depsgraph);
271 const double time = BKE_cachefile_time_offset(cache_file, double(frame), FPS);
272 const char *err_str = nullptr;
273
274 if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
276 BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
277 if (!mcmd->reader) {
279 ctx->object, md, "Could not create reader for file %s", cache_file->filepath);
280 return mesh;
281 }
282 }
283
284 /* Do not process data if using a render procedural, return a box instead for displaying in the
285 * viewport. */
286 if (BKE_cache_file_uses_render_procedural(cache_file, scene)) {
287 return generate_bounding_box_mesh(org_mesh->bounds_min_max(), org_mesh->mat, org_mesh->totcol);
288 }
289
290 /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we
291 * must return the mesh as-is instead of deforming it. */
292 if (can_use_mesh_for_orco_evaluation(mcmd, ctx, mesh, time, &err_str)) {
293 return mesh;
294 }
295
296 if (object_mesh != nullptr) {
297 const Span<float3> mesh_positions = mesh->vert_positions();
298 const Span<blender::int2> mesh_edges = mesh->edges();
299 const blender::OffsetIndices mesh_faces = mesh->faces();
300 const Span<float3> me_positions = object_mesh->vert_positions();
301 const Span<blender::int2> me_edges = object_mesh->edges();
302 const blender::OffsetIndices me_faces = object_mesh->faces();
303
304 /* TODO(sybren+bastien): possibly check relevant custom data layers (UV/color depending on
305 * flags) and duplicate those too.
306 * XXX(Hans): This probably isn't true anymore with various copy-on-eval improvements, etc. */
307 if ((me_positions.data() == mesh_positions.data()) || (me_edges.data() == mesh_edges.data()) ||
308 (me_faces.data() == mesh_faces.data()))
309 {
310 /* We need to duplicate data here, otherwise we'll modify org mesh, see #51701. */
311 mesh = reinterpret_cast<Mesh *>(
312 BKE_id_copy_ex(nullptr,
313 &mesh->id,
314 nullptr,
317 }
318 }
319
321 mesh, bke::GeometryOwnershipType::Editable);
322 modify_geometry_set(md, ctx, &geometry_set);
323 Mesh *result = geometry_set.get_component_for_write<bke::MeshComponent>().release();
324
325 if (!ELEM(result, nullptr, mesh) && (mesh != org_mesh)) {
326 BKE_id_free(nullptr, mesh);
327 mesh = org_mesh;
328 }
329
330 return result ? result : mesh;
331#else
332 UNUSED_VARS(ctx, md);
333 return mesh;
334#endif
335}
336
337static bool depends_on_time(Scene *scene, ModifierData *md)
338{
339#if defined(WITH_USD) || defined(WITH_ALEMBIC)
340 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
341 /* Do not evaluate animations if using the render engine procedural. */
342 return (mcmd->cache_file != nullptr) &&
344#else
345 UNUSED_VARS(scene, md);
346 return false;
347#endif
348}
349
350static void foreach_ID_link(ModifierData *md, Object *ob, IDWalkFunc walk, void *user_data)
351{
352 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
353
354 walk(user_data, ob, reinterpret_cast<ID **>(&mcmd->cache_file), IDWALK_CB_USER);
355}
356
358{
359 MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
360
361 if (mcmd->cache_file != nullptr) {
363 ctx->node, mcmd->cache_file, DEG_OB_COMP_CACHE, "Mesh Cache File");
364 }
365}
366
367static void panel_draw(const bContext *C, Panel *panel)
368{
369 uiLayout *layout = panel->layout;
370
371 PointerRNA ob_ptr;
373
374 PointerRNA cache_file_ptr = RNA_pointer_get(ptr, "cache_file");
375 bool has_cache_file = !RNA_pointer_is_null(&cache_file_ptr);
376
377 uiLayoutSetPropSep(layout, true);
378
379 uiTemplateCacheFile(layout, C, ptr, "cache_file");
380
381 if (has_cache_file) {
383 layout, ptr, "object_path", &cache_file_ptr, "object_paths", nullptr, ICON_NONE);
384 }
385
386 if (RNA_enum_get(&ob_ptr, "type") == OB_MESH) {
387 uiItemR(layout, ptr, "read_data", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
388 uiItemR(layout, ptr, "use_vertex_interpolation", UI_ITEM_NONE, nullptr, ICON_NONE);
389 }
390
391 modifier_panel_end(layout, ptr);
392}
393
394static void velocity_panel_draw(const bContext * /*C*/, Panel *panel)
395{
396 uiLayout *layout = panel->layout;
397
398 PointerRNA ob_ptr;
400
401 PointerRNA fileptr;
402 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
403 return;
404 }
405
406 uiLayoutSetPropSep(layout, true);
407 uiTemplateCacheFileVelocity(layout, &fileptr);
408 uiItemR(layout, ptr, "velocity_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
409}
410
411static void time_panel_draw(const bContext * /*C*/, Panel *panel)
412{
413 uiLayout *layout = panel->layout;
414
415 PointerRNA ob_ptr;
417
418 PointerRNA fileptr;
419 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
420 return;
421 }
422
423 uiLayoutSetPropSep(layout, true);
424 uiTemplateCacheFileTimeSettings(layout, &fileptr);
425}
426
427static void render_procedural_panel_draw(const bContext *C, Panel *panel)
428{
429 uiLayout *layout = panel->layout;
430
431 PointerRNA ob_ptr;
433
434 PointerRNA fileptr;
435 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
436 return;
437 }
438
439 uiLayoutSetPropSep(layout, true);
440 uiTemplateCacheFileProcedural(layout, C, &fileptr);
441}
442
443static void override_layers_panel_draw(const bContext *C, Panel *panel)
444{
445 uiLayout *layout = panel->layout;
446
447 PointerRNA ob_ptr;
449
450 PointerRNA fileptr;
451 if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
452 return;
453 }
454
455 uiLayoutSetPropSep(layout, true);
456 uiTemplateCacheFileLayers(layout, C, &fileptr);
457}
458
459static void panel_register(ARegionType *region_type)
460{
463 modifier_subpanel_register(region_type, "time", "Time", nullptr, time_panel_draw, panel_type);
464 modifier_subpanel_register(region_type,
465 "render_procedural",
466 "Render Procedural",
467 nullptr,
469 panel_type);
471 region_type, "velocity", "Velocity", nullptr, velocity_panel_draw, panel_type);
472 modifier_subpanel_register(region_type,
473 "override_layers",
474 "Override Layers",
475 nullptr,
477 panel_type);
478}
479
480static void blend_read(BlendDataReader * /*reader*/, ModifierData *md)
481{
482 MeshSeqCacheModifierData *msmcd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
483 msmcd->reader = nullptr;
484 msmcd->reader_object_path[0] = '\0';
485}
486
488 /*idname*/ "MeshSequenceCache",
489 /*name*/ N_("MeshSequenceCache"),
490 /*struct_name*/ "MeshSeqCacheModifierData",
491 /*struct_size*/ sizeof(MeshSeqCacheModifierData),
492 /*srna*/ &RNA_MeshSequenceCacheModifier,
494 /*flags*/
496 /*icon*/ ICON_MOD_MESHDEFORM, /* TODO: Use correct icon. */
497
498 /*copy_data*/ copy_data,
499
500 /*deform_verts*/ nullptr,
501 /*deform_matrices*/ nullptr,
502 /*deform_verts_EM*/ nullptr,
503 /*deform_matrices_EM*/ nullptr,
504 /*modify_mesh*/ modify_mesh,
505 /*modify_geometry_set*/ modify_geometry_set,
506
507 /*init_data*/ init_data,
508 /*required_data_mask*/ nullptr,
509 /*free_data*/ free_data,
510 /*is_disabled*/ is_disabled,
511 /*update_depsgraph*/ update_depsgraph,
512 /*depends_on_time*/ depends_on_time,
513 /*depends_on_normals*/ nullptr,
514 /*foreach_ID_link*/ foreach_ID_link,
515 /*foreach_tex_link*/ nullptr,
516 /*free_runtime_data*/ nullptr,
517 /*panel_register*/ panel_register,
518 /*blend_write*/ nullptr,
519 /*blend_read*/ blend_read,
520 /*foreach_cache*/ nullptr,
521};
bool ABC_mesh_topology_changed(struct CacheReader *reader, struct Object *ob, const struct Mesh *existing_mesh, double time, const char **r_err_str)
bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file, Scene *scene)
Definition cachefile.cc:418
void BKE_cachefile_reader_free(CacheFile *cache_file, CacheReader **reader)
Definition cachefile.cc:217
double BKE_cachefile_time_offset(const CacheFile *cache_file, double time, double fps)
Definition cachefile.cc:411
void BKE_cachefile_reader_open(CacheFile *cache_file, CacheReader **reader, Object *object, const char *object_path)
Definition cachefile.cc:166
@ LIB_ID_COPY_NO_PREVIEW
@ LIB_ID_CREATE_NO_USER_REFCOUNT
@ LIB_ID_CREATE_NO_MAIN
@ LIB_ID_CREATE_NO_DEG_TAG
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:760
@ IDWALK_CB_USER
void BKE_mesh_translate(Mesh *mesh, const float offset[3], bool do_keys)
void BKE_modifier_copydata_generic(const ModifierData *md, ModifierData *md_dst, int flag)
ModifierTypeFlag
@ eModifierTypeFlag_AcceptsCVs
@ eModifierTypeFlag_AcceptsMesh
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
@ MOD_APPLY_ORCO
#define BLI_assert(a)
Definition BLI_assert.h:50
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#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)
struct MeshSeqCacheModifierData MeshSeqCacheModifierData
@ eModifierType_MeshSequenceCache
#define MOD_MESHSEQ_READ_ALL
Object is a sort of wrapper for general info.
@ OB_MESH
#define FPS
static bool is_disabled
Read Guarded memory(de)allocation.
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)
static void render_procedural_panel_draw(const bContext *C, Panel *panel)
PanelType * modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
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)
void uiTemplateCacheFileProcedural(uiLayout *layout, const bContext *C, PointerRNA *fileptr)
void uiTemplateCacheFileLayers(uiLayout *layout, const bContext *C, PointerRNA *fileptr)
bool uiTemplateCacheFilePointer(PointerRNA *ptr, const char *propname, PointerRNA *r_file_ptr)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiItemPointerR(uiLayout *layout, PointerRNA *ptr, const char *propname, PointerRNA *searchptr, const char *searchpropname, const char *name, int icon)
void uiTemplateCacheFileVelocity(uiLayout *layout, PointerRNA *fileptr)
void uiTemplateCacheFile(uiLayout *layout, const bContext *C, PointerRNA *ptr, const char *propname)
void uiTemplateCacheFileTimeSettings(uiLayout *layout, PointerRNA *fileptr)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_EXPAND
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:216
double time
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
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)
T midpoint(const T &a, const T &b)
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:413
struct CacheReader * reader
struct Material ** mat
short totcol
ModifierApplyFlag flag
struct uiLayout * layout
struct Material ** mat
GeometryComponent & get_component_for_write(GeometryComponent::Type component_type)
static GeometrySet from_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
const PointCloud * get_pointcloud() const
const Mesh * get_mesh() const
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4126
uint8_t flag
Definition wm_window.cc:138