Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_attribute.h8
-rw-r--r--source/blender/blenkernel/BKE_cachefile.h4
-rw-r--r--source/blender/blenkernel/BKE_modifier.h4
-rw-r--r--source/blender/blenkernel/BKE_pointcloud.h3
-rw-r--r--source/blender/blenkernel/intern/attribute.c12
-rw-r--r--source/blender/blenkernel/intern/cachefile.c21
-rw-r--r--source/blender/blenkernel/intern/displist.cc8
-rw-r--r--source/blender/blenkernel/intern/modifier.c25
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc41
-rw-r--r--source/blender/editors/include/UI_interface.h7
-rw-r--r--source/blender/editors/interface/interface_intern.h3
-rw-r--r--source/blender/editors/interface/interface_template_list.cc1
-rw-r--r--source/blender/editors/interface/interface_templates.c57
-rw-r--r--source/blender/editors/io/io_cache.c84
-rw-r--r--source/blender/editors/io/io_cache.h3
-rw-r--r--source/blender/editors/io/io_ops.c3
-rw-r--r--source/blender/io/alembic/ABC_alembic.h27
-rw-r--r--source/blender/io/alembic/CMakeLists.txt6
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc1461
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.h102
-rw-r--r--source/blender/io/alembic/intern/abc_reader_camera.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_camera.h4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.cc247
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.h17
-rw-r--r--source/blender/io/alembic/intern/abc_reader_instance.cc74
-rw-r--r--source/blender/io/alembic/intern/abc_reader_instance.h42
-rw-r--r--source/blender/io/alembic/intern/abc_reader_manager.cc55
-rw-r--r--source/blender/io/alembic/intern/abc_reader_manager.h78
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc316
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.h50
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.h4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc48
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.h27
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.cc157
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.h22
-rw-r--r--source/blender/io/alembic/intern/abc_reader_transform.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_transform.h4
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc102
-rw-r--r--source/blender/io/usd/CMakeLists.txt1
-rw-r--r--source/blender/io/usd/intern/usd_capi_import.cc16
-rw-r--r--source/blender/io/usd/intern/usd_reader_curve.cc93
-rw-r--r--source/blender/io/usd/intern/usd_reader_curve.h8
-rw-r--r--source/blender/io/usd/intern/usd_reader_geom.h9
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.cc14
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.h13
-rw-r--r--source/blender/io/usd/intern/usd_reader_nurbs.cc91
-rw-r--r--source/blender/io/usd/intern/usd_reader_nurbs.h8
-rw-r--r--source/blender/io/usd/usd.h17
-rw-r--r--source/blender/makesdna/DNA_cachefile_types.h42
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c188
-rw-r--r--source/blender/modifiers/CMakeLists.txt2
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.cc (renamed from source/blender/modifiers/intern/MOD_meshsequencecache.c)314
54 files changed, 3014 insertions, 942 deletions
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index ff207997e79..73ade2ff7ac 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -65,6 +65,14 @@ struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id,
int type,
AttributeDomain domain);
+/* Return an existing CustomDataLayer, or create a new one if none is found for the given
+ * parameters and return it. */
+struct CustomDataLayer *BKE_id_attribute_ensure(struct ID *id,
+ const char *name,
+ const int type,
+ const AttributeDomain domain,
+ struct ReportList *reports);
+
AttributeDomain BKE_id_attribute_domain(struct ID *id, struct CustomDataLayer *layer);
int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer);
bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer);
diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h
index c6821d88d2a..70d6985153c 100644
--- a/source/blender/blenkernel/BKE_cachefile.h
+++ b/source/blender/blenkernel/BKE_cachefile.h
@@ -27,6 +27,7 @@
extern "C" {
#endif
+struct CacheAttributeMapping;
struct CacheFile;
struct CacheFileLayer;
struct CacheReader;
@@ -79,6 +80,9 @@ struct CacheFileLayer *BKE_cachefile_get_active_layer(struct CacheFile *cache_fi
void BKE_cachefile_remove_layer(struct CacheFile *cache_file, struct CacheFileLayer *layer);
+struct CacheAttributeMapping *BKE_cachefile_get_active_attribute_mapping(
+ struct CacheFile *cache_file);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index a05ed67063a..8fd22c70128 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -611,6 +611,10 @@ void BKE_modifier_blend_read_data(struct BlendDataReader *reader,
struct Object *ob);
void BKE_modifier_blend_read_lib(struct BlendLibReader *reader, struct Object *ob);
+/* Return whether the modifier can operate directly on a Curve, i.e. without needing a conversion
+ * to a temporary Mesh. */
+bool BKE_modifier_supports_curve_data(const ModifierData *md);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h
index d330ea41e6a..e796967eb61 100644
--- a/source/blender/blenkernel/BKE_pointcloud.h
+++ b/source/blender/blenkernel/BKE_pointcloud.h
@@ -39,6 +39,9 @@ extern const char *POINTCLOUD_ATTR_RADIUS;
void *BKE_pointcloud_add(struct Main *bmain, const char *name);
void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
struct PointCloud *BKE_pointcloud_new_nomain(int totpoint);
+void BKE_pointcloud_nomain_to_pointcloud(struct PointCloud *pointcloud_src,
+ struct PointCloud *pointcloud_dst,
+ bool take_ownership);
struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
bool BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]);
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index fdd84f3dfd4..4ee5abc1816 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -245,6 +245,18 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id,
return NULL;
}
+CustomDataLayer *BKE_id_attribute_ensure(
+ ID *id, const char *name, const int type, const AttributeDomain domain, ReportList *reports)
+{
+ CustomDataLayer *layer = BKE_id_attribute_find(id, name, type, domain);
+
+ if (layer) {
+ return layer;
+ }
+
+ return BKE_id_attribute_new(id, name, type, domain, reports);
+}
+
int BKE_id_attributes_length(ID *id, const CustomDataMask mask)
{
DomainInfo info[ATTR_DOMAIN_NUM];
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index 75df2e98fcd..12133b5a5dc 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -89,6 +89,7 @@ static void cache_file_copy_data(Main *UNUSED(bmain),
cache_file_dst->handle_readers = NULL;
BLI_duplicatelist(&cache_file_dst->object_paths, &cache_file_src->object_paths);
BLI_duplicatelist(&cache_file_dst->layers, &cache_file_src->layers);
+ BLI_duplicatelist(&cache_file_dst->attribute_mappings, &cache_file_src->attribute_mappings);
}
static void cache_file_free_data(ID *id)
@@ -97,6 +98,7 @@ static void cache_file_free_data(ID *id)
cachefile_handle_free(cache_file);
BLI_freelistN(&cache_file->object_paths);
BLI_freelistN(&cache_file->layers);
+ BLI_freelistN(&cache_file->attribute_mappings);
}
static void cache_file_foreach_path(ID *id, BPathForeachPathData *bpath_data)
@@ -126,6 +128,11 @@ static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_a
LISTBASE_FOREACH (CacheFileLayer *, layer, &cache_file->layers) {
BLO_write_struct(writer, CacheFileLayer, layer);
}
+
+ /* write attribute mappings */
+ LISTBASE_FOREACH (CacheAttributeMapping *, mapping, &cache_file->attribute_mappings) {
+ BLO_write_struct(writer, CacheAttributeMapping, mapping);
+ }
}
static void cache_file_blend_read_data(BlendDataReader *reader, ID *id)
@@ -142,6 +149,9 @@ static void cache_file_blend_read_data(BlendDataReader *reader, ID *id)
/* relink layers */
BLO_read_list(reader, &cache_file->layers);
+
+ /* relink attribute mappings */
+ BLO_read_list(reader, &cache_file->attribute_mappings);
}
IDTypeInfo IDType_ID_CF = {
@@ -449,6 +459,11 @@ bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file,
return cache_file->use_render_procedural && !is_final_render;
}
+bool BKE_cache_file_supports_layers(const CacheFile *cache_file)
+{
+ return cache_file->type == CACHEFILE_TYPE_ALEMBIC;
+}
+
CacheFileLayer *BKE_cachefile_add_layer(CacheFile *cache_file, const char filename[1024])
{
for (CacheFileLayer *layer = cache_file->layers.first; layer; layer = layer->next) {
@@ -480,3 +495,9 @@ void BKE_cachefile_remove_layer(CacheFile *cache_file, CacheFileLayer *layer)
BLI_remlink(&cache_file->layers, layer);
MEM_freeN(layer);
}
+
+CacheAttributeMapping *BKE_cachefile_get_active_attribute_mapping(CacheFile *cache_file)
+{
+ /* BLI_findlink handles the out of bounds checks. */
+ return BLI_findlink(&cache_file->attribute_mappings, cache_file->active_attribute_mapping - 1);
+}
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index 78177095a77..53a976adc89 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -828,12 +828,12 @@ static bool do_curve_implicit_mesh_conversion(const Curve *curve,
return true;
}
- /* If a non-geometry-nodes modifier is enabled before a nodes modifier,
- * force conversion to mesh, since only the nodes modifier supports curve data. */
+ /* If a modifier which does not support curve data is enabled before one that does,
+ * force conversion to mesh. */
ModifierData *md = first_modifier;
for (; md; md = md->next) {
if (BKE_modifier_is_enabled(scene, md, required_mode)) {
- if (md->type == eModifierType_Nodes) {
+ if (BKE_modifier_supports_curve_data(md)) {
break;
}
return true;
@@ -890,7 +890,7 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph,
continue;
}
- if (md->type == eModifierType_Nodes) {
+ if (BKE_modifier_supports_curve_data(md)) {
mti->modifyGeometrySet(md, &mectx_apply, &geometry_set);
continue;
}
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 4cedaff7c00..598d4e495d6 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -36,6 +36,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_armature_types.h"
+#include "DNA_cachefile_types.h"
#include "DNA_cloth_types.h"
#include "DNA_dynamicpaint_types.h"
#include "DNA_fluid_types.h"
@@ -1517,3 +1518,27 @@ void BKE_modifier_blend_read_lib(BlendLibReader *reader, Object *ob)
}
}
}
+
+bool BKE_modifier_supports_curve_data(const ModifierData *md)
+{
+ /* Sanity check. */
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ if (mti->modifyGeometrySet == NULL) {
+ return false;
+ }
+
+ if (md->type == eModifierType_Nodes) {
+ return true;
+ }
+
+ /* For the MeshSequenceCache, only the Alembic backend supports directly reading/modifying
+ * curves.
+ */
+ if (md->type == eModifierType_MeshSequenceCache) {
+ MeshSeqCacheModifierData *msmd = (MeshSeqCacheModifierData *)md;
+ CacheFile *cache_file = msmd->cache_file;
+ return cache_file && cache_file->type == CACHEFILE_TYPE_ALEMBIC;
+ }
+
+ return false;
+}
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index b5f016e4d76..2090434a5de 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -268,6 +268,47 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
return pointcloud;
}
+void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src,
+ PointCloud *pointcloud_dst,
+ bool take_ownership)
+{
+ BLI_assert(pointcloud_src->id.tag & LIB_TAG_NO_MAIN);
+
+ /* pointcloud_src might depend on pointcloud_dst, so we need to do everything with a local copy
+ */
+ PointCloud tmp = *pointcloud_dst;
+ eCDAllocType alloctype = CD_DUPLICATE;
+
+ if (take_ownership) {
+ bool has_any_referenced_layers = CustomData_has_referenced(&pointcloud_src->pdata);
+
+ if (!has_any_referenced_layers) {
+ alloctype = CD_ASSIGN;
+ }
+ }
+
+ CustomData_reset(&tmp.pdata);
+
+ const CustomDataMask pmask = CD_MASK_ALL;
+
+ const int totpoint = tmp.totpoint = pointcloud_src->totpoint;
+ CustomData_copy(&pointcloud_src->pdata, &tmp.pdata, pmask, alloctype, totpoint);
+
+ BKE_pointcloud_update_customdata_pointers(&tmp);
+
+ CustomData_free(&pointcloud_dst->pdata, pointcloud_dst->totpoint);
+
+ /* skip the listbase */
+ MEMCPY_STRUCT_AFTER(pointcloud_dst, &tmp, id.prev);
+
+ if (take_ownership) {
+ if (alloctype == CD_ASSIGN) {
+ CustomData_free_typemask(&pointcloud_src->pdata, pointcloud_src->totpoint, ~pmask);
+ }
+ BKE_id_free(nullptr, pointcloud_src);
+ }
+}
+
struct MinMaxResult {
float3 min;
float3 max;
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index ae4c2ff16fd..2118a2e6b85 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2435,6 +2435,13 @@ void uiTemplateCacheFileLayers(uiLayout *layout,
const struct bContext *C,
struct PointerRNA *fileptr);
+/**
+ * Draw the attribute remapping related properties of the CacheFile.
+ */
+void uiTemplateCacheFileAttributeRemapping(uiLayout *layout,
+ const struct bContext *C,
+ struct PointerRNA *fileptr);
+
/* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */
#define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"
enum uiTemplateListFlags {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 2d1138b46a7..665a6fe8e44 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1493,6 +1493,9 @@ void UI_OT_eyedropper_gpencil_color(struct wmOperatorType *ot);
struct uiListType *UI_UL_asset_view(void);
+/* interface_templates.c */
+struct uiListType *UI_UL_cache_file_attribute_mappings(void);
+
/**
* For use with #ui_rna_collection_search_update_fn.
*/
diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc
index 817599605a9..754a97338ff 100644
--- a/source/blender/editors/interface/interface_template_list.cc
+++ b/source/blender/editors/interface/interface_template_list.cc
@@ -1323,6 +1323,7 @@ void ED_uilisttypes_ui()
{
WM_uilisttype_add(UI_UL_asset_view());
WM_uilisttype_add(UI_UL_cache_file_layers());
+ WM_uilisttype_add(UI_UL_cache_file_attribute_mappings());
}
/** \} */
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 03d0cba5ec8..db33141de38 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -6503,6 +6503,63 @@ bool uiTemplateCacheFilePointer(PointerRNA *ptr, const char *propname, PointerRN
return true;
}
+static void cache_file_attribute_mapping_item(uiList *UNUSED(ui_list),
+ bContext *UNUSED(C),
+ uiLayout *layout,
+ PointerRNA *UNUSED(dataptr),
+ PointerRNA *itemptr,
+ int UNUSED(icon),
+ PointerRNA *UNUSED(active_dataptr),
+ const char *UNUSED(active_propname),
+ int UNUSED(index),
+ int UNUSED(flt_flag))
+{
+ uiLayout *row = uiLayoutRow(layout, true);
+ uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE);
+ uiItemR(row, itemptr, "mapping", UI_ITEM_R_NO_BG, "", ICON_NONE);
+ uiItemR(row, itemptr, "domain", UI_ITEM_R_NO_BG, "", ICON_NONE);
+}
+
+uiListType *UI_UL_cache_file_attribute_mappings()
+{
+ uiListType *list_type = (uiListType *)MEM_callocN(sizeof(*list_type), __func__);
+
+ BLI_strncpy(list_type->idname, "UI_UL_cache_file_attribute_mappings", sizeof(list_type->idname));
+ list_type->draw_item = cache_file_attribute_mapping_item;
+
+ return list_type;
+}
+
+void uiTemplateCacheFileAttributeRemapping(uiLayout *layout,
+ const bContext *C,
+ PointerRNA *fileptr)
+{
+ /* Ensure that the context has a CacheFile as this may not be set inside of modifiers panels. */
+ uiLayoutSetContextPointer(layout, "edit_cachefile", fileptr);
+
+ uiLayout *row = uiLayoutRow(layout, false);
+ uiLayout *col = uiLayoutColumn(row, true);
+
+ uiTemplateList(col,
+ (bContext *)C,
+ "UI_UL_cache_file_attribute_mappings",
+ "cache_file_attribute_mappings",
+ fileptr,
+ "attribute_mappings",
+ fileptr,
+ "active_attribute_mapping_index",
+ "",
+ 1,
+ 5,
+ UILST_LAYOUT_DEFAULT,
+ 1,
+ UI_TEMPLATE_LIST_FLAG_NONE);
+
+ col = uiLayoutColumn(row, true);
+ uiItemO(col, "", ICON_ADD, "cachefile.attribute_mapping_add");
+ uiItemO(col, "", ICON_REMOVE, "cachefile.attribute_mapping_remove");
+}
+
void uiTemplateCacheFile(uiLayout *layout,
const bContext *C,
PointerRNA *ptr,
diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c
index 4f71a804986..0bd842d3fbe 100644
--- a/source/blender/editors/io/io_cache.c
+++ b/source/blender/editors/io/io_cache.c
@@ -328,3 +328,87 @@ void CACHEFILE_OT_layer_move(wmOperatorType *ot)
"Direction",
"Direction to move the active vertex group towards");
}
+
+/* ***************************** Add Attribute Mapping Operator **************************** */
+
+static bool cachefile_attribute_mapping_poll(bContext *C)
+{
+ return CTX_data_edit_cachefile(C) != NULL;
+}
+
+static int cachefile_attribute_mapping_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ CacheFile *cache_file = CTX_data_edit_cachefile(C);
+
+ if (!cache_file) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const int current_mapping_count = BLI_listbase_count(&cache_file->attribute_mappings);
+
+ CacheAttributeMapping *mapping = MEM_callocN(sizeof(CacheAttributeMapping),
+ "CacheAttributeMapping");
+
+ BLI_addtail(&cache_file->attribute_mappings, mapping);
+
+ cache_file->active_attribute_mapping = current_mapping_count + 1;
+
+ /* Since the mapping is not initialized, adding a mapping does not trigger a CacheFile update. */
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHEFILE_OT_attribute_mapping_add(wmOperatorType *ot)
+{
+ ot->name = "Add Attribute Mapping";
+ ot->description = "Add an attribute mapping for this CacheFile";
+ ot->idname = __func__;
+
+ /* api callbacks */
+ ot->exec = cachefile_attribute_mapping_add_exec;
+ ot->poll = cachefile_attribute_mapping_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************************** Remove Attribute Mapping Operator **************************** */
+
+static int cachefile_attribute_mapping_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ CacheFile *cache_file = CTX_data_edit_cachefile(C);
+
+ if (!cache_file) {
+ return OPERATOR_CANCELLED;
+ }
+
+ CacheAttributeMapping *mapping = BKE_cachefile_get_active_attribute_mapping(cache_file);
+
+ /* Reset this now as it will have to be done whether we have a mapping or not. */
+ cache_file->active_attribute_mapping = 0;
+
+ if (!mapping) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_remlink(&cache_file->attribute_mappings, mapping);
+
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ BKE_cachefile_reload(depsgraph, cache_file);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHEFILE_OT_attribute_mapping_remove(wmOperatorType *ot)
+{
+ ot->name = "Remove Attribute Mapping";
+ ot->description = "Remove an attribute mapping from this CacheFile";
+ ot->idname = __func__;
+
+ /* api callbacks */
+ ot->exec = cachefile_attribute_mapping_remove_exec;
+ ot->poll = cachefile_attribute_mapping_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/io/io_cache.h b/source/blender/editors/io/io_cache.h
index 297e065434f..2868e57687b 100644
--- a/source/blender/editors/io/io_cache.h
+++ b/source/blender/editors/io/io_cache.h
@@ -31,3 +31,6 @@ void CACHEFILE_OT_reload(struct wmOperatorType *ot);
void CACHEFILE_OT_layer_add(struct wmOperatorType *ot);
void CACHEFILE_OT_layer_remove(struct wmOperatorType *ot);
void CACHEFILE_OT_layer_move(struct wmOperatorType *ot);
+
+void CACHEFILE_OT_attribute_mapping_add(struct wmOperatorType *ot);
+void CACHEFILE_OT_attribute_mapping_remove(struct wmOperatorType *ot);
diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c
index d9bbd7d8692..6c8ed5c7756 100644
--- a/source/blender/editors/io/io_ops.c
+++ b/source/blender/editors/io/io_ops.c
@@ -74,5 +74,8 @@ void ED_operatortypes_io(void)
WM_operatortype_append(CACHEFILE_OT_layer_remove);
WM_operatortype_append(CACHEFILE_OT_layer_move);
+ WM_operatortype_append(CACHEFILE_OT_attribute_mapping_add);
+ WM_operatortype_append(CACHEFILE_OT_attribute_mapping_remove);
+
WM_operatortype_append(WM_OT_obj_export);
}
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 18b0c91b67c..e603418eb40 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -28,6 +28,7 @@ extern "C" {
struct CacheArchiveHandle;
struct CacheFileLayer;
struct CacheReader;
+struct GeometrySet;
struct ListBase;
struct Main;
struct Mesh;
@@ -113,15 +114,23 @@ void ABC_get_transform(struct CacheReader *reader,
float time,
float scale);
-/* Either modifies existing_mesh in-place or constructs a new mesh. */
-struct Mesh *ABC_read_mesh(struct CacheReader *reader,
- struct Object *ob,
- struct Mesh *existing_mesh,
- float time,
- const char **err_str,
- int read_flags,
- const char *velocity_name,
- float velocity_scale);
+typedef struct ABCReadParams {
+ float time;
+ int read_flags;
+ const char *velocity_name;
+ float velocity_scale;
+
+ ListBase *mappings;
+} ABCReadParams;
+
+#ifdef __cplusplus
+/* Either modifies the existing geometry component, or create a new one. */
+void ABC_read_geometry(CacheReader *reader,
+ Object *ob,
+ GeometrySet &geometry_set,
+ const ABCReadParams *params,
+ const char **err_str);
+#endif
bool ABC_mesh_topology_changed(struct CacheReader *reader,
struct Object *ob,
diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt
index d55f2382a9b..919376b971b 100644
--- a/source/blender/io/alembic/CMakeLists.txt
+++ b/source/blender/io/alembic/CMakeLists.txt
@@ -24,9 +24,11 @@ set(INC
../../blenkernel
../../blenlib
../../blenloader
+ ../../blentranslation
../../bmesh
../../depsgraph
../../editors/include
+ ../../functions
../../makesdna
../../makesrna
../../windowmanager
@@ -47,6 +49,8 @@ set(SRC
intern/abc_reader_archive.cc
intern/abc_reader_camera.cc
intern/abc_reader_curves.cc
+ intern/abc_reader_instance.cc
+ intern/abc_reader_manager.cc
intern/abc_reader_mesh.cc
intern/abc_reader_nurbs.cc
intern/abc_reader_object.cc
@@ -77,6 +81,8 @@ set(SRC
intern/abc_reader_archive.h
intern/abc_reader_camera.h
intern/abc_reader_curves.h
+ intern/abc_reader_instance.h
+ intern/abc_reader_manager.h
intern/abc_reader_mesh.h
intern/abc_reader_nurbs.h
intern/abc_reader_object.h
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index c3ff64fd0ad..9f9bbc246f5 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -26,15 +26,26 @@
#include <Alembic/AbcGeom/All.h>
#include <algorithm>
+#include <optional>
#include <unordered_map>
+#include "DNA_cachefile_types.h"
#include "DNA_customdata_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "BLI_assert.h"
+#include "BLI_color.hh"
#include "BLI_math_base.h"
+#include "BLI_math_bits.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
+#include "BLT_translation.h"
+
+#include "BKE_attribute.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
@@ -44,6 +55,8 @@
* such data in a way that lets other DCC know what they are for. See comments
* in the write code for the conventions. */
+using namespace Alembic::AbcGeom;
+
using Alembic::AbcGeom::kFacevaryingScope;
using Alembic::AbcGeom::kVaryingScope;
using Alembic::AbcGeom::kVertexScope;
@@ -328,42 +341,409 @@ using Alembic::AbcGeom::IC4fGeomParam;
using Alembic::AbcGeom::IV2fGeomParam;
using Alembic::AbcGeom::IV3fGeomParam;
-static void read_uvs(const CDStreamConfig &config,
- void *data,
- const AbcUvScope uv_scope,
- const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
- const UInt32ArraySamplePtr &indices)
+/* -------------------------------------------------------------------- */
+
+/** \name BlenderScope
+ *
+ * This enumeration is used to translate the Alembic scope to a scope more suitable for Blender's
+ * possible CustomData and attribute domains.
+ * It is defined as a bit field in case routines need to resolve the scope for a set of potential
+ * scopes. This can be done by or-ing the candidate scopes together, and validate by checking that
+ * only a single bit is set.
+ * \{ */
+
+enum class BlenderScope : uint32_t {
+ UNKNOWN = 0,
+ POINT = (1 << 0),
+ POLYGON = (1 << 1),
+ LOOPS = (1 << 2),
+};
+
+static BlenderScope &operator|=(BlenderScope &lhs, BlenderScope rhs)
{
- MPoly *mpolys = config.mpoly;
- MLoop *mloops = config.mloop;
- MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
+ lhs = static_cast<BlenderScope>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
+ return lhs;
+}
- unsigned int uv_index, loop_index, rev_loop_index;
+/* Check the bits for the scope and return it if only one of them is active. */
+static BlenderScope valid_scope_or_unknown(BlenderScope scope)
+{
+ if (count_bits_i(static_cast<uint32_t>(scope)) == 1) {
+ return scope;
+ }
- BLI_assert(uv_scope != ABC_UV_SCOPE_NONE);
- const bool do_uvs_per_loop = (uv_scope == ABC_UV_SCOPE_LOOP);
+ return BlenderScope::UNKNOWN;
+}
- for (int i = 0; i < config.totpoly; i++) {
- MPoly &poly = mpolys[i];
- unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1;
+/* Determine the matching scope given some number of values. The dimensions parameter is used to
+ * scale the scope_sizes and is meant to be used (not equal to 1) when processing a scalar array
+ * for a possible remapping to some n-dimensionnal data type. */
+static BlenderScope matching_scope(const ScopeSizeInfo scope_sizes,
+ int dimensions,
+ size_t num_values,
+ char requested_domain)
+{
+ if (requested_domain != CACHEFILE_ATTR_MAP_DOMAIN_AUTO) {
+ if (requested_domain == CACHEFILE_ATTR_MAP_DOMAIN_POINT) {
+ if (static_cast<size_t>(scope_sizes.point_scope_size * dimensions) == num_values) {
+ return BlenderScope::POINT;
+ }
- for (int f = 0; f < poly.totloop; f++) {
- rev_loop_index = rev_loop_offset - f;
- loop_index = do_uvs_per_loop ? poly.loopstart + f : mloops[rev_loop_index].v;
- uv_index = (*indices)[loop_index];
- const Imath::V2f &uv = (*uvs)[uv_index];
+ return BlenderScope::UNKNOWN;
+ }
+
+ if (requested_domain == CACHEFILE_ATTR_MAP_DOMAIN_FACE_CORNER) {
+ if (static_cast<size_t>(scope_sizes.loop_scope_size * dimensions) == num_values) {
+ return BlenderScope::LOOPS;
+ }
+
+ return BlenderScope::UNKNOWN;
+ }
+
+ if (requested_domain == CACHEFILE_ATTR_MAP_DOMAIN_FACE) {
+ if (static_cast<size_t>(scope_sizes.polygon_scope_size * dimensions) == num_values) {
+ return BlenderScope::POLYGON;
+ }
+
+ return BlenderScope::UNKNOWN;
+ }
+
+ return BlenderScope::UNKNOWN;
+ }
+
+ BlenderScope scope = BlenderScope::UNKNOWN;
+
+ if (static_cast<size_t>(scope_sizes.loop_scope_size * dimensions) == num_values) {
+ scope |= BlenderScope::LOOPS;
+ }
+
+ if (static_cast<size_t>(scope_sizes.polygon_scope_size * dimensions) == num_values) {
+ scope |= BlenderScope::POLYGON;
+ }
+
+ if (static_cast<size_t>(scope_sizes.point_scope_size * dimensions) == num_values) {
+ scope |= BlenderScope::POINT;
+ }
+
+ return valid_scope_or_unknown(scope);
+}
+
+static CustomData *custom_data_for_scope(ScopeCustomDataPointers custom_data_pointers,
+ BlenderScope bl_scope)
+{
+ if (bl_scope == BlenderScope::POINT) {
+ return custom_data_pointers.point_custom_data;
+ }
+
+ if (bl_scope == BlenderScope::POLYGON) {
+ return custom_data_pointers.polygon_custom_data;
+ }
+
+ if (bl_scope == BlenderScope::LOOPS) {
+ return custom_data_pointers.loop_custom_data;
+ }
+
+ return nullptr;
+}
+
+#if 0
+/* Useful for debugging. Turned off to quiet warnings. */
+static std::ostream &operator<<(std::ostream &os, BlenderScope bl_scope)
+{
+ switch (bl_scope) {
+ case BlenderScope::POINT: {
+ os << "POINT";
+ break;
+ }
+ case BlenderScope::LOOPS: {
+ os << "LOOPS";
+ break;
+ }
+ case BlenderScope::POLYGON: {
+ os << "POLYGON";
+ break;
+ }
+ default: {
+ os << "UNKNOWN";
+ break;
+ }
+ }
+ return os;
+}
+#endif
+
+static int size_for_scope(const ScopeSizeInfo scope_sizes, BlenderScope scope)
+{
+ if (scope == BlenderScope::POINT) {
+ return scope_sizes.point_scope_size;
+ }
+
+ if (scope == BlenderScope::LOOPS) {
+ return scope_sizes.loop_scope_size;
+ }
+
+ if (scope == BlenderScope::POLYGON) {
+ return scope_sizes.polygon_scope_size;
+ }
+
+ return 0;
+}
+
+/* UVs can be defined per-loop (one value per vertex per face), or per-vertex (one value per
+ * vertex). The first case is the most common, as this is the standard way of storing this data
+ * given that some vertices might be on UV seams and have multiple possible UV coordinates; the
+ * second case can happen when the mesh is split according to the UV islands, in which case storing
+ * a single UV value per vertex allows to deduplicate data and thus to reduce the file size since
+ * vertices are guaranteed to only have a single UV coordinate. */
+static bool is_valid_uv_scope(BlenderScope scope)
+{
+ return scope == BlenderScope::LOOPS || scope == BlenderScope::POINT;
+}
+
+static bool is_valid_vertex_color_scope(BlenderScope scope)
+{
+ return scope == BlenderScope::LOOPS || scope == BlenderScope::POINT;
+}
+
+static bool is_valid_vertex_group_scope(BlenderScope scope)
+{
+ return scope == BlenderScope::POINT;
+}
+
+static bool is_valid_scope_for_layer(BlenderScope scope, CustomDataType custom_data_type)
+{
+ if (scope == BlenderScope::UNKNOWN) {
+ return false;
+ }
+
+ if (custom_data_type == CD_MLOOPUV) {
+ return is_valid_uv_scope(scope);
+ }
+
+ if (custom_data_type == CD_MCOL) {
+ return is_valid_vertex_color_scope(scope);
+ }
+
+ if (custom_data_type == CD_ORCO) {
+ return scope == BlenderScope::POINT;
+ }
+
+ return true;
+}
- MLoopUV &loopuv = mloopuvs[rev_loop_index];
- loopuv.uv[0] = uv[0];
- loopuv.uv[1] = uv[1];
+/* Converts an Alembic scope to a Blender one. We need to be careful as some Alembic scopes depend
+ * on the domain on which they appear. For example, kVaryingScope could mean that the data is
+ * varying across the polygons or the vertices. To resolve this, we also use the expected number of
+ * elements for each scopes, and the matching scope is returned based on this.
+ * If we cannot resolve the scope, BlenderScope::UNKNOWN is returned.
+ */
+static BlenderScope to_blender_scope(Alembic::AbcGeom::GeometryScope abc_scope,
+ size_t element_size,
+ ScopeSizeInfo scope_sizes)
+{
+ switch (abc_scope) {
+ case kConstantScope: {
+ return matching_scope(scope_sizes, 1, element_size, CACHEFILE_ATTR_MAP_DOMAIN_AUTO);
+ }
+ case kUniformScope: {
+ /* This would mean one value for the whole object, but we don't support that yet. */
+ return BlenderScope::UNKNOWN;
+ }
+ case kVertexScope: {
+ /* This is pretty straightforward, just ensure that the sizes match. */
+ if (static_cast<size_t>(scope_sizes.point_scope_size) == element_size) {
+ return BlenderScope::POINT;
+ }
+ return BlenderScope::UNKNOWN;
+ }
+ case kFacevaryingScope: {
+ /* Fist check that the loops size match, since this is the right domain. */
+ if (static_cast<size_t>(scope_sizes.loop_scope_size) == element_size) {
+ return BlenderScope::LOOPS;
+ }
+ /* If not, we may have some data that is face varying, but was written for each vertex
+ * instead (e.g. vertex colors). */
+ if (static_cast<size_t>(scope_sizes.point_scope_size) == element_size) {
+ return BlenderScope::POINT;
+ }
+ return BlenderScope::UNKNOWN;
+ }
+ case kVaryingScope: {
+ /* We have a varying field over some domain, which we need to determine. */
+ return matching_scope(scope_sizes, 1, element_size, CACHEFILE_ATTR_MAP_DOMAIN_AUTO);
}
+ case kUnknownScope: {
+ return BlenderScope::UNKNOWN;
+ }
+ }
+
+ return BlenderScope::UNKNOWN;
+}
+
+static AttributeDomain attribute_domain_from_blender_scope(BlenderScope scope)
+{
+ if (scope == BlenderScope::POINT) {
+ return ATTR_DOMAIN_POINT;
+ }
+
+ if (scope == BlenderScope::LOOPS) {
+ return ATTR_DOMAIN_CORNER;
+ }
+
+ if (scope == BlenderScope::POLYGON) {
+ return ATTR_DOMAIN_FACE;
}
+
+ BLI_assert_msg(0, "Obtained an invalid scope when converting to an AttributeDomain");
+ return ATTR_DOMAIN_NUM;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \name value_type_converter
+ *
+ * Utilities to convert values from the types used in Alembic to the ones used in Blender. Besides
+ * type conversions, vectors are also converted from Y-up to Z-up.
+ * \{ */
+
+template<typename T> struct value_type_converter {
+ static bool map_to_bool(const T *value)
+ {
+ return static_cast<bool>(value[0]);
+ }
+
+ static int32_t map_to_int32(const T *value)
+ {
+ return static_cast<int32_t>(value[0]);
+ }
+
+ static float map_to_float(const T *value)
+ {
+ return static_cast<float>(value[0]);
+ }
+
+ static float2 map_to_float2(const T *value)
+ {
+ return {static_cast<float>(value[0]), static_cast<float>(value[1])};
+ }
+
+ static float3 map_to_float3(const T *value)
+ {
+ float3 result{
+ static_cast<float>(value[0]), static_cast<float>(value[1]), static_cast<float>(value[2])};
+ copy_zup_from_yup(result, result);
+ return result;
+ }
+
+ static ColorGeometry4f map_to_color_from_rgb(const T *ptr)
+ {
+ return ColorGeometry4f(
+ static_cast<float>(ptr[0]), static_cast<float>(ptr[1]), static_cast<float>(ptr[2]), 1.0f);
+ }
+
+ static ColorGeometry4f map_to_color_from_rgba(const T *ptr)
+ {
+ return ColorGeometry4f(static_cast<float>(ptr[0]),
+ static_cast<float>(ptr[1]),
+ static_cast<float>(ptr[2]),
+ static_cast<float>(ptr[3]));
+ }
+
+ static MCol map_to_mcol_from_rgb(const T *ptr)
+ {
+ MCol mcol;
+ mcol.a = unit_float_to_uchar_clamp(static_cast<float>(ptr[0]));
+ mcol.r = unit_float_to_uchar_clamp(static_cast<float>(ptr[1]));
+ mcol.g = unit_float_to_uchar_clamp(static_cast<float>(ptr[2]));
+ mcol.b = 255;
+ return mcol;
+ }
+
+ static MCol map_to_mcol_from_rgba(const T *ptr)
+ {
+ MCol mcol;
+ mcol.a = unit_float_to_uchar_clamp(static_cast<float>(ptr[0]));
+ mcol.r = unit_float_to_uchar_clamp(static_cast<float>(ptr[1]));
+ mcol.g = unit_float_to_uchar_clamp(static_cast<float>(ptr[2]));
+ mcol.b = unit_float_to_uchar_clamp(static_cast<float>(ptr[3]));
+ return mcol;
+ }
+};
+
+template<> struct value_type_converter<Imath::V3f> {
+ static float3 convert_value(const Imath::V3f &v)
+ {
+ return v.getValue();
+ }
+};
+
+template<> struct value_type_converter<Imath::V3d> {
+ static float3 convert_value(const Imath::V3d &v)
+ {
+ const double *ptr = v.getValue();
+ return float3(
+ static_cast<float>(ptr[0]), static_cast<float>(ptr[1]), static_cast<float>(ptr[2]));
+ }
+};
+
+template<> struct value_type_converter<Imath::C3f> {
+ static MCol convert_value(const Imath::C3f &v)
+ {
+ MCol mcol;
+ mcol.a = unit_float_to_uchar_clamp(static_cast<float>(v[0]));
+ mcol.r = unit_float_to_uchar_clamp(static_cast<float>(v[1]));
+ mcol.g = unit_float_to_uchar_clamp(static_cast<float>(v[2]));
+ mcol.b = 255;
+ return mcol;
+ }
+};
+
+template<> struct value_type_converter<Imath::C4f> {
+ static MCol convert_value(const Imath::C4f &v)
+ {
+ MCol mcol;
+ mcol.a = unit_float_to_uchar_clamp(static_cast<float>(v[0]));
+ mcol.r = unit_float_to_uchar_clamp(static_cast<float>(v[1]));
+ mcol.g = unit_float_to_uchar_clamp(static_cast<float>(v[2]));
+ mcol.b = unit_float_to_uchar_clamp(static_cast<float>(v[3]));
+ return mcol;
+ }
+};
+
+template<typename T> struct to_scalar_type {
+ using type = T;
+};
+
+template<> struct to_scalar_type<Imath::V2f> {
+ using type = float;
+};
+
+template<> struct to_scalar_type<Imath::V3f> {
+ using type = float;
+};
+
+template<> struct to_scalar_type<Imath::V3d> {
+ using type = double;
+};
+
+template<> struct to_scalar_type<Imath::C3f> {
+ using type = float;
+};
+
+template<> struct to_scalar_type<Imath::C4f> {
+ using type = float;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
static size_t mcols_out_of_bounds_check(const size_t color_index,
const size_t array_size,
const std::string &iobject_full_name,
- const PropertyHeader &prop_header,
+ const std::string &prop_name,
bool &r_is_out_of_bounds,
bool &r_bounds_warning_given)
{
@@ -374,257 +754,942 @@ static size_t mcols_out_of_bounds_check(const size_t color_index,
if (!r_bounds_warning_given) {
std::cerr << "Alembic: color index out of bounds "
"reading face colors for object "
- << iobject_full_name << ", property " << prop_header.getName() << std::endl;
+ << iobject_full_name << ", property " << prop_name << std::endl;
r_bounds_warning_given = true;
}
r_is_out_of_bounds = true;
return 0;
}
-static void read_custom_data_mcols(const std::string &iobject_full_name,
- const ICompoundProperty &arbGeomParams,
- const PropertyHeader &prop_header,
- const CDStreamConfig &config,
- const Alembic::Abc::ISampleSelector &iss)
+/* -------------------------------------------------------------------- */
+
+/** \name AbcAttributeMapping
+ *
+ * Mirror of the mapping enumeration in DNA_cachefile_types.h. It is more specific and used as
+ * some sort of token to indicate that the mapping is valid for the attribute.
+ * \{ */
+
+struct AbcAttributeMapping {
+ CustomDataType type;
+ BlenderScope scope;
+ /* This is to differentiate between RGB and RGBA colors. */
+ bool is_rgba = false;
+};
+
+/* Try to resolve the mapping for an attribute from the number of individual elements in the array
+ * sample and the mapping as desired by the user. */
+static std::optional<AbcAttributeMapping> final_mapping_from_cache_mapping(
+ const CacheAttributeMapping &mapping,
+ const ScopeSizeInfo scope_sizes,
+ AbcAttributeMapping original_mapping,
+ const uint num_elements)
{
- C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
- C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
- Alembic::Abc::UInt32ArraySamplePtr indices;
- bool use_c3f_ptr;
- bool is_facevarying;
+ BLI_assert_msg(num_elements != 0,
+ "The number of elements in the array is null, although this should have been "
+ "checked before.");
+
+ switch (mapping.mapping) {
+ case CACHEFILE_ATTRIBUTE_MAP_NONE: {
+ if (mapping.domain == CACHEFILE_ATTR_MAP_DOMAIN_POINT) {
+ original_mapping.scope = BlenderScope::POINT;
+ }
+ if (mapping.domain == CACHEFILE_ATTR_MAP_DOMAIN_FACE_CORNER) {
+ original_mapping.scope = BlenderScope::LOOPS;
+ }
+ return original_mapping;
+ }
+ case CACHEFILE_ATTRIBUTE_MAP_TO_UVS: {
+ /* UVs have 2 values per element. */
+ const BlenderScope scope_hint = matching_scope(scope_sizes, 2, num_elements, mapping.domain);
+ if (!is_valid_uv_scope(scope_hint)) {
+ return {};
+ }
+ return AbcAttributeMapping{CD_MLOOPUV, scope_hint};
+ }
+ case CACHEFILE_ATTRIBUTE_MAP_TO_VERTEX_COLORS: {
+ /* 3 values for RGB, 4 for RGBA */
+ BlenderScope rgb_scope_hint = matching_scope(scope_sizes, 3, num_elements, mapping.domain);
+ BlenderScope rgba_scope_hint = matching_scope(scope_sizes, 4, num_elements, mapping.domain);
+
+ if (is_valid_vertex_color_scope(rgb_scope_hint) &&
+ rgba_scope_hint == BlenderScope::UNKNOWN) {
+ return AbcAttributeMapping{CD_MCOL, rgb_scope_hint, false};
+ }
- /* Find the correct interpretation of the data */
- if (IC3fGeomParam::matches(prop_header)) {
- IC3fGeomParam color_param(arbGeomParams, prop_header.getName());
- IC3fGeomParam::Sample sample;
- BLI_assert(STREQ("rgb", color_param.getInterpretation()));
+ if (rgb_scope_hint == BlenderScope::UNKNOWN &&
+ is_valid_vertex_color_scope(rgba_scope_hint)) {
+ return AbcAttributeMapping{CD_MCOL, rgba_scope_hint, true};
+ }
- color_param.getIndexed(sample, iss);
- is_facevarying = sample.getScope() == kFacevaryingScope &&
- config.totloop == sample.getIndices()->size();
+ /* Either both are unknown, or we matched two different scopes in which case we cannot
+ * resolve the ambiguity. */
+ return {};
+ }
+ case CACHEFILE_ATTRIBUTE_MAP_TO_WEIGHT_GROUPS: {
+ BlenderScope r_scope_hint = matching_scope(scope_sizes, 1, num_elements, mapping.domain);
+ if (!is_valid_vertex_group_scope(r_scope_hint)) {
+ return {};
+ }
+ return AbcAttributeMapping{CD_BWEIGHT, r_scope_hint};
+ }
+ case CACHEFILE_ATTRIBUTE_MAP_TO_FLOAT2: {
+ BlenderScope r_scope_hint = matching_scope(scope_sizes, 2, num_elements, mapping.domain);
+ if (r_scope_hint == BlenderScope::UNKNOWN) {
+ return {};
+ }
+ return AbcAttributeMapping{CD_PROP_FLOAT2, r_scope_hint};
+ }
+ case CACHEFILE_ATTRIBUTE_MAP_TO_FLOAT3: {
+ BlenderScope r_scope_hint = matching_scope(scope_sizes, 3, num_elements, mapping.domain);
+ if (r_scope_hint == BlenderScope::UNKNOWN) {
+ return {};
+ }
+ return AbcAttributeMapping{CD_PROP_FLOAT3, r_scope_hint};
+ }
+ case CACHEFILE_ATTRIBUTE_MAP_TO_COLOR: {
+ /* 3 values for RGB, 4 for RGBA */
+ BlenderScope rgb_scope_hint = matching_scope(scope_sizes, 3, num_elements, mapping.domain);
+ BlenderScope rgba_scope_hint = matching_scope(scope_sizes, 4, num_elements, mapping.domain);
- c3f_ptr = sample.getVals();
- indices = sample.getIndices();
- use_c3f_ptr = true;
- }
- else if (IC4fGeomParam::matches(prop_header)) {
- IC4fGeomParam color_param(arbGeomParams, prop_header.getName());
- IC4fGeomParam::Sample sample;
- BLI_assert(STREQ("rgba", color_param.getInterpretation()));
+ if (rgb_scope_hint != BlenderScope::UNKNOWN && rgba_scope_hint == BlenderScope::UNKNOWN) {
+ return AbcAttributeMapping{CD_PROP_COLOR, rgb_scope_hint, false};
+ }
- color_param.getIndexed(sample, iss);
- is_facevarying = sample.getScope() == kFacevaryingScope &&
- config.totloop == sample.getIndices()->size();
+ if (rgb_scope_hint == BlenderScope::UNKNOWN && rgba_scope_hint != BlenderScope::UNKNOWN) {
+ return AbcAttributeMapping{CD_PROP_COLOR, rgba_scope_hint, true};
+ }
- c4f_ptr = sample.getVals();
- indices = sample.getIndices();
- use_c3f_ptr = false;
+ /* Either both are unknown, or we matched two different scopes in which case we cannot
+ * resolve the ambiguity. */
+ return {};
+ }
}
- else {
- /* this won't happen due to the checks in read_custom_data() */
- return;
+
+ return {};
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+static bool can_add_custom_data_layer(const CustomData *data,
+ CustomDataType type,
+ int max_allowed_layers)
+{
+ if (data == nullptr) {
+ return false;
}
- BLI_assert(c3f_ptr || c4f_ptr);
- /* Read the vertex colors */
- void *cd_data = config.add_customdata_cb(
- config.mesh, prop_header.getName().c_str(), CD_MLOOPCOL);
- MCol *cfaces = static_cast<MCol *>(cd_data);
- MPoly *mpolys = config.mpoly;
- MLoop *mloops = config.mloop;
+ return CustomData_number_of_layers(data, type) < max_allowed_layers;
+}
- size_t face_index = 0;
- size_t color_index;
- bool bounds_warning_given = false;
+/* Check if we do not already have reached the maximum allowed number of vertex colors layers. */
+static bool can_add_vertex_color_layer(const CDStreamConfig &config)
+{
+ return can_add_custom_data_layer(
+ config.custom_data_pointers.loop_custom_data, CD_MLOOPCOL, MAX_MCOL);
+}
- /* The colors can go through two layers of indexing. Often the 'indices'
- * array doesn't do anything (i.e. indices[n] = n), but when it does, it's
- * important. Blender 2.79 writes indices incorrectly (see T53745), which
- * is why we have to check for indices->size() > 0 */
- bool use_dual_indexing = is_facevarying && indices->size() > 0;
+/* Check if we do not already have reached the maximum allowed number of UV layers. */
+static bool can_add_uv_layer(const CDStreamConfig &config)
+{
+ return can_add_custom_data_layer(
+ config.custom_data_pointers.loop_custom_data, CD_MLOOPUV, MAX_MTFACE);
+}
- for (int i = 0; i < config.totpoly; i++) {
- MPoly *poly = &mpolys[i];
- MCol *cface = &cfaces[poly->loopstart + poly->totloop];
- MLoop *mloop = &mloops[poly->loopstart + poly->totloop];
+/* For converting attributes with a loop scope, we need to convert the polygon winding order as
+ * well, as viewed from Blender, Alembic orders vertices around a polygon in reverse.
+ * The callback will called for each loop, and the index passed to it will be the index of the data
+ * in the source_scope. If source_scope is POINT, the passed index will be that of the point of the
+ * corresponding loop vertex. If it is LOOPS, it will be that of the loop.
+ * For now, we do support POLYGON scope as a source_scope.
+ */
+template<typename BlenderType, typename Callback>
+static void iterate_attribute_loop_scope(const CDStreamConfig &config,
+ BlenderType *attribute_data,
+ BlenderScope source_scope,
+ Callback callback)
+{
+ if (source_scope != BlenderScope::LOOPS && source_scope != BlenderScope::POINT) {
+ return;
+ }
- for (int j = 0; j < poly->totloop; j++, face_index++) {
- cface--;
- mloop--;
+ Mesh *mesh = config.mesh;
+ MPoly *mpolys = mesh->mpoly;
+ MLoop *mloops = mesh->mloop;
+ unsigned int loop_index, rev_loop_index;
+ const bool index_per_loop = source_scope == BlenderScope::LOOPS;
- color_index = is_facevarying ? face_index : mloop->v;
- if (use_dual_indexing) {
- color_index = (*indices)[color_index];
- }
- if (use_c3f_ptr) {
- bool is_mcols_out_of_bounds = false;
- color_index = mcols_out_of_bounds_check(color_index,
- c3f_ptr->size(),
- iobject_full_name,
- prop_header,
- is_mcols_out_of_bounds,
- bounds_warning_given);
- if (is_mcols_out_of_bounds) {
- continue;
- }
- const Imath::C3f &color = (*c3f_ptr)[color_index];
- cface->a = unit_float_to_uchar_clamp(color[0]);
- cface->r = unit_float_to_uchar_clamp(color[1]);
- cface->g = unit_float_to_uchar_clamp(color[2]);
- cface->b = 255;
- }
- else {
- bool is_mcols_out_of_bounds = false;
- color_index = mcols_out_of_bounds_check(color_index,
- c4f_ptr->size(),
- iobject_full_name,
- prop_header,
- is_mcols_out_of_bounds,
- bounds_warning_given);
- if (is_mcols_out_of_bounds) {
- continue;
- }
- const Imath::C4f &color = (*c4f_ptr)[color_index];
- cface->a = unit_float_to_uchar_clamp(color[0]);
- cface->r = unit_float_to_uchar_clamp(color[1]);
- cface->g = unit_float_to_uchar_clamp(color[2]);
- cface->b = unit_float_to_uchar_clamp(color[3]);
- }
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly &poly = mpolys[i];
+ unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1;
+
+ for (int f = 0; f < poly.totloop; f++) {
+ rev_loop_index = rev_loop_offset - f;
+ loop_index = index_per_loop ? poly.loopstart + f : mloops[rev_loop_index].v;
+ attribute_data[rev_loop_index] = callback(loop_index);
}
}
}
-static void read_custom_data_uvs(const ICompoundProperty &prop,
- const PropertyHeader &prop_header,
- const CDStreamConfig &config,
- const Alembic::Abc::ISampleSelector &iss)
+/* Main entry point for creating a custom data layer from an Alembic attribute.
+ *
+ * The callback is responsible for converting filling the custom data layer, one value at a time.
+ * Its signature should be : BlenderType(size_t). The parameter is the current index of the
+ * iteration, and is dependant on the target_scope.
+ *
+ * The source_scope is the resolved scope for the Alembic data, while the target_scope is the scope
+ * of the final Blender data. We split these as they can be different either due to remapping, or
+ * because we have an attribute that is always on one scope in Blender, but can be expressed in
+ * multiple scopes in Alembic (e.g. UV maps are loop scope in Blender, but loop or vertex in
+ * Alembic).
+ */
+template<typename BlenderType, typename Callback>
+static void create_layer_for_scope(const CDStreamConfig &config,
+ BlenderScope source_scope,
+ BlenderScope target_scope,
+ CustomDataType cd_type,
+ const std::string &name,
+ Callback callback)
{
- IV2fGeomParam uv_param(prop, prop_header.getName());
+ BLI_assert(source_scope != BlenderScope::UNKNOWN);
+ BLI_assert(target_scope != BlenderScope::UNKNOWN);
- if (!uv_param.isIndexed()) {
+ const ScopeCustomDataPointers &custom_data_pointers = config.custom_data_pointers;
+ CustomData *custom_data = custom_data_for_scope(custom_data_pointers, target_scope);
+ if (!custom_data) {
return;
}
- IV2fGeomParam::Sample sample;
- uv_param.getIndexed(sample, iss);
+ const int layer_size = size_for_scope(config.scope_sizes, target_scope);
- UInt32ArraySamplePtr uvs_indices = sample.getIndices();
+ const AttributeDomain domain = attribute_domain_from_blender_scope(target_scope);
+ CustomDataLayer *layer = BKE_id_attribute_ensure(
+ config.id, name.c_str(), cd_type, domain, nullptr);
- const AbcUvScope uv_scope = get_uv_scope(uv_param.getScope(), config, uvs_indices);
+ BlenderType *layer_data = static_cast<BlenderType *>(layer->data);
- if (uv_scope == ABC_UV_SCOPE_NONE) {
+ if (target_scope == BlenderScope::LOOPS) {
+ iterate_attribute_loop_scope(config, layer_data, source_scope, callback);
return;
}
- void *cd_data = config.add_customdata_cb(config.mesh, prop_header.getName().c_str(), CD_MLOOPUV);
+ /* POINT and POLYGON scopes can be simply iterated. */
+ for (size_t i = 0; i < static_cast<size_t>(layer_size); i++) {
+ *layer_data++ = callback(i);
+ }
+}
+
+/* Wrapper around create_layer_for_scope with similar source and target scopes. */
+template<typename BlenderType, typename Callback>
+static void create_layer_for_scope(const CDStreamConfig &config,
+ BlenderScope bl_scope,
+ CustomDataType cd_type,
+ const std::string &name,
+ Callback callback)
+{
+ return create_layer_for_scope<BlenderType>(config, bl_scope, bl_scope, cd_type, name, callback);
+}
- read_uvs(config, cd_data, uv_scope, sample.getVals(), uvs_indices);
+/* Wrapper around create_layer_for_scope with the loop scope being the target scope. */
+template<typename BlenderType, typename Callback>
+static void create_loop_layer_for_scope(const CDStreamConfig &config,
+ BlenderScope source_scope,
+ CustomDataType cd_type,
+ const std::string &name,
+ Callback callback)
+{
+ return create_layer_for_scope<BlenderType>(
+ config, source_scope, BlenderScope::LOOPS, cd_type, name, callback);
}
-void read_generated_coordinates(const ICompoundProperty &prop,
- const CDStreamConfig &config,
- const Alembic::Abc::ISampleSelector &iss)
+enum class AbcAttributeReadError {
+ /* Default value for success. */
+ READ_SUCCESS,
+ /* We do not support this attribute type. */
+ UNSUPPORTED_TYPE,
+ /* The attribute is invalid (e.g. corrupt file or data). */
+ INVALID_ATTRIBUTE,
+ /* We cannot determine the scope for the attribute, either from mismatching element size, or
+ * ambiguous scope used in the Alembic archive. */
+ SCOPE_RESOLUTION_FAILED,
+ /* Scope resolution succeeded, but the scope is not valid for the data. */
+ INVALID_SCOPE,
+ /* The mapping selected by the user is not possible. */
+ MAPPING_IMPOSSIBLE,
+ /* The limit of a attribute for the CustomData layer is reached (this should only concern UVs and
+ * vertex colors). */
+ TOO_MANY_ATTRIBUTES,
+};
+
+static CustomDataType custom_data_type_for_pod(PlainOldDataType pod_type, uint extent)
{
- if (!prop.valid() || prop.getPropertyHeader(propNameOriginalCoordinates) == nullptr) {
- /* The ORCO property isn't there, so don't bother trying to process it. */
- return;
+ switch (pod_type) {
+ case kBooleanPOD: {
+ return CD_PROP_BOOL;
+ }
+ case kUint8POD:
+ case kInt8POD:
+ case kUint16POD:
+ case kInt16POD:
+ case kUint32POD:
+ case kInt32POD: {
+ return CD_PROP_INT32;
+ }
+ case kFloat32POD:
+ case kFloat64POD: {
+ if (extent == 2) {
+ return CD_PROP_FLOAT2;
+ }
+
+ if (extent == 3) {
+ return CD_PROP_FLOAT3;
+ }
+
+ return CD_PROP_FLOAT;
+ }
+ /* These are unsupported for now. */
+ case kFloat16POD:
+ case kUint64POD:
+ case kInt64POD:
+ case kStringPOD:
+ case kWstringPOD:
+ default: {
+ /* Use this as invalid value. */
+ return CD_AUTO_FROM_NAME;
+ }
}
+}
- IV3fGeomParam param(prop, propNameOriginalCoordinates);
- if (!param.valid() || param.isIndexed()) {
- /* Invalid or indexed coordinates aren't supported. */
- return;
+template<typename TRAIT>
+static CustomDataType get_default_custom_data_type(const CDStreamConfig &config,
+ const ITypedGeomParam<TRAIT> &param,
+ bool &r_is_rgba)
+{
+ if (std::is_same_v<ITypedGeomParam<TRAIT>, IC3fGeomParam>) {
+ if (!can_add_vertex_color_layer(config)) {
+ /* CD_MCOL is full, fallback to generic colors. */
+ return CD_PROP_COLOR;
+ }
+
+ return CD_MCOL;
}
- if (param.getScope() != kVertexScope) {
- /* These are original vertex coordinates, so must be vertex-scoped. */
- return;
+
+ if (std::is_same_v<ITypedGeomParam<TRAIT>, IC4fGeomParam>) {
+ r_is_rgba = true;
+ if (!can_add_vertex_color_layer(config)) {
+ /* CD_MCOL is full, fallback to generic colors. */
+ return CD_PROP_COLOR;
+ }
+
+ return CD_MCOL;
}
- IV3fGeomParam::Sample sample = param.getExpandedValue(iss);
- Alembic::AbcGeom::V3fArraySamplePtr abc_orco = sample.getVals();
- const size_t totvert = abc_orco.get()->size();
- Mesh *mesh = config.mesh;
+ if (std::is_same_v<ITypedGeomParam<TRAIT>, IV2fGeomParam>) {
+ if (Alembic::AbcGeom::isUV(param.getHeader())) {
+ if (can_add_uv_layer(config)) {
+ return CD_MLOOPUV;
+ }
+ }
- if (totvert != mesh->totvert) {
- /* Either the data is somehow corrupted, or we have a dynamic simulation where only the ORCOs
- * for the first frame were exported. */
- return;
+ /* If it is not a UV map or if CD_MLOOPUV is full, fallback to generic 2D vectors. */
+ return CD_PROP_FLOAT2;
}
- void *cd_data;
- if (CustomData_has_layer(&mesh->vdata, CD_ORCO)) {
- cd_data = CustomData_get_layer(&mesh->vdata, CD_ORCO);
+ /* Ensure UVs are also detected if written as doubles. */
+ if (std::is_same_v<ITypedGeomParam<TRAIT>, IV2dGeomParam>) {
+ if (Alembic::AbcGeom::isUV(param.getHeader())) {
+ if (can_add_uv_layer(config)) {
+ return CD_MLOOPUV;
+ }
+ }
+
+ /* If it is not a UV map or if CD_MLOOPUV is full, fallback to generic 2D vectors. */
+ return CD_PROP_FLOAT2;
}
- else {
- cd_data = CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
+
+ if (param.getName() == propNameOriginalCoordinates) {
+ return CD_ORCO;
}
- float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
- for (int vertex_idx = 0; vertex_idx < totvert; ++vertex_idx) {
- const Imath::V3f &abc_coords = (*abc_orco)[vertex_idx];
- copy_zup_from_yup(orcodata[vertex_idx], abc_coords.getValue());
+ const DataType &data_type = param.getDataType();
+ return custom_data_type_for_pod(data_type.getPod(), data_type.getExtent());
+}
+
+template<typename TRAIT>
+static std::optional<AbcAttributeMapping> determine_attribute_mapping(
+ const CDStreamConfig &config,
+ const ITypedGeomParam<TRAIT> &param,
+ size_t num_values,
+ const CacheAttributeMapping *desired_mapping)
+{
+ const BlenderScope bl_scope = to_blender_scope(param.getScope(), num_values, config.scope_sizes);
+ AbcAttributeMapping default_mapping;
+ default_mapping.scope = bl_scope;
+ default_mapping.type = get_default_custom_data_type(config, param, default_mapping.is_rgba);
+
+ if (desired_mapping) {
+ auto opt_final_mapping = final_mapping_from_cache_mapping(*desired_mapping,
+ config.scope_sizes,
+ default_mapping,
+ num_values *
+ param.getDataType().getExtent());
+
+ if (opt_final_mapping.has_value()) {
+ /* Verify that the scope is valid, it may be that we cannot apply the desired mapping, or
+ * that the data matches the default mapping, but that is also invalid. */
+ AbcAttributeMapping final_mapping = *opt_final_mapping;
+ if (final_mapping.scope != BlenderScope::UNKNOWN) {
+ return opt_final_mapping;
+ }
+ }
}
- /* ORCOs are always stored in the normalized 0..1 range in Blender, but Alembic stores them
- * unnormalized, so we need to normalize them. */
- BKE_mesh_orco_verts_transform(mesh, orcodata, mesh->totvert, false);
+ if (!is_valid_scope_for_layer(default_mapping.scope, default_mapping.type)) {
+ return {};
+ }
+
+ return default_mapping;
}
-void read_custom_data(const std::string &iobject_full_name,
- const ICompoundProperty &prop,
- const CDStreamConfig &config,
- const Alembic::Abc::ISampleSelector &iss)
+template<typename TRAIT>
+static AbcAttributeReadError process_typed_attribute(const CDStreamConfig &config,
+ const CacheAttributeMapping *mapping,
+ const ITypedGeomParam<TRAIT> &param,
+ ISampleSelector iss)
{
- if (!prop.valid()) {
- return;
+ typename ITypedGeomParam<TRAIT>::Sample sample;
+ param.getIndexed(sample, iss);
+
+ if (!sample.valid()) {
+ return AbcAttributeReadError::INVALID_ATTRIBUTE;
}
- int num_uvs = 0;
- int num_colors = 0;
+ const AttributeSelector &attr_sel = *config.attr_selector;
- const size_t num_props = prop.getNumProperties();
+ const TypedArraySample<TRAIT> &values = *sample.getVals();
+ const UInt32ArraySamplePtr indices = sample.getIndices();
+ const size_t num_values = indices->size() > 0 ? indices->size() : values.size();
- for (size_t i = 0; i < num_props; i++) {
- const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
+ std::optional<AbcAttributeMapping> opt_abc_mapping = determine_attribute_mapping(
+ config, param, num_values, mapping);
- /* Read UVs according to convention. */
- if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
- if (++num_uvs > MAX_MTFACE) {
- continue;
+ if (!opt_abc_mapping.has_value()) {
+ return AbcAttributeReadError::MAPPING_IMPOSSIBLE;
+ }
+
+ AbcAttributeMapping abc_mapping = *opt_abc_mapping;
+ BlenderScope bl_scope = abc_mapping.scope;
+
+ if (!attr_sel.select_attribute(param.getName())) {
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+
+ using abc_type = typename TRAIT::value_type;
+ using abc_scalar_type = typename to_scalar_type<abc_type>::type;
+
+ const abc_scalar_type *input_data = reinterpret_cast<const abc_scalar_type *>(values.get());
+
+ switch (abc_mapping.type) {
+ default: {
+ return AbcAttributeReadError::MAPPING_IMPOSSIBLE;
+ }
+ case CD_PROP_BOOL: {
+ create_layer_for_scope<bool>(config, bl_scope, CD_PROP_BOOL, param.getName(), [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_bool(&input_data[i]);
+ });
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_PROP_INT32: {
+ create_layer_for_scope<int32_t>(
+ config, bl_scope, CD_PROP_INT32, param.getName(), [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_int32(&input_data[i]);
+ });
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_PROP_FLOAT: {
+ create_layer_for_scope<float>(
+ config, bl_scope, CD_PROP_FLOAT, param.getName(), [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_float(&input_data[i]);
+ });
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_PROP_FLOAT2: {
+ create_layer_for_scope<float2>(
+ config, bl_scope, CD_PROP_FLOAT2, param.getName(), [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_float2(&input_data[i * 2]);
+ });
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_ORCO:
+ case CD_PROP_FLOAT3: {
+ std::string param_name = param.getName();
+ if (param.getName() == attr_sel.velocity_name()) {
+ param_name = "velocity";
+
+ /* Check that we indeed have an attribute on the points. */
+ if (bl_scope != BlenderScope::POINT) {
+ return AbcAttributeReadError::INVALID_SCOPE;
+ }
}
- read_custom_data_uvs(prop, prop_header, config, iss);
- continue;
+ create_layer_for_scope<float3>(
+ config, bl_scope, abc_mapping.type, param_name, [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_float3(&input_data[i * 3]);
+ });
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_PROP_COLOR: {
+ if (abc_mapping.is_rgba) {
+ create_layer_for_scope<ColorGeometry4f>(
+ config, bl_scope, CD_PROP_COLOR, param.getName(), [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_color_from_rgba(
+ &input_data[i * 4]);
+ });
+ }
+ else {
+ create_layer_for_scope<ColorGeometry4f>(
+ config, bl_scope, CD_PROP_COLOR, param.getName(), [&](size_t i) {
+ return value_type_converter<abc_scalar_type>::map_to_color_from_rgb(
+ &input_data[i * 3]);
+ });
+ }
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_MLOOPUV: {
+ if (!can_add_uv_layer(config)) {
+ return AbcAttributeReadError::TOO_MANY_ATTRIBUTES;
+ }
+
+ create_loop_layer_for_scope<float2>(
+ config, bl_scope, CD_MLOOPUV, param.getName(), [&](size_t i) {
+ i = indices->size() ? (*indices)[i] : i;
+ return value_type_converter<abc_scalar_type>::map_to_float2(&input_data[i * 2]);
+ });
+
+ return AbcAttributeReadError::READ_SUCCESS;
}
+ case CD_MCOL: {
+ if (!can_add_vertex_color_layer(config)) {
+ return AbcAttributeReadError::TOO_MANY_ATTRIBUTES;
+ }
- /* Read vertex colors according to convention. */
- if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
- if (++num_colors > MAX_MCOL) {
- continue;
+ const bool is_facevarying = bl_scope == BlenderScope::LOOPS;
+
+ /* Read the vertex colors */
+ bool bounds_warning_given = false;
+
+ /* The colors can go through two layers of indexing. Often the 'indices'
+ * array doesn't do anything (i.e. indices[n] = n), but when it does, it's
+ * important. Blender 2.79 writes indices incorrectly (see T53745), which
+ * is why we have to check for indices->size() > 0 */
+ bool use_dual_indexing = is_facevarying && indices->size() > 0;
+
+ create_loop_layer_for_scope<MCol>(
+ config, bl_scope, CD_MLOOPCOL, param.getName(), [&](size_t loop_index) {
+ size_t color_index = loop_index;
+ if (use_dual_indexing) {
+ color_index = (*indices)[color_index];
+ }
+
+ bool is_mcols_out_of_bounds = false;
+ color_index = mcols_out_of_bounds_check(color_index,
+ values.size(),
+ config.iobject_full_name,
+ param.getName(),
+ is_mcols_out_of_bounds,
+ bounds_warning_given);
+
+ if (is_mcols_out_of_bounds) {
+ return MCol{};
+ }
+
+ return value_type_converter<abc_scalar_type>::map_to_mcol_from_rgb(
+ &input_data[loop_index * (3 + abc_mapping.is_rgba)]);
+ });
+
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ case CD_BWEIGHT: {
+ if (config.mesh) {
+ Mesh *mesh = config.mesh;
+ MVert *mvert = config.mvert;
+ for (int i = 0; i < mesh->totvert; i++) {
+ mvert[i].bweight = unit_float_to_uchar_clamp(
+ value_type_converter<abc_scalar_type>::map_to_float(&input_data[i]));
+ }
}
+ return AbcAttributeReadError::READ_SUCCESS;
+ }
+ }
+ return AbcAttributeReadError::READ_SUCCESS;
+}
+
+static AbcAttributeReadError read_mesh_uvs(const CDStreamConfig &config,
+ const IV2fGeomParam &uv_param,
+ ISampleSelector sample_sel)
+{
+ BLI_assert(config.mesh);
+
+ if (!uv_param.valid() || !uv_param.isIndexed()) {
+ return AbcAttributeReadError::INVALID_ATTRIBUTE;
+ }
+
+ IV2fGeomParam::Sample sample;
+ uv_param.getIndexed(sample, sample_sel);
+
+ const Alembic::AbcGeom::V2fArraySamplePtr &uvs = sample.getVals();
+ const Alembic::AbcGeom::UInt32ArraySamplePtr &indices = sample.getIndices();
+
+ const BlenderScope bl_scope = to_blender_scope(
+ uv_param.getScope(), indices->size(), config.scope_sizes);
+
+ if (!is_valid_uv_scope(bl_scope)) {
+ return AbcAttributeReadError::INVALID_SCOPE;
+ }
+
+ /* According to the convention, primary UVs should have had their name set using
+ * Alembic::Abc::SetSourceName. If there is no such name, use the name defined for the uv_param.
+ */
+ std::string name = Alembic::Abc::GetSourceName(uv_param.getMetaData());
+ if (name.empty()) {
+ name = uv_param.getName();
+ }
+
+ create_loop_layer_for_scope<MLoopUV>(config, bl_scope, CD_MLOOPUV, name, [&](size_t loop_index) {
+ size_t uv_index = (*indices)[loop_index];
+ const Imath::V2f &uv = (*uvs)[uv_index];
+
+ MLoopUV result;
+ result.uv[0] = uv[0];
+ result.uv[1] = uv[1];
+ return result;
+ });
+
+ return AbcAttributeReadError::READ_SUCCESS;
+}
+
+/* This structure holds data for an attribute found on the Alembic object. */
+struct ParsedAttributeDesc {
+ ICompoundProperty parent;
+ const PropertyHeader &prop_header;
+ const CacheAttributeMapping *mapping;
+};
+
+/* Extract supported attributes from the ICompoundProperty, and associate them with any mapping
+ * with a matching name. */
+static Vector<ParsedAttributeDesc> parse_attributes(const AttributeSelector *attr_sel,
+ const ICompoundProperty &arb_geom_params)
+{
+ Vector<ParsedAttributeDesc> result;
+ if (!arb_geom_params.valid()) {
+ return result;
+ }
+
+ for (size_t i = 0; i < arb_geom_params.getNumProperties(); ++i) {
+ const PropertyHeader &prop = arb_geom_params.getPropertyHeader(i);
- read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss);
+ /* TODO(kevindietrich): support scalar properties. */
+ if (prop.isScalar()) {
continue;
}
+
+ const CacheAttributeMapping *mapping = attr_sel ? attr_sel->get_mapping(prop.getName()) :
+ nullptr;
+ result.append({arb_geom_params, prop, mapping});
+ }
+
+ return result;
+}
+
+/* This function should be used for any attribute processing to ensure that all supported attribute
+ * types are handled.
+ *
+ * Unsupported attribute types are:
+ * - int64 and uint64, since we may have data loss as Blender only supports int32
+ * - half floating points (scalar, vec2, vec3, color)
+ * - matrices (3x3, 4x4)
+ * - string
+ * - quaternions
+ * - 2d normals
+ * - 2d & 3d bounding boxes
+ */
+template<typename OpType>
+static auto abc_attribute_type_operation(const ICompoundProperty &parent_prop,
+ const PropertyHeader &prop,
+ OpType &&op)
+{
+ if (IFloatGeomParam::matches(prop)) {
+ IFloatGeomParam param = IFloatGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IDoubleGeomParam::matches(prop)) {
+ IDoubleGeomParam param = IDoubleGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IBoolGeomParam::matches(prop)) {
+ IBoolGeomParam param = IBoolGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (ICharGeomParam::matches(prop)) {
+ ICharGeomParam param = ICharGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IInt16GeomParam::matches(prop)) {
+ IInt16GeomParam param = IInt16GeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IInt32GeomParam::matches(prop)) {
+ IInt32GeomParam param = IInt32GeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IUcharGeomParam::matches(prop)) {
+ IUcharGeomParam param = IUcharGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IUInt16GeomParam::matches(prop)) {
+ IUInt16GeomParam param = IUInt16GeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IUInt32GeomParam::matches(prop)) {
+ IUInt32GeomParam param = IUInt32GeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IV2fGeomParam::matches(prop)) {
+ IV2fGeomParam param = IV2fGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IN3fGeomParam::matches(prop)) {
+ IN3fGeomParam param = IN3fGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IN3dGeomParam::matches(prop)) {
+ IN3dGeomParam param = IN3dGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IP3fGeomParam::matches(prop)) {
+ IP3fGeomParam param = IP3fGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IP3dGeomParam::matches(prop)) {
+ IP3dGeomParam param = IP3dGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IV3fGeomParam::matches(prop)) {
+ IV3fGeomParam param = IV3fGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IV3dGeomParam::matches(prop)) {
+ IV3dGeomParam param = IV3dGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+ if (IC3fGeomParam::matches(prop)) {
+ IC3fGeomParam param = IC3fGeomParam(parent_prop, prop.getName());
+ return op(param);
}
+ if (IC4fGeomParam::matches(prop)) {
+ IC4fGeomParam param = IC4fGeomParam(parent_prop, prop.getName());
+ return op(param);
+ }
+
+ return op.default_handler_for_unsupported_attribute_type(prop.getName());
}
-AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope,
- const CDStreamConfig &config,
- const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
+struct AttributeReadOperator {
+ const CDStreamConfig &config;
+ const ISampleSelector &sample_sel;
+ const AttributeSelector &attr_sel;
+ float velocity_scale;
+
+ ParsedAttributeDesc *desc;
+
+ bool has_error = false;
+
+ void handle_error(AbcAttributeReadError error, const std::string &attribute_name)
+ {
+ has_error |= error != AbcAttributeReadError::READ_SUCCESS;
+
+ switch (error) {
+ case AbcAttributeReadError::READ_SUCCESS: {
+ return;
+ }
+ case AbcAttributeReadError::TOO_MANY_ATTRIBUTES: {
+ std::cerr << "Cannot read attribute \"" << attribute_name
+ << "\", too many attributes of the same type have been read!\n";
+ return;
+ }
+ case AbcAttributeReadError::INVALID_ATTRIBUTE: {
+ std::cerr << "Cannot read attribute \"" << attribute_name << "\" as it is invalid!\n";
+ return;
+ }
+ case AbcAttributeReadError::MAPPING_IMPOSSIBLE: {
+ std::cerr << "Cannot read attribute \"" << attribute_name
+ << "\" as the mapping is impossible!\n";
+ return;
+ }
+ case AbcAttributeReadError::SCOPE_RESOLUTION_FAILED: {
+ std::cerr << "Cannot read attribute \"" << attribute_name
+ << "\" as the scope is undeterminable!\n";
+ return;
+ }
+ case AbcAttributeReadError::INVALID_SCOPE: {
+ std::cerr << "Cannot read attribute \"" << attribute_name
+ << "\" as the scope is invalid for the specific data type!\n";
+ return;
+ }
+ case AbcAttributeReadError::UNSUPPORTED_TYPE: {
+ std::cerr << "Cannot read attribute \"" << attribute_name << "\""
+ << " as the data type is not supported!\n";
+ return;
+ }
+ }
+ }
+
+ void default_handler_for_unsupported_attribute_type(const std::string &param_name)
+ {
+ handle_error(AbcAttributeReadError::UNSUPPORTED_TYPE, param_name);
+ }
+
+ template<typename GeomParamType> void operator()(const GeomParamType &param)
+ {
+ AbcAttributeReadError error = process_typed_attribute(
+ config, desc->mapping, param, sample_sel);
+ handle_error(error, desc->prop_header.getName());
+ }
+};
+
+void read_arbitrary_attributes(const CDStreamConfig &config,
+ const ICompoundProperty &schema,
+ const IV2fGeomParam &primary_uvs,
+ const ISampleSelector &sample_sel,
+ float velocity_scale)
{
- if (scope == kFacevaryingScope && indices->size() == config.totloop) {
- return ABC_UV_SCOPE_LOOP;
+
+ /* The attribute selector may be null when run through the import operator. */
+ if (!config.attr_selector) {
+ return;
+ }
+
+ /* Manually extract the arbitrary geometry parameters. We do it this way to avoid complicating
+ * the code when dealing with schemas and default velocities which are not accessible via an
+ * IGeomParam as we would like for the sake of genericity, but as an IArrayProperty. */
+ const PropertyHeader *arb_geom_prop_header = schema.getPropertyHeader(".arbGeomParams");
+ ICompoundProperty arb_geom_params;
+ if (arb_geom_prop_header) {
+ arb_geom_params = ICompoundProperty(schema, ".arbGeomParams");
}
- /* kVaryingScope is sometimes used for vertex scopes as the values vary across the vertices. To
- * be sure, one has to check the size of the data against the number of vertices, as it could
- * also be a varying attribute across the faces (i.e. one value per face). */
- if ((ELEM(scope, kVaryingScope, kVertexScope)) && indices->size() == config.totvert) {
- return ABC_UV_SCOPE_VERTEX;
+ const AttributeSelector &attr_sel = *config.attr_selector;
+
+ Vector<ParsedAttributeDesc> attributes = parse_attributes(config.attr_selector, arb_geom_params);
+
+ /* At this point the velocities attribute should have the default, standard, attribute velocity
+ * name in Alembic. So we also expect any remapping to also use this name. If there is no
+ * attribute with the standard name, then either there are no velocities, or it has a different
+ * name which should have been set in the UI (both for the selection and the remapping). */
+ const PropertyHeader *velocity_prop_header = schema.getPropertyHeader(".velocities");
+ if (velocity_prop_header) {
+ attributes.append({schema, *velocity_prop_header, attr_sel.get_mapping(".velocities")});
+ }
+
+ if (primary_uvs.valid() && attr_sel.uvs_requested()) {
+ read_mesh_uvs(config, primary_uvs, sample_sel);
+ }
+
+ AttributeReadOperator op{config, sample_sel, attr_sel, velocity_scale, nullptr};
+
+ for (ParsedAttributeDesc desc : attributes) {
+ op.desc = &desc;
+ abc_attribute_type_operation(desc.parent, desc.prop_header, op);
+ }
+
+ if (op.has_error && config.modifier_error_message) {
+ *config.modifier_error_message = N_(
+ "Errors while trying to read attributes, see console for details...");
+ }
+}
+
+struct AnimatedAttributeOperator {
+ bool default_handler_for_unsupported_attribute_type(const std::string & /*param_name*/)
+ {
+ return false;
+ }
+
+ template<typename ParamType> bool operator()(const ParamType &param)
+ {
+ return param.valid() && !param.isConstant();
+ }
+};
+
+bool has_animated_attributes(const ICompoundProperty &arb_geom_params)
+{
+ /* Since this function is mainly called during data creation when importing an archive, we do not
+ * have an attribute selector to pass here. Also most of its settings (what to read, velocity
+ * name, etc.) would not be initialized anyway. */
+ Vector<ParsedAttributeDesc> attributes = parse_attributes(nullptr, arb_geom_params);
+
+ AnimatedAttributeOperator animated_param_op;
+ for (ParsedAttributeDesc desc : attributes) {
+ const PropertyHeader &prop = desc.prop_header;
+ if (abc_attribute_type_operation(desc.parent, prop, animated_param_op)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AttributeSelector::set_read_flags(int flags)
+{
+ read_flags = flags;
+}
+
+const CacheAttributeMapping *AttributeSelector::get_mapping(const std::string &attr_name) const
+{
+ for (const CacheAttributeMapping *mapping : mappings) {
+ if (attr_name == mapping->name) {
+ return mapping;
+ }
+ }
+
+ return nullptr;
+}
+
+const std::string &AttributeSelector::velocity_name() const
+{
+ return velocity_attribute;
+}
+
+void AttributeSelector::set_velocity_attribute(const char *name)
+{
+ velocity_attribute = name;
+}
+
+bool AttributeSelector::uvs_requested() const
+{
+ return (read_flags & MOD_MESHSEQ_READ_UV) != 0;
+}
+
+bool AttributeSelector::vertex_colors_requested() const
+{
+ return (read_flags & MOD_MESHSEQ_READ_COLOR) != 0;
+}
+
+bool AttributeSelector::original_coordinates_requested() const
+{
+ /* There is no flag for the ORCO layer, we load it if the vertices are requested. */
+ return (read_flags & MOD_MESHSEQ_READ_VERT) != 0;
+}
+
+bool AttributeSelector::select_attribute(const std::string &attr_name) const
+{
+ /* Empty names are invalid. Those beginning with a '.' are special and correspond to data
+ * accessible through specific APIs (e.g. the vertices are named ".P") */
+ if (attr_name.empty() || (attr_name[0] == '.' && attr_name != velocity_attribute)) {
+ return false;
}
- return ABC_UV_SCOPE_NONE;
+ return true;
}
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h
index 97a9235753f..54c15cd02e5 100644
--- a/source/blender/io/alembic/intern/abc_customdata.h
+++ b/source/blender/io/alembic/intern/abc_customdata.h
@@ -27,7 +27,11 @@
#include <map>
+#include "BLI_listbase_wrapper.hh"
+
+struct CacheAttributeMapping;
struct CustomData;
+struct ID;
struct MLoop;
struct MLoopUV;
struct MPoly;
@@ -38,11 +42,26 @@ using Alembic::Abc::ICompoundProperty;
using Alembic::Abc::OCompoundProperty;
namespace blender::io::alembic {
+class AttributeSelector;
+enum class BlenderScope : uint32_t;
+
struct UVSample {
std::vector<Imath::V2f> uvs;
std::vector<uint32_t> indices;
};
+struct ScopeSizeInfo {
+ int point_scope_size;
+ int polygon_scope_size;
+ int loop_scope_size;
+};
+
+struct ScopeCustomDataPointers {
+ CustomData *point_custom_data;
+ CustomData *polygon_custom_data;
+ CustomData *loop_custom_data;
+};
+
struct CDStreamConfig {
MLoop *mloop;
int totloop;
@@ -59,10 +78,12 @@ struct CDStreamConfig {
bool pack_uvs;
- /* TODO(kevin): might need a better way to handle adding and/or updating
- * custom data such that it updates the custom data holder and its pointers properly. */
+ /* NOTE: the mesh is mostly used for iterating over loops for loop attributes (UVs, MCol, etc.).
+ * It would be nice to remove it, in favor of a more generic way to iterate valid attribute
+ * indices.
+ */
Mesh *mesh;
- void *(*add_customdata_cb)(Mesh *mesh, const char *name, int data_type);
+ ID *id;
float weight;
float time;
@@ -73,6 +94,14 @@ struct CDStreamConfig {
const char **modifier_error_message;
+ ScopeCustomDataPointers custom_data_pointers;
+ ScopeSizeInfo scope_sizes;
+
+ /* For error reporting when reading vertex colors. */
+ std::string iobject_full_name;
+
+ const AttributeSelector *attr_selector;
+
/* Alembic needs Blender to keep references to C++ objects (the destructors finalize the writing
* to ABC). The following fields are all used to keep these references. */
@@ -94,12 +123,12 @@ struct CDStreamConfig {
totvert(0),
pack_uvs(false),
mesh(NULL),
- add_customdata_cb(NULL),
weight(0.0f),
time(0.0f),
index(0),
ceil_index(0),
- modifier_error_message(NULL)
+ modifier_error_message(NULL),
+ attr_selector(nullptr)
{
}
};
@@ -112,36 +141,47 @@ const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, Custom
void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig &config);
-void read_generated_coordinates(const ICompoundProperty &prop,
- const CDStreamConfig &config,
- const Alembic::Abc::ISampleSelector &iss);
-
void write_custom_data(const OCompoundProperty &prop,
CDStreamConfig &config,
CustomData *data,
int data_type);
-void read_custom_data(const std::string &iobject_full_name,
- const ICompoundProperty &prop,
- const CDStreamConfig &config,
- const Alembic::Abc::ISampleSelector &iss);
-
-typedef enum {
- ABC_UV_SCOPE_NONE,
- ABC_UV_SCOPE_LOOP,
- ABC_UV_SCOPE_VERTEX,
-} AbcUvScope;
-
-/**
- * UVs can be defined per-loop (one value per vertex per face), or per-vertex (one value per
- * vertex). The first case is the most common, as this is the standard way of storing this data
- * given that some vertices might be on UV seams and have multiple possible UV coordinates; the
- * second case can happen when the mesh is split according to the UV islands, in which case storing
- * a single UV value per vertex allows to de-duplicate data and thus to reduce the file size since
- * vertices are guaranteed to only have a single UV coordinate.
- */
-AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope,
- const CDStreamConfig &config,
- const Alembic::AbcGeom::UInt32ArraySamplePtr &indices);
+class AttributeSelector {
+ /* Name of the velocity attribute, it is ignored since we deal with separately. */
+ std::string velocity_attribute = "";
+
+ int read_flags = 0;
+
+ ListBaseWrapper<const CacheAttributeMapping> mappings;
+
+ public:
+ AttributeSelector(ListBase *mappings_) : mappings(mappings_)
+ {
+ }
+
+ void set_velocity_attribute(const char *name);
+
+ void set_read_flags(int flags);
+
+ const CacheAttributeMapping *get_mapping(const std::string &attr_name) const;
+
+ const std::string &velocity_name() const;
+
+ bool uvs_requested() const;
+
+ bool vertex_colors_requested() const;
+
+ bool original_coordinates_requested() const;
+
+ bool select_attribute(const std::string &attr_name) const;
+};
+
+void read_arbitrary_attributes(const CDStreamConfig &config,
+ const ICompoundProperty &schema,
+ const Alembic::AbcGeom::IV2fGeomParam &primary_uvs,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ float velocity_scale);
+
+bool has_animated_attributes(const ICompoundProperty &arb_geom_params);
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_camera.cc b/source/blender/io/alembic/intern/abc_reader_camera.cc
index d7a096a4ed8..ae0bf901758 100644
--- a/source/blender/io/alembic/intern/abc_reader_camera.cc
+++ b/source/blender/io/alembic/intern/abc_reader_camera.cc
@@ -73,7 +73,9 @@ bool AbcCameraReader::accepts_object_type(
return true;
}
-void AbcCameraReader::readObjectData(Main *bmain, const ISampleSelector &sample_sel)
+void AbcCameraReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const ISampleSelector &sample_sel)
{
Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, m_data_name.c_str()));
diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h
index ca8dee80c9d..929832c89d7 100644
--- a/source/blender/io/alembic/intern/abc_reader_camera.h
+++ b/source/blender/io/alembic/intern/abc_reader_camera.h
@@ -34,7 +34,9 @@ class AbcCameraReader final : public AbcObjectReader {
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc
index bd1e57da648..702176672c1 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.cc
+++ b/source/blender/io/alembic/intern/abc_reader_curves.cc
@@ -36,8 +36,10 @@
#include "BLI_listbase.h"
#include "BKE_curve.h"
+#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_spline.hh"
using Alembic::Abc::FloatArraySamplePtr;
using Alembic::Abc::Int32ArraySamplePtr;
@@ -90,23 +92,90 @@ bool AbcCurveReader::accepts_object_type(
return true;
}
-void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+static short get_curve_resolution(const ICurvesSchema &schema,
+ const Alembic::Abc::ISampleSelector &sample_sel)
{
- Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE);
-
- cu->flag |= CU_3D;
- cu->actvert = CU_ACT_NONE;
- cu->resolu = 1;
-
- ICompoundProperty user_props = m_curves_schema.getUserProperties();
+ ICompoundProperty user_props = schema.getUserProperties();
if (user_props) {
const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME);
if (header != nullptr && header->isScalar() && IInt16Property::matches(*header)) {
IInt16Property resolu(user_props, header->getName());
- cu->resolu = resolu.getValue(sample_sel);
+ return resolu.getValue(sample_sel);
}
}
+ return 1;
+}
+
+static short get_curve_order(Alembic::AbcGeom::CurveType abc_curve_type,
+ const UcharArraySamplePtr orders,
+ size_t curve_index)
+{
+ switch (abc_curve_type) {
+ case Alembic::AbcGeom::kCubic:
+ return 4;
+ case Alembic::AbcGeom::kVariableOrder:
+ if (orders && orders->size() > curve_index) {
+ return static_cast<short>((*orders)[curve_index]);
+ }
+ ATTR_FALLTHROUGH;
+ case Alembic::AbcGeom::kLinear:
+ default:
+ return 2;
+ }
+}
+
+static int get_curve_overlap(Alembic::AbcGeom::CurvePeriodicity periodicity,
+ const P3fArraySamplePtr positions,
+ int idx,
+ int num_verts,
+ short order)
+{
+ if (periodicity == Alembic::AbcGeom::kPeriodic) {
+ /* Check the number of points which overlap, we don't have
+ * overlapping points in Blender, but other software do use them to
+ * indicate that a curve is actually cyclic. Usually the number of
+ * overlapping points is equal to the order/degree of the curve.
+ */
+
+ const int start = idx;
+ const int end = idx + num_verts;
+ int overlap = 0;
+
+ for (int j = start, k = end - order; j < order; j++, k++) {
+ const Imath::V3f &p1 = (*positions)[j];
+ const Imath::V3f &p2 = (*positions)[k];
+
+ if (p1 != p2) {
+ break;
+ }
+
+ overlap++;
+ }
+
+ /* TODO: Special case, need to figure out how it coincides with knots. */
+ if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) {
+ overlap = 1;
+ }
+
+ /* There is no real cycles. */
+ return overlap;
+ }
+
+ /* kNonPeriodic is always assumed to have no overlap. */
+ return 0;
+}
+
+void AbcCurveReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const Alembic::Abc::ISampleSelector &sample_sel)
+{
+ Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE);
+
+ cu->flag |= CU_3D;
+ cu->actvert = CU_ACT_NONE;
+ cu->resolu = get_curve_resolution(m_curves_schema, sample_sel);
+
m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());
m_object->data = cu;
@@ -161,60 +230,15 @@ void AbcCurveReader::read_curve_sample(Curve *cu,
nu->pntsu = num_verts;
nu->pntsv = 1;
nu->flag |= CU_SMOOTH;
+ nu->orderu = get_curve_order(smp.getType(), orders, i);
- switch (smp.getType()) {
- case Alembic::AbcGeom::kCubic:
- nu->orderu = 4;
- break;
- case Alembic::AbcGeom::kVariableOrder:
- if (orders && orders->size() > i) {
- nu->orderu = static_cast<short>((*orders)[i]);
- break;
- }
- ATTR_FALLTHROUGH;
- case Alembic::AbcGeom::kLinear:
- default:
- nu->orderu = 2;
- }
+ const int overlap = get_curve_overlap(periodicity, positions, idx, num_verts, nu->orderu);
- if (periodicity == Alembic::AbcGeom::kNonPeriodic) {
+ if (overlap == 0) {
nu->flagu |= CU_NURB_ENDPOINT;
}
- else if (periodicity == Alembic::AbcGeom::kPeriodic) {
+ else {
nu->flagu |= CU_NURB_CYCLIC;
-
- /* Check the number of points which overlap, we don't have
- * overlapping points in Blender, but other software do use them to
- * indicate that a curve is actually cyclic. Usually the number of
- * overlapping points is equal to the order/degree of the curve.
- */
-
- const int start = idx;
- const int end = idx + num_verts;
- int overlap = 0;
-
- for (int j = start, k = end - nu->orderu; j < nu->orderu; j++, k++) {
- const Imath::V3f &p1 = (*positions)[j];
- const Imath::V3f &p2 = (*positions)[k];
-
- if (p1 != p2) {
- break;
- }
-
- overlap++;
- }
-
- /* TODO: Special case, need to figure out how it coincides with knots. */
- if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) {
- overlap = 1;
- }
-
- /* There is no real cycles. */
- if (overlap == 0) {
- nu->flagu &= ~CU_NURB_CYCLIC;
- nu->flagu |= CU_NURB_ENDPOINT;
- }
-
nu->pntsu -= overlap;
}
@@ -274,12 +298,39 @@ void AbcCurveReader::read_curve_sample(Curve *cu,
}
}
-Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh,
- const ISampleSelector &sample_sel,
- int /*read_flag*/,
- const char * /*velocity_name*/,
- const float /*velocity_scale*/,
- const char **err_str)
+static bool topology_changed(CurveEval *curve_eval, const Int32ArraySamplePtr &num_vertices)
+{
+ if (!curve_eval) {
+ return true;
+ }
+
+ const size_t num_curves = num_vertices->size();
+ if (num_curves != curve_eval->splines().size()) {
+ return true;
+ }
+
+ for (size_t i = 0; i < num_vertices->size(); i++) {
+ const Spline *spline = curve_eval->splines()[i].get();
+
+ if (!spline) {
+ return true;
+ }
+
+ // TODO(kevindietrich) : this should check for the overlap.
+ if (spline->positions().size() != (*num_vertices)[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void AbcCurveReader::read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector * /*attribute_selector*/,
+ int /*read_flag*/,
+ const float /*velocity_scale*/,
+ const char **err_str)
{
ICurvesSchema::Sample sample;
@@ -293,61 +344,49 @@ Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh,
m_curves_schema.getName().c_str(),
sample_sel.getRequestedTime(),
ex.what());
- return existing_mesh;
+ return;
}
- const P3fArraySamplePtr &positions = sample.getPositions();
- const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices();
-
- int vertex_idx = 0;
- int curve_idx;
- Curve *curve = static_cast<Curve *>(m_object->data);
-
- const int curve_count = BLI_listbase_count(&curve->nurb);
- bool same_topology = curve_count == num_vertices->size();
+ CurveEval *curve_eval = geometry_set.get_curve_for_write();
- if (same_topology) {
- Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
- for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
- const int num_in_alembic = (*num_vertices)[curve_idx];
- const int num_in_blender = nurbs->pntsu;
-
- if (num_in_alembic != num_in_blender) {
- same_topology = false;
- break;
- }
- }
- }
+ const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices();
+ const P3fArraySamplePtr &positions = sample.getPositions();
- if (!same_topology) {
+ if (blender::io::alembic::topology_changed(curve_eval, num_vertices)) {
+ Curve *curve = static_cast<Curve *>(m_object->data);
BKE_nurbList_free(&curve->nurb);
read_curve_sample(curve, m_curves_schema, sample_sel);
+
+ std::unique_ptr<CurveEval> new_curve_eval = curve_eval_from_dna_curve(*curve);
+ geometry_set.replace_curve(new_curve_eval.release(), GeometryOwnershipType::Editable);
}
else {
- Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
- for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
- const int totpoint = (*num_vertices)[curve_idx];
+ const IFloatGeomParam widths_param = m_curves_schema.getWidthsParam();
+ FloatArraySamplePtr radiuses;
- if (nurbs->bp) {
- BPoint *point = nurbs->bp;
+ if (widths_param.valid()) {
+ IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
+ radiuses = wsample.getVals();
+ }
- for (int i = 0; i < totpoint; i++, point++, vertex_idx++) {
- const Imath::V3f &pos = (*positions)[vertex_idx];
- copy_zup_from_yup(point->vec, pos.getValue());
- }
+ const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
+ float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f;
+
+ size_t position_index = 0;
+ size_t radius_index = 0;
+
+ for (size_t i = 0; i < num_vertices->size(); i++) {
+ Spline *spline = curve_eval->splines()[i].get();
+
+ for (float3 &position : spline->positions()) {
+ copy_zup_from_yup(position, (*positions)[position_index++].getValue());
}
- else if (nurbs->bezt) {
- BezTriple *bezier = nurbs->bezt;
- for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) {
- const Imath::V3f &pos = (*positions)[vertex_idx];
- copy_zup_from_yup(bezier->vec[1], pos.getValue());
- }
+ for (float &r : spline->radii()) {
+ r = (do_radius) ? (*radiuses)[radius_index++] : radius;
}
}
}
-
- return BKE_mesh_new_nomain_from_curve(m_object);
}
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h
index e7e6fa9f0b2..88956c4415c 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.h
+++ b/source/blender/io/alembic/intern/abc_reader_curves.h
@@ -42,7 +42,10 @@ class AbcCurveReader final : public AbcObjectReader {
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
+
/**
* \note Alembic only stores data about control points, but the Mesh
* passed from the cache modifier contains the #DispList, which has more data
@@ -50,12 +53,12 @@ class AbcCurveReader final : public AbcObjectReader {
* object directly and create a new Mesh from that. Also we might need to
* create new or delete existing NURBS in the curve.
*/
- struct Mesh *read_mesh(struct Mesh *existing_mesh,
- const Alembic::Abc::ISampleSelector &sample_sel,
- int read_flag,
- const char *velocity_name,
- float velocity_scale,
- const char **err_str) override;
+ void read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str) override;
void read_curve_sample(Curve *cu,
const Alembic::AbcGeom::ICurvesSchema &schema,
diff --git a/source/blender/io/alembic/intern/abc_reader_instance.cc b/source/blender/io/alembic/intern/abc_reader_instance.cc
new file mode 100644
index 00000000000..ee7c42a5ec1
--- /dev/null
+++ b/source/blender/io/alembic/intern/abc_reader_instance.cc
@@ -0,0 +1,74 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_reader_instance.h"
+
+#include "DNA_object_types.h"
+
+#include "BLI_assert.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_lib_remap.h"
+#include "BKE_object.h"
+
+#include "abc_reader_manager.h"
+
+namespace blender::io::alembic {
+
+AbcInstanceReader::AbcInstanceReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+ : AbcObjectReader(object, settings)
+{
+}
+
+bool AbcInstanceReader::valid() const
+{
+ // TODO(kevindietrich)
+ return true;
+}
+
+bool AbcInstanceReader::accepts_object_type(
+ const Alembic::AbcCoreAbstract::ObjectHeader & /*alembic_header*/,
+ const Object *const /*ob*/,
+ const char ** /*err_str*/) const
+{
+ /* TODO(kevindietrich) */
+ return true;
+}
+
+void AbcInstanceReader::readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector & /* sample_sel */)
+{
+ /* For reference on duplication, see ED_object_add_duplicate_linked.
+ *
+ * In this function, we only duplicate the object, as the rest (adding to view layer, tagging the
+ * depsgraph, etc.) is done at the end of the import.
+ */
+
+ Object *ob = manager.get_blender_object_for_path(m_iobject.instanceSourcePath());
+ BLI_assert(ob);
+ /* 0 = linked */
+ uint dupflag = 0;
+ uint duplicate_options = LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID;
+
+ m_object = static_cast<Object *>(
+ ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options)));
+
+ /* link own references to the newly duplicated data T26816. */
+ BKE_libblock_relink_to_newid(bmain, &m_object->id, 0);
+}
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_instance.h b/source/blender/io/alembic/intern/abc_reader_instance.h
new file mode 100644
index 00000000000..9901cbf62ff
--- /dev/null
+++ b/source/blender/io/alembic/intern/abc_reader_instance.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_reader_object.h"
+
+namespace blender::io::alembic {
+
+class AbcInstanceReader final : public AbcObjectReader {
+ public:
+ AbcInstanceReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+ bool valid() const override;
+
+ bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
+ const Object *const ob,
+ const char **err_str) const override;
+
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
+};
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_manager.cc b/source/blender/io/alembic/intern/abc_reader_manager.cc
new file mode 100644
index 00000000000..486c417cb26
--- /dev/null
+++ b/source/blender/io/alembic/intern/abc_reader_manager.cc
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_reader_manager.h"
+
+#include "abc_reader_instance.h"
+
+namespace blender::io::alembic {
+
+AbcObjectReader *AbcReaderManager::create_instance_reader(Alembic::Abc::v12::IObject iobject,
+ ImportSettings &settings)
+{
+ std::cerr << "Creating an instance reader...\n";
+ AbcObjectReader *reader = new AbcInstanceReader(iobject, settings);
+ m_instance_readers.push_back(reader);
+ m_readers_all.push_back(reader);
+ return reader;
+}
+
+Object *AbcReaderManager::get_blender_object_for_path(const std::string &path) const
+{
+ AbcObjectReader *reader = get_object_reader_for_path(path);
+ if (!reader) {
+ return nullptr;
+ }
+ return reader->object();
+}
+
+AbcObjectReader *AbcReaderManager::get_object_reader_for_path(const std::string &path) const
+{
+ MapIteratorType iter = m_readers_map.find(path);
+ if (iter == m_readers_map.end()) {
+ return nullptr;
+ }
+ return iter->second;
+}
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_manager.h b/source/blender/io/alembic/intern/abc_reader_manager.h
new file mode 100644
index 00000000000..5f990f4b8f9
--- /dev/null
+++ b/source/blender/io/alembic/intern/abc_reader_manager.h
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+/** \file
+ * \ingroup balembic
+ */
+
+#include "abc_reader_object.h"
+
+namespace blender::io::alembic {
+
+class AbcReaderManager {
+ using MapType = std::map<std::string, AbcObjectReader *>;
+ using MapIteratorType = MapType::const_iterator;
+ std::map<std::string, AbcObjectReader *> m_readers_map{};
+
+ AbcObjectReader::ptr_vector m_readers;
+ AbcObjectReader::ptr_vector m_readers_all;
+ AbcObjectReader::ptr_vector m_instance_readers;
+
+ public:
+ template<typename ReaderType>
+ AbcObjectReader *create(Alembic::Abc::IObject iobject, ImportSettings &settings)
+ {
+ static_assert(
+ std::is_base_of_v<AbcObjectReader, ReaderType>,
+ "Trying to create a reader from a class which does not derive from AbcObjectReader !");
+
+ if (iobject.isInstanceRoot()) {
+ return create_instance_reader(iobject, settings);
+ }
+
+ ReaderType *reader = new ReaderType(iobject, settings);
+ m_readers_map[iobject.getFullName()] = reader;
+ m_readers.push_back(reader);
+ m_readers_all.push_back(reader);
+ return reader;
+ }
+
+ AbcObjectReader *create_instance_reader(Alembic::Abc::IObject iobject, ImportSettings &settings);
+
+ Object *get_blender_object_for_path(const std::string &path) const;
+
+ const AbcObjectReader::ptr_vector &all_readers() const
+ {
+ return m_readers_all;
+ }
+
+ const AbcObjectReader::ptr_vector &instance_readers() const
+ {
+ return m_instance_readers;
+ }
+
+ const AbcObjectReader::ptr_vector &data_readers() const
+ {
+ return m_readers;
+ }
+
+ private:
+ AbcObjectReader *get_object_reader_for_path(const std::string &path) const;
+};
+
+} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index 43581ad48d9..4234fb34015 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -38,7 +38,7 @@
#include "BLI_listbase.h"
#include "BLI_math_geom.h"
-#include "BKE_attribute.h"
+#include "BKE_geometry_set.hh"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -126,10 +126,6 @@ struct AbcMeshData {
P3fArraySamplePtr positions;
P3fArraySamplePtr ceil_positions;
-
- AbcUvScope uv_scope;
- V2fArraySamplePtr uvs;
- UInt32ArraySamplePtr uvs_indices;
};
static void read_mverts_interp(MVert *mverts,
@@ -189,21 +185,12 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
{
MPoly *mpolys = config.mpoly;
MLoop *mloops = config.mloop;
- MLoopUV *mloopuvs = config.mloopuv;
const Int32ArraySamplePtr &face_indices = mesh_data.face_indices;
const Int32ArraySamplePtr &face_counts = mesh_data.face_counts;
- const V2fArraySamplePtr &uvs = mesh_data.uvs;
- const size_t uvs_size = uvs == nullptr ? 0 : uvs->size();
-
- const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
- const bool do_uvs = (mloopuvs && uvs && uvs_indices);
- const bool do_uvs_per_loop = do_uvs && mesh_data.uv_scope == ABC_UV_SCOPE_LOOP;
- BLI_assert(!do_uvs || mesh_data.uv_scope != ABC_UV_SCOPE_NONE);
unsigned int loop_index = 0;
unsigned int rev_loop_index = 0;
- unsigned int uv_index = 0;
bool seen_invalid_geometry = false;
for (int i = 0; i < face_counts->size(); i++) {
@@ -231,19 +218,6 @@ static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
seen_invalid_geometry = true;
}
last_vertex_index = loop.v;
-
- if (do_uvs) {
- MLoopUV &loopuv = mloopuvs[rev_loop_index];
- uv_index = (*uvs_indices)[do_uvs_per_loop ? loop_index : loop.v];
-
- /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */
- if (uv_index >= uvs_size) {
- continue;
- }
-
- loopuv.uv[0] = (*uvs)[uv_index][0];
- loopuv.uv[1] = (*uvs)[uv_index][1];
- }
}
}
@@ -351,68 +325,6 @@ static void process_normals(CDStreamConfig &config,
}
}
-BLI_INLINE void read_uvs_params(CDStreamConfig &config,
- AbcMeshData &abc_data,
- const IV2fGeomParam &uv,
- const ISampleSelector &selector)
-{
- if (!uv.valid()) {
- return;
- }
-
- IV2fGeomParam::Sample uvsamp;
- uv.getIndexed(uvsamp, selector);
-
- UInt32ArraySamplePtr uvs_indices = uvsamp.getIndices();
-
- const AbcUvScope uv_scope = get_uv_scope(uv.getScope(), config, uvs_indices);
-
- if (uv_scope == ABC_UV_SCOPE_NONE) {
- return;
- }
-
- abc_data.uv_scope = uv_scope;
- abc_data.uvs = uvsamp.getVals();
- abc_data.uvs_indices = uvs_indices;
-
- std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
-
- /* According to the convention, primary UVs should have had their name
- * set using Alembic::Abc::SetSourceName, but you can't expect everyone
- * to follow it! :) */
- if (name.empty()) {
- name = uv.getName();
- }
-
- void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV);
- config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
-}
-
-static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type)
-{
- CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
- void *cd_ptr;
- CustomData *loopdata;
- int numloops;
-
- /* unsupported custom data type -- don't do anything. */
- if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
- return nullptr;
- }
-
- loopdata = &mesh->ldata;
- cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
- if (cd_ptr != nullptr) {
- /* layer already exists, so just return it. */
- return cd_ptr;
- }
-
- /* Create a new layer. */
- numloops = mesh->totloop;
- cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
- return cd_ptr;
-}
-
static void get_weight_and_index(CDStreamConfig &config,
Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling,
size_t samples_number)
@@ -425,65 +337,7 @@ static void get_weight_and_index(CDStreamConfig &config,
config.ceil_index = i1;
}
-static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema,
- const ISampleSelector &selector,
- const std::string &name)
-{
- for (size_t i = 0; i < schema.getNumProperties(); i++) {
- const PropertyHeader &header = schema.getPropertyHeader(i);
-
- if (header.isCompound()) {
- const ICompoundProperty &prop = ICompoundProperty(schema, header.getName());
-
- if (has_property(prop, name)) {
- /* Header cannot be null here, as its presence is checked via has_property, so it is safe
- * to dereference. */
- const PropertyHeader *header = prop.getPropertyHeader(name);
- if (!IV3fArrayProperty::matches(*header)) {
- continue;
- }
-
- const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0);
- if (velocity_prop) {
- return velocity_prop.getValue(selector);
- }
- }
- }
- else if (header.isArray()) {
- if (header.getName() == name && IV3fArrayProperty::matches(header)) {
- const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0);
- return velocity_prop.getValue(selector);
- }
- }
- }
-
- return V3fArraySamplePtr();
-}
-
-static void read_velocity(const V3fArraySamplePtr &velocities,
- const CDStreamConfig &config,
- const float velocity_scale)
-{
- const int num_velocity_vectors = static_cast<int>(velocities->size());
- if (num_velocity_vectors != config.mesh->totvert) {
- /* Files containing videogrammetry data may be malformed and export velocity data on missing
- * frames (most likely by copying the last valid data). */
- return;
- }
-
- CustomDataLayer *velocity_layer = BKE_id_attribute_new(
- &config.mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT, nullptr);
- float(*velocity)[3] = (float(*)[3])velocity_layer->data;
-
- for (int i = 0; i < num_velocity_vectors; i++) {
- const Imath::V3f &vel_in = (*velocities)[i];
- copy_zup_from_yup(velocity[i], vel_in.getValue());
- mul_v3_fl(velocity[i], velocity_scale);
- }
-}
-
-static void read_mesh_sample(const std::string &iobject_full_name,
- ImportSettings *settings,
+static void read_mesh_sample(ImportSettings *settings,
const IPolyMeshSchema &schema,
const ISampleSelector &selector,
CDStreamConfig &config)
@@ -503,13 +357,8 @@ static void read_mesh_sample(const std::string &iobject_full_name,
abc_mesh_data.ceil_positions = ceil_sample.getPositions();
}
- if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
- read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
- }
-
if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
read_mverts(config, abc_mesh_data);
- read_generated_coordinates(schema.getArbGeomParams(), config, selector);
}
if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
@@ -517,25 +366,21 @@ static void read_mesh_sample(const std::string &iobject_full_name,
process_normals(config, schema.getNormalsParam(), selector);
}
- if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
- read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
- }
-
- if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) {
- V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name);
- if (velocities) {
- read_velocity(velocities, config, settings->velocity_scale);
- }
- }
+ read_arbitrary_attributes(
+ config, schema, schema.getUVsParam(), selector, settings->velocity_scale);
}
-CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation)
+CDStreamConfig get_config(Mesh *mesh,
+ const AttributeSelector *attr_selector,
+ const std::string &iobject_full_name,
+ const bool use_vertex_interpolation)
{
CDStreamConfig config;
BLI_assert(mesh->mvert || mesh->totvert == 0);
config.mesh = mesh;
+ config.id = &mesh->id;
config.mvert = mesh->mvert;
config.mloop = mesh->mloop;
config.mpoly = mesh->mpoly;
@@ -543,8 +388,11 @@ CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation)
config.totloop = mesh->totloop;
config.totpoly = mesh->totpoly;
config.loopdata = &mesh->ldata;
- config.add_customdata_cb = add_customdata_cb;
config.use_vertex_interpolation = use_vertex_interpolation;
+ config.attr_selector = attr_selector;
+ config.iobject_full_name = iobject_full_name;
+ config.scope_sizes = {mesh->totvert, mesh->totpoly, mesh->totloop};
+ config.custom_data_pointers = {&mesh->vdata, &mesh->pdata, &mesh->ldata};
return config;
}
@@ -567,39 +415,6 @@ bool AbcMeshReader::valid() const
return m_schema.valid();
}
-template<class typedGeomParam>
-bool is_valid_animated(const ICompoundProperty arbGeomParams, const PropertyHeader &prop_header)
-{
- if (!typedGeomParam::matches(prop_header)) {
- return false;
- }
-
- typedGeomParam geom_param(arbGeomParams, prop_header.getName());
- return geom_param.valid() && !geom_param.isConstant();
-}
-
-static bool has_animated_geom_params(const ICompoundProperty arbGeomParams)
-{
- if (!arbGeomParams.valid()) {
- return false;
- }
-
- const int num_props = arbGeomParams.getNumProperties();
- for (int i = 0; i < num_props; i++) {
- const PropertyHeader &prop_header = arbGeomParams.getPropertyHeader(i);
-
- /* These are interpreted as vertex colors later (see 'read_custom_data'). */
- if (is_valid_animated<IC3fGeomParam>(arbGeomParams, prop_header)) {
- return true;
- }
- if (is_valid_animated<IC4fGeomParam>(arbGeomParams, prop_header)) {
- return true;
- }
- }
-
- return false;
-}
-
/* Specialization of #has_animations() as defined in abc_reader_object.h. */
template<> bool has_animations(Alembic::AbcGeom::IPolyMeshSchema &schema, ImportSettings *settings)
{
@@ -618,21 +433,30 @@ template<> bool has_animations(Alembic::AbcGeom::IPolyMeshSchema &schema, Import
}
ICompoundProperty arbGeomParams = schema.getArbGeomParams();
- if (has_animated_geom_params(arbGeomParams)) {
+ if (has_animated_attributes(arbGeomParams)) {
return true;
}
return false;
}
-void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+void AbcMeshReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const Alembic::Abc::ISampleSelector &sample_sel)
{
Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
m_object->data = mesh;
- Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
+ /* Default AttributeSelector to ensure that at least UVs and vertex colors are read. To load
+ * other attributes, a modifier should be added as there are no clear conventions for them. */
+ ListBase lb = {nullptr, nullptr};
+ AttributeSelector attr_selector(&lb);
+ attr_selector.set_read_flags(MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR);
+
+ Mesh *read_mesh = this->read_mesh(
+ mesh, sample_sel, &attr_selector, MOD_MESHSEQ_READ_ALL, 0.0f, nullptr);
if (read_mesh != mesh) {
/* XXX FIXME: after 2.80; mesh->flag isn't copied by #BKE_mesh_nomain_to_mesh(). */
/* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
@@ -697,10 +521,31 @@ bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector
face_indices->size() != existing_mesh->totloop;
}
+void AbcMeshReader::read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str)
+{
+ Mesh *mesh = geometry_set.get_mesh_for_write();
+
+ if (mesh == nullptr) {
+ return;
+ }
+
+ Mesh *new_mesh = read_mesh(
+ mesh, sample_sel, attribute_selector, read_flag, velocity_scale, err_str);
+
+ if (new_mesh != mesh) {
+ geometry_set.replace_mesh(new_mesh);
+ }
+}
+
Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
const ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
const int read_flag,
- const char *velocity_name,
const float velocity_scale,
const char **err_str)
{
@@ -744,7 +589,6 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
/* Only read point data when streaming meshes, unless we need to create new ones. */
ImportSettings settings;
settings.read_flag |= read_flag;
- settings.velocity_name = velocity_name;
settings.velocity_scale = velocity_scale;
if (topology_changed(existing_mesh, sample_sel)) {
@@ -771,11 +615,12 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
const bool use_vertex_interpolation = read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
- CDStreamConfig config = get_config(mesh_to_export, use_vertex_interpolation);
+ CDStreamConfig config = get_config(
+ mesh_to_export, attribute_selector, m_iobject.getFullName(), use_vertex_interpolation);
config.time = sample_sel.getRequestedTime();
config.modifier_error_message = err_str;
- read_mesh_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
+ read_mesh_sample(&settings, m_schema, sample_sel, config);
if (new_mesh) {
/* Here we assume that the number of materials doesn't change, i.e. that
@@ -862,8 +707,7 @@ BLI_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
return nullptr;
}
-static void read_subd_sample(const std::string &iobject_full_name,
- ImportSettings *settings,
+static void read_subd_sample(ImportSettings *settings,
const ISubDSchema &schema,
const ISampleSelector &selector,
CDStreamConfig &config)
@@ -883,10 +727,6 @@ static void read_subd_sample(const std::string &iobject_full_name,
abc_mesh_data.ceil_positions = ceil_sample.getPositions();
}
- if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
- read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
- }
-
if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
read_mverts(config, abc_mesh_data);
}
@@ -899,16 +739,8 @@ static void read_subd_sample(const std::string &iobject_full_name,
process_no_normals(config);
}
- if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
- read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector);
- }
-
- if (!settings->velocity_name.empty() && settings->velocity_scale != 0.0f) {
- V3fArraySamplePtr velocities = get_velocity_prop(schema, selector, settings->velocity_name);
- if (velocities) {
- read_velocity(velocities, config, settings->velocity_scale);
- }
- }
+ read_arbitrary_attributes(
+ config, schema, schema.getUVsParam(), selector, settings->velocity_scale);
}
static void read_vertex_creases(Mesh *mesh,
@@ -1009,14 +841,23 @@ bool AbcSubDReader::accepts_object_type(
return true;
}
-void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+void AbcSubDReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const Alembic::Abc::ISampleSelector &sample_sel)
{
Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
m_object->data = mesh;
- Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
+ /* Default AttributeSelector to ensure that at least UVs and vertex colors are read. To load
+ * other attributes, a modifier should be added as there are no clear conventions for them. */
+ ListBase lb = {nullptr, nullptr};
+ AttributeSelector attr_selector(&lb);
+ attr_selector.set_read_flags(MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR);
+
+ Mesh *read_mesh = this->read_mesh(
+ mesh, sample_sel, &attr_selector, MOD_MESHSEQ_READ_ALL, 0.0f, nullptr);
if (read_mesh != mesh) {
BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
}
@@ -1049,8 +890,8 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh,
const ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
const int read_flag,
- const char *velocity_name,
const float velocity_scale,
const char **err_str)
{
@@ -1078,7 +919,6 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh,
ImportSettings settings;
settings.read_flag |= read_flag;
- settings.velocity_name = velocity_name;
settings.velocity_scale = velocity_scale;
if (existing_mesh->totvert != positions->size()) {
@@ -1106,11 +946,33 @@ Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh,
/* Only read point data when streaming meshes, unless we need to create new ones. */
Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
const bool use_vertex_interpolation = read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
- CDStreamConfig config = get_config(mesh_to_export, use_vertex_interpolation);
+ CDStreamConfig config = get_config(
+ mesh_to_export, attribute_selector, m_iobject.getFullName(), use_vertex_interpolation);
config.time = sample_sel.getRequestedTime();
- read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config);
+ read_subd_sample(&settings, m_schema, sample_sel, config);
return mesh_to_export;
}
+void AbcSubDReader::read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str)
+{
+ Mesh *mesh = geometry_set.get_mesh_for_write();
+
+ if (mesh == nullptr) {
+ return;
+ }
+
+ Mesh *new_mesh = read_mesh(
+ mesh, sample_sel, attribute_selector, read_flag, velocity_scale, err_str);
+
+ if (new_mesh != mesh) {
+ geometry_set.replace_mesh(new_mesh);
+ }
+}
+
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h
index 1123616943e..ac5939155dc 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.h
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.h
@@ -38,17 +38,20 @@ class AbcMeshReader final : public AbcObjectReader {
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
- struct Mesh *read_mesh(struct Mesh *existing_mesh,
- const Alembic::Abc::ISampleSelector &sample_sel,
- int read_flag,
- const char *velocity_name,
- float velocity_scale,
- const char **err_str) override;
bool topology_changed(Mesh *existing_mesh,
const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str) override;
+
private:
void readFaceSetsSample(Main *bmain,
Mesh *mesh,
@@ -58,6 +61,13 @@ class AbcMeshReader final : public AbcObjectReader {
MPoly *mpoly,
int totpoly,
std::map<std::string, int> &r_mat_map);
+
+ struct Mesh *read_mesh(struct Mesh *existing_mesh,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ const int read_flag,
+ const float velocity_scale,
+ const char **err_str);
};
class AbcSubDReader final : public AbcObjectReader {
@@ -72,19 +82,33 @@ class AbcSubDReader final : public AbcObjectReader {
bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header,
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
+
+ void read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str) override;
+
+ private:
struct Mesh *read_mesh(struct Mesh *existing_mesh,
const Alembic::Abc::ISampleSelector &sample_sel,
- int read_flag,
- const char *velocity_name,
- float velocity_scale,
- const char **err_str) override;
+ const AttributeSelector *attribute_selector,
+ const int read_flag,
+ const float velocity_scale,
+ const char **err_str);
};
void read_mverts(Mesh &mesh,
const Alembic::AbcGeom::P3fArraySamplePtr positions,
const Alembic::AbcGeom::N3fArraySamplePtr normals);
-CDStreamConfig get_config(struct Mesh *mesh, bool use_vertex_interpolation);
+CDStreamConfig get_config(struct Mesh *mesh,
+ const AttributeSelector *attr_selector,
+ const std::string &iobject_full_name,
+ bool use_vertex_interpolation);
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
index 4492d1e1ca0..fd3c1a708ec 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
@@ -108,7 +108,9 @@ static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
return true;
}
-void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+void AbcNurbsReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const Alembic::Abc::ISampleSelector &sample_sel)
{
Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, m_data_name.c_str(), OB_SURF));
cu->actvert = CU_ACT_NONE;
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h
index 66e68cf6942..e775cc8af8e 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.h
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h
@@ -35,7 +35,9 @@ class AbcNurbsReader final : public AbcObjectReader {
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
private:
void getNurbsPatches(const Alembic::Abc::IObject &obj);
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index 86fa580bf1f..68979f3a076 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -144,16 +144,6 @@ Imath::M44d get_matrix(const IXformSchema &schema, const float time)
return s0.getMatrix();
}
-struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh,
- const Alembic::Abc::ISampleSelector &UNUSED(sample_sel),
- int UNUSED(read_flag),
- const char *UNUSED(velocity_name),
- const float UNUSED(velocity_scale),
- const char **UNUSED(err_str))
-{
- return existing_mesh;
-}
-
bool AbcObjectReader::topology_changed(Mesh * /*existing_mesh*/,
const Alembic::Abc::ISampleSelector & /*sample_sel*/)
{
@@ -162,6 +152,16 @@ bool AbcObjectReader::topology_changed(Mesh * /*existing_mesh*/,
return false;
}
+void AbcObjectReader::read_geometry(GeometrySet &UNUSED(geometry_set),
+ const Alembic::Abc::ISampleSelector &UNUSED(sample_sel),
+ const AttributeSelector *UNUSED(attribute_selector),
+ int UNUSED(read_flag),
+ const float UNUSED(velocity_scale),
+ const char **UNUSED(err_str))
+{
+ return;
+}
+
void AbcObjectReader::setupObjectTransform(const float time)
{
bool is_constant = false;
@@ -180,14 +180,30 @@ void AbcObjectReader::setupObjectTransform(const float time)
BKE_object_to_mat4(m_object, m_object->obmat);
if (!is_constant || m_settings->always_add_cache_reader) {
- bConstraint *con = BKE_constraint_add_for_object(
- m_object, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE);
- bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
- BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+ bTransformCacheConstraint *constraint = getOrCreateConstraint();
+ BLI_strncpy(constraint->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+ }
+}
- data->cache_file = m_settings->cache_file;
- id_us_plus(&data->cache_file->id);
+bTransformCacheConstraint *AbcObjectReader::getOrCreateConstraint()
+{
+ if (m_iobject.isInstanceRoot()) {
+ /* As this is an instance we may already have created a constraint when duplicating the source
+ * object, if so return it. Note that it is possible for an instance to have an animated
+ * transform, but not for the source object. */
+ bConstraint *constraint = static_cast<bConstraint *>(m_object->constraints.last);
+ if (constraint && constraint->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
+ return static_cast<bTransformCacheConstraint *>(constraint->data);
+ }
}
+
+ /* Create a new constraint. */
+ bConstraint *constraint = BKE_constraint_add_for_object(
+ m_object, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE);
+ bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(constraint->data);
+ data->cache_file = m_settings->cache_file;
+ id_us_plus(&data->cache_file->id);
+ return data;
}
Alembic::AbcGeom::IXform AbcObjectReader::xform()
diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h
index 5b327a9ddd9..33c7a98b091 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.h
+++ b/source/blender/io/alembic/intern/abc_reader_object.h
@@ -24,7 +24,9 @@
#include "DNA_ID.h"
+struct bTransformCacheConstraint;
struct CacheFile;
+struct GeometrySet;
struct Main;
struct Mesh;
struct Object;
@@ -33,6 +35,9 @@ using Alembic::AbcCoreAbstract::chrono_t;
namespace blender::io::alembic {
+class AbcReaderManager;
+class AttributeSelector;
+
struct ImportSettings {
bool do_convert_mat;
float conversion_mat[4][4];
@@ -51,7 +56,6 @@ struct ImportSettings {
int read_flag;
/* From CacheFile and MeshSeqCacheModifierData */
- std::string velocity_name;
float velocity_scale;
bool validate_meshes;
@@ -69,7 +73,6 @@ struct ImportSettings {
sequence_len(1),
sequence_offset(0),
read_flag(0),
- velocity_name(""),
velocity_scale(1.0f),
validate_meshes(false),
always_add_cache_reader(false),
@@ -145,17 +148,20 @@ class AbcObjectReader {
const Object *const ob,
const char **err_str) const = 0;
- virtual void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) = 0;
+ virtual void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) = 0;
- virtual struct Mesh *read_mesh(struct Mesh *mesh,
- const Alembic::Abc::ISampleSelector &sample_sel,
- int read_flag,
- const char *velocity_name,
- float velocity_scale,
- const char **err_str);
virtual bool topology_changed(Mesh *existing_mesh,
const Alembic::Abc::ISampleSelector &sample_sel);
+ virtual void read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str);
+
/** Reads the object matrix and sets up an object transform if animated. */
void setupObjectTransform(float time);
@@ -173,6 +179,9 @@ class AbcObjectReader {
protected:
/** Determine whether we can inherit our parent's XForm. */
void determine_inherits_xform();
+
+ private:
+ bTransformCacheConstraint *getOrCreateConstraint();
};
Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, float time);
diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc
index 0135e150097..a1e5c0afa78 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.cc
+++ b/source/blender/io/alembic/intern/abc_reader_points.cc
@@ -22,6 +22,7 @@
*/
#include "abc_reader_points.h"
+#include "abc_axis_conversion.h"
#include "abc_reader_mesh.h"
#include "abc_reader_transform.h"
#include "abc_util.h"
@@ -29,15 +30,21 @@
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
#include "BKE_customdata.h"
+#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_pointcloud.h"
using Alembic::AbcGeom::kWrapExisting;
using Alembic::AbcGeom::N3fArraySamplePtr;
using Alembic::AbcGeom::P3fArraySamplePtr;
+using namespace Alembic::AbcGeom;
+
using Alembic::AbcGeom::ICompoundProperty;
using Alembic::AbcGeom::IN3fArrayProperty;
using Alembic::AbcGeom::IPoints;
@@ -71,7 +78,7 @@ bool AbcPointsReader::accepts_object_type(
return false;
}
- if (ob->type != OB_MESH) {
+ if (ob->type != OB_POINTCLOUD) {
*err_str = "Object type mismatch, Alembic object path points to Points.";
return false;
}
@@ -79,58 +86,89 @@ bool AbcPointsReader::accepts_object_type(
return true;
}
-void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
+void AbcPointsReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const Alembic::Abc::ISampleSelector &sample_sel)
{
- Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
- Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr);
+ PointCloud *point_cloud = static_cast<PointCloud *>(
+ BKE_pointcloud_add_default(bmain, m_data_name.c_str()));
- if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true);
- }
+ GeometrySet geometry_set = GeometrySet::create_with_pointcloud(point_cloud,
+ GeometryOwnershipType::Editable);
+ read_geometry(geometry_set, sample_sel, nullptr, 0, 0.0f, nullptr);
- if (m_settings->validate_meshes) {
- BKE_mesh_validate(mesh, false, false);
+ PointCloud *read_point_cloud =
+ geometry_set.get_component_for_write<PointCloudComponent>().release();
+
+ if (read_point_cloud != point_cloud) {
+ BKE_pointcloud_nomain_to_pointcloud(read_point_cloud, point_cloud, true);
}
- m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
- m_object->data = mesh;
+ m_object = BKE_object_add_only_object(bmain, OB_POINTCLOUD, m_object_name.c_str());
+ m_object->data = point_cloud;
if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
addCacheModifier();
}
}
-void read_points_sample(const IPointsSchema &schema,
- const ISampleSelector &selector,
- CDStreamConfig &config)
+static void read_points_interp(const P3fArraySamplePtr positions,
+ const P3fArraySamplePtr ceil_positions,
+ const float weight,
+ float3 *r_points)
+{
+ float3 tmp;
+ for (size_t i = 0; i < positions->size(); i++) {
+ const Imath::V3f &floor_pos = (*positions)[i];
+ const Imath::V3f &ceil_pos = (*ceil_positions)[i];
+ interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight);
+ copy_zup_from_yup(r_points[i], (*positions)[i].getValue());
+ }
+}
+
+static void read_points(const P3fArraySamplePtr positions, float3 *r_points)
+{
+ for (size_t i = 0; i < positions->size(); i++) {
+ copy_zup_from_yup(r_points[i], (*positions)[i].getValue());
+ }
+}
+
+static void read_points_sample(const IPointsSchema &schema,
+ const ISampleSelector &selector,
+ CDStreamConfig &config,
+ float3 *r_points)
{
Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector);
const P3fArraySamplePtr &positions = sample.getPositions();
ICompoundProperty prop = schema.getArbGeomParams();
- N3fArraySamplePtr vnormals;
- if (has_property(prop, "N")) {
- const Alembic::Util::uint32_t itime = static_cast<Alembic::Util::uint32_t>(
- selector.getRequestedTime());
- const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", itime);
+ Alembic::AbcGeom::index_t i0, i1;
+ const float weight = get_weight_and_index(
+ config.time, schema.getTimeSampling(), schema.getNumSamples(), i0, i1);
- if (normals_prop) {
- vnormals = normals_prop.getValue(selector);
- }
+ if (config.use_vertex_interpolation && weight != 0.0f) {
+ Alembic::AbcGeom::IPointsSchema::Sample ceil_sample;
+ schema.get(ceil_sample, Alembic::Abc::ISampleSelector(i1));
+ P3fArraySamplePtr ceil_positions = ceil_sample.getPositions();
+
+ read_points_interp(positions, ceil_positions, weight, r_points);
+ return;
}
- read_mverts(*config.mesh, positions, vnormals);
+ read_points(positions, r_points);
}
-struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh,
- const ISampleSelector &sample_sel,
- int read_flag,
- const char * /*velocity_name*/,
- const float /*velocity_scale*/,
- const char **err_str)
+void AbcPointsReader::read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str)
{
+ assert(geometry_set.has_pointcloud());
+
IPointsSchema::Sample sample;
try {
sample = m_schema.getValue(sample_sel);
@@ -142,23 +180,66 @@ struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh,
m_schema.getName().c_str(),
sample_sel.getRequestedTime(),
ex.what());
- return existing_mesh;
+ return;
}
+ PointCloud *existing_point_cloud = geometry_set.get_pointcloud_for_write();
+ PointCloud *point_cloud = existing_point_cloud;
+
const P3fArraySamplePtr &positions = sample.getPositions();
- Mesh *new_mesh = nullptr;
+ const IFloatGeomParam widths_param = m_schema.getWidthsParam();
+ FloatArraySamplePtr radiuses;
+
+ if (widths_param.valid()) {
+ IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
+ radiuses = wsample.getVals();
+ }
- if (existing_mesh->totvert != positions->size()) {
- new_mesh = BKE_mesh_new_nomain(positions->size(), 0, 0, 0, 0);
+ if (point_cloud->totpoint != positions->size()) {
+ point_cloud = BKE_pointcloud_new_nomain(positions->size());
}
- Mesh *mesh_to_export = new_mesh ? new_mesh : existing_mesh;
- const bool use_vertex_interpolation = read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES;
- CDStreamConfig config = get_config(mesh_to_export, use_vertex_interpolation);
- read_points_sample(m_schema, sample_sel, config);
+ ScopeCustomDataPointers custom_data_pointers = {&point_cloud->pdata, nullptr, nullptr};
+ ScopeSizeInfo scope_sizes = {point_cloud->totpoint, 0, 0};
+ CDStreamConfig config;
+ config.custom_data_pointers = custom_data_pointers;
+ config.scope_sizes = scope_sizes;
+ config.id = &point_cloud->id;
+ config.attr_selector = attribute_selector;
+ config.time = sample_sel.getRequestedTime();
+ config.use_vertex_interpolation = (read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES) != 0;
+
+ read_points_sample(m_schema, sample_sel, config, reinterpret_cast<float3 *>(point_cloud->co));
+
+ if (radiuses) {
+ for (size_t i = 0; i < radiuses->size(); i++) {
+ point_cloud->radius[i] = (*radiuses)[i];
+ }
+ }
+ else {
+ for (int i = 0; i < point_cloud->totpoint; i++) {
+ point_cloud->radius[i] = 0.01f;
+ }
+ }
- return mesh_to_export;
+ UInt64ArraySamplePtr ids = sample.getIds();
+ if (ids && ids->size() == positions->size()) {
+ CustomDataLayer *ids_layer = BKE_id_attribute_ensure(
+ &point_cloud->id, "ids", CD_PROP_INT32, ATTR_DOMAIN_POINT, nullptr);
+ int *ids_layer_data = static_cast<int *>(ids_layer->data);
+
+ for (size_t i = 0; i < ids->size(); i++) {
+ ids_layer_data[i] = static_cast<int>((*ids)[i]);
+ }
+ }
+
+ /* Attributes */
+ read_arbitrary_attributes(config, m_schema, {}, sample_sel, velocity_scale);
+
+ if (point_cloud != existing_point_cloud) {
+ geometry_set.replace_pointcloud(point_cloud);
+ }
}
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h
index 2f91e07d26a..33742235720 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.h
+++ b/source/blender/io/alembic/intern/abc_reader_points.h
@@ -39,18 +39,16 @@ class AbcPointsReader final : public AbcObjectReader {
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
-
- struct Mesh *read_mesh(struct Mesh *existing_mesh,
- const Alembic::Abc::ISampleSelector &sample_sel,
- int read_flag,
- const char *velocity_name,
- float velocity_scale,
- const char **err_str) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
+
+ void read_geometry(GeometrySet &geometry_set,
+ const Alembic::Abc::ISampleSelector &sample_sel,
+ const AttributeSelector *attribute_selector,
+ int read_flag,
+ const float velocity_scale,
+ const char **err_str) override;
};
-void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema,
- const Alembic::AbcGeom::ISampleSelector &selector,
- CDStreamConfig &config);
-
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/abc_reader_transform.cc b/source/blender/io/alembic/intern/abc_reader_transform.cc
index 625cd255dba..25cd7c6e5f8 100644
--- a/source/blender/io/alembic/intern/abc_reader_transform.cc
+++ b/source/blender/io/alembic/intern/abc_reader_transform.cc
@@ -69,7 +69,9 @@ bool AbcEmptyReader::accepts_object_type(
return true;
}
-void AbcEmptyReader::readObjectData(Main *bmain, const ISampleSelector &UNUSED(sample_sel))
+void AbcEmptyReader::readObjectData(Main *bmain,
+ const AbcReaderManager & /*manager*/,
+ const ISampleSelector &UNUSED(sample_sel))
{
m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str());
m_object->data = nullptr;
diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h
index 6810cc214b7..e4d67320881 100644
--- a/source/blender/io/alembic/intern/abc_reader_transform.h
+++ b/source/blender/io/alembic/intern/abc_reader_transform.h
@@ -36,7 +36,9 @@ class AbcEmptyReader final : public AbcObjectReader {
const Object *const ob,
const char **err_str) const override;
- void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override;
+ void readObjectData(Main *bmain,
+ const AbcReaderManager &manager,
+ const Alembic::Abc::ISampleSelector &sample_sel) override;
};
} // namespace blender::io::alembic
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index 58fc4dd599d..03e9ce6547e 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -27,6 +27,7 @@
#include "abc_reader_archive.h"
#include "abc_reader_camera.h"
#include "abc_reader_curves.h"
+#include "abc_reader_manager.h"
#include "abc_reader_mesh.h"
#include "abc_reader_nurbs.h"
#include "abc_reader_points.h"
@@ -225,7 +226,7 @@ static void find_iobject(const IObject &object, IObject &ret, const std::string
* Generates an AbcObjectReader for this Alembic object and its children.
*
* \param object: The Alembic IObject to visit.
- * \param readers: The created AbcObjectReader * will be appended to this vector.
+ * \param manager: The manager responsible for creating the readers.
* \param settings: Import settings, not used directly but passed to the
* AbcObjectReader subclass constructors.
* \param r_assign_as_parent: Return parameter, contains a list of reader
@@ -243,7 +244,7 @@ static void find_iobject(const IObject &object, IObject &ret, const std::string
* them in sync. */
static std::pair<bool, AbcObjectReader *> visit_object(
const IObject &object,
- AbcObjectReader::ptr_vector &readers,
+ AbcReaderManager &manager,
ImportSettings &settings,
AbcObjectReader::ptr_vector &r_assign_as_parent)
{
@@ -268,7 +269,7 @@ static std::pair<bool, AbcObjectReader *> visit_object(
/* TODO: When we only support C++11, use std::tie() instead. */
std::pair<bool, AbcObjectReader *> child_result;
- child_result = visit_object(ichild, readers, settings, assign_as_parent);
+ child_result = visit_object(ichild, manager, settings, assign_as_parent);
bool child_claims_this_object = child_result.first;
AbcObjectReader *child_reader = child_result.second;
@@ -313,15 +314,15 @@ static std::pair<bool, AbcObjectReader *> visit_object(
}
if (create_empty) {
- reader = new AbcEmptyReader(object, settings);
+ reader = manager.create<AbcEmptyReader>(object, settings);
}
}
else if (IPolyMesh::matches(md)) {
- reader = new AbcMeshReader(object, settings);
+ reader = manager.create<AbcMeshReader>(object, settings);
parent_is_part_of_this_object = true;
}
else if (ISubD::matches(md)) {
- reader = new AbcSubDReader(object, settings);
+ reader = manager.create<AbcSubDReader>(object, settings);
parent_is_part_of_this_object = true;
}
else if (INuPatch::matches(md)) {
@@ -332,16 +333,16 @@ static std::pair<bool, AbcObjectReader *> visit_object(
* Blender. Need to figure out exactly how these points are
* duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
* Until this is fixed, disabling NURBS reading. */
- reader = new AbcNurbsReader(object, settings);
+ reader = manager.create<AbcNurbsReader>(object, settings);
parent_is_part_of_this_object = true;
#endif
}
else if (ICamera::matches(md)) {
- reader = new AbcCameraReader(object, settings);
+ reader = manager.create<AbcCameraReader>(object, settings);
parent_is_part_of_this_object = true;
}
else if (IPoints::matches(md)) {
- reader = new AbcPointsReader(object, settings);
+ reader = manager.create<AbcPointsReader>(object, settings);
parent_is_part_of_this_object = true;
}
else if (IMaterial::matches(md)) {
@@ -354,7 +355,7 @@ static std::pair<bool, AbcObjectReader *> visit_object(
/* Pass, those are handled in the mesh reader. */
}
else if (ICurves::matches(md)) {
- reader = new AbcCurveReader(object, settings);
+ reader = manager.create<AbcCurveReader>(object, settings);
parent_is_part_of_this_object = true;
}
else {
@@ -367,7 +368,6 @@ static std::pair<bool, AbcObjectReader *> visit_object(
* not claimed as part of any child Alembic object. */
BLI_assert(claiming_child_readers.empty());
- readers.push_back(reader);
reader->incref();
CacheObjectPath *abc_path = static_cast<CacheObjectPath *>(
@@ -437,7 +437,7 @@ struct ImportJobData {
ImportSettings settings;
ArchiveReader *archive;
- std::vector<AbcObjectReader *> readers;
+ AbcReaderManager reader_manager;
short *stop;
short *do_update;
@@ -489,7 +489,8 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
/* Parse Alembic Archive. */
AbcObjectReader::ptr_vector assign_as_parent;
- visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent);
+ IObject root = archive->getTop();
+ visit_object(archive->getTop(), data->reader_manager, data->settings, assign_as_parent);
/* There shouldn't be any orphans. */
BLI_assert(assign_as_parent.empty());
@@ -504,19 +505,16 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
/* Create objects and set scene frame range. */
- const float size = static_cast<float>(data->readers.size());
+ const float size = static_cast<float>(data->reader_manager.all_readers().size());
size_t i = 0;
chrono_t min_time = std::numeric_limits<chrono_t>::max();
chrono_t max_time = std::numeric_limits<chrono_t>::min();
ISampleSelector sample_sel(0.0f);
- std::vector<AbcObjectReader *>::iterator iter;
- for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
- AbcObjectReader *reader = *iter;
-
+ for (AbcObjectReader *reader : data->reader_manager.data_readers()) {
if (reader->valid()) {
- reader->readObjectData(data->bmain, sample_sel);
+ reader->readObjectData(data->bmain, data->reader_manager, sample_sel);
min_time = std::min(min_time, reader->minTime());
max_time = std::max(max_time, reader->maxTime());
@@ -551,8 +549,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
}
/* Setup parenthood. */
- for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
- const AbcObjectReader *reader = *iter;
+ for (AbcObjectReader *reader : data->reader_manager.data_readers()) {
const AbcObjectReader *parent_reader = reader->parent_reader;
Object *ob = reader->object();
@@ -566,8 +563,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
/* Setup transformations and constraints. */
i = 0;
- for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
- AbcObjectReader *reader = *iter;
+ for (AbcObjectReader *reader : data->reader_manager.data_readers()) {
reader->setupObjectTransform(0.0f);
*data->progress = 0.7f + 0.3f * (++i / size);
@@ -586,12 +582,10 @@ static void import_endjob(void *user_data)
ImportJobData *data = static_cast<ImportJobData *>(user_data);
- std::vector<AbcObjectReader *>::iterator iter;
-
/* Delete objects on cancellation. */
if (data->was_cancelled) {
- for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
- Object *ob = (*iter)->object();
+ for (AbcObjectReader *reader : data->reader_manager.all_readers()) {
+ Object *ob = reader->object();
/* It's possible that cancellation occurred between the creation of
* the reader and the creation of the Blender object. */
@@ -612,8 +606,8 @@ static void import_endjob(void *user_data)
lc = BKE_layer_collection_get_active(view_layer);
- for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
- Object *ob = (*iter)->object();
+ for (AbcObjectReader *reader : data->reader_manager.data_readers()) {
+ Object *ob = reader->object();
BKE_collection_object_add(data->bmain, lc->collection, ob);
@@ -628,6 +622,24 @@ static void import_endjob(void *user_data)
ID_RECALC_BASE_FLAGS);
}
+ /* Finally, create instances. */
+ for (AbcObjectReader *reader : data->reader_manager.instance_readers()) {
+ if (!reader->valid()) {
+ continue;
+ }
+
+ reader->readObjectData(data->bmain, data->reader_manager, {});
+ /* We assume that the instance's parent is the same as that of the source object, so we do
+ * not setup parenthood. */
+ reader->setupObjectTransform(0.0f);
+
+ Object *ob = reader->object();
+ BKE_collection_object_add(data->bmain, lc->collection, ob);
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ BKE_main_id_newptr_and_tag_clear(data->bmain);
+ }
+
DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(data->bmain);
@@ -638,8 +650,7 @@ static void import_endjob(void *user_data)
}
}
- for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
- AbcObjectReader *reader = *iter;
+ for (AbcObjectReader *reader : data->reader_manager.all_readers()) {
reader->decref();
if (reader->refcount() == 0) {
@@ -795,23 +806,28 @@ static ISampleSelector sample_selector_for_time(float time)
return ISampleSelector(time, ISampleSelector::kFloorIndex);
}
-Mesh *ABC_read_mesh(CacheReader *reader,
- Object *ob,
- Mesh *existing_mesh,
- const float time,
- const char **err_str,
- const int read_flag,
- const char *velocity_name,
- const float velocity_scale)
+void ABC_read_geometry(CacheReader *reader,
+ Object *ob,
+ GeometrySet &geometry_set,
+ const ABCReadParams *params,
+ const char **err_str)
{
AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str);
if (abc_reader == nullptr) {
- return nullptr;
+ return;
}
- ISampleSelector sample_sel = sample_selector_for_time(time);
- return abc_reader->read_mesh(
- existing_mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str);
+ AttributeSelector attribute_selector(params->mappings);
+ attribute_selector.set_velocity_attribute(params->velocity_name);
+ attribute_selector.set_read_flags(params->read_flags);
+
+ ISampleSelector sample_sel = sample_selector_for_time(params->time);
+ abc_reader->read_geometry(geometry_set,
+ sample_sel,
+ &attribute_selector,
+ params->read_flags,
+ params->velocity_scale,
+ err_str);
}
bool ABC_mesh_topology_changed(
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 980f33fffa1..773518cb817 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -42,6 +42,7 @@ set(INC
../../bmesh
../../depsgraph
../../editors/include
+ ../../functions
../../makesdna
../../makesrna
../../windowmanager
diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc
index 789ff20ba82..0d8fb1ee557 100644
--- a/source/blender/io/usd/intern/usd_capi_import.cc
+++ b/source/blender/io/usd/intern/usd_capi_import.cc
@@ -436,20 +436,20 @@ static USDPrimReader *get_usd_reader(CacheReader *reader, Object * /* ob */, con
return usd_reader;
}
-struct Mesh *USD_read_mesh(struct CacheReader *reader,
- struct Object *ob,
- struct Mesh *existing_mesh,
- const float time,
- const char **err_str,
- const int read_flag)
+void USD_read_geometry(CacheReader *reader,
+ Object *ob,
+ GeometrySet &geometry_set,
+ const float time,
+ const char **err_str,
+ const int read_flag)
{
USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str));
if (usd_reader == nullptr) {
- return nullptr;
+ return;
}
- return usd_reader->read_mesh(existing_mesh, time, read_flag, err_str);
+ return usd_reader->read_geometry(geometry_set, time, read_flag, err_str);
}
bool USD_mesh_topology_changed(
diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc
index 5007d204020..f8a8d30948a 100644
--- a/source/blender/io/usd/intern/usd_reader_curve.cc
+++ b/source/blender/io/usd/intern/usd_reader_curve.cc
@@ -23,8 +23,10 @@
#include "usd_reader_curve.h"
#include "BKE_curve.h"
+#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_spline.hh"
#include "BLI_listbase.h"
@@ -179,13 +181,36 @@ void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime
}
}
-Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh,
- const double motionSampleTime,
- const int /* read_flag */,
- const char ** /* err_str */)
+static bool topology_changed(CurveEval *curve_eval, const pxr::VtIntArray &usdCounts)
+{
+ if (!curve_eval) {
+ return true;
+ }
+
+ if (curve_eval->splines().size() != usdCounts.size()) {
+ return true;
+ }
+
+ int curve_idx = 0;
+ for (const SplinePtr &spline : curve_eval->splines()) {
+ const int num_in_usd = usdCounts[curve_idx++];
+ const int num_in_blender = spline->positions().size();
+
+ if (num_in_usd != num_in_blender) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void USDCurvesReader::read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int /* read_flag */,
+ const char ** /* err_str */)
{
if (!curve_prim_) {
- return existing_mesh;
+ return;
}
pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
@@ -195,62 +220,36 @@ Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh,
pxr::VtIntArray usdCounts;
vertexAttr.Get(&usdCounts, motionSampleTime);
- int num_subcurves = usdCounts.size();
pxr::VtVec3fArray usdPoints;
pointsAttr.Get(&usdPoints, motionSampleTime);
int vertex_idx = 0;
int curve_idx;
- Curve *curve = static_cast<Curve *>(object_->data);
-
- const int curve_count = BLI_listbase_count(&curve->nurb);
- bool same_topology = curve_count == num_subcurves;
+ CurveEval *curve_eval = geometry_set.get_curve_for_write();
- if (same_topology) {
- Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
- for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
- const int num_in_usd = usdCounts[curve_idx];
- const int num_in_blender = nurbs->pntsu;
-
- if (num_in_usd != num_in_blender) {
- same_topology = false;
- break;
- }
- }
- }
-
- if (!same_topology) {
+ if (blender::io::usd::topology_changed(curve_eval, usdCounts)) {
+ Curve *curve = static_cast<Curve *>(object_->data);
BKE_nurbList_free(&curve->nurb);
read_curve_sample(curve, motionSampleTime);
+
+ std::unique_ptr<CurveEval> new_curve_eval = curve_eval_from_dna_curve(*curve);
+ geometry_set.replace_curve(new_curve_eval.release(), GeometryOwnershipType::Editable);
}
else {
- Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
- for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
- const int totpoint = usdCounts[curve_idx];
-
- if (nurbs->bp) {
- BPoint *point = nurbs->bp;
-
- for (int i = 0; i < totpoint; i++, point++, vertex_idx++) {
- point->vec[0] = usdPoints[vertex_idx][0];
- point->vec[1] = usdPoints[vertex_idx][1];
- point->vec[2] = usdPoints[vertex_idx][2];
- }
- }
- else if (nurbs->bezt) {
- BezTriple *bezier = nurbs->bezt;
-
- for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) {
- bezier->vec[1][0] = usdPoints[vertex_idx][0];
- bezier->vec[1][1] = usdPoints[vertex_idx][1];
- bezier->vec[1][2] = usdPoints[vertex_idx][2];
- }
+ BLI_assert_msg(curve_eval, "curve_eval is null although the topology is the same !");
+ const int curve_count = curve_eval->splines().size();
+ for (curve_idx = 0; curve_idx < curve_count; curve_idx++) {
+ Spline *spline = curve_eval->splines()[curve_idx].get();
+
+ for (float3 &position : spline->positions()) {
+ position.x = usdPoints[vertex_idx][0];
+ position.y = usdPoints[vertex_idx][0];
+ position.z = usdPoints[vertex_idx][0];
+ vertex_idx++;
}
}
}
-
- return BKE_mesh_new_nomain_from_curve(object_);
}
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_curve.h b/source/blender/io/usd/intern/usd_reader_curve.h
index 1e676bbbd02..ab27f2efaae 100644
--- a/source/blender/io/usd/intern/usd_reader_curve.h
+++ b/source/blender/io/usd/intern/usd_reader_curve.h
@@ -53,10 +53,10 @@ class USDCurvesReader : public USDGeomReader {
void read_curve_sample(Curve *cu, double motionSampleTime);
- Mesh *read_mesh(struct Mesh *existing_mesh,
- double motionSampleTime,
- int read_flag,
- const char **err_str) override;
+ void read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) override;
};
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_geom.h b/source/blender/io/usd/intern/usd_reader_geom.h
index 99e22248f2b..cde0f3842cf 100644
--- a/source/blender/io/usd/intern/usd_reader_geom.h
+++ b/source/blender/io/usd/intern/usd_reader_geom.h
@@ -21,6 +21,7 @@
#include "usd.h"
#include "usd_reader_xform.h"
+struct GeometrySet;
struct Mesh;
namespace blender::io::usd {
@@ -35,10 +36,10 @@ class USDGeomReader : public USDXformReader {
{
}
- virtual Mesh *read_mesh(struct Mesh *existing_mesh,
- double motionSampleTime,
- int read_flag,
- const char **err_str) = 0;
+ virtual void read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) = 0;
virtual bool topology_changed(Mesh * /* existing_mesh */, double /* motionSampleTime */)
{
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
index 95b73c85da6..4e5b67a54a9 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.cc
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -23,6 +23,7 @@
#include "usd_reader_material.h"
#include "BKE_customdata.h"
+#include "BKE_geometry_set.hh"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -882,4 +883,17 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
return active_mesh;
}
+void USDMeshReader::read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str)
+{
+ Mesh *existing_mesh = geometry_set.get_mesh_for_write();
+ Mesh *new_mesh = read_mesh(existing_mesh, motionSampleTime, read_flag, err_str);
+
+ if (new_mesh != existing_mesh) {
+ geometry_set.replace_mesh(new_mesh);
+ }
+}
+
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h
index c5869fcbd32..cd3f2cc97a4 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.h
+++ b/source/blender/io/usd/intern/usd_reader_mesh.h
@@ -65,10 +65,10 @@ class USDMeshReader : public USDGeomReader {
void create_object(Main *bmain, double motionSampleTime) override;
void read_object_data(Main *bmain, double motionSampleTime) override;
- struct Mesh *read_mesh(struct Mesh *existing_mesh,
- double motionSampleTime,
- int read_flag,
- const char **err_str) override;
+ void read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) override;
bool topology_changed(Mesh *existing_mesh, double motionSampleTime) override;
@@ -92,6 +92,11 @@ class USDMeshReader : public USDGeomReader {
Mesh *mesh,
double motionSampleTime,
bool new_mesh);
+
+ struct Mesh *read_mesh(struct Mesh *existing_mesh,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str);
};
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc
index 8370804b776..d724536856e 100644
--- a/source/blender/io/usd/intern/usd_reader_nurbs.cc
+++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc
@@ -22,8 +22,10 @@
#include "usd_reader_nurbs.h"
#include "BKE_curve.h"
+#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_object.h"
+#include "BKE_spline.hh"
#include "BLI_listbase.h"
@@ -181,10 +183,33 @@ void USDNurbsReader::read_curve_sample(Curve *cu, const double motionSampleTime)
}
}
-Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */,
- const double motionSampleTime,
- const int /* read_flag */,
- const char ** /* err_str */)
+static bool topology_changed(CurveEval *curve_eval, const pxr::VtIntArray &usdCounts)
+{
+ if (!curve_eval) {
+ return true;
+ }
+
+ if (curve_eval->splines().size() != usdCounts.size()) {
+ return true;
+ }
+
+ int curve_idx = 0;
+ for (const SplinePtr &spline : curve_eval->splines()) {
+ const int num_in_usd = usdCounts[curve_idx++];
+ const int num_in_blender = spline->positions().size();
+
+ if (num_in_usd != num_in_blender) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void USDNurbsReader::read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int /* read_flag */,
+ const char ** /* err_str */)
{
pxr::UsdGeomCurves curve_prim_(prim_);
@@ -195,62 +220,36 @@ Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */,
pxr::VtIntArray usdCounts;
vertexAttr.Get(&usdCounts, motionSampleTime);
- int num_subcurves = usdCounts.size();
pxr::VtVec3fArray usdPoints;
pointsAttr.Get(&usdPoints, motionSampleTime);
int vertex_idx = 0;
int curve_idx;
- Curve *curve = static_cast<Curve *>(object_->data);
-
- const int curve_count = BLI_listbase_count(&curve->nurb);
- bool same_topology = curve_count == num_subcurves;
-
- if (same_topology) {
- Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
- for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
- const int num_in_usd = usdCounts[curve_idx];
- const int num_in_blender = nurbs->pntsu;
-
- if (num_in_usd != num_in_blender) {
- same_topology = false;
- break;
- }
- }
- }
+ CurveEval *curve_eval = geometry_set.get_curve_for_write();
- if (!same_topology) {
+ if (blender::io::usd::topology_changed(curve_eval, usdCounts)) {
+ Curve *curve = static_cast<Curve *>(object_->data);
BKE_nurbList_free(&curve->nurb);
read_curve_sample(curve, motionSampleTime);
+
+ std::unique_ptr<CurveEval> new_curve_eval = curve_eval_from_dna_curve(*curve);
+ geometry_set.replace_curve(new_curve_eval.release(), GeometryOwnershipType::Editable);
}
else {
- Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
- for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
- const int totpoint = usdCounts[curve_idx];
-
- if (nurbs->bp) {
- BPoint *point = nurbs->bp;
-
- for (int i = 0; i < totpoint; i++, point++, vertex_idx++) {
- point->vec[0] = usdPoints[vertex_idx][0];
- point->vec[1] = usdPoints[vertex_idx][1];
- point->vec[2] = usdPoints[vertex_idx][2];
- }
- }
- else if (nurbs->bezt) {
- BezTriple *bezier = nurbs->bezt;
-
- for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) {
- bezier->vec[1][0] = usdPoints[vertex_idx][0];
- bezier->vec[1][1] = usdPoints[vertex_idx][1];
- bezier->vec[1][2] = usdPoints[vertex_idx][2];
- }
+ BLI_assert_msg(curve_eval, "curve_eval is null although the topology is the same !");
+ const int curve_count = curve_eval->splines().size();
+ for (curve_idx = 0; curve_idx < curve_count; curve_idx++) {
+ Spline *spline = curve_eval->splines()[curve_idx].get();
+
+ for (float3 &position : spline->positions()) {
+ position.x = usdPoints[vertex_idx][0];
+ position.y = usdPoints[vertex_idx][0];
+ position.z = usdPoints[vertex_idx][0];
+ vertex_idx++;
}
}
}
-
- return BKE_mesh_new_nomain_from_curve(object_);
}
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.h b/source/blender/io/usd/intern/usd_reader_nurbs.h
index 33a4acf503e..ac118791c72 100644
--- a/source/blender/io/usd/intern/usd_reader_nurbs.h
+++ b/source/blender/io/usd/intern/usd_reader_nurbs.h
@@ -52,10 +52,10 @@ class USDNurbsReader : public USDGeomReader {
void read_curve_sample(Curve *cu, double motionSampleTime);
- Mesh *read_mesh(struct Mesh *existing_mesh,
- double motionSampleTime,
- int read_flag,
- const char **err_str) override;
+ void read_geometry(GeometrySet &geometry_set,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) override;
};
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index 505f917ede5..c9267d78572 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -28,6 +28,8 @@ extern "C" {
struct CacheArchiveHandle;
struct CacheFile;
struct CacheReader;
+struct GeometrySet;
+struct Mesh;
struct Object;
struct bContext;
@@ -106,13 +108,14 @@ void USD_free_handle(struct CacheArchiveHandle *handle);
void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale);
-/* Either modifies current_mesh in-place or constructs a new mesh. */
-struct Mesh *USD_read_mesh(struct CacheReader *reader,
- struct Object *ob,
- struct Mesh *existing_mesh,
- float time,
- const char **err_str,
- int read_flag);
+#ifdef __cplusplus
+void USD_read_geometry(CacheReader *reader,
+ Object *ob,
+ GeometrySet &geometry_set,
+ const float time,
+ const char **err_str,
+ const int read_flag);
+#endif
bool USD_mesh_topology_changed(struct CacheReader *reader,
struct Object *ob,
diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h
index 65e1dd6c096..e5216bbc21e 100644
--- a/source/blender/makesdna/DNA_cachefile_types.h
+++ b/source/blender/makesdna/DNA_cachefile_types.h
@@ -71,6 +71,43 @@ typedef struct CacheFileLayer {
int _pad;
} CacheFileLayer;
+/* CacheAttributeMapping::mapping */
+enum {
+ /* Default mapping, so we do not make an arbitrary decision as to what is the default. Also used
+ * to mark the mapping as ignored. */
+ CACHEFILE_ATTRIBUTE_MAP_NONE,
+ CACHEFILE_ATTRIBUTE_MAP_TO_UVS,
+ CACHEFILE_ATTRIBUTE_MAP_TO_VERTEX_COLORS,
+ CACHEFILE_ATTRIBUTE_MAP_TO_WEIGHT_GROUPS,
+ CACHEFILE_ATTRIBUTE_MAP_TO_FLOAT2,
+ CACHEFILE_ATTRIBUTE_MAP_TO_FLOAT3,
+ CACHEFILE_ATTRIBUTE_MAP_TO_COLOR,
+};
+
+/* CacheAttributeMapping::domain */
+enum {
+ /* Try to automatically map the attribute to the right domain (default behavior without mapping.)
+ */
+ CACHEFILE_ATTR_MAP_DOMAIN_AUTO,
+ CACHEFILE_ATTR_MAP_DOMAIN_POINT,
+ CACHEFILE_ATTR_MAP_DOMAIN_FACE_CORNER,
+ CACHEFILE_ATTR_MAP_DOMAIN_FACE,
+};
+
+/* Custom data mapping for the attributes in the CacheFile. Since there might not be a standard way
+ * of expressing what an attribute should be (e.g. is this float2 attribute a UV map?), and since
+ * some software might write multi-dimensionnal data as arrays of 1D element (although the size of
+ * the array will be N-time its expected size were it written as N-D data), we delegate to the user
+ * the task of telling us what is supposed to be what through these mappings. */
+typedef struct CacheAttributeMapping {
+ struct CacheAttributeMapping *next, *prev;
+
+ char name[64];
+ char mapping;
+ char domain;
+ char _pad[6];
+} CacheAttributeMapping;
+
/* CacheFile::velocity_unit
* Determines what temporal unit is used to interpret velocity vectors for motion blur effects. */
enum {
@@ -101,7 +138,8 @@ typedef struct CacheFile {
/** The frame offset to subtract. */
float frame_offset;
- char _pad[4];
+ /** Index of the currently selected attribute mapping in the UI, starts at 1 */
+ int active_attribute_mapping;
/** Animation flag. */
short flag;
@@ -132,6 +170,8 @@ typedef struct CacheFile {
/* Name of the velocity property in the archive. */
char velocity_name[64];
+ ListBase attribute_mappings;
+
/* Runtime */
struct CacheArchiveHandle *handle;
char handle_filepath[1024];
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 47afa0f9a13..410e3a97f2a 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -106,6 +106,7 @@ extern StructRNA RNA_ByteColorAttribute;
extern StructRNA RNA_ByteColorAttributeValue;
extern StructRNA RNA_ByteIntAttribute;
extern StructRNA RNA_ByteIntAttributeValue;
+extern StructRNA RNA_CacheAttributeMapping;
extern StructRNA RNA_CacheFile;
extern StructRNA RNA_CacheFileLayer;
extern StructRNA RNA_Camera;
diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c
index 2f8fc004d85..6d5b4dcd63f 100644
--- a/source/blender/makesrna/intern/rna_cachefile.c
+++ b/source/blender/makesrna/intern/rna_cachefile.c
@@ -63,6 +63,16 @@ static void rna_CacheFileLayer_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
}
+static void rna_CacheFile_attribute_mapping_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->owner_id;
+
+ DEG_id_tag_update(&cache_file->id, ID_RECALC_COPY_ON_WRITE);
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
+}
+
static void rna_CacheFile_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
rna_CacheFile_update(bmain, scene, ptr);
@@ -160,6 +170,53 @@ static void rna_CacheFile_layer_remove(CacheFile *cache_file, bContext *C, Point
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
}
+static PointerRNA rna_CacheFile_active_attribute_mapping_get(PointerRNA *ptr)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->owner_id;
+ return rna_pointer_inherit_refine(
+ ptr, &RNA_CacheAttributeMapping, BKE_cachefile_get_active_attribute_mapping(cache_file));
+}
+
+static void rna_CacheFile_active_attribute_mapping_set(PointerRNA *ptr,
+ PointerRNA value,
+ struct ReportList *reports)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->owner_id;
+ int index = BLI_findindex(&cache_file->attribute_mappings, value.data);
+ if (index == -1) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Attribute mapping '%s' not found in object '%s'",
+ ((CacheAttributeMapping *)value.data)->name,
+ cache_file->id.name + 2);
+ cache_file->active_attribute_mapping = 0;
+ return;
+ }
+
+ cache_file->active_attribute_mapping = index + 1;
+}
+
+static int rna_CacheFile_active_attribute_mapping_index_get(PointerRNA *ptr)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->owner_id;
+ return cache_file->active_attribute_mapping - 1;
+}
+
+static void rna_CacheFile_active_attribute_mapping_index_set(PointerRNA *ptr, int value)
+{
+ CacheFile *cache_file = (CacheFile *)ptr->owner_id;
+ cache_file->active_attribute_mapping = value + 1;
+}
+
+static void rna_CacheFile_active_attribute_mapping_index_range(
+ PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ CacheFile *cache_file = (CacheFile *)ptr->owner_id;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&cache_file->attribute_mappings) - 1);
+}
+
#else
/* cachefile.object_paths */
@@ -179,6 +236,107 @@ static void rna_def_alembic_object_path(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
+static void rna_def_cachefile_attribute_mapping(BlenderRNA *brna)
+{
+ StructRNA *srna = RNA_def_struct(brna, "CacheAttributeMapping", NULL);
+ RNA_def_struct_sdna(srna, "CacheAttributeMapping");
+ RNA_def_struct_ui_text(
+ srna,
+ "Cache Attribute Mapping",
+ "Attribute Mappin of the cache, used to define how to interpret certain attributes");
+
+ PropertyRNA *prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the attribute to map");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_attribute_mapping_update");
+
+ static const EnumPropertyItem rna_enum_cache_attribute_mapping_items[] = {
+ {CACHEFILE_ATTRIBUTE_MAP_NONE, "MAP_NONE", 0, "None", ""},
+ {CACHEFILE_ATTRIBUTE_MAP_TO_UVS,
+ "MAP_TO_UVS",
+ 0,
+ "UVs",
+ "Read the attribute as a UV map of the same name"},
+ {CACHEFILE_ATTRIBUTE_MAP_TO_VERTEX_COLORS,
+ "MAP_TO_VERTEX_COLORS",
+ 0,
+ "Vertex Colors",
+ "Read the attribute as a vertex color layer of the same name"},
+ {CACHEFILE_ATTRIBUTE_MAP_TO_WEIGHT_GROUPS,
+ "MAP_TO_WEIGHT_GROUPS",
+ 0,
+ "Weight Group",
+ "Read the attribute as a weight group channel of the same name"},
+ {CACHEFILE_ATTRIBUTE_MAP_TO_FLOAT2,
+ "MAP_TO_FLOAT2",
+ 0,
+ "2D Vector",
+ "Interpret the attribute's data as generic 2D vectors"},
+ {CACHEFILE_ATTRIBUTE_MAP_TO_FLOAT3,
+ "MAP_TO_FLOAT3",
+ 0,
+ "3D Vector",
+ "Interpret the attribute's data as generic 3D vectors"},
+ {CACHEFILE_ATTRIBUTE_MAP_TO_COLOR,
+ "MAP_TO_COLOR",
+ 0,
+ "Color",
+ "Interpret the attribute's data as colors (RGBA)"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_cache_attribute_mapping_items);
+ RNA_def_property_update(prop, 0, "rna_CacheFile_attribute_mapping_update");
+ RNA_def_property_ui_text(prop, "Data Type", "Define the data type of the attribute");
+
+ static const EnumPropertyItem rna_enum_cache_attribute_domain_items[] = {
+ {CACHEFILE_ATTR_MAP_DOMAIN_AUTO,
+ "AUTO",
+ 0,
+ "Automatic",
+ "Try to automatically determine the domain of the attribute"},
+ {CACHEFILE_ATTR_MAP_DOMAIN_POINT,
+ "POINT",
+ 0,
+ "Point",
+ "The attribute is defined on the points"},
+ {CACHEFILE_ATTR_MAP_DOMAIN_FACE_CORNER,
+ "FACE_CORNER",
+ 0,
+ "Face Corner",
+ "The attribute is defined on the face corners"},
+ {CACHEFILE_ATTR_MAP_DOMAIN_FACE, "FACE", 0, "Face", "The attribute is defined on the faces"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_cache_attribute_domain_items);
+ RNA_def_property_update(prop, 0, "rna_CacheFile_attribute_mapping_update");
+ RNA_def_property_ui_text(prop, "Domain", "Define the domain on which the attribute is written");
+}
+
+static void rna_def_cachefile_attribute_mappings(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ RNA_def_property_srna(cprop, "CacheAttributeMappings");
+ StructRNA *srna = RNA_def_struct(brna, "CacheAttributeMappings", NULL);
+ RNA_def_struct_sdna(srna, "CacheFile");
+ RNA_def_struct_ui_text(
+ srna, "Cache Attribute Mappings", "Collection of cache attribute mappings");
+
+ PropertyRNA *prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CacheAttributeMapping");
+ RNA_def_property_pointer_funcs(prop,
+ "rna_CacheFile_active_attribute_mapping_get",
+ "rna_CacheFile_active_attribute_mapping_set",
+ NULL,
+ NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Active Attribute Mapping", "Active attribute mapping of the CacheFile");
+ RNA_def_property_ui_text(
+ prop, "Active Attribute Mapping Index", "Active index in attribute mappings array");
+}
+
/* cachefile.object_paths */
static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop)
{
@@ -271,6 +429,18 @@ static void rna_def_cachefile(BlenderRNA *brna)
"Alembic data themselves if possible");
RNA_def_property_update(prop, 0, "rna_CacheFile_dependency_update");
+ static const EnumPropertyItem cache_file_type_items[] = {
+ {CACHE_FILE_TYPE_INVALID, "INVALID", 0, "Invalid", ""},
+ {CACHEFILE_TYPE_ALEMBIC, "ALEMBIC", 0, "Alembic", ""},
+ {CACHEFILE_TYPE_USD, "USD", 0, "USD", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, cache_file_type_items);
+ RNA_def_property_ui_text(prop, "Type", "Type of the file used for storing data");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
/* ----------------- For Scene time ------------------- */
prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE);
@@ -400,6 +570,23 @@ static void rna_def_cachefile(BlenderRNA *brna)
"rna_CacheFile_active_layer_index_set",
"rna_CacheFile_active_layer_index_range");
+ /* ----------------- Attribute Mappings ----------------- */
+
+ prop = RNA_def_property(srna, "attribute_mappings", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "attribute_mappings", NULL);
+ RNA_def_property_struct_type(prop, "CacheAttributeMapping");
+ RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_ui_text(prop, "Cache Attribute Mappings", "Attribute mappings of the cache");
+ rna_def_cachefile_attribute_mappings(brna, prop);
+
+ prop = RNA_def_property(srna, "active_attribute_mapping_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_sdna(prop, NULL, "active_attribute_mapping");
+ RNA_def_property_int_funcs(prop,
+ "rna_CacheFile_active_attribute_mapping_index_get",
+ "rna_CacheFile_active_attribute_mapping_index_set",
+ "rna_CacheFile_active_attribute_mapping_index_range");
+
RNA_define_lib_overridable(false);
rna_def_cachefile_object_paths(brna, prop);
@@ -412,6 +599,7 @@ void RNA_def_cachefile(BlenderRNA *brna)
rna_def_cachefile(brna);
rna_def_alembic_object_path(brna);
rna_def_cachefile_layer(brna);
+ rna_def_cachefile_attribute_mapping(brna);
}
#endif
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 719a07adbab..55c4b3e9705 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -76,7 +76,7 @@ set(SRC
intern/MOD_meshcache_pc2.c
intern/MOD_meshcache_util.c
intern/MOD_meshdeform.c
- intern/MOD_meshsequencecache.c
+ intern/MOD_meshsequencecache.cc
intern/MOD_mirror.c
intern/MOD_multires.c
intern/MOD_nodes.cc
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
index 8bde0cab3dc..7d07e854ae7 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
@@ -39,11 +39,14 @@
#include "BKE_cachefile.h"
#include "BKE_context.h"
+#include "BKE_curve_to_mesh.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_lib_query.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "BKE_spline.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -93,7 +96,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
BKE_modifier_copydata_generic(md, target, flag);
- tmcmd->reader = NULL;
+ tmcmd->reader = nullptr;
tmcmd->reader_object_path[0] = '\0';
}
@@ -114,7 +117,41 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
/* leave it up to the modifier to check the file is valid on calculation */
- return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0');
+ return (mcmd->cache_file == nullptr) || (mcmd->object_path[0] == '\0');
+}
+
+/* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we
+ * must return the mesh as-is instead of deforming it. */
+static bool need_orco_mesh(MeshSeqCacheModifierData *mcmd,
+ const ModifierEvalContext *ctx,
+ Mesh *mesh,
+ float time,
+ const char **err_str)
+{
+ CacheFile *cache_file = mcmd->cache_file;
+
+ if (ctx->flag & MOD_APPLY_ORCO) {
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+#ifdef WITH_ALEMBIC
+ if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, err_str)) {
+ return true;
+ }
+#endif
+ break;
+ case CACHEFILE_TYPE_USD:
+#ifdef WITH_USD
+ if (!USD_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, err_str)) {
+ return true;
+ }
+#endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
+ }
+
+ return false;
}
static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh)
@@ -155,20 +192,103 @@ static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh)
return result;
}
+static void modifyGeometry(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet &geometry_set)
+{
+#ifdef WITH_ALEMBIC
+ MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
+
+ Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
+ CacheFile *cache_file = mcmd->cache_file;
+ const float frame = DEG_get_ctime(ctx->depsgraph);
+ const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
+ const char *err_str = nullptr;
+
+ if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
+ STRNCPY(mcmd->reader_object_path, mcmd->object_path);
+ BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
+ if (!mcmd->reader) {
+ BKE_modifier_set_error(
+ ctx->object, md, "Could not create Alembic reader for file %s", cache_file->filepath);
+ return;
+ }
+ }
+
+ if (geometry_set.has_mesh()) {
+ Mesh *mesh = geometry_set.get_mesh_for_write();
+ if (need_orco_mesh(mcmd, ctx, mesh, time, &err_str)) {
+ return;
+ }
+ }
+
+ /* Do not process data if using a render procedural, return a box instead for displaying in the
+ * viewport. */
+ if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(ctx->depsgraph))) {
+ Mesh *org_mesh = nullptr;
+ if (geometry_set.has_mesh()) {
+ org_mesh = geometry_set.get_mesh_for_write();
+ }
+
+ Mesh *bbox = generate_bounding_box_mesh(ctx->object, org_mesh);
+ geometry_set = GeometrySet::create_with_mesh(bbox, GeometryOwnershipType::Editable);
+ return;
+ }
+
+ /* Time (in frames or seconds) between two velocity samples. Automatically computed to
+ * scale the velocity vectors at render time for generating proper motion blur data. */
+ float velocity_scale = mcmd->velocity_scale;
+ if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_FRAME) {
+ velocity_scale *= FPS;
+ }
+
+ switch (cache_file->type) {
+ default: {
+ break;
+ }
+# ifdef WITH_ALEMBIC
+ case CACHEFILE_TYPE_ALEMBIC: {
+ ABCReadParams params;
+ params.time = time;
+ params.read_flags = mcmd->read_flag;
+ params.velocity_name = mcmd->cache_file->velocity_name;
+ params.velocity_scale = velocity_scale;
+ params.mappings = &mcmd->cache_file->attribute_mappings;
+ ABC_read_geometry(mcmd->reader, ctx->object, geometry_set, &params, &err_str);
+ break;
+ }
+# endif
+# ifdef WITH_USD
+ case CACHEFILE_TYPE_USD: {
+ USD_read_geometry(mcmd->reader, ctx->object, geometry_set, time, &err_str, mcmd->read_flag);
+ }
+# endif
+ }
+
+ if (err_str) {
+ BKE_modifier_set_error(ctx->object, md, "%s", err_str);
+ }
+
+#else
+ UNUSED_VARS(ctx, md, geometry_set);
+ return;
+#endif
+}
+
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
#if defined(WITH_USD) || defined(WITH_ALEMBIC)
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
/* Only used to check whether we are operating on org data or not... */
- Mesh *me = (ctx->object->type == OB_MESH) ? ctx->object->data : NULL;
+ Mesh *me = (ctx->object->type == OB_MESH) ? static_cast<Mesh *>(ctx->object->data) : nullptr;
Mesh *org_mesh = mesh;
Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph);
CacheFile *cache_file = mcmd->cache_file;
const float frame = DEG_get_ctime(ctx->depsgraph);
const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
- const char *err_str = NULL;
+ const char *err_str = nullptr;
if (!mcmd->reader || !STREQ(mcmd->reader_object_path, mcmd->object_path)) {
STRNCPY(mcmd->reader_object_path, mcmd->object_path);
@@ -186,30 +306,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return generate_bounding_box_mesh(ctx->object, org_mesh);
}
- /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we
- * must return the mesh as-is instead of deforming it. */
- if (ctx->flag & MOD_APPLY_ORCO) {
- switch (cache_file->type) {
- case CACHEFILE_TYPE_ALEMBIC:
-# ifdef WITH_ALEMBIC
- if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) {
- return mesh;
- }
-# endif
- break;
- case CACHEFILE_TYPE_USD:
-# ifdef WITH_USD
- if (!USD_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) {
- return mesh;
- }
-# endif
- break;
- case CACHE_FILE_TYPE_INVALID:
- break;
- }
+ if (need_orco_mesh(mcmd, ctx, mesh, time, &err_str)) {
+ return mesh;
}
- if (me != NULL) {
+ if (me != nullptr) {
MVert *mvert = mesh->mvert;
MEdge *medge = mesh->medge;
MPoly *mpoly = mesh->mpoly;
@@ -218,53 +319,44 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
* flags) and duplicate those too. */
if ((me->mvert == mvert) || (me->medge == medge) || (me->mpoly == mpoly)) {
/* We need to duplicate data here, otherwise we'll modify org mesh, see T51701. */
- mesh = (Mesh *)BKE_id_copy_ex(NULL,
+ mesh = (Mesh *)BKE_id_copy_ex(nullptr,
&mesh->id,
- NULL,
+ nullptr,
LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT |
LIB_ID_CREATE_NO_DEG_TAG | LIB_ID_COPY_NO_PREVIEW);
}
}
- Mesh *result = NULL;
+ Mesh *result = nullptr;
- switch (cache_file->type) {
- case CACHEFILE_TYPE_ALEMBIC: {
-# ifdef WITH_ALEMBIC
- /* Time (in frames or seconds) between two velocity samples. Automatically computed to
- * scale the velocity vectors at render time for generating proper motion blur data. */
- float velocity_scale = mcmd->velocity_scale;
- if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_FRAME) {
- velocity_scale *= FPS;
- }
-
- result = ABC_read_mesh(mcmd->reader,
- ctx->object,
- mesh,
- time,
- &err_str,
- mcmd->read_flag,
- mcmd->cache_file->velocity_name,
- velocity_scale);
-# endif
- break;
- }
- case CACHEFILE_TYPE_USD:
-# ifdef WITH_USD
- result = USD_read_mesh(
- mcmd->reader, ctx->object, mesh, time * FPS, &err_str, mcmd->read_flag);
-# endif
- break;
- case CACHE_FILE_TYPE_INVALID:
- break;
+ GeometrySet geometry_set;
+
+ if (ctx->object->type == OB_CURVE) {
+ std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(
+ *static_cast<Curve *>(ctx->object->data));
+ geometry_set = GeometrySet::create_with_curve(curve_eval.release(),
+ GeometryOwnershipType::Editable);
+ }
+ else {
+ geometry_set = GeometrySet::create_with_mesh(mesh, GeometryOwnershipType::Editable);
}
- if (err_str) {
- BKE_modifier_set_error(ctx->object, md, "%s", err_str);
+ modifyGeometry(md, ctx, geometry_set);
+
+ if (ctx->object->type == OB_CURVE) {
+ CurveEval *curve_eval = geometry_set.get_component_for_write<CurveComponent>().release();
+
+ if (curve_eval) {
+ result = blender::bke::curve_to_wire_mesh(*curve_eval);
+ delete curve_eval;
+ }
+ }
+ else {
+ result = geometry_set.get_component_for_write<MeshComponent>().release();
}
- if (!ELEM(result, NULL, mesh) && (mesh != org_mesh)) {
- BKE_id_free(NULL, mesh);
+ if (!ELEM(result, nullptr, mesh) && (mesh != org_mesh)) {
+ BKE_id_free(nullptr, mesh);
mesh = org_mesh;
}
@@ -275,6 +367,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
#endif
}
+static void modifyGeometrySet(ModifierData *md,
+ const ModifierEvalContext *ctx,
+ GeometrySet *geometry_set)
+{
+ modifyGeometry(md, ctx, *geometry_set);
+}
+
static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mode)
{
#if defined(WITH_USD) || defined(WITH_ALEMBIC)
@@ -299,7 +398,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
{
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
- if (mcmd->cache_file != NULL) {
+ if (mcmd->cache_file != nullptr) {
DEG_add_object_cache_relation(
ctx->node, mcmd->cache_file, DEG_OB_COMP_CACHE, "Mesh Cache File");
}
@@ -320,12 +419,13 @@ static void panel_draw(const bContext *C, Panel *panel)
uiTemplateCacheFile(layout, C, ptr, "cache_file");
if (has_cache_file) {
- uiItemPointerR(layout, ptr, "object_path", &cache_file_ptr, "object_paths", NULL, ICON_NONE);
+ uiItemPointerR(
+ layout, ptr, "object_path", &cache_file_ptr, "object_paths", nullptr, ICON_NONE);
}
if (RNA_enum_get(&ob_ptr, "type") == OB_MESH) {
- uiItemR(layout, ptr, "read_data", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
- uiItemR(layout, ptr, "use_vertex_interpolation", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "read_data", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "use_vertex_interpolation", 0, nullptr, ICON_NONE);
}
modifier_panel_end(layout, ptr);
@@ -343,16 +443,12 @@ static void velocity_panel_draw(const bContext *UNUSED(C), Panel *panel)
return;
}
- if (RNA_pointer_is_null(&fileptr)) {
- return;
- }
-
uiLayoutSetPropSep(layout, true);
uiTemplateCacheFileVelocity(layout, &fileptr);
- uiItemR(layout, ptr, "velocity_scale", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "velocity_scale", 0, nullptr, ICON_NONE);
}
-static void time_panel_draw(const bContext *UNUSED(C), Panel *panel)
+static void attribute_remapping_panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -364,15 +460,11 @@ static void time_panel_draw(const bContext *UNUSED(C), Panel *panel)
return;
}
- if (RNA_pointer_is_null(&fileptr)) {
- return;
- }
-
uiLayoutSetPropSep(layout, true);
- uiTemplateCacheFileTimeSettings(layout, &fileptr);
+ uiTemplateCacheFileAttributeRemapping(layout, C, &fileptr);
}
-static void render_procedural_panel_draw(const bContext *C, Panel *panel)
+static void override_layers_panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -384,15 +476,27 @@ static void render_procedural_panel_draw(const bContext *C, Panel *panel)
return;
}
- if (RNA_pointer_is_null(&fileptr)) {
+ uiLayoutSetPropSep(layout, true);
+ uiTemplateCacheFileLayers(layout, C, &fileptr);
+}
+
+static void time_settings_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ PointerRNA fileptr;
+ if (!uiTemplateCacheFilePointer(ptr, "cache_file", &fileptr)) {
return;
}
uiLayoutSetPropSep(layout, true);
- uiTemplateCacheFileProcedural(layout, C, &fileptr);
+ uiTemplateCacheFileTimeSettings(layout, &fileptr);
}
-static void override_layers_panel_draw(const bContext *C, Panel *panel)
+static void render_procedural_panel_draw(const bContext *C, Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -409,34 +513,45 @@ static void override_layers_panel_draw(const bContext *C, Panel *panel)
}
uiLayoutSetPropSep(layout, true);
- uiTemplateCacheFileLayers(layout, C, &fileptr);
+ uiTemplateCacheFileProcedural(layout, C, &fileptr);
}
static void panelRegister(ARegionType *region_type)
{
PanelType *panel_type = modifier_panel_register(
region_type, eModifierType_MeshSequenceCache, panel_draw);
- modifier_subpanel_register(region_type, "time", "Time", NULL, time_panel_draw, panel_type);
+ modifier_subpanel_register(region_type,
+ "time_settings",
+ "Time Settings",
+ nullptr,
+ time_settings_panel_draw,
+ panel_type);
modifier_subpanel_register(region_type,
"render_procedural",
"Render Procedural",
- NULL,
+ nullptr,
render_procedural_panel_draw,
panel_type);
- modifier_subpanel_register(
- region_type, "velocity", "Velocity", NULL, velocity_panel_draw, panel_type);
modifier_subpanel_register(region_type,
"override_layers",
"Override Layers",
- NULL,
+ nullptr,
override_layers_panel_draw,
panel_type);
+ modifier_subpanel_register(
+ region_type, "velocity", "Velocity", nullptr, velocity_panel_draw, panel_type);
+ modifier_subpanel_register(region_type,
+ "attribute_remapping",
+ "Attribute Remapping",
+ nullptr,
+ attribute_remapping_panel_draw,
+ panel_type);
}
static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
{
MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md;
- msmcd->reader = NULL;
+ msmcd->reader = nullptr;
msmcd->reader_object_path[0] = '\0';
}
@@ -446,29 +561,30 @@ ModifierTypeInfo modifierType_MeshSequenceCache = {
/* structSize */ sizeof(MeshSeqCacheModifierData),
/* srna */ &RNA_MeshSequenceCacheModifier,
/* type */ eModifierTypeType_Constructive,
- /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs,
+ /* flags */
+ static_cast<ModifierTypeFlag>(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs),
/* icon */ ICON_MOD_MESHDEFORM, /* TODO: Use correct icon. */
/* copyData */ copyData,
- /* deformVerts */ NULL,
- /* deformMatrices */ NULL,
- /* deformVertsEM */ NULL,
- /* deformMatricesEM */ NULL,
+ /* deformVerts */ nullptr,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
/* modifyMesh */ modifyMesh,
- /* modifyGeometrySet */ NULL,
+ /* modifyGeometrySet */ modifyGeometrySet,
/* initData */ initData,
- /* requiredDataMask */ NULL,
+ /* requiredDataMask */ nullptr,
/* freeData */ freeData,
/* isDisabled */ isDisabled,
/* updateDepsgraph */ updateDepsgraph,
/* dependsOnTime */ dependsOnTime,
- /* dependsOnNormals */ NULL,
+ /* dependsOnNormals */ nullptr,
/* foreachIDLink */ foreachIDLink,
- /* foreachTexLink */ NULL,
- /* freeRuntimeData */ NULL,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
/* panelRegister */ panelRegister,
- /* blendWrite */ NULL,
+ /* blendWrite */ nullptr,
/* blendRead */ blendRead,
};