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

Make SurfaceTool.generate_normals() behave consistently with smoothing groups #68034

Merged
merged 1 commit into from
Jan 26, 2023

Conversation

Klowner
Copy link
Contributor

@Klowner Klowner commented Oct 29, 2022

I'm not entirely sure how smooth groups is supposed to work within SurfaceTool since it seems to be undocumented, but I think this relatively small change makes the behavior easier to reason about and produce results in a consistent manner.

This change addresses a couple problems(?) with generate_normals(). The first issue is that vertices are binned using a hash map which takes into account the entire vertex (including uvs, colors, bones, etc.). To address this issue, this PR introduces a SmoothGroupVertex struct and accompanying SmoothGroupVertexHasher which only takes vertex position and smooth group into account. This means that vertices of adjacent faces sharing the same smooth group will contribute to and receive smoothed normals regardless of other vertex attributes.

The other potential issue that some other tickets (see below) bring up is that for a user to generate normals for a mesh with flat faces it would be necessary to use different smooth groups for each face (or at least ensuring that no adjacent vertices share smooth groups) which seems to me like an unnecessary burden for the user. This PR also includes changes which imply that surface group 0 is meant to be flat shaded. If a user wishes to create a smooth shaded mesh, a single call to surface_tool.set_smooth_group(1) would mean the entire mesh will be smooth. This behavior makes sense to me, but I defer to the group 😁 .

Here's a screenshot of the output of the attached example project (adapted from a related issue below):
generate_normals_changes
Example:
SurfaceToolSmoothShading4.zip

Related issues:

Although this PR is not applicable to 3.x, I think similar behavior can be implemented with the old add_smooth_group(bool) method.

Hopefully this is a welcome contribution, any feedback is greatly appreciated!

Bugsquad edit:

@Klowner Klowner force-pushed the surfacetool-generate-normals branch from 7fb7d98 to 511a40f Compare October 29, 2022 21:47
@fire fire requested a review from a team October 29, 2022 22:48
@kleonc kleonc added this to the 4.0 milestone Oct 30, 2022
@kleonc
Copy link
Member

kleonc commented Oct 30, 2022

Breaks compat as it changes the default behavior (although it could be treated as a bugfix).

Can't tell if that's the best / preferred approach but the justification makes sense. The changes should probably be fine as long as the new behavior is documented. So yeah, please update the docs (SurfaceTool.xml) according to the changes made in this PR as well.

cc @clayjohn? (kinda related to rendering)

@Klowner Klowner requested a review from a team as a code owner October 30, 2022 15:16
@Klowner Klowner force-pushed the surfacetool-generate-normals branch from 46860da to 1484e55 Compare October 30, 2022 15:20
@Klowner
Copy link
Contributor Author

Klowner commented Oct 30, 2022

Added docs!

@clayjohn
Copy link
Member

See also: #65113

I don't love using 0 as the setting to disable smooth groups, I would much prefer -1, although using -1 would require us to use an int64_t instead of a uint32_t for smooth group.

I think adding SmoothGroupVertex so that vertices with different UV coordinates (for example) can still be smoothed makes sense. But will need @reduz's input as he is still the owner of this code

@clayjohn clayjohn requested a review from reduz October 31, 2022 17:42
@pseidemann
Copy link

hi,
nice change! I just ran into this as well.
does this change also fix a potential bug that UVs are considered when comparing vertices when searching for duplicates e.g. in index()?
right now I think there is a related bug that once I set different UVs for vertices, not only set_smooth_group() does not work properly but also index() and I actually think also commit() will generate more vertices than needed because of this.

might be worth fixing the overall definition of when two vertices are considered identical/redundant?

@pseidemann
Copy link

is there any way to smoothen the normals after committing in go 3.5 maybe with MeshDataTool?

@pseidemann
Copy link

after some fiddling I was able to write a function which computes smooth normals for a given mesh (should work with any godot version):

func smooth_normals(input_mesh, output_mesh):
	var mdt = MeshDataTool.new()

	# read input mesh
	mdt.create_from_surface(input_mesh, 0)

	# group vertices by position
	var grouped = {}
	for i in range(mdt.get_vertex_count()):
		var pos = mdt.get_vertex(i)
		if !grouped.has(pos):
			grouped[pos] = []
		grouped[pos].push_back(i)

	# process every distinct position only once
	for pos in grouped:
		var vertex_ids = grouped[pos]
		var adjacent_faces = []

		# iterate all vertices with the same position
		for i in vertex_ids:
			var faces = mdt.get_vertex_faces(i)
			# put all faces of all vertices with the same position in one list
			adjacent_faces.append_array(faces)

		# get sum of all face normals of all adjacent faces of current position
		var normal = Vector3.ZERO
		for i in adjacent_faces:
			normal += mdt.get_face_normal(i)
		normal = normal.normalized()

		# set computed normal for all vertices with same position
		for i in vertex_ids:
			mdt.set_vertex_normal(i, normal)

	# write to output mesh
	mdt.commit_to_surface(output_mesh)

@Klowner
Copy link
Contributor Author

Klowner commented Nov 17, 2022

@pseidemann yep, this change fixes the issue where smoothing isn't performed if UVs are incongruous.

Considering how indexed rendering works, index() creating additional vertices when setting UVs isn't so much a bug as it would be expected behavior, no?

I'm also happy to remove the 0 indicating flat shading from this PR.

@pseidemann
Copy link

@Klowner

Considering how indexed rendering works, index() creating additional vertices when setting UVs isn't so much a bug as it would be expected behavior, no?

true

@clayjohn
Copy link
Member

I'm also happy to remove the 0 indicating flat shading from this PR.

Without that change this PR is a bugfix and we could go ahead and merge it without @reduz's review.

That being said, I agree that needing to specify a unique smooth group per vertex to get flat shaded normals is awkward at best. So it may be worthwhile to wait for his input so we can improve the behaviour while we are at it

@clayjohn
Copy link
Member

We discussed this on the rendering Rocketchat and agreed that -1 would be a better value for specifying flat normals. I rebased this PR and made the change in this commit clayjohn@4f26b57

@Klowner Will you have a chance to incorporate this into your PR sometime this week? If not, I can make a PR with your changes and list you as the co-author.

@Klowner
Copy link
Contributor Author

Klowner commented Jan 24, 2023 via email

@Klowner Klowner force-pushed the surfacetool-generate-normals branch from 1484e55 to 4aa517d Compare January 26, 2023 15:51
@Klowner
Copy link
Contributor Author

Klowner commented Jan 26, 2023

Added your change, @clayjohn. Also rebased atop master and incorporated the for (v: vectors) iterator syntax (which I assume is the new preferred style). 🕺

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.

Thank you for updating this so quickly! Looks good to me

@clayjohn
Copy link
Member

The final step before merging is to squash all the commits together so that the whole PR only contains 1 big commit with all your changes. We like to merge one commit at a time to keep the git history clean and navigable.

If you don't know how to do that, we have a helpful tutorial in the official documentation https://docs.godotengine.org/en/latest/community/contributing/pr_workflow.html#the-interactive-rebase

…regard to smoothing groups, imply group 0 is flat
@Klowner Klowner force-pushed the surfacetool-generate-normals branch from e5fa070 to da893c1 Compare January 26, 2023 18:32
@Klowner
Copy link
Contributor Author

Klowner commented Jan 26, 2023

Ah, thank you, flat as a pancake 👍

@akien-mga akien-mga merged commit 7cf21f2 into godotengine:master Jan 26, 2023
@akien-mga
Copy link
Member

Thanks!

@akien-mga
Copy link
Member

Related issues:

Are those 3 fixed by this PR?

@Klowner
Copy link
Contributor Author

Klowner commented Jan 26, 2023

Yep! fixes #6146, fixes #65076, and fixes #65104.

@Klowner
Copy link
Contributor Author

Klowner commented Jan 26, 2023

Fixes #66765 as well, since it should now be possible to disable smoothing.

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