Solved, I also need to transform the mesh normals to World, when comparing them against the raycast normal.
So instead of just: meshtool.get_face_normal(idx)
, I'm now doing mesh_instance.global_transform.basis.xform(meshtool.get_face_normal(idx))
.
Working code
Extremely messy and unoptimized code because it comes from this final experimentation before being refactored to final usage, but for those looking to convert a World Coordinate into an UV coordinate for any mesh, the code with all the logic is below.
Usage: Cast a raycast on any mesh and with the result, call get_uv_coords(raycast.position, raycast.normal)
.
extends Node
var meshtool
var mesh
var mesh_instance
var transform_vertex_to_global = true
func set_mesh(_mesh_instance):
mesh_instance = _mesh_instance
mesh = _mesh_instance.mesh
meshtool = MeshDataTool.new()
meshtool.create_from_surface(mesh, 0)
# Extracts position data for this triangle
func _get_triangle_data(datatool, p1i, p2i, p3i):
var p1 = datatool.get_vertex(p1i)
var p2 = datatool.get_vertex(p2i)
var p3 = datatool.get_vertex(p3i)
return [p1, p2, p3]
func equals_with_epsilon(v1, v2, epsilon):
if (v1.distance_to(v2) < epsilon):
return true
return false
func get_face(point, normal, epsilon = 0.2):
for idx in range(meshtool.get_face_count()):
var world_normal = mesh_instance.global_transform.basis.xform(meshtool.get_face_normal(idx))
if !equals_with_epsilon(world_normal, normal, epsilon):
continue
# Normal is the same-ish, so we need to check if the point is on this face
var v1 = meshtool.get_vertex(meshtool.get_face_vertex(idx, 0))
var v2 = meshtool.get_vertex(meshtool.get_face_vertex(idx, 1))
var v3 = meshtool.get_vertex(meshtool.get_face_vertex(idx, 2))
if transform_vertex_to_global:
v1 = mesh_instance.global_transform.xform(v1)
v2 = mesh_instance.global_transform.xform(v2)
v3 = mesh_instance.global_transform.xform(v3)
if is_point_in_triangle(point, v1, v2, v3):
return idx
return null
func barycentric(P, A, B, C):
# Returns barycentric co-ordinates of point P in triangle ABC
var mat1 = Basis(A, B, C)
var det = mat1.determinant()
var mat2 = Basis(P, B, C)
var factor_alpha = mat2.determinant()
var mat3 = Basis(P, C, A)
var factor_beta = mat3.determinant()
var alpha = factor_alpha / det;
var beta = factor_beta / det;
var gamma = 1.0 - alpha - beta;
return Vector3(alpha, beta, gamma)
func cart2bary(p : Vector3, a : Vector3, b : Vector3, c: Vector3) -> Vector3:
var v0 := b - a
var v1 := c - a
var v2 := p - a
var d00 := v0.dot(v0)
var d01 := v0.dot(v1)
var d11 := v1.dot(v1)
var d20 := v2.dot(v0)
var d21 := v2.dot(v1)
var denom := d00 * d11 - d01 * d01
var v = (d11 * d20 - d01 * d21) / denom
var w = (d00 * d21 - d01 * d20) / denom
var u = 1.0 - v - w
return Vector3(u, v, w)
func transfer_point(from : Basis, to : Basis, point : Vector3) -> Vector3:
return (to * from.inverse()).xform(point)
func bary2cart(a : Vector3, b : Vector3, c: Vector3, barycentric: Vector3) -> Vector3:
return barycentric.x * a + barycentric.y * b + barycentric.z * c
func is_point_in_triangle(point, v1, v2, v3):
#bc = barycentric(point, v1, v2, v3)
var bc = barycentric(point, v1, v2, v3)
if bc.x < 0 or bc.x > 1:
return false
if bc.y < 0 or bc.y > 1:
return false
if bc.z < 0 or bc.z > 1:
return false
return true
func get_uv_coords(point, normal, transform = true):
# Gets the uv coordinates on the mesh given a point on the mesh and normal
# these values can be obtained from a raycast
transform_vertex_to_global = transform
var face = get_face(point, normal)
if face == null:
return null
var v1 = meshtool.get_vertex(meshtool.get_face_vertex(face, 0))
var v2 = meshtool.get_vertex(meshtool.get_face_vertex(face, 1))
var v3 = meshtool.get_vertex(meshtool.get_face_vertex(face, 2))
if transform_vertex_to_global:
v1 = mesh_instance.global_transform.xform(v1)
v2 = mesh_instance.global_transform.xform(v2)
v3 = mesh_instance.global_transform.xform(v3)
var bc = barycentric(point, v1, v2, v3)
var uv1 = meshtool.get_vertex_uv(meshtool.get_face_vertex(face, 0))
var uv2 = meshtool.get_vertex_uv(meshtool.get_face_vertex(face, 1))
var uv3 = meshtool.get_vertex_uv(meshtool.get_face_vertex(face, 2))
return (uv1 * bc.x) + (uv2 * bc.y) + (uv3 * bc.z)