diff --git a/game/addons/mesh_data_resource_editor/plugin.gd b/game/addons/mesh_data_resource_editor/plugin.gd index 6cf2f341..6988825a 100644 --- a/game/addons/mesh_data_resource_editor/plugin.gd +++ b/game/addons/mesh_data_resource_editor/plugin.gd @@ -124,448 +124,16 @@ func uv_unwrap() -> void: var mesh : Array = mdr.get_array() - # partition the meshes along the already existing seams (no new vertices) - var partitioned_meshes : Array = partition_mesh(mesh) + var uvs : PoolVector2Array = MeshUtils.uv_unwrap(mesh) - for m in partitioned_meshes: - #m.print() - m.unwrap() - - mesh[ArrayMesh.ARRAY_TEX_UV] = m.processed_calc_uvs - + mesh[ArrayMesh.ARRAY_TEX_UV] = uvs + mdr.set_array(mesh) if mdr: mdi_ed_gui.set_mesh_data_resource(mdr) -class STriangle: - var i1 : int = 0 - var i2 : int = 0 - var i3 : int = 0 - - var index : int = 0 - - var v1 : Vector3 - var v2 : Vector3 - var v3 : Vector3 - - var vn1 : Vector3 - var vn2 : Vector3 - var vn3 : Vector3 - - var vns1 : Vector2 - var vns2 : Vector2 - var vns3 : Vector2 - - var uv1 : Vector2 - var uv2 : Vector2 - var uv3 : Vector2 - - var n1 : Vector3 - var n2 : Vector3 - var n3 : Vector3 - - var neighbour_v1_v2 : int = -1 - var neighbour_v2_v3 : int = -1 - var neighbour_v1_v3 : int = -1 - - var face_normal : Vector3 - var basis : Basis - - func set_indices(pi1 : int, pi2 : int, pi3 : int, pindex : int): - i1 = pi1 - i2 = pi2 - i3 = pi3 - index = pindex - - func set_vertices(pv1 : Vector3, pv2 : Vector3, pv3 : Vector3): - v1 = pv1 - v2 = pv2 - v3 = pv3 - - #for debugging - func set_normals(pn1 : Vector3, pn2 : Vector3, pn3 : Vector3): - n1 = pn1 - n2 = pn2 - n3 = pn3 - - func is_neighbour(pi1 : int, pi2 : int, pi3 : int) -> bool: - var c : int = 0 - - if i1 == pi1 || i1 == pi2 || i1 == pi3: - c += 1 - - if i2 == pi1 || i2 == pi2 || i2 == pi3: - c += 1 - - if i3 == pi1 || i3 == pi2 || i3 == pi3: - c += 1 - - if c >= 1: - return true - else: - return false - - func project_vertices() -> void: - vn3 = v3 - v2 - vn1 = v1 - v2 - vn2 = Vector3() #v2 - v2 - - face_normal = vn1.cross(vn3).normalized() - - # face normal has to end up the y coordinate in the world coordinate system a.k.a 0 1 0 - # then triangle is in x, z plane - - basis = Basis(vn1.normalized(), face_normal, vn3.normalized()) - basis = basis.orthonormalized() - - vn1 = basis.xform_inv(vn1) - vn2 = basis.xform_inv(vn2) - vn3 = basis.xform_inv(vn3) - - #these are not real uvs yet, just projections of the vertices to a 2d plane (v2 is at the origin) - #where the plane is parallel to our triangle - uv1 = Vector2(vn1.x, vn1.z) - uv2 = Vector2(vn2.x, vn2.z) - uv3 = Vector2(vn3.x, vn3.z) - - vns1 = uv1 - vns2 = uv2 - vns3 = uv3 - - func has_edge(pi1 : int, pi2 : int): - if i1 == pi1 || i2 == pi1 || i3 == pi1: - if i1 == pi2 || i2 == pi2 || i3 == pi2: - return true - - return false - - func print(): - print("[ Tri: " + str(i1) + ", " + str(i2) + ", " + str(i3) + ", " + str(index) + " ]") - - func print_verts(): - print("[ Tri vets: " + str(v1) + ", " + str(v2) + ", " + str(v3) + " ]") - - func print_nverts(): - print("[ Tri nvets: " + str(vn1) + ", " + str(vn2) + ", " + str(vn3) + " ]") - - func print_uvs(): - print("[ Tri uvs: " + str(uv1) + ", " + str(uv2) + ", " + str(uv3) + " ]") - - func print_normals(): - print("[ Tri normals: " + str(n1) + ", " + str(n2) + ", " + str(n3) + " ]") - - func print_neighbours(): - print("[ Tri neighbours: " + str(neighbour_v1_v2) + ", " + str(neighbour_v2_v3) + ", " + str(neighbour_v1_v3) + " ]") - - -class SMesh: - var indices : PoolIntArray - var vertices : PoolVector3Array - var uvs : PoolVector2Array - var triangles : Array - - var processed_vertices : Dictionary = Dictionary() - var processed_triangles : Dictionary = Dictionary() - var processed_calc_uvs : PoolVector2Array = PoolVector2Array() - - func is_triangle_neighbour(tri : STriangle) -> bool: - for t in triangles: - if t.is_neighbour(tri.i1, tri.i2, tri.i3): - return true - - return false - - func try_to_merge(o : SMesh) -> bool: - for t in o.triangles: - if is_triangle_neighbour(t): - triangles.append_array(o.triangles) - return true - - return false - - func unwrap(): - project_vertices() - find_neighbours() - layout_uvs() - normalize_uvs() - -# print(processed_calc_uvs) - - func project_vertices(): - for t in triangles: - t.project_vertices() - - func find_neighbours(): - for i in range(triangles.size()): - var t = triangles[i] - - t.neighbour_v1_v2 = find_neighbour(i, t.i1, t.i2) - t.neighbour_v2_v3 = find_neighbour(i, t.i2, t.i3) - t.neighbour_v1_v3 = find_neighbour(i, t.i1, t.i3) - - func layout_uvs(): - processed_vertices = Dictionary() - processed_triangles = Dictionary() - processed_calc_uvs = PoolVector2Array() - - processed_calc_uvs.resize(vertices.size()) - - for i in range(processed_calc_uvs.size()): - processed_calc_uvs.set(i, Vector2()) - - var t = triangles[0] - - #don't - #processed_triangles[t.index] = true - - processed_vertices[t.i1] = true - processed_vertices[t.i2] = true - processed_vertices[t.i3] = true - - processed_calc_uvs[t.i1] = t.uv1 - processed_calc_uvs[t.i2] = t.uv2 - processed_calc_uvs[t.i3] = t.uv3 - - #print(str(t.uv1) + str(t.uv2) + str(t.uv3)) - - join_triangles(0) - - var count : int = 0 - - func join_triangles(tindex): - if tindex == -1: - return - - if processed_triangles.has(tindex): - return - - processed_triangles[tindex] = true - - var t = triangles[tindex] - - #note that only one of these can be false - #except for the first triangle (where all sould be true see layout_uvs() - var v1d : bool = processed_vertices.has(t.i1) - var v2d : bool = processed_vertices.has(t.i2) - var v3d : bool = processed_vertices.has(t.i3) - - if !v1d: - processed_vertices[t.i1] = true - - processed_calc_uvs[t.i1] = transform_uv(t.vns2, t.vns3, t.vns1, processed_calc_uvs[t.i3], processed_calc_uvs[t.i2]) - #print(processed_calc_uvs[t.i1]) - - if !v2d: - processed_vertices[t.i2] = true - - processed_calc_uvs[t.i2] = transform_uv(t.vns1, t.vns3, t.vns2, processed_calc_uvs[t.i3], processed_calc_uvs[t.i1]) - - if !v3d: - processed_vertices[t.i3] = true - - processed_calc_uvs[t.i3] = transform_uv(t.vns1, t.vns2, t.vns3, processed_calc_uvs[t.i2], processed_calc_uvs[t.i1]) - - join_triangles(t.neighbour_v1_v2) - join_triangles(t.neighbour_v2_v3) - join_triangles(t.neighbour_v1_v3) - - func transform_uv(v1 : Vector2, v2 : Vector2, v3 : Vector2, vt1 : Vector2, vt2 : Vector2) -> Vector2: - if count >= 5: - return Vector2() - - count += 1 - - var a : float = (v2 - v1).angle_to(v3 - v1) - var l : float = (v3 - v1).length() - - var ret : Vector2 = (vt2 - vt1).normalized() * l - - var retr : Vector2 = ret.rotated(-a) - - return retr - - func overlaps(v1 : Vector2, v2 : Vector2, v3 : Vector2, vs1 : Vector2, vs2 : Vector2, vs3 : Vector2) -> bool: - var tri1 : PoolVector2Array = PoolVector2Array() - var tri2 : PoolVector2Array = PoolVector2Array() - - tri1.resize(3) - tri2.resize(3) - - tri1[0] = v1 - tri1[1] = v2 - tri1[2] = v3 - - tri2[0] = vs1 - tri2[1] = vs2 - tri2[2] = vs3 - - return tri_tri_overlap_test(tri1, tri2) - - #from https://rosettacode.org/wiki/Determine_if_two_triangles_overlap - #there is probably a simpler way in this case, as one edge is shared - func tri_tri_overlap_test(tri1 : PoolVector2Array, tri2 : PoolVector2Array) -> bool: - for i in range(3): - var j : int = (i + 1) % 3 - - if bound_doesnt_collide_check(tri1[i], tri1[j], tri2[0]) && bound_doesnt_collide_check(tri1[i], tri1[j], tri2[1]) && bound_doesnt_collide_check(tri1[i], tri1[j], tri2[2]): - return false - - for i in range(3): - var j : int = (i + 1) % 3 - - if bound_doesnt_collide_check(tri2[i], tri2[j], tri1[0]) && bound_doesnt_collide_check(tri2[i], tri2[j], tri1[1]) && bound_doesnt_collide_check(tri2[i], tri2[j], tri1[2]): - return false - - return true - - func det2d(p1 : Vector2, p2 : Vector2, p3 : Vector2) -> float: - return p1.x * (p2.y - p3.y) + p2.x * (p3.y - p1.y) + p3.x * (p1.y - p2.y) - - func bound_doesnt_collide_check(p1 : Vector2, p2 : Vector2, p3 : Vector2, eps : float = 0.00001): - return det2d(p1, p2, p3) <= eps - - func normalize_uvs(): - var uv_xmin : float = processed_calc_uvs[0].x - var uv_xmax : float = processed_calc_uvs[0].x - var uv_ymin : float = processed_calc_uvs[0].y - var uv_ymax : float = processed_calc_uvs[0].y - - var incvec : Vector2 = Vector2() - - for i in range(1, processed_calc_uvs.size()): - var uv : Vector2 = processed_calc_uvs[i] - - if uv.x < uv_xmin: - uv_xmin = uv.x - - if uv.x > uv_xmax: - uv_xmax = uv.x - - if uv.y < uv_ymin: - uv_ymin = uv.y - - if uv.y > uv_ymax: - uv_ymax = uv.y - - if uv_ymin < 0 || uv_xmin < 0: - - - if uv_xmin < 0: - incvec.x = -uv_xmin - uv_xmin = 0 - uv_xmax += incvec.x - - if uv_ymin < 0: - incvec.y = -uv_ymin - uv_ymin = 0 - uv_ymax += incvec.y - - for i in range(processed_calc_uvs.size()): - processed_calc_uvs[i] += incvec - - var rx : float = uv_xmax - uv_xmin - var ry : float = uv_ymax - uv_ymin - - for i in range(processed_calc_uvs.size()): - var v : Vector2 = processed_calc_uvs[i] - - v.x -= uv_xmin - v.y -= uv_ymin - v.x /= rx - v.y /= ry - - processed_calc_uvs[i] = v - - #debug - var z : Vector2 = Vector2(incvec.x, incvec.y) - - z.x -= uv_xmin - z.y -= uv_ymin - z.x /= rx - z.y /= ry - - for i in range(processed_calc_uvs.size()): - if processed_calc_uvs[i].is_equal_approx(z): - processed_calc_uvs[i] = Vector2() - - func find_neighbour(current_index : int, i1 : int, i2 : int): - for i in range(triangles.size()): - if current_index == i: - continue - - if triangles[i].has_edge(i1, i2): - return i - - return -1 - - func print(): - print("[ SMesh:") - - for t in triangles: - t.print() - - print("]") - - -func partition_mesh(mesh : Array) -> Array: - var meshes : Array = Array() - - if mesh.size() != ArrayMesh.ARRAY_MAX: - return meshes - - var vertices : PoolVector3Array = mesh[ArrayMesh.ARRAY_VERTEX] - #for debugging - var normals : PoolVector3Array = mesh[ArrayMesh.ARRAY_NORMAL] - var indices : PoolIntArray = mesh[ArrayMesh.ARRAY_INDEX] - - if vertices.size() == 0: - return meshes - - if indices.size() == 0: - return meshes - - var tricount : int = indices.size() / 3 - - for it in range(tricount): - var iit : int = it * 3 - - var tri : STriangle = STriangle.new() - tri.set_indices(indices[iit], indices[iit + 1], indices[iit + 2], it) - tri.set_vertices(vertices[indices[iit]], vertices[indices[iit + 1]], vertices[indices[iit + 2]]) - tri.set_normals(normals[indices[iit]], normals[indices[iit + 1]], normals[indices[iit + 2]]) - - var found : bool = false - for m in meshes: - if m.is_triangle_neighbour(tri): - m.triangles.append(tri) - found = true - break - - if !found: - var sm : SMesh = SMesh.new() - sm.triangles.append(tri) - - meshes.append(sm) - - var changed : bool = true - while changed: - changed = false - - for i in range(meshes.size() - 1): - if meshes[i].try_to_merge(meshes[i + 1]): - changed = true - meshes.remove(i + 1) - break - - for m in meshes: - m.vertices = vertices - m.indices = indices - m.uvs.resize(vertices.size()) - - return meshes - #func forward_spatial_gui_input(camera, event): # for g in active_gizmos: # if g.forward_spatial_gui_input(0, camera, event):