Using generic attribute data type for UV layers

Well. For me it’s not so much ‘remembering’. More ‘discovering’ :smiley:

But I indeed found out that when you create a BMesh from a Mesh all associated CustomData/attributes is copied as well. It’s probably done that way for a good reason, but still it seems slow?

Anyway just to clarify my mental image:

  • If you have a float attribute on vertex corners, in a Mesh that is stored as a contiguous array of floats, in the same order as the MLoops in the mesh.
    Mesh -> |MLoop array                                 |
            |Customdata Array 1                          |
            |Customdata Array 2                          |
  • The same attribute in a BMesh is stored interleaved with all other attributes and the BMLoop structs.
BMESH -> |array of {BMLoop, CustomData1,CustomData2}      |

Is this correct? I think this is what I deduced from mentally parsing all those BM_ITERATE macros…

Yes slow, but it’s necessary for correctness, otherwise attributes would be lost when entering edit mode, for example.

Yeah, that’s right.

Another question:

In BM_mesh_bm_from_me( ) I can see that not all customdata layers are converted, but a mask is used to determine which layers to add/skip. The default mask is CD_MASK_BMESH , but extra layers can be passed in via params.

However, I don’t get how to make it pass a specific general purpose attribute layer. I think the fact that CD_PROP_ALL is added to CD_MASK_BMESH means none of the attribute layers are copied to the BMesh? But the masking works on types of layers, not specific indices…

I’ll go on trying to figure it out, but if someone could steer me in the right direction…

In this case the “mask” is more like a “selection”, it describes all of the layers that are copied to the BMesh.

Hm. I don’t really understand. I go into the uv editor with a breakpoint on BM_mesh_bm_from_me( ), and the result is a BMesh with only a single CustomData layer (just the UVs) and not other stuff. Even though I created an attribute layer as well.

Oh well, I’ll step through it again, to see why I end up with only that layer…

edit: found it. I was looking at ldata instead of vdata, while I cretaed my test-attribute on vertices.

Another thing I don’t completely understand…

How to get the offset of a named layer.

  • There’s CustomData_get_named_layer_index(…) to get the index of the layer (in all layers)
  • There’s CustomData_get_n_offset(…) to get the offset. Which uses an offset relative to the first layer of that type

I could also use data->layers[index].offset direcly , but I guess that’s a bad idea? Or should I just do it that way? I’d expect there to be a CustomData_get_index_offset(…) as well…

Edit: Or a CustomData_get_named_offset(…) for that matter.

Edit:2 changed something which I misunderstood.

I like the idea of CustomData_get_named_offset. that seems like the most understandable API, especially in relation to the design of attributes.

While CustomData_get_named_offset is nice to have, I think we’d need a CustomData_get_index_offset as well (or CustomData_get_offset_index, depending on the naming of the existing functions).

It turns out that this function if quite often going to be called inside a loop where the name stays the same for each call. Seems a pity to waste time looking up the name for each iteration…

Status update.

After a marathon renaming and searching/replacing (and some sed trickery) session I now have a prototype version where the MLOOPUV_VERTSEL flag is stored in a generic attribute.

There’s all sorts of stuff wrong with it yet:

  • the python api accesses the old flag
  • you have to create the attribute manually before switching to the uv-editor (otherwise->crash)
  • the name of the attribute is hardcoded to be “UV-vertsel”. Creating multiple uv maps just isn’t handled yet.

But it does work.

My plan now is first to do the same for the other flag. And then maybe do some benchmarking?

3 Likes

Another slight problem I encountered:

The python api accesses the flag values from an MLoopUV pointer, without further context (as far as I can see). So in the getter/setter (in bmesh_py_types_meshdata.c) functions we need to find a way to lookup the original UV layer metadata (name, first element) to be able to get at the correct attribute layers.

I don’t really understand how all the python interfacing works (yet). Maybe we could store some extra metadata in the BPy_BMLoopUV struct?

edit: The same problem (and performancewise probably a bit harder to solve?) exists in
layerInterp_mloopuv() which is used to interpolate the UV layer in the CustomData layer handling. ( customdata.cc ) though this case maybe solves itself by just not interpolating the flag values here and relying on the fact that the independent bool attributes will get interpolated?

2022-02-09T23:00:00Z
edit ( wanted this to be a separate post for clarity, but apparently I’m not allowed to post more than 3 times in a row without someone else replying in between :smiley: ):

Just a quick update of a problem I don’t yet know how to solve…

  • In blenkernel/customdata.cc there is a table describing various layertypes.
  • That table contains callbacks to interpolate various layers.
  • The callbacks are defined to take raw pointers to the data elements of the layers.
  • That means that the interpolate function for UV data gets an array of raw MLoopUV pointers.
  • This means that in the interpolation function I don’t have a way to get back to the layer data, so I don’t have a way to find the associated attribute layers.

The only way I can think of to solve this is to change the LayerTypeInfo callbackfunctions to take CustomDataLayer * instead of a void * directly to the data elements, and then rewrite all the callbacks to use those.

and another edit: I thought that maybe we could just rely on the boolean layers getting interpolated independently. This would work in the UV case, but it would fail if we’d ever want to split up some struct into multiple attributes and the interpolation needs several members of the struct.

@HooglyBoogly Do you have any ideas on this?

I think it’s fine, good even, to rely on separate interpolation for boolean attributes.

In this case I don’t even think the boolean interpolation is critical, just nice to have, since the data is mostly for editing, i.e. only visibly changed when applying modifiers, in which case we wouldn’t usually expect that sort of thing to be interpolated.

That sounds quite reasonable to me. Though I don’t have any experience with BMesh Python API code.

After a lot of work this patch is now in a somewhat testable state.

If people would want to give it a testdrive and tell me any crashes/unexpected behaviour in this topic I would be grateful.

Don’t save over important files

This patch changes your file in an incompatible way, which will probably not even be compatible with the final implementation.

Here is the patch:
https://developer.blender.org/D14365

6 Likes

Download a build from here:

again: don’t use in production. don’t save over files. it will screw them up :wink: