Odd Results from Barycentric Coordinates and Mesh Normals [4.2 dev6]
Asked Answered
C

7

0

Hey y'all,
I wanted to experiment with the new barycentric coordinate function coming in 4.2, but I seem to be getting some odd results with meshes imported from Blender. In a new project I'm using the code found in the example of this pull request (#71233), but with a small modification to allow for controlling the traveling object with the arrow keys; as opposed to an AnimationPlayer. This appears to work correctly with the track mesh provided in the example:

To double check my interpretation of the code, I made the same modification to the example project and it also works.
However upon trying a different track mesh imported from Blender (3.4.0), I get erratic behavior:

Leading up the ramp, the traveling object begins to jitter and doesn't align with the surface. When traveling to the left, it slowly begins to angle to the right even through the track mesh is flat...

It seems the normal data is bad? I tried exporting the mesh in different file formats (.glTF 2.0, .escn, and .obj), but regardless the behavior was the same or worse. I also tried triangulating the mesh in Blender with preserved normals, and made sure to have smooth shading enabled. To make things more confusing, when the barycentric function is disabled so we're using just the face normal of the track mesh, we do get the correct direction and the traveling object aligns properly (though without the smooth interpolation of course):

Granted, I'm fairly new to Godot (coming from a Unreal background) and am likely missing an import/export setting or not preparing the mesh properly. If any help or suggestions on how to fix this would be deeply appreciated, thanks!

Clientage answered 6/10, 2023 at 5:11 Comment(0)
C
0

Still haven't managed to make any head way on this. I've reduced the geometry of the new track mesh down to something similar to the track mesh in the example, I even imported the example's mesh into Blender to compare the normals and everything seems the same.
Example's:

My Mesh:

With the reduced geometry the erroneous behavior is even more apparent:

And again, when disabling the barycentric function so we're only using the face normals, the traveling object correctly orientates with the track.

Because everything works with the example's mesh, I'm not inclined to believe that the issue with the code itself. Never the less here's what it looks like:
Traveling Object:

	extends CharacterBody3D

	var target_velocity = Vector3.ZERO;
	var speed = 4;

	@export_category("Settings")
	@export var use_bary_coords = false;

	@onready var ray_cast: RayCast3D = $RayCast3D;

	func _ready():
		print("READY");

	func _physics_process(_delta: float) -> void:
		if ray_cast.is_colliding():
			var other: CollisionObject3D = ray_cast.get_collider()
			position.y = ray_cast.get_collision_point().y + .1
			align_up_direction(ray_cast.get_collision_normal())

			if ray_cast.get_collider().is_in_group("mesh_colliders") and use_bary_coords:
				var vertices: Array[Vector3] = other.get_vertex_positions_at_face_index(ray_cast.get_collision_face_index())
				var vertex_normals: Array[Vector3] = other.get_vertex_normals_at_face_index(ray_cast.get_collision_face_index())
				var bary_coords: Vector3 = Geometry3D.get_triangle_barycentric_coords(ray_cast.get_collision_point(), vertices[0], vertices[1], vertices[2])
				var up_normal: Vector3 = (vertex_normals[0] * bary_coords.x) + (vertex_normals[1] * bary_coords.y) + (vertex_normals[2] * bary_coords.z)
				up_normal = up_normal.normalized()
				align_up_direction(up_normal)
				print(up_normal)
				
		
		
		
		target_velocity.z = int(Input.is_action_pressed("ui_left")) + -int(Input.is_action_pressed("ui_right"));
		velocity = target_velocity*speed;
		move_and_slide();

	func align_up_direction(up_normal: Vector3) -> void:
		var new_basis: Basis = transform.basis
		new_basis.y = up_normal
		new_basis.x = -basis.z.cross(basis.y)
		new_basis = new_basis.orthonormalized()
		basis = new_basis

Track Object:

	extends StaticBody3D
	class_name TRACKOBJ_testoval


	@onready var mesh: Mesh = $basic_Flat_003.mesh
	var mesh_data: MeshDataTool

	func _ready() -> void:
		mesh_data = MeshDataTool.new()
		mesh_data.create_from_surface(mesh, 0)


	func get_vertex_normals_at_face_index(index: int) -> Array[Vector3]:
		var normals: Array[Vector3] = []
		for i in range(0, 3):
			normals.append(mesh_data.get_vertex_normal(mesh_data.get_face_vertex(index, i))) 
		return normals

	func get_vertex_positions_at_face_index(index: int) -> Array[Vector3]:
		var vertices: Array[Vector3] = []
		for i in range(0, 3):
			vertices.append(mesh_data.get_vertex(mesh_data.get_face_vertex(index, i)))
		return vertices

Maybe it's possible that the vertices are incorrectly ordered in the new track mesh? Not sure how to check that other then drawing debug geometry based on the normals and vertices arrays. Honestly I'm kinda spitballing at this point, if anyone else wants to take a crack at it here's the project file:

Clientage answered 7/10, 2023 at 0:33 Comment(0)
O
0

Clientage Harden the side edges in Blender. Normals on the mesh look smoothed even for 90 deg edges, so 3 vertex normals for each triangle will differ greatly and the interpolation from barycentric weights will cause the hit normal to fluctuate a lot as you traverse each triangle.

Offenseless answered 7/10, 2023 at 0:49 Comment(0)
C
0

Offenseless My apologies, not sure if I understand. By harden the normals are you referring to the setting in the bevel modifier? I tried converting the track mesh into a plane by removing the side geometry, and then setting the normals from the faces; so now they should be perfectly perpendicular with the mesh:

This is the result:

Side note, exporting the mesh with flat shading will cause the traveling object to behave as if the barycentric function is disabled - which is expected. So it would appear the issue lies somewhere with how the normals are being smoothed.

Clientage answered 7/10, 2023 at 4:59 Comment(0)
O
0

Clientage Try permutating normal indices (0,1,2) when doing interpolation.

Offenseless answered 7/10, 2023 at 6:19 Comment(0)
P
0

In blender, right click the object and select smooth shading. This will make the vert normals average among their neighbors vs just pointing in the same direction as the face normal.

In your code, you need to adapt the script to set the arrow's Node3D.transform.basis to align to the new normal which involves getting cross products.

If you don't understand the math, you can try asking chat GPT to explain it 🙂

Periapt answered 6/12, 2023 at 6:43 Comment(0)
P
0

In fact you can actually make an F-Zero type player controller with just face normals, not using barycentric vertex normals, so you can start trying to make that, and when you have that figured out, you can throw in the barycentric calculated normal which is the average of the three vertex normals according to your position on the triangle which will only smoothen an already functioning wall riding player controller.

Hope this helps.

Periapt answered 7/12, 2023 at 2:5 Comment(0)
S
0

Sprocket - Were you able to resolve this issue? May I ask that you share the solution with us?

I'm experiencing the same problem, despite hardening the side edges and smoothing the shading in Blender. The moment my character comes in contact with my track and starts using barycentric coordinates to determine gravity, I get extremely erratic results. None of which seem to correlate to the expected normal vector.

Slider answered 25/2 at 2:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.