Files
godot-demo-holes/demo/assets/scripts/destructable/geometry/geopolymesh.gd
2025-11-13 16:33:08 -05:00

202 lines
6.3 KiB
GDScript

extends Node
class_name GeoPolyMesh
var surface_array = []
var verts = PackedVector3Array()
var uvs = PackedVector3Array()
var normals = PackedVector3Array()
var array_mesh = ArrayMesh.new()
var _v_indexes = []
var _v_points = []
var edges = {}
func _init(vector_indexes: PackedInt32Array, vector_points: PackedVector2Array, depth: float = -0.1):
assert(len(vector_indexes) % 3 == 0 && len(vector_indexes) != 0, "Number of vertex points is not divisible by 3, invalid triangle verticies")
surface_array.resize(Mesh.ARRAY_MAX)
self._v_indexes = vector_indexes
for point in vector_points:
self._v_points.append(Vector3(point.x, point.y, 0))
self._pre_process_edges()
self._draw_mesh()
self._extrude_mesh(depth)
self._draw_sides(depth)
func calculate_area(mesh_vertices: PackedVector2Array) -> float:
var result := 0.0
var num_vertices := mesh_vertices.size()
for q in range(num_vertices):
var p = (q - 1 + num_vertices) % num_vertices
result += mesh_vertices[q].cross(mesh_vertices[p])
return result * 0.5
# edges are any verticies that share only on triangle
func _pre_process_edges():
var faces = UTIL.chunk_array(self._v_indexes, 3)
for vertices in faces:
for v_inx in range(len(vertices)):
var vertexA = vertices[v_inx]
var vertexB = vertices[(v_inx+1) % 3]
var min_index = min(vertexA, vertexB)
var max_index = max(vertexA, vertexB)
var edge = self.edges.get([min_index, max_index])
if !edge:
self.edges[[min_index, max_index]] = 1
else:
self.edges[[min_index, max_index]] += 1
func get_loops():
var unvisted_edges = self.get_outline_edge()
var loops = []
while len(unvisted_edges) != 0:
var loop = []
loop.append_array(unvisted_edges.pop_back())
var v_next = loop.back()
while loop[0] != v_next:
for ue_ind in range(len(unvisted_edges)):
var v1 = unvisted_edges[ue_ind][0]
var v2 = unvisted_edges[ue_ind][1]
if v_next == v1:
loop.append(v2)
unvisted_edges.pop_at(ue_ind)
v_next = loop.back()
break
elif v_next == v2:
loop.append(v1)
unvisted_edges.pop_at(ue_ind)
v_next = loop.back()
break
loops.append(loop)
var vector2_loops = []
for loop in loops:
var vector2_loop = []
for ind in loop:
vector2_loop.append(self._v_points[ind])
vector2_loops.append(vector2_loop)
var area = []
for vector2_loop in vector2_loops:
var result = self.calculate_area(vector2_loop)
area.append(result)
# reorder the loops where outer boundary is the first one
var index = 0
var index_value = area[0]
for ind in range(len(area)):
var val = abs(area[ind])
if val > index_value:
index = ind
index_value = val
var bloop = loops.pop_at(index)
var barea = area.pop_at(index)
loops.push_front(bloop)
area.push_front(barea)
# check for CW or CCW
if area[0] > 0: # Outer boundary should be CW, if area is <0 then it is CCW
loops[0].reverse()
for area_index in range(1, len(area)):
var a = area[area_index]
if a < 0: # all inside loops need to rendered in CCW, if + then it is CW
loops[area_index].reverse()
return loops
func get_outline_edge():
var outline_edges = []
for e in self.edges:
var value = self.edges[e]
if value == 1:
outline_edges.append(e)
return outline_edges
func _calc_triangle_normal(a: Vector3, b: Vector3, c: Vector3):
return ((b-a).cross(c-a)).normalized()
func _draw_sides(depth: float):
var loops = get_loops()
for l in loops:
var outline_edges_ordered = l
for outline_vector_ind in range(len(outline_edges_ordered) - 1):
var v0 = self._v_points[outline_edges_ordered[outline_vector_ind]]
var v1 = self._v_points[outline_edges_ordered[outline_vector_ind + 1]]
var v2 = self._v_points[outline_edges_ordered[outline_vector_ind]] + Vector3(0, 0, depth)
var v3 = self._v_points[outline_edges_ordered[outline_vector_ind + 1]]+ Vector3(0, 0, depth)
var n1 = self._calc_triangle_normal(v2, v1, v0)
self.verts.append_array([v0, v1, v2, v1, v3, v2])
self.normals.append_array([n1, n1, n1, n1, n1, n1])
# front face
func _draw_mesh():
var vector3A = self._v_points[self._v_indexes[0]]
var vector3B = self._v_points[self._v_indexes[1]]
var vector3C = self._v_points[self._v_indexes[2]]
var normal = self._calc_triangle_normal(vector3C, vector3B, vector3A)
self.verts.append_array([vector3A, vector3B, vector3C])
self.normals.append_array([normal, normal, normal])
# insert each front face into the mesh "clockwise"
for index in range(3, len(self._v_indexes) - 1, 3):
vector3A = self._v_points[self._v_indexes[index]]
vector3B = self._v_points[self._v_indexes[index+1]]
vector3C = self._v_points[self._v_indexes[index+2]]
self.verts.append_array([vector3A, vector3B, vector3C])
self.normals.append_array([normal, normal, normal])
#
## back face
func _extrude_mesh(depth: float):
var vector3A = self._v_points[self._v_indexes[len(self._v_indexes) - 1]]
var vector3B = self._v_points[self._v_indexes[len(self._v_indexes) - 2]]
var vector3C = self._v_points[self._v_indexes[len(self._v_indexes) - 3]]
var normal = self._calc_triangle_normal(vector3C, vector3B, vector3A)
vector3A = self._v_points[self._v_indexes[len(self._v_indexes) - 1]] - (normal * Vector3(depth, depth, depth))
vector3B = self._v_points[self._v_indexes[len(self._v_indexes) - 2]] - (normal * Vector3(depth, depth, depth))
vector3C = self._v_points[self._v_indexes[len(self._v_indexes) - 3]] - (normal * Vector3(depth, depth, depth))
self.verts.append_array([vector3A, vector3B, vector3C])
self.normals.append_array([normal, normal, normal])
# insert each back face into the mesh "counter-clockwise" extruded
for index in range(len(self._v_indexes) - 4, -1, -3):
vector3A = self._v_points[self._v_indexes[index]] - (normal * Vector3(depth, depth, depth))
vector3B = self._v_points[self._v_indexes[index-1]] - (normal * Vector3(depth, depth, depth))
vector3C = self._v_points[self._v_indexes[index-2]] - (normal * Vector3(depth, depth, depth))
self.verts.append_array([vector3A, vector3B, vector3C])
self.normals.append_array([normal, normal, normal])
func commit_mesh() -> Mesh:
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_NORMAL] = normals
#surface_array[Mesh.ARRAY_TEX_UV] = uvs
#print("VERTS: ", self.verts)
array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
return array_mesh