Maniphest T101090

Alembic attribute import remapping
Confirmed, NormalDESIGN

Assigned To
None
Authored By
Kévin Dietrich (kevindietrich)
Sep 15 2022, 4:18 PM
Tags
  • Pipeline, Assets & I/O
Subscribers
Brecht Van Lommel (brecht)
Hans Goudey (HooglyBoogly)
Julien Kaspar (JulienKaspar)
Kévin Dietrich (kevindietrich)
Michael Kowalski (makowalski)
Rowan Ibbeken (Rowquino)
Sybren A. Stüvel (sybren)

Description

D11591: Alembic: import arbitrary attributes adds arbitrary attribute import to the Alembic importer, which allows for attributes other than UVs, vertex colors, and generated coordinates to be imported.

It also introduces a remapping system whose main purpose is to fix data exported by external software who:

  • may not be in the right type: we may have a vec3 (float3) attribute that is not typed as such, but as a flat array of floats that is 3 times the size of the expected domain (a popular node based software does this a lot)
  • may be set in a domain that Blender does not support for the type: we may also have a UV map that is set on the vertex domain, but we only allow for face corner domain
  • may have an ambiguous or unknown domain, where two potential domains have the same size: e.g. a torus has the same number of faces and vertices, so we need to know where to load the data, there are cases in the current code where this fails

The second point is an extension and replacement of rB3385c04598f2: Alembic: support reading per-vertex UV sets.

Scope ambiguity

Alembic actually has some ambiguous scope enumeration, that may be interpreted or used differently:

  • kVaryingScope is left to interpretation. Is it varying over the faces, vertices, or something else?
  • kUniformScope should be for the entire object (i.e. a single value, that could be loaded as IDProperty), but some use it with kVaryingScope to differentiate between vertices/points and face corners
  • kFacevaryingScope is face corners, but can be used to say that the data is for this domain, but written in another domain (likely point domain)

Without remapping, the patch already tries a little to salvage the data when these are used by trying different domains. E.g. for kFacevaryingScope, if face corner is not matching, we try point instead. Although, without a user defined mapping, those checks expect the data size to match.

Another dimension to the problem, is that in Alembic, arrays can have optional index buffers which should be used to determine the actual scope. If there is an index buffer, then surely the data must be the right type, and not some flattened vector.

Limitations

The remapping system only allows to change the type of an attribute if the data size matches (see first point above) and if the domain allows it. For example, a vec2 (float2 in Blender) can be converted to UV map. But we can not convert a float3 to a UV map with it. These types of processing are better left to Geometry Nodes.

Essentially, the remapping system places the burder of telling to Blender what the data is supposed to be on the users so Blender does not have to make wild guesses.

Implementation details.

Attributes are read during import, and during modifier evaluation if they are animated or if the modifiers' or cache file's settings for attributes are modified. Those settings include the already existing read flags (which affect UVs), the standard name of the velocity attribute, and the newly introduced attribute remappings. These settings, along with the output for error messages, are stored in some AttributeReadHelper class to keep them together, and to avoid having too many parameters to functions. Since settings can change, AttributeReadHelper are temporary objects which are created during import and modifier evaluation.

Attributes from the Alembic schemas are analyzed and only attributes which are supposed to be read given the settings (excluding remappings at this step) stored in AttributeReadHelper are considered for import/streaming. Then, we verify if the attribute can be actually loaded to its target destination (e.g. that vertex attributes map to vertices). For this, we use CacheAttributeMappings. If there is no user supplied mapping for the current attribute (looked up via AttributeReadHelper), we create a default one given what we expect the attribute to be and on which domain it should be read. If reading the attribute given this mapping fails, we skip it, an error is printed in the console, and users are notified via a note on the modifiers' UI.

Revisions and Commits

rB Blender
Needs Revision

Related Objects

Mentioned In
D11591: Alembic: import arbitrary attributes
Mentioned Here
rB3385c04598f2: Alembic: support reading per-vertex UV sets
D11591: Alembic: import arbitrary attributes

Event Timeline

Kévin Dietrich (kevindietrich) created this task.Sep 15 2022, 4:18 PM
Kévin Dietrich (kevindietrich) mentioned this in D11591: Alembic: import arbitrary attributes.
Hans Goudey (HooglyBoogly) added a subscriber: Hans Goudey (HooglyBoogly).Sep 15 2022, 4:27 PM

It would be nice to avoid this sort of complicated system in my opinion, and rely on geometry nodes to do necessary attribute operations afterwards, but..

as a flat array of floats that is 3 times the size of the expected domain

We can't handle this in geometry nodes currently, so it wouldn't be possible.

So I think this sounds like a reasonable solution.

Hans Goudey (HooglyBoogly) changed the task status from Needs Triage to Confirmed.Sep 15 2022, 4:27 PM
Kévin Dietrich (kevindietrich) added a project: Pipeline, Assets & I/O.Sep 15 2022, 5:03 PM
Brecht Van Lommel (brecht) added a subscriber: Brecht Van Lommel (brecht).Sep 15 2022, 5:08 PM

My assumptions was that this was for more general remapping between conventions, but it seems to be mostly for poorly written files? Hopefully USD is better defined and doesn't need this?

To play devil's advocate, I'm wondering if we could get away without the UI.

  • may not be in the right type: we may have a vec3 (float3) attribute that is not typed as such, but as a flat array of floats that is 3 times the size of the expected domain (a popular node based software does this a lot)

This seems like it could be handled automatically? Unless the domain is unkown as in the last point?

If the domain is known it would still have to guess if it's a vector, quaternion or color, though geometry nodes could do the conversion.

  • may be set in a domain that Blender does not support for the type: we may also have a UV map that is set on the vertex domain, but we only allow for face corner domain

Are there cases where rB3385c04598f2: Alembic: support reading per-vertex UV sets fails that can't be fixed with geometry nodes?

  • may have an ambiguous or unknown domain, where two potential domains have the same size: e.g. a torus has the same number of faces and vertices, so we need to know where to load the data, there are cases in the current code where this fails

Geometry nodes can't handle this, so maybe this is the best motivation. But is this common?

Kévin Dietrich (kevindietrich) added a subscriber: Michael Kowalski (makowalski).Sep 15 2022, 6:37 PM
In T101090#1417736, @Brecht Van Lommel (brecht) wrote:

My assumptions was that this was for more general remapping between conventions, but it seems to be mostly for poorly written files? Hopefully USD is better defined and doesn't need this?

Yes, this is for poorly written files, and the lack of good definition. For USD, maybe @Michael Kowalski (makowalski) would know better.

To play devil's advocate, I'm wondering if we could get away without the UI.

  • may not be in the right type: we may have a vec3 (float3) attribute that is not typed as such, but as a flat array of floats that is 3 times the size of the expected domain (a popular node based software does this a lot)

This seems like it could be handled automatically? Unless the domain is unkown as in the last point?

If the domain is known it would still have to guess if it's a vector, quaternion or color, though geometry nodes could do the conversion.

Alembic actually has some ambiguous scope enumeration, that may be interpreted or used differently:

  • kVaryingScope is left to interpretation. Is it varying over the faces, vertices, or something else?
  • kUniformScope should be for the entire object (i.e. a single value, that could be loaded as IDProperty), but some use it with kVaryingScope to differentiate between vertices/points and face corners
  • kFacevaryingScope is face corners, but can be used to say that the data is for this domain, but written in another domain (likely point domain)

Without remapping, the patch already tries a little to salvage the data when these are used by trying different domains, because I don't trust people. E.g. for kFacevaryingScope, if face corner is not matching, we try point instead. Although, without a user defined mapping, those checks expect the data size to match. Bumping the number of dimensions until something matches feels like a shot in the dark for me. For point clouds this could work as there is only one domain, but curves and meshes have too many potential matches across domains here.

Another dimension to the problem, is that in Alembic, arrays can have optional index buffers. So now it is the index buffer that should be used to determine the actual scope. I forgot about this "detail" when creating the task. If there is an index buffer, then surely the data must be the right type, and not some flattened vector. Thinking about this, we should be able to get rid of those by always expanding the arrays beforehand, which would use memory but simplify some logic.

  • may be set in a domain that Blender does not support for the type: we may also have a UV map that is set on the vertex domain, but we only allow for face corner domain

Are there cases where rB3385c04598f2: Alembic: support reading per-vertex UV sets fails that can't be fixed with geometry nodes?

I don't know for sure. If there were failures we would have bug reports already, as the imported UVs would be wrong, and people might not think about using geometry nodes to fix it. Since UVs are now read with the generic attribute reader, it made sense to put that in the remapping.

  • may have an ambiguous or unknown domain, where two potential domains have the same size: e.g. a torus has the same number of faces and vertices, so we need to know where to load the data, there are cases in the current code where this fails

Geometry nodes can't handle this, so maybe this is the best motivation. But is this common?

I don't know how common, but from the files that I have (including those from bug reports), I noticed it is mostly flattened vector arrays the problem. For domain size ambiguity, this comes mostly from what I said above about Alembic scopes.

Brecht Van Lommel (brecht) added a comment.Sep 15 2022, 10:16 PM

Thanks for the explanation. I wasn't aware it was this bad, I then also don't have any alternative to a UI for this.

Rowan Ibbeken (Rowquino) added a subscriber: Rowan Ibbeken (Rowquino).Sep 19 2022, 3:52 PM
Michael Kowalski (makowalski) added a comment.Sep 19 2022, 6:32 PM
In T101090#1417736, @Brecht Van Lommel (brecht) wrote:

My assumptions was that this was for more general remapping between conventions, but it seems to be mostly for poorly written files? Hopefully USD is better defined and doesn't need this?

I don't think there is much ambiguity in how USD interpolation types are defined:

https://graphics.pixar.com/usd/release/api/class_usd_geom_primvar.html#Usd_InterpolationVals

Still, in my opinion, a remapping system like this would be helpful for USD IO as well, to help resolve some of the ambiguous cases Kevin mentioned.

Kévin Dietrich (kevindietrich) updated the task description.Sep 27 2022, 5:26 AM
Brecht Van Lommel (brecht) added a comment.EditedSep 27 2022, 5:50 AM
In T101090#1419593, @Michael Kowalski (makowalski) wrote:

I don't think there is much ambiguity in how USD interpolation types are defined:

https://graphics.pixar.com/usd/release/api/class_usd_geom_primvar.html#Usd_InterpolationVals

Right, from my experience with Renderman and OpenSubdiv these have a clear meaning, where each of these values unambiguously maps to a Blender domain.

The one issue I see is that there is no equivalent to the edge domain in Blender, so conversion to USD can lose some data.

Still, in my opinion, a remapping system like this would be helpful for USD IO as well, to help resolve some of the ambiguous cases Kevin mentioned.

Not sure which ambiguous cases this is referring to, I was hoping none of them exist in USD. But maybe that's too optimistic.

Julien Kaspar (JulienKaspar) added a subscriber: Julien Kaspar (JulienKaspar).Sep 27 2022, 12:56 PM
Michael Kowalski (makowalski) added a comment.Oct 3 2022, 6:06 PM
In T101090#1423263, @Brecht Van Lommel (brecht) wrote:
In T101090#1419593, @Michael Kowalski (makowalski) wrote:

Still, in my opinion, a remapping system like this would be helpful for USD IO as well, to help resolve some of the ambiguous cases Kevin mentioned.

Not sure which ambiguous cases this is referring to, I was hoping none of them exist in USD. But maybe that's too optimistic.

@Brecht Van Lommel (brecht) My apologies for the delayed response! I was referring to cases when the USD was incorrectly authored and the attribute data size doesn't match the given interpolation type. Kevin mentioned that inferring the correct interpolation type from the data size can't always be done unambiguously. However, thinking about this further, I'm reconsidering my original comment. Such cases are sufficiently rare that it probably doesn't justify adding a UI for this reason alone, so I agree with you that this is probably not needed for USD.

Bastien Montagne (mont29) moved this task from Backlog to Under Discussion on the Pipeline, Assets & I/O board.Oct 10 2022, 5:38 PM
Kévin Dietrich (kevindietrich) added a revision: D11591: Alembic: import arbitrary attributes.Oct 13 2022, 5:12 PM
Sybren A. Stüvel (sybren) added a subscriber: Sybren A. Stüvel (sybren).Nov 11 2022, 2:54 PM

Although the info given in the task description is good to have, there is no actual description of the design of the functionality. How does it work?

Kévin Dietrich (kevindietrich) updated the task description.Dec 7 2022, 7:13 PM
Sybren A. Stüvel (sybren) added a comment.Dec 9 2022, 3:08 PM

Attributes from the Alembic schemas are analyzed and only attributes which are supposed to be read given the settings (excluding remappings at this step) stored in AttributeReadHelper are considered for import/streaming.

That's quite a hard sentence to parse. Just to check, is this rewording actually correct?

"Attributes from the Alembic schemas are analyzed, and a subset of those are considered for import/streaming. The settings that determine this subset are stored in AttributeReadHelper."

I'm not sure what "excluding remappings at this step" means, though.

Kévin Dietrich (kevindietrich) added a comment.Dec 15 2022, 6:20 AM
In T101090#1457639, @Sybren A. Stüvel (sybren) wrote:

Attributes from the Alembic schemas are analyzed and only attributes which are supposed to be read given the settings (excluding remappings at this step) stored in AttributeReadHelper are considered for import/streaming.

That's quite a hard sentence to parse. Just to check, is this rewording actually correct?

"Attributes from the Alembic schemas are analyzed, and a subset of those are considered for import/streaming. The settings that determine this subset are stored in AttributeReadHelper."

Yes, the rewording is correct.

I'm not sure what "excluding remappings at this step" means, though.

Remappings are considered at a later time, not during the initial subset filtering.