1
1

Compare commits

...

97 Commits

Author SHA1 Message Date
c0ef2a5cf5 Start replacing write_custom_data with the attribute exporter. 2022-03-13 19:50:35 +01:00
5a0efc9c8b Merge branch 'master' into temp-abc-features 2022-03-13 18:05:40 +01:00
1ca7821b15 Cleanup, reduce code changes. 2022-03-04 22:41:47 +01:00
7ce3751757 Wrap Alembic implementation. 2022-03-04 14:43:14 +01:00
70a80c7faa Remove debug prints. 2022-03-04 14:12:54 +01:00
789d82ffc9 Cleanup, some fixes. 2022-03-04 14:04:43 +01:00
1ce6836849 Merge branch 'master' into temp-abc-features 2022-03-04 12:02:26 +01:00
b392982e3c First pass for attribute export. 2022-02-26 04:41:32 +01:00
7d7232c142 Merge branch 'master' into temp-abc-features 2022-02-26 03:21:57 +01:00
5b64e59b76 Merge branch 'master' into temp-abc-features 2022-02-17 16:14:49 +01:00
f43ccc010d remove unnecessary change 2022-02-14 15:12:23 +01:00
21c395fa83 Merge branch 'master' into temp-abc-features 2022-02-14 15:10:34 +01:00
6143f97531 Merge branch 'master' into temp-abc-features 2022-02-11 13:50:09 +01:00
491fc93fc6 Merge branch 'master' into temp-abc-features 2022-02-11 08:14:28 +01:00
711794ffab SPDX License 2022-02-11 01:15:30 +01:00
e48f74fe4d Merge branch 'master' into temp-abc-features 2022-02-11 01:12:49 +01:00
749748c140 Comments. 2022-02-10 14:28:00 +01:00
bf103be1eb Remove todo. 2022-02-10 14:21:50 +01:00
d05fd030df Specify "too many attributes" error. 2022-02-10 14:19:31 +01:00
ce7abd00ec Pass errors. 2022-02-10 14:19:23 +01:00
b5088f19ff Cleanup. 2022-02-10 13:32:46 +01:00
ed062656fa Support curve domain. 2022-02-10 13:27:10 +01:00
04af3002d2 Cleanup, use attribute domain info. 2022-02-10 12:30:06 +01:00
4617835753 RNA APIs for the mappings. 2022-02-10 08:30:30 +01:00
50a306e799 First pass at supporting Curves in the Alembic loader. 2022-02-10 07:55:57 +01:00
3f278af8e7 Support hair attribute rendering for Cycles. 2022-02-10 07:51:30 +01:00
c1178d6d6e Fix velocity scale not taken into account. 2022-02-10 07:51:16 +01:00
08e90abdee Merge branch 'master' into temp-abc-features 2022-02-10 01:58:15 +01:00
7c1ddc7755 Merge branch 'master' into temp-abc-features 2022-02-10 01:57:57 +01:00
4a5756bc35 Cleanup. 2022-01-20 23:52:30 +01:00
82610dcb7c Merge branch 'master' into temp-abc-features 2022-01-20 23:40:42 +01:00
793e0f85c7 Read point clouds ids. 2022-01-16 15:04:17 +01:00
b5d861904a Support interpolating between points. 2022-01-16 14:55:46 +01:00
c54e260c34 Merge branch 'master' into temp-abc-features 2022-01-16 14:20:08 +01:00
74138fb2d4 Merge branch 'master' into temp-abc-features 2021-12-22 13:07:46 +01:00
2529300f69 Fix compile error. 2021-12-17 00:51:51 +01:00
4a5082317e Merge branch 'master' into temp-abc-features 2021-12-17 00:49:10 +01:00
f3594f83e1 Merge branch 'master' into temp-abc-features 2021-11-17 16:27:41 +01:00
90dcbcb236 Fix crash when importing an object without arbitrary attributes. 2021-11-15 23:04:16 +01:00
26672cb7b5 Merge branch 'master' into temp-abc-features 2021-11-15 18:46:20 +01:00
899a790bf4 Fix crash when modifying Curve data. 2021-11-10 21:27:15 +01:00
ade48c51ba Merge branch 'master' into temp-abc-features 2021-11-10 20:56:21 +01:00
0183d70df2 Add a message for unsupported attribute types 2021-11-09 13:31:55 +01:00
0c71d949c0 Merge branch 'master' into temp-abc-features 2021-11-09 13:16:23 +01:00
945b118a15 Fix class specifier for GeometrySet. 2021-11-06 08:55:22 +01:00
b709e64dbd Merge branch 'master' into temp-abc-features 2021-11-06 07:23:06 +01:00
d2e6fcfb77 Fix missing velocities import 2021-11-06 07:21:48 +01:00
3c6df4305d Fix wrong scope used for creating scalar attributes 2021-11-06 07:21:30 +01:00
ede08b7b48 Try to fix another compile error on windows 2021-11-04 06:39:26 +01:00
ad698d8001 Merge branch 'master' into temp-abc-features 2021-11-04 05:51:14 +01:00
872da801a8 Fix compile errors 2021-11-04 05:50:26 +01:00
1b2b41110f Fix compile error. 2021-11-03 16:57:36 +01:00
d5aea45a86 Merge branch 'master' into temp-abc-features 2021-11-03 16:31:40 +01:00
1b0b987db2 Fix coordinates for float3s. 2021-11-03 16:30:02 +01:00
487e4b09f6 Report an error in the modifier if attributes could not be read. 2021-11-03 16:27:40 +01:00
5794312f89 Cleanup, quiet warnings, unused function. 2021-11-03 15:52:20 +01:00
1cee4aa18c Add a way to specify the original domain of the data. 2021-11-03 15:49:26 +01:00
a89d5e9fcc Further simplifications, fix typo. 2021-11-03 13:35:46 +01:00
33e42a1755 Simplify and cleanup a bit attribute processing. 2021-11-03 11:46:33 +01:00
18877cedb2 Fix duplicate constraints on instances. 2021-11-02 13:29:55 +01:00
a90012ceeb Fix compile error due to new changes in master. 2021-11-02 12:32:41 +01:00
f62d9b3b8f Merge branch 'master' into temp-abc-features 2021-11-02 12:05:29 +01:00
a83dfe5c3f Support instancing.
Instances are read as linked duplicates of the source object.
2021-10-29 05:58:17 +02:00
4bea7c806a Use GeometrySets for USD as well.
This was breaking the modifier.
2021-10-29 03:05:43 +02:00
d081856bc5 Expose CacheFile.type to the RNA. 2021-10-29 00:50:10 +02:00
6e16db01df Fix missing CacheFile from Context. 2021-10-29 00:25:12 +02:00
baab3e3a4f Cleanup: quiet warnings. 2021-10-29 00:16:01 +02:00
0ec0a097bd Merge branch 'master' into temp-abc-features 2021-10-29 00:03:24 +02:00
9c45860cc4 Add BKE_id_attribute_ensure to avoid creating multiple layers for the
same attribute.
2021-10-26 16:20:51 +02:00
f5efb15b30 Add a error return value for specifying error messages. 2021-10-26 16:17:07 +02:00
290f0d4d89 Merge branch 'master' into temp-abc-features 2021-10-26 15:41:38 +02:00
a3723635e8 Use a generic function to operate on attribute to ensure that all types
are always supported.
2021-10-26 03:51:26 +02:00
2fa51473df Cleanup: pass prop name instead of prop
This will allow for more genericity.
2021-10-26 03:05:20 +02:00
5243afbcd8 Import changes from attribute review branch 2021-10-26 02:14:08 +02:00
9a19253ab2 Merge branch 'master' into temp-abc-features 2021-10-26 02:12:14 +02:00
16bc4e2607 Merge branch 'master' into temp-abc-features 2021-10-22 14:42:35 +02:00
a3f7f4bdfe Use a single VBO per attribute. 2021-10-21 21:49:14 +02:00
9369691900 Merge branch 'master' into temp-abc-features 2021-10-20 21:27:10 +02:00
01b5ff266a Merge branch 'master' into temp-abc-features 2021-10-20 20:03:41 +02:00
e2a0c5dfbf Fix UI. 2021-10-20 18:25:21 +02:00
3f7a93a4df Remove attribute regexes.
This was too cumbersome to use.
2021-10-20 18:10:03 +02:00
3e1a05763b Use panels to organize buttons in the modifier. 2021-10-20 16:50:37 +02:00
45c7468e42 Fix missing null-terminator in BLI_string_join_arrayN
Although the documentation says so, the null-terminator was missing.
This could cause crashes when logging shader linking errors as shader
sources are empty in this case.
2021-10-20 13:22:38 +02:00
cda3622cb8 Merge branch 'master' into temp-abc-features 2021-10-18 20:12:06 +02:00
07f1505d6d Generic structures to handle attributes, handle sculpt vcol as regular
color.
2021-10-18 20:10:01 +02:00
e6f1e47a2c Prepare ground for bool and int attributes. 2021-10-15 14:00:06 +02:00
24034bf3d3 Separate attribute and VBO types for the conversion.
Also do not promote readily all attributes to float4.
2021-10-15 13:50:30 +02:00
0eb3bf108e Merge branch 'master' into temp-abc-features 2021-10-15 13:31:11 +02:00
0e54e5181b Fix compile error on Windows 2021-10-11 08:14:49 +02:00
d1fa9f1138 Merge branch 'master' into abc-features 2021-10-11 07:09:21 +02:00
de2242eed7 Viewport: support rendering point attributes.
This adds support for rendering arbitrary attributes on point domains.
For now only float, float2, and float3 attributes are supported. For
more attribute types, and domains, a more flexible system to gather
needed attributes will be required, as we are currently running out of
bits.
2021-10-11 05:03:58 +02:00
bbbbdacf39 Merge branch 'master' into abc-features 2021-10-07 06:07:45 +02:00
27af10e687 Fix missing draw updates when modifying the layers. 2021-10-07 05:59:44 +02:00
cb666041ec Fix unitialized layer flag. 2021-10-07 05:59:13 +02:00
e08fe0daa0 Alembic: use GeometrySets in the modifier
This uses GeometrySets to import data from Alembic cache. The main idea
is to easily extend the modifier's capabilities by directly generating
an object of the right type instead of always outputing a Mesh.

This will also make it much easier to use Alembic objects from Geometry
nodes until a Alembic node is available there, and can be considered a
first step to supporting such a node.

For meshes, nothing really changes.

For points, a PointCloud object is generated, which can also be used
inside of Geometry nodes. The radius information is also read now, as
well as arbitrary attributes.

For curves, the CurveEval is directly modified if the data did not
change. Otherwise, we keep the current behavior of modifying the
original Curve object and create a new CurveEval from it.
2021-10-07 05:47:30 +02:00
05184e8276 Alembic: generic attribute import
This adds support for reading arbitrary attributes from Alembic
archives.

This is for now only supported for meshes and point clouds (as it reuses
the CDStreamConfig from the Mesh), although it is being made generic
with the usage of an ID and Blender's attribute API. Attributes are
added either on points, face corners, or faces depending on their
original scope.

In order to choose which attributes to load, parameters are added to let
users input the names of the attributes. The names have to be comma
separated, and are segregated by domain (point, face, face corners). A
wildcard (*) can be used to load every attribute of the domain.

Since some software export vector or color attributes as a flat array of
floats, a mechanism to remap attributes is added and accessible through
the user interface. Remapping is only available for float and double
types. The arrays can be remapped to UV maps, vertex colors, float2,
float3, color types, and vertex groups.

Supported attribute types are bool, int, float, 2D & 3D vectors, uvs,
and colors, as well as double precision versions of those.
2021-10-07 03:29:01 +02:00
5cf5e60918 Alembic: add support for archive layers
This adds support for Alembic archive layers for the Blender CacheFile
and the Cycles Alembic procedural.

Layers are a feature of the Alembic library in which an archive can be
opened from a list of archives or file paths, and where archives
override data from the others. The order of the files matter since
latter files override data from former ones (i.e. a stack).

This feature is useful to, for example, replace a UV map or the
animation of an object without having to re-export the entire scene;
only the affected hierarchy needs to be re-exported, and "imported" as a
layer.

This adds a UI list for adding layers to the CacheFile, with buttons to
change their relative order. The filepath of the CacheFile is
implicitely considered as the bottom layer, although it is not shown as
such in the UI.

In the future, maybe with T68933, this could be the basis for a non-
destructive workflow where edits to an Alembic archive are exported as
layers.
2021-10-06 03:55:30 +02:00
57 changed files with 3707 additions and 1137 deletions

View File

@@ -49,6 +49,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);
@@ -65,6 +73,13 @@ int *BKE_id_attributes_active_index_p(struct ID *id);
CustomData *BKE_id_attributes_iterator_next_domain(struct ID *id, struct CustomDataLayer *layers);
typedef struct DomainInfo {
CustomData *customdata;
int length;
} DomainInfo;
void BKE_id_attribute_get_domains(const struct ID *id, DomainInfo info[ATTR_DOMAIN_NUM]);
#ifdef __cplusplus
}
#endif

View File

@@ -11,6 +11,7 @@
extern "C" {
#endif
struct CacheAttributeMapping;
struct CacheFile;
struct CacheFileLayer;
struct CacheReader;
@@ -63,6 +64,17 @@ 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_add_attribute_mapping(struct CacheFile *cache_file,
const char *name,
int mapping_type,
int domain);
struct CacheAttributeMapping *BKE_cachefile_get_active_attribute_mapping(
struct CacheFile *cache_file);
void BKE_cachefile_remove_attribute_mapping(struct CacheFile *cache_file,
struct CacheAttributeMapping *mapping);
#ifdef __cplusplus
}
#endif

View File

@@ -601,6 +601,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

View File

@@ -25,6 +25,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]);

View File

@@ -29,12 +29,7 @@
#include "RNA_access.h"
typedef struct DomainInfo {
CustomData *customdata;
int length;
} DomainInfo;
static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
void BKE_id_attribute_get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
{
memset(info, 0, sizeof(DomainInfo) * ATTR_DOMAIN_NUM);
@@ -87,7 +82,7 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
static CustomData *attribute_customdata_find(ID *id, CustomDataLayer *layer)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) {
CustomData *customdata = info[domain].customdata;
@@ -102,7 +97,7 @@ static CustomData *attribute_customdata_find(ID *id, CustomDataLayer *layer)
bool BKE_id_attributes_supported(struct ID *id)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) {
if (info[domain].customdata) {
return true;
@@ -136,7 +131,7 @@ CustomDataLayer *BKE_id_attribute_new(
ID *id, const char *name, const int type, const AttributeDomain domain, ReportList *reports)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
CustomData *customdata = info[domain].customdata;
if (customdata == NULL) {
@@ -212,7 +207,7 @@ CustomDataLayer *BKE_id_attribute_find(const ID *id,
const AttributeDomain domain)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
CustomData *customdata = info[domain].customdata;
if (customdata == NULL) {
@@ -229,10 +224,22 @@ 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];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
int length = 0;
@@ -249,7 +256,7 @@ int BKE_id_attributes_length(ID *id, const CustomDataMask mask)
AttributeDomain BKE_id_attribute_domain(ID *id, CustomDataLayer *layer)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) {
CustomData *customdata = info[domain].customdata;
@@ -265,7 +272,7 @@ AttributeDomain BKE_id_attribute_domain(ID *id, CustomDataLayer *layer)
int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) {
CustomData *customdata = info[domain].customdata;
@@ -300,7 +307,7 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id)
}
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
int index = 0;
@@ -325,7 +332,7 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id)
void BKE_id_attributes_active_set(ID *id, CustomDataLayer *active_layer)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
int index = 0;
@@ -366,7 +373,7 @@ int *BKE_id_attributes_active_index_p(ID *id)
CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *layers)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
BKE_id_attribute_get_domains(id, info);
bool use_next = (layers == NULL);

View File

@@ -73,6 +73,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)
@@ -81,6 +82,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)
@@ -110,6 +112,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)
@@ -126,6 +133,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 = {
@@ -464,3 +474,38 @@ void BKE_cachefile_remove_layer(CacheFile *cache_file, CacheFileLayer *layer)
BLI_remlink(&cache_file->layers, layer);
MEM_freeN(layer);
}
CacheAttributeMapping *BKE_cachefile_add_attribute_mapping(CacheFile *cache_file,
const char *name,
const int mapping_type,
const int domain)
{
const int current_mapping_count = BLI_listbase_count(&cache_file->attribute_mappings);
CacheAttributeMapping *mapping = MEM_callocN(sizeof(CacheAttributeMapping),
"CacheAttributeMapping");
if (name) {
BLI_strncpy(mapping->name, name, sizeof(mapping->name));
}
mapping->mapping = (char)mapping_type;
mapping->domain = (char)domain;
BLI_addtail(&cache_file->attribute_mappings, mapping);
cache_file->active_attribute_mapping = current_mapping_count + 1;
return mapping;
}
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);
}
void BKE_cachefile_remove_attribute_mapping(CacheFile *cache_file, CacheAttributeMapping *mapping)
{
cache_file->active_attribute_mapping = 0;
BLI_remlink(&cache_file->attribute_mappings, mapping);
MEM_freeN(mapping);
}

View File

@@ -27,6 +27,7 @@
#include "BKE_anim_data.h"
#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -308,8 +309,15 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
if ((mti->type == eModifierTypeType_OnlyDeform) &&
(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
if (mti->modifyGeometrySet) {
GeometrySet geometry_set = GeometrySet::create_with_curves(curves,
GeometryOwnershipType::ReadOnly);
mti->modifyGeometrySet(md, &mectx, &geometry_set);
curves = geometry_set.get_component_for_write<CurveComponent>().release();
}
else if ((mti->type == eModifierTypeType_OnlyDeform) &&
(mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
/* Ensure we are not modifying the input. */
if (curves == curves_input) {
curves = BKE_curves_copy_for_eval(curves, true);

View File

@@ -812,12 +812,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;
@@ -874,7 +874,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;
}

View File

@@ -20,6 +20,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"
@@ -1518,3 +1519,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;
}

View File

@@ -255,6 +255,52 @@ 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;
};
static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds(
const PointCloud &pointcloud)
{

View File

@@ -2419,6 +2419,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 {

View File

@@ -1477,6 +1477,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.
*/

View File

@@ -1309,6 +1309,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());
}
/** \} */

View File

@@ -6505,6 +6505,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,

View File

@@ -312,3 +312,78 @@ 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;
}
BKE_cachefile_add_attribute_mapping(
cache_file, NULL, CACHEFILE_ATTRIBUTE_MAP_NONE, CACHEFILE_ATTR_MAP_DOMAIN_AUTO);
/* 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);
if (!mapping) {
return OPERATOR_CANCELLED;
}
BKE_cachefile_remove_attribute_mapping(cache_file, 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;
}

View File

@@ -15,3 +15,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);

View File

@@ -58,5 +58,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);
}

View File

@@ -14,6 +14,8 @@ extern "C" {
struct CacheArchiveHandle;
struct CacheFileLayer;
struct CacheReader;
struct Curves;
struct GeometrySet;
struct ListBase;
struct Main;
struct Mesh;
@@ -99,15 +101,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,

View File

@@ -7,9 +7,11 @@ set(INC
../../blenkernel
../../blenlib
../../blenloader
../../blentranslation
../../bmesh
../../depsgraph
../../editors/include
../../functions
../../makesdna
../../makesrna
../../windowmanager
@@ -30,6 +32,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
@@ -60,6 +64,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

View File

@@ -76,6 +76,12 @@ static void get_loop_normals(struct Mesh *mesh,
ABCGenericMeshWriter::ABCGenericMeshWriter(const ABCWriterConstructorArgs &args)
: ABCAbstractWriter(args), is_subd_(false)
{
attribute_exporter_ = nullptr;
}
ABCGenericMeshWriter::~ABCGenericMeshWriter()
{
delete_attribute_exporter(attribute_exporter_);
}
void ABCGenericMeshWriter::create_alembic_objects(const HierarchyContext *context)
@@ -254,10 +260,6 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
mesh_sample.setNormals(normals_sample);
}
if (args_.export_params->orcos) {
write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config);
}
if (get_velocities(mesh, velocities)) {
mesh_sample.setVelocities(V3fArraySample(velocities));
}
@@ -266,7 +268,6 @@ void ABCGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh)
mesh_sample.setSelfBounds(bounding_box_);
abc_poly_mesh_schema_.set(mesh_sample);
write_arb_geo_params(mesh);
}
@@ -308,10 +309,6 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me
abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV);
}
if (args_.export_params->orcos) {
write_generated_coordinates(abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config);
}
if (!edge_crease_indices.empty()) {
subdiv_sample.setCreaseIndices(Int32ArraySample(edge_crease_indices));
subdiv_sample.setCreaseLengths(Int32ArraySample(edge_crease_lengths));
@@ -347,8 +344,15 @@ void ABCGenericMeshWriter::write_face_sets(Object *object, struct Mesh *mesh, Sc
void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me)
{
int64_t cd_mask = CD_MASK_ALL;
if (!args_.export_params->vcolors) {
return;
cd_mask &= ~CD_MASK_MCOL;
}
if (!args_.export_params->uvs) {
cd_mask &= ~CD_MASK_MLOOPUV;
}
if (!args_.export_params->orcos) {
cd_mask &= ~CD_MASK_ORCO;
}
OCompoundProperty arb_geom_params;
@@ -358,7 +362,13 @@ void ABCGenericMeshWriter::write_arb_geo_params(struct Mesh *me)
else {
arb_geom_params = abc_poly_mesh_.getSchema().getArbGeomParams();
}
write_custom_data(arb_geom_params, m_custom_data_config, &me->ldata, CD_MLOOPCOL);
if (!attribute_exporter_) {
attribute_exporter_ = make_attribute_exporter(&me->id, cd_mask, arb_geom_params);
}
set_timesample_index(attribute_exporter_, m_custom_data_config.timesample_index);
attribute_exporter_->export_attributes();
}
bool ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::V3f> &vels)

View File

@@ -34,9 +34,13 @@ class ABCGenericMeshWriter : public ABCAbstractWriter {
CDStreamConfig m_custom_data_config;
GenericAttributeExporter *attribute_exporter_;
public:
explicit ABCGenericMeshWriter(const ABCWriterConstructorArgs &args);
~ABCGenericMeshWriter();
virtual void create_alembic_objects(const HierarchyContext *context) override;
virtual Alembic::Abc::OObject get_alembic_object() const override;
Alembic::Abc::OCompoundProperty abc_prop_for_custom_props() override;

File diff suppressed because it is too large Load Diff

View File

@@ -11,17 +11,29 @@
#include <map>
#include "BKE_attribute.h"
#include "BLI_color.hh"
#include "BLI_listbase_wrapper.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_span.hh"
struct CacheAttributeMapping;
struct CustomData;
struct ID;
struct MLoop;
struct MLoopUV;
struct MPoly;
struct MVert;
struct Mesh;
struct MCol;
using Alembic::Abc::ICompoundProperty;
using Alembic::Abc::OCompoundProperty;
namespace blender::io::alembic {
class AttributeSelector;
struct UVSample {
std::vector<Imath::V2f> uvs;
std::vector<uint32_t> indices;
@@ -43,10 +55,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;
@@ -57,6 +71,13 @@ struct CDStreamConfig {
const char **modifier_error_message;
DomainInfo domain_info[ATTR_DOMAIN_NUM];
/* 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. */
@@ -78,12 +99,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)
{
}
};
@@ -94,38 +115,126 @@ struct CDStreamConfig {
* For now the active layer is used, maybe needs a better way to choose this. */
const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
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.
/* Need special handling for:
* - creases (vertex/edge)
* - velocity
* - generated coordinate
* - UVs
* - vertex colors
*/
AbcUvScope get_uv_scope(const Alembic::AbcGeom::GeometryScope scope,
const CDStreamConfig &config,
const Alembic::AbcGeom::UInt32ArraySamplePtr &indices);
class GenericAttributeExporter {
ID *m_id;
int64_t cd_mask = CD_MASK_ALL;
public:
GenericAttributeExporter(ID *id, int64_t cd_mask_) : m_id(id), cd_mask(cd_mask_)
{
}
void export_attributes();
protected:
virtual void export_attribute(blender::Span<bool> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<char> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<int> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<float> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<float2> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<float3> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<ColorGeometry4f> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<MLoopUV> span,
const std::string &name,
AttributeDomain domain) = 0;
virtual void export_attribute(blender::Span<MCol> span,
const std::string &name,
AttributeDomain domain) = 0;
template<typename BlenderDataType>
void export_customdata_layer(CustomDataLayer *layer, DomainInfo info, AttributeDomain domain)
{
BlenderDataType *data = static_cast<BlenderDataType *>(layer->data);
int64_t size = static_cast<int64_t>(info.length);
blender::Span<BlenderDataType> data_span(data, size);
this->export_attribute(data_span, layer->name, domain);
}
void export_generated_coordinates(CustomDataLayer *layer,
DomainInfo info,
AttributeDomain domain);
void export_attribute_for_domain(DomainInfo info, AttributeDomain domain);
};
GenericAttributeExporter *make_attribute_exporter(ID *id,
int64_t cd_mask,
OCompoundProperty &prop);
void set_timesample_index(GenericAttributeExporter *exporter, int timesample_index);
void delete_attribute_exporter(GenericAttributeExporter *exporter);
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

View File

@@ -59,7 +59,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()));

View File

@@ -20,7 +20,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

View File

@@ -7,6 +7,7 @@
#include "abc_reader_curves.h"
#include "abc_axis_conversion.h"
#include "abc_customdata.h"
#include "abc_reader_transform.h"
#include "abc_util.h"
@@ -14,14 +15,17 @@
#include "MEM_guardedalloc.h"
#include "DNA_curve_types.h"
#include "DNA_curves_types.h"
#include "DNA_object_types.h"
#include "BLI_listbase.h"
#include "BKE_curve.h"
#include "BKE_curves.h"
#include "BKE_curves.hh"
#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;
@@ -66,7 +70,7 @@ bool AbcCurveReader::accepts_object_type(
return false;
}
if (ob->type != OB_CURVES_LEGACY) {
if (ob->type != OB_CURVES) {
*err_str = "Object type mismatch, Alembic object path points to Curves.";
return false;
}
@@ -74,36 +78,234 @@ bool AbcCurveReader::accepts_object_type(
return true;
}
void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
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_CURVES_LEGACY);
Curves *curves = static_cast<Curves *>(BKE_curves_add(bmain, m_data_name.c_str()));
cu->flag |= CU_3D;
cu->actvert = CU_ACT_NONE;
cu->resolu = 1;
m_object = BKE_object_add_only_object(bmain, OB_CURVES, m_object_name.c_str());
m_object->data = curves;
ICompoundProperty user_props = m_curves_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);
}
}
m_object = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, m_object_name.c_str());
m_object->data = cu;
read_curve_sample(cu, m_curves_schema, sample_sel);
read_curves_sample(curves, m_curves_schema, sample_sel);
if (m_settings->always_add_cache_reader || has_animations(m_curves_schema, m_settings)) {
addCacheModifier();
}
}
void AbcCurveReader::read_curve_sample(Curve *cu,
const ICurvesSchema &schema,
const ISampleSelector &sample_sel)
static short get_curve_resolution(const ICurvesSchema &schema,
const Alembic::Abc::ISampleSelector &sample_sel)
{
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());
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;
}
static int abc_curves_get_total_point_size(const Int32ArraySamplePtr num_vertices)
{
if (!num_vertices) {
return 0;
}
int result = 0;
for (size_t i = 0; i < num_vertices->size(); i++) {
result += (*num_vertices)[i];
}
return result;
}
static void read_curves_sample_ex(Curves *curves,
const ICurvesSchema &schema,
const ICurvesSchema::Sample &smp,
const ISampleSelector sample_sel,
const AttributeSelector *attribute_selector,
const float velocity_scale,
const char **err_str)
{
bke::CurvesGeometry &geometry = bke::CurvesGeometry::wrap(curves->geometry);
MutableSpan<int> offsets = geometry.offsets();
MutableSpan<float3> positions_ = geometry.positions();
MutableSpan<bool> cyclic = geometry.cyclic();
float *curves_weights = nullptr;
int *curves_orders = nullptr;
int *curves_resolution = static_cast<int *>(CustomData_add_layer_named(&geometry.curve_data,
CD_PROP_INT32,
CD_DEFAULT,
nullptr,
geometry.curve_size,
"resolution"));
const int resolution = get_curve_resolution(schema, sample_sel);
for (int64_t i : geometry.curves_range()) {
curves_resolution[i] = resolution;
}
if (!geometry.radius) {
geometry.radius = static_cast<float *>(CustomData_add_layer_named(
&geometry.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, geometry.point_size, "radius"));
}
/* Knots are not imported anymore. */
const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices();
const P3fArraySamplePtr positions = smp.getPositions();
const FloatArraySamplePtr weights = smp.getPositionWeights();
const CurvePeriodicity periodicity = smp.getWrap();
const UcharArraySamplePtr orders = smp.getOrders();
const IFloatGeomParam widths_param = schema.getWidthsParam();
FloatArraySamplePtr radiuses;
if (widths_param.valid()) {
IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
radiuses = wsample.getVals();
}
const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f;
const bool do_weights = (weights != nullptr) && (weights->size() > 1);
if (do_weights) {
curves_weights = static_cast<float *>(CustomData_add_layer_named(&geometry.point_data,
CD_PROP_FLOAT,
CD_DEFAULT,
nullptr,
geometry.point_size,
"nurbs_weight"));
}
const bool do_curves_orders = (orders != nullptr) && (orders->size() > 1);
if (do_curves_orders) {
curves_orders = static_cast<int *>(CustomData_add_layer_named(&geometry.curve_data,
CD_PROP_INT32,
CD_DEFAULT,
nullptr,
geometry.curve_size,
"nurbs_order"));
}
int offset = 0;
size_t position_offset = 0;
for (size_t i = 0; i < num_vertices->size(); i++) {
const int num_verts = (*num_vertices)[i];
offsets[i] = offset;
const int curve_order = get_curve_order(smp.getType(), orders, i);
if (do_curves_orders) {
curves_orders[i] = curve_order;
}
/* Check if the curve is cyclic. */
const int overlap = get_curve_overlap(
periodicity, positions, position_offset, num_verts, curve_order);
const bool is_cyclic = overlap != 0;
cyclic[i] = is_cyclic;
for (int j = 0; j < num_verts - overlap; j++, position_offset++) {
const Imath::V3f &pos = (*positions)[position_offset];
copy_zup_from_yup(positions_[position_offset], pos.getValue());
if (do_radius) {
radius = (*radiuses)[position_offset];
}
geometry.radius[position_offset] = radius;
if (do_weights) {
curves_weights[position_offset] = (*weights)[position_offset];
}
}
offset += num_verts;
/* Skip duplicate positions due to cyclicity. */
position_offset += overlap;
}
offsets[geometry.curve_size] = offset;
/* Attributes. */
CDStreamConfig config;
config.id = &curves->id;
config.attr_selector = attribute_selector;
config.time = sample_sel.getRequestedTime();
config.modifier_error_message = err_str;
BKE_id_attribute_get_domains(config.id, config.domain_info);
read_arbitrary_attributes(config, schema, {}, sample_sel, velocity_scale);
}
void AbcCurveReader::read_curves_sample(Curves *curves,
const ICurvesSchema &schema,
const ISampleSelector &sample_sel)
{
ICurvesSchema::Sample smp;
try {
@@ -120,150 +322,22 @@ void AbcCurveReader::read_curve_sample(Curve *cu,
const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices();
const P3fArraySamplePtr positions = smp.getPositions();
const FloatArraySamplePtr weights = smp.getPositionWeights();
const FloatArraySamplePtr knots = smp.getKnots();
const CurvePeriodicity periodicity = smp.getWrap();
const UcharArraySamplePtr orders = smp.getOrders();
const IFloatGeomParam widths_param = schema.getWidthsParam();
FloatArraySamplePtr radiuses;
const int point_size = abc_curves_get_total_point_size(num_vertices);
const int curve_size = static_cast<int>(num_vertices->size());
if (widths_param.valid()) {
IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
radiuses = wsample.getVals();
}
bke::CurvesGeometry &geometry = bke::CurvesGeometry::wrap(curves->geometry);
geometry.resize(point_size, curve_size);
int knot_offset = 0;
size_t idx = 0;
for (size_t i = 0; i < num_vertices->size(); i++) {
const int num_verts = (*num_vertices)[i];
Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
nu->resolu = cu->resolu;
nu->resolv = cu->resolv;
nu->pntsu = num_verts;
nu->pntsv = 1;
nu->flag |= CU_SMOOTH;
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;
}
if (periodicity == Alembic::AbcGeom::kNonPeriodic) {
nu->flagu |= CU_NURB_ENDPOINT;
}
else if (periodicity == Alembic::AbcGeom::kPeriodic) {
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;
}
const bool do_weights = (weights != nullptr) && (weights->size() > 1);
float weight = 1.0f;
const bool do_radius = (radiuses != nullptr) && (radiuses->size() > 1);
float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f;
nu->type = CU_NURBS;
nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb"));
BPoint *bp = nu->bp;
for (int j = 0; j < nu->pntsu; j++, bp++, idx++) {
const Imath::V3f &pos = (*positions)[idx];
if (do_radius) {
radius = (*radiuses)[idx];
}
if (do_weights) {
weight = (*weights)[idx];
}
copy_zup_from_yup(bp->vec, pos.getValue());
bp->vec[3] = weight;
bp->f1 = SELECT;
bp->radius = radius;
bp->weight = 1.0f;
}
if (knots && knots->size() != 0) {
nu->knotsu = static_cast<float *>(
MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu"));
/* TODO: second check is temporary, for until the check for cycles is rock solid. */
if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) {
/* Skip first and last knots. */
for (size_t i = 1; i < knots->size() - 1; i++) {
nu->knotsu[i - 1] = (*knots)[knot_offset + i];
}
}
else {
/* TODO: figure out how to use the knots array from other
* software in this case. */
BKE_nurb_knot_calc_u(nu);
}
knot_offset += knots->size();
}
else {
BKE_nurb_knot_calc_u(nu);
}
BLI_addtail(BKE_curve_nurbs_get(cu), nu);
}
read_curves_sample_ex(curves, m_curves_schema, smp, sample_sel, nullptr, 0.0f, nullptr);
}
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)
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;
@@ -277,61 +351,22 @@ 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();
const P3fArraySamplePtr positions = sample.getPositions();
int vertex_idx = 0;
int curve_idx;
Curve *curve = static_cast<Curve *>(m_object->data);
Curves *curves = geometry_set.get_curves_for_write();
const int curve_count = BLI_listbase_count(&curve->nurb);
bool same_topology = curve_count == num_vertices->size();
const int point_size = abc_curves_get_total_point_size(num_vertices);
const int curve_size = static_cast<int>(num_vertices->size());
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;
bke::CurvesGeometry &geometry = bke::CurvesGeometry::wrap(curves->geometry);
geometry.resize(point_size, curve_size);
if (num_in_alembic != num_in_blender) {
same_topology = false;
break;
}
}
}
if (!same_topology) {
BKE_nurbList_free(&curve->nurb);
read_curve_sample(curve, m_curves_schema, sample_sel);
}
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];
if (nurbs->bp) {
BPoint *point = nurbs->bp;
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());
}
}
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());
}
}
}
}
return BKE_mesh_new_nomain_from_curve(m_object);
read_curves_sample_ex(
curves, m_curves_schema, sample, sample_sel, attribute_selector, velocity_scale, err_str);
}
} // namespace blender::io::alembic

View File

@@ -9,7 +9,7 @@
#include "abc_reader_mesh.h"
#include "abc_reader_object.h"
struct Curve;
struct Curves;
#define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution"
@@ -26,7 +26,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
@@ -34,16 +37,16 @@ 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,
const Alembic::Abc::ISampleSelector &sample_selector);
void read_curves_sample(Curves *curves,
const Alembic::AbcGeom::ICurvesSchema &schema,
const Alembic::Abc::ISampleSelector &sample_selector);
};
} // namespace blender::io::alembic

View File

@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#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

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#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

View File

@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \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

View File

@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#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

View File

@@ -24,7 +24,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"
@@ -112,10 +112,6 @@ struct AbcMeshData {
P3fArraySamplePtr positions;
P3fArraySamplePtr ceil_positions;
AbcUvScope uv_scope;
V2fArraySamplePtr uvs;
UInt32ArraySamplePtr uvs_indices;
};
static void read_mverts_interp(MVert *mverts,
@@ -175,21 +171,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++) {
@@ -217,19 +204,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];
}
}
}
@@ -337,68 +311,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)
@@ -411,65 +323,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)
@@ -489,13 +343,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) {
@@ -503,25 +352,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;
@@ -529,8 +374,10 @@ 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;
BKE_id_attribute_get_domains(config.id, config.domain_info);
return config;
}
@@ -553,39 +400,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)
{
@@ -604,21 +418,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. */
@@ -683,10 +506,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)
{
@@ -730,7 +574,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)) {
@@ -757,11 +600,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
@@ -848,8 +692,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)
@@ -869,10 +712,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);
}
@@ -885,16 +724,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,
@@ -995,14 +826,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);
}
@@ -1035,8 +875,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)
{
@@ -1064,7 +904,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()) {
@@ -1092,11 +931,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

View File

@@ -24,17 +24,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,
@@ -44,6 +47,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 {
@@ -58,19 +68,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

View File

@@ -94,7 +94,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;

View File

@@ -21,7 +21,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);

View File

@@ -130,16 +130,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*/)
{
@@ -148,6 +138,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;
@@ -166,16 +166,32 @@ 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);
data->cache_file = m_settings->cache_file;
id_us_plus(&data->cache_file->id);
bTransformCacheConstraint *constraint = getOrCreateConstraint();
BLI_strncpy(constraint->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
}
}
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()
{
/* Check that we have an empty object (locator, bone head/tail...). */

View File

@@ -10,7 +10,9 @@
#include "DNA_ID.h"
struct bTransformCacheConstraint;
struct CacheFile;
struct GeometrySet;
struct Main;
struct Mesh;
struct Object;
@@ -19,6 +21,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];
@@ -37,7 +42,6 @@ struct ImportSettings {
int read_flag;
/* From CacheFile and MeshSeqCacheModifierData */
std::string velocity_name;
float velocity_scale;
bool validate_meshes;
@@ -55,7 +59,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),
@@ -131,17 +134,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);
@@ -159,6 +165,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);

View File

@@ -6,6 +6,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"
@@ -13,15 +14,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;
@@ -55,7 +62,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;
}
@@ -63,58 +70,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);
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);
}
if (m_settings->validate_meshes) {
BKE_mesh_validate(mesh, false, false);
}
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);
@@ -126,23 +164,64 @@ 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 (existing_mesh->totvert != positions->size()) {
new_mesh = BKE_mesh_new_nomain(positions->size(), 0, 0, 0, 0);
if (widths_param.valid()) {
IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
radiuses = wsample.getVals();
}
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);
if (point_cloud->totpoint != positions->size()) {
point_cloud = BKE_pointcloud_new_nomain(positions->size());
}
return mesh_to_export;
CDStreamConfig config;
config.id = &point_cloud->id;
BKE_id_attribute_get_domains(config.id, config.domain_info);
config.attr_selector = attribute_selector;
config.time = sample_sel.getRequestedTime();
config.use_vertex_interpolation = (read_flag & MOD_MESHSEQ_INTERPOLATE_VERTICES) != 0;
config.modifier_error_message = err_str;
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;
}
}
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

View File

@@ -23,18 +23,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;
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;
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

View File

@@ -55,7 +55,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;

View File

@@ -22,7 +22,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

View File

@@ -13,6 +13,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"
@@ -211,7 +212,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
@@ -229,7 +230,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)
{
@@ -254,7 +255,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;
@@ -299,15 +300,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)) {
@@ -318,16 +319,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)) {
@@ -340,7 +341,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 {
@@ -353,7 +354,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 *>(
@@ -423,7 +423,7 @@ struct ImportJobData {
ImportSettings settings;
ArchiveReader *archive;
std::vector<AbcObjectReader *> readers;
AbcReaderManager reader_manager;
short *stop;
short *do_update;
@@ -475,7 +475,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());
@@ -490,19 +491,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());
@@ -537,8 +535,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();
@@ -552,8 +549,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);
@@ -572,12 +568,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. */
@@ -598,8 +592,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);
@@ -614,6 +608,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);
@@ -624,8 +636,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) {
@@ -781,23 +792,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(

View File

@@ -25,6 +25,7 @@ set(INC
../../bmesh
../../depsgraph
../../editors/include
../../functions
../../makesdna
../../makesrna
../../windowmanager

View File

@@ -420,20 +420,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(

View File

@@ -5,8 +5,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"
@@ -161,15 +163,39 @@ 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_prim_) {
return existing_mesh;
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;
}
#if 0
pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr();
pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr();
@@ -177,62 +203,37 @@ 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);
CurveEval *curve_eval = geometry_set.get_curve_for_write();
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;
}
}
}
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];
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();
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];
}
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_);
#endif
}
} // namespace blender::io::usd

View File

@@ -35,10 +35,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

View File

@@ -5,6 +5,7 @@
#include "usd.h"
#include "usd_reader_xform.h"
struct GeometrySet;
struct Mesh;
namespace blender::io::usd {
@@ -19,10 +20,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 */)
{

View File

@@ -7,6 +7,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"
@@ -866,4 +867,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

View File

@@ -48,10 +48,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;
@@ -75,6 +75,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

View File

@@ -5,8 +5,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"
@@ -164,11 +166,35 @@ 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 */)
{
#if 0
pxr::UsdGeomCurves curve_prim_(prim_);
pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
@@ -178,62 +204,37 @@ 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);
CurveEval *curve_eval = geometry_set.get_curve_for_write();
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;
}
}
}
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];
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();
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];
}
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_);
#endif
}
} // namespace blender::io::usd

View File

@@ -35,10 +35,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

View File

@@ -12,6 +12,8 @@ extern "C" {
struct CacheArchiveHandle;
struct CacheFile;
struct CacheReader;
struct GeometrySet;
struct Mesh;
struct Object;
struct bContext;
@@ -90,13 +92,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,

View File

@@ -55,6 +55,44 @@ 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,
CACHEFILE_ATTR_MAP_DOMAIN_CURVE,
};
/* 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 {
@@ -85,7 +123,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;
@@ -116,6 +155,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];

View File

@@ -92,6 +92,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;

View File

@@ -47,6 +47,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);
@@ -144,6 +154,87 @@ static void rna_CacheFile_layer_remove(CacheFile *cache_file, bContext *C, Point
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
}
static CacheAttributeMapping *rna_CacheFile_attribute_mapping_new(CacheFile *cache_file,
bContext *C,
ReportList *reports,
const char *name,
const int mapping_type,
const int domain)
{
CacheAttributeMapping *mapping = BKE_cachefile_add_attribute_mapping(
cache_file, name, mapping_type, domain);
if (mapping == NULL) {
BKE_reportf(reports,
RPT_ERROR,
"Cannot add an attribute mapping to CacheFile '%s'",
cache_file->id.name + 2);
return NULL;
}
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
BKE_cachefile_reload(depsgraph, cache_file);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
return mapping;
}
static void rna_CacheFile_attribute_mapping_remove(CacheFile *cache_file,
bContext *C,
PointerRNA *mapping_ptr)
{
CacheAttributeMapping *mapping = mapping_ptr->data;
BKE_cachefile_remove_attribute_mapping(cache_file, mapping);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
BKE_cachefile_reload(depsgraph, cache_file);
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 */
@@ -163,6 +254,148 @@ static void rna_def_alembic_object_path(BlenderRNA *brna)
RNA_define_lib_overridable(false);
}
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},
};
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 for each point"},
{CACHEFILE_ATTR_MAP_DOMAIN_FACE_CORNER,
"FACE_CORNER",
0,
"Face Corner",
"The attribute is defined for each face corner"},
{CACHEFILE_ATTR_MAP_DOMAIN_FACE, "FACE", 0, "Face", "The attribute is defined for each face"},
{CACHEFILE_ATTR_MAP_DOMAIN_CURVE,
"CURVE",
0,
"Curve",
"The attribute is defined for each curve"},
{0, NULL, 0, NULL, NULL},
};
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");
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");
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");
FunctionRNA *func;
PropertyRNA *parm;
/* Add a mapping. */
func = RNA_def_function(srna, "new", "rna_CacheFile_attribute_mapping_new");
RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func, "Add a new attribute mapping");
/* Optional parameters. */
parm = RNA_def_string(func, "name", "Name", 64, "Name", "Name of the attribute to remap");
parm = RNA_def_enum(func,
"mapping_type",
rna_enum_cache_attribute_mapping_items,
CACHEFILE_ATTRIBUTE_MAP_NONE,
"Mapping Type",
"Type of the mapping");
parm = RNA_def_enum(func,
"domain",
rna_enum_cache_attribute_domain_items,
CACHEFILE_ATTR_MAP_DOMAIN_AUTO,
"Domain",
"Original domain of the attribute to remap");
/* Return type. */
parm = RNA_def_pointer(
func, "mapping", "CacheAttributeMapping", "", "Newly created attribute mapping");
RNA_def_function_return(func, parm);
/* Remove a mapping. */
func = RNA_def_function(srna, "remove", "rna_CacheFile_attribute_mapping_remove");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func,
"Remove an existing attribute mapping from the cache file");
parm = RNA_def_pointer(
func, "mapping", "CacheAttributeMapping", "", "Attribute mapping to remove");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
}
/* cachefile.object_paths */
static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop)
{
@@ -255,6 +488,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);
@@ -384,6 +629,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);
@@ -396,6 +658,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

View File

@@ -59,7 +59,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

View File

@@ -25,11 +25,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"
@@ -79,7 +82,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';
}
@@ -100,7 +103,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)
@@ -141,20 +178,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);
@@ -172,30 +292,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;
@@ -204,53 +305,46 @@ 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;
}
GeometrySet geometry_set;
result = ABC_read_mesh(mcmd->reader,
ctx->object,
mesh,
time,
&err_str,
mcmd->read_flag,
mcmd->cache_file->velocity_name,
velocity_scale);
# endif
break;
/* if (ctx->object->type == OB_CURVES_LEGACY) {
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);
}
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;
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_CURVES_LEGACY) {
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;
}
@@ -261,6 +355,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)
@@ -285,7 +386,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");
}
@@ -306,12 +407,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);
@@ -331,10 +433,42 @@ static void velocity_panel_draw(const bContext *UNUSED(C), Panel *panel)
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;
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);
uiTemplateCacheFileAttributeRemapping(layout, C, &fileptr);
}
static void override_layers_panel_draw(const bContext *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);
uiTemplateCacheFileLayers(layout, C, &fileptr);
}
static void time_settings_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -366,47 +500,42 @@ static void render_procedural_panel_draw(const bContext *C, Panel *panel)
uiTemplateCacheFileProcedural(layout, C, &fileptr);
}
static void override_layers_panel_draw(const bContext *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);
uiTemplateCacheFileLayers(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';
}
@@ -416,29 +545,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,
};