Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround to get shape key normals faster #2125

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Mysteryem
Copy link
Contributor

@Mysteryem Mysteryem commented Jan 26, 2024

Getting shape key normals with ShapeKey.normals_split_get() is
particularly slow because Blender first has to iterate the data into a
tuple and then NumPy has to iterate that tuple into an array.

To work around these iterations, the shape key coordinates can be copied
into a mesh's 'position' attribute and then the normals can be
retrieved from the mesh's corner_normals property after forcing the
mesh's normals to be recalculated.

This makes use of the new ShapeKey.points property added in Blender
4.1.0 that has faster access through foreach_get than ShapeKey.data.

For large meshes and/or meshes with many shape keys, this can reduce
export times by a few seconds.


This is very much a workaround, so I understand if this PR is rejected on that basis alone.

Making a copy of the mesh that is being exported isn't strictly necessary because the original 'position' attribute values could be saved and then restored once finished, but I think it's better to create a copy so that the meshes that are being exported are untouched.

I'm not sure if there are some export cases where self.blender_object.data != self.blender_mesh, so I added an alternate control flow for when that is the case. If self.blender_object.data is always the same as self.blender_mesh, then the alternate control flow can be removed.

As well as tmp_mesh.update(), tmp_mesh.vertices[0].co.x = tmp_mesh.vertices[0].co.x also causes normals to be recalculated, but that doesn't seem very reliable to me. I couldn't find other ways to force the normals to be recalculated, things that I thought would work like tmp_mesh.vertices.update() or tmp_mesh.attributes['position'].data.update() didn't.

Setting self.normals to the normals of key_blocks[0].relative_key is a bit odd to me because the relative key of the first shape can rarely not be itself (e.g. if another shape key is moved to the top), but I kept the existing behaviour in this PR.

On a larger humanoid rigged model I have with a few hundred shape keys per mesh, this patch brings the export duration down from 40s to 29s (before this patch, it exports in 6s if I disable exporting shape key normals).

If the changes in this PR will not be possible to be merged even with modifications, I do have an alternative that simply creates numpy arrays faster from the tuples returned by ShapeKey.normals_split_get() by using struct.pack_into(). The export duration with the same humanoid rigged model, but using only this alternative is 35s, which is still a decent change:

    @staticmethod
    def __tuple1d_to_ndarray(tup, dtype):
        ndarray = np.empty(len(tup), dtype=dtype)
        pack_format = '%i%s' % (len(tup), ndarray.data.format)
        offset = 0
        # Slightly faster than struct.pack_into(format, ndarray, offset, *tup)
        args = (pack_format, ndarray, offset) + tup
        pack_into(*args)
        return ndarray

Getting shape key normals with ShapeKey.normals_split_get() is
particularly slow because Blender first has to iterate the data into a
tuple and then NumPy has to iterate that tuple into an array.

To work around these iterations, the shape key coordinates can be copied
into a mesh's 'position' attribute and then the normals can be
retrieved from the mesh's `corner_normals` property after forcing the
mesh's normals to be recalculated.

This makes use of the new ShapeKey.points property added in Blender
4.1.0 that has faster access through `foreach_get` than ShapeKey.data.

For large meshes and/or meshes with many shape keys, this can reduce
export times by a few seconds.
@scurest
Copy link
Contributor

scurest commented Jan 26, 2024

Something to think about for the future... I'm pretty sure this is how getting shape key tangents should work too (I strongly suspect the current code for getting morph tangents is junk).

@julienduroure
Copy link
Collaborator

Thanks !
I will have a look soon.

@julienduroure
Copy link
Collaborator

I closed the PR in blender repo ( https://projects.blender.org/blender/blender-addons/pulls/105134 )
to be sure that all discussion happen here, on upstream repo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants