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

Transform mesh's AABB to skeleton's space when calculating mesh's bounds #84451

Merged
merged 1 commit into from
Dec 11, 2023

Conversation

ShirenY
Copy link
Contributor

@ShirenY ShirenY commented Nov 4, 2023

Fixes #74656 fixes #81983 fixes #74132, and there maybe more duplicated reports I havent found, since this bug is quite common.

This PR is following @clayjohn 's pr #76184. Thanks to guys working in this threads, they have actually identified the cause of this issue:
It's because when resolving animated meshes AABBs with skeleton. The AABBs is actually in mesh instance's space. But skeleton animation assumes differently: all the meshes are in skeleton's space.
That's why the PR tried to solve it by introduce the mesh item's xform to mesh_get_aabb. But we quickly found item's xform is not enough. As it's a local transform, it means it will still has the bug when the Polygon2d in a different hierarchy with the skeleton.

So I come up with a solution:
Introduce a new transform stored in Mesh::Surface, which contains the transform differences between the Polygon2d and its skeleton. This transform is generated when polygon recreating the mesh. So that in run time, it can be used to transform the bones' AABBs back and forth.

I believe in 3d we have same issue, since the code is shared same logic. And I didn't address 3d part, because I believe this bug in 3d is quite rare, since people tends to have same root space for skinned meshes and skeletons in 3d. So I guess we can leave it for further PRs.

Plz NOTE that this PR is NOT tring to fix every edge cases, like if you move around polygon2d or skeleton's position in runtime, the AABBs will be incorrect until regenerating. And if people move around Polygon2ds in the editor when bones are not in their rest pos, there will be issues not just rect culling but also mesh rendering. I believe these cases are not really intentional usage. Since move mesh's transform after skinning in 3d is really weird practice.

This is my first PR to godot, so let me know if I messed up anything:)

@ShirenY ShirenY requested review from a team as code owners November 4, 2023 14:57
@AThousandShips AThousandShips added this to the 4.3 milestone Nov 4, 2023
@ShirenY ShirenY force-pushed the FixSkeletonMeshCulling branch 2 times, most recently from ef5f774 to c1790ad Compare November 4, 2023 15:18
@ShirenY ShirenY changed the title Transform mesh's AABB to skeleton's space when calculate mesh's bounds for fix incorrect rect culling with Polygon2d Transform mesh's AABB to skeleton's space when calculate mesh's bounds for fixing incorrect rect culling with Polygon2d Nov 4, 2023
@fire
Copy link
Member

fire commented Nov 4, 2023

There's a notification for "move mesh's transform" on Node3D. NOTIFICATION_TRANSFORM_CHANGED NOTIFICATION_LOCAL_TRANSFORM_CHANGED you can consider if it's a rare operation.

Copy link
Member

@fire fire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some comments on a paper review. Godot uses snake_case for variable names. It looks ok.

Now to run the Godot Engine pull request in a compile.

Tempted to run 500 avatars moving to see what happens -- with the aabb calculations performance wise.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 5, 2023

There's a notification for "move mesh's transform" on Node3D. NOTIFICATION_TRANSFORM_CHANGED NOTIFICATION_LOCAL_TRANSFORM_CHANGED you can consider if it's a rare operation.

Pushed a new version to address this. I was intend to only do it in the editor, since I assumes the relative transform between mesh and skeleton won't change in runtime.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 5, 2023

Also hope @clayjohn @SlugFiller @lawnjelly can check this out, as this PR follows their previous work.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 5, 2023

Added Engine::get_singleton()->is_editor_hint() to make sure only recreat mesh for transform change event in Editor.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 5, 2023

Oops, looks like swich-case fall through triggered some warnning in linux. Changed the way how it does the editor check.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 6, 2023

As @fire suggested, I made a performance test project based on @Zoomulator's project at #74656
polygon2dcullbug.zip
It spawns 900 skeletons with meshes.
This is what I got from engine at 4.1.3 without the fix. Frame time in range between 90ms - 110ms
Polygon2dOrigProfile

This is the profiler with this fix at 4.1.3. Frame time in range between 120ms - 140ms
Polygon2dCullingFixProfile

Looks like this fix do cause some frame time increase. But to be honest, I'm not sure which part causes this result. Because it costs more when culling working correctly: you got more drawcalls to cover. And godot's profiler looks quite rough to me, I can't see time in details. I'll try to do more test on this.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 6, 2023

So I made second performance test, make a new animation, but everything won't get culled in origin code. So both version will have same amount of meshes draw to the viewport.
As I suspected, the performance shows no measurable differences this time.
This is the orign engine test result,
Polygon2dOrigProfile2

This is the fixed version test result,
Polygon2dCullingFixProfile2

And this is the second test project I used
polygon2dcullbug.zip

@fire
Copy link
Member

fire commented Nov 7, 2023

@lawnjelly If you have some spare time, can you review this?


const Transform2D meshTransform = this->get_global_transform();
const Transform2D skeletonTransform = skeleton_node->get_global_transform();
const Transform2D mesh_to_sk2d = meshTransform * skeletonTransform.inverse();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be better to use affine_inverse() here (that's what I used in 3.x). inverse() just uses the transpose which is cheaper but doesn't work in some situations (I think transpose depends on being orthonormal but I forget).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, transpose is only valid if the matrix is orthonormal. I don't know if this transform is guaranteed to be orthonormal or not. So it's probably safest to use affine_inverse()

} else {
laabb.merge_with(baabb);
}
}
}

if (found_bone_aabb) {
// Transform skeleton bounds back to mesh's space if any animated AABB applied.
laabb = surface.mesh_to_skeleton_xform.inverse().xform(laabb);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider pre-calculating affine inverse. Again affine_inverse preferred over inverse, and as it is a little more expensive to calculate, it can just be stored.

@lawnjelly
Copy link
Member

lawnjelly commented Nov 7, 2023

This looks great!

It turns out to be simpler than #75612 in 3.x, maybe the bone AABBs were unavailable there so it had to jump through some extra hoops.

EDIT:
Yes looking back at 3.x, the bone AABBs were not available like in 4.x (there is no mesh involved in 3.x), so I pre-calculated the bone bounds and stored them on the Polygon. Aside from this difference, the PRs look to be doing essentially the same thing so this should hopefully work fine in 4.x.

Re: performance, as the author says if you compare scenes that are culling differently you are not comparing like with like. It should be pretty ok from what I can see, but have mentioned pre-calculating affine inverse which might be a tad quicker / more accurate.

I don't know if @clayjohn might have opinions about storing the relative transform on each surface (even when not used), but maybe it can be reused for 3D skinning (no idea if the same problem exists there in 4.x).

I haven't fully tested, but my old test project when I was looking at this seems to work ok and the debug bounds are correct.

Copy link
Member

@lawnjelly lawnjelly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve in principle, see if author wants to make changes for affine inverse.

Also could do with a 4.x maintainer looking over to check they are happy with the housekeeping aspects (I'm not so familiar with rendering in 4.x).

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 7, 2023

Thanks @lawnjelly for the carefully review.

About affine inverse(I dont know this before) and storing inverse matrix. I'm totally fine with these two change. I didn't code these just because I want this change as small as possible, and I don't think a inverse matrix calculation can make any differences per instance in performance wise :) I mean, there at least will be dozens matrix operation in cpu when you do a skeleton animation.
But I totally understand good programmers want every cpu cycle counts, I do that too. So I will definitely take the affine one.
And for storing inverse matrix, there could be some concern on memory and software engineer aspect. I personally don't want store more data, because it usually means more bugs. But I'm totally open on this, you guys could made better choice on these changes than me who just get to the engine in two weeks.

Talking about performance, there actually is a more intense place than the inverse matrix. Like, is it correct and faster to mutliply the two matrix before apply to the bone AABB? I just haven't been very clear on the math correction with this. Futher more, can we just generate these AABBs in skeleton space, to save the matrix-aabb operation? Maybe, but there will be more related parts about these AABBs to be consider. I would like to leave these consideration in futher PRs, if we think the time worth it.

And again, this PR is more focused on fixing this common issue than optimization as this is not usually the hotspot, I want to keep it as simple and small as possible.

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 7, 2023

Oops, Did I mess up anything? The compare looks so wrong to me

@AThousandShips
Copy link
Member

You did not, you just updated your branch at the same time

Now when applying the remaining changes you can do so with git commit --amend to not create a new commit

@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 7, 2023

Looks good to me now. Thanks @AThousandShips
Definitely learned something from this PR XD

@lawnjelly
Copy link
Member

Looks fine now. 👍

Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me too. Great work! And thanks for your dedication in taking this the extra mile

@clayjohn clayjohn added topic:2d cherrypick:4.1 Considered for cherry-picking into a future 4.1.x release cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release labels Nov 8, 2023
@ShirenY
Copy link
Contributor Author

ShirenY commented Nov 8, 2023

Thanks for devs' excellent work 💗

@ShirenY
Copy link
Contributor Author

ShirenY commented Dec 6, 2023

Hi devs, by no means to push, but is there anything need to be down for this PR to be merged?
From my point of view, all the things need to be down in my side have been down. And all stake holders have approved. It's been quite a while, I've seen a lot of PRs later than this one is merged quickly.
@AThousandShips @clayjohn @lawnjelly @fire

@lawnjelly
Copy link
Member

This needs production team to merge, no need to poke reviewers (we generally don't merge).
Delay may have been due to feature freeze.

I'm sure they list the approved PRs during merging sprees, but just in case pinging @akien-mga .

@akien-mga
Copy link
Member

akien-mga commented Dec 6, 2023

It's tracked for merging soon, but right now we focus on PRs that we want to cherrypick for 4.2.1 imminently, and this seems like one that should have some testing in 4.3-dev before we should consider a cherrypick.

scene/2d/polygon_2d.cpp Outdated Show resolved Hide resolved
@akien-mga akien-mga merged commit 4943b6e into godotengine:master Dec 11, 2023
15 checks passed
@akien-mga
Copy link
Member

Thanks! And congrats for your first merged Godot contribution 🎉

@ShirenY ShirenY deleted the FixSkeletonMeshCulling branch December 12, 2023 06:19
@YuriSizov YuriSizov removed the cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release label Jan 24, 2024
@YuriSizov
Copy link
Contributor

Cherry-picked for 4.2.2.

@YuriSizov YuriSizov removed the cherrypick:4.1 Considered for cherry-picking into a future 4.1.x release label Jan 24, 2024
@YuriSizov
Copy link
Contributor

Cherry-picked for 4.1.4.

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