@tool class_name DestructableWall extends Node3D enum ExtrudeDirection { BACK = 0, # z RIGHT = 1, # x UP = 2 # y } @export_category("TrechBroom Generated Values") @export var verts2d: PackedVector2Array @export var meshInstance3d: MeshInstance3D @export var extrusion_direction: ExtrudeDirection @export var depth: float = 0.1 @export var depth_position_offset: Vector3 = Vector3.ZERO @export var edge_non_fracture: float = 0.1 @onready var parentNode = get_node("..") var outer_polygon: PackedVector2Array var inner_polygons: Array[PackedVector2Array] var static_body: StaticBody3D func _func_godot_apply_properties(entity_properties: Dictionary) -> void: meshInstance3d = _find_mesh_body() var verticies = meshInstance3d.mesh.surface_get_arrays(0)[Mesh.ARRAY_VERTEX] extrusion_direction = entity_properties["extrude_direction"] var depth_min: float var depth_max: float var verts_2d: PackedVector2Array = [] if extrusion_direction == ExtrudeDirection.BACK: for vert in verticies: verts_2d.append(Vector2(vert.x, vert.y)) var d = vert.z if d > depth_max: depth_max = d elif d < depth_min: depth_min = d depth = -(abs(depth_min) + abs(depth_max)) depth_position_offset = -(Vector3.BACK * (depth/2)) elif extrusion_direction == ExtrudeDirection.RIGHT: for vert in verticies: verts_2d.append(Vector2(vert.z, vert.y)) var d = vert.x if d > depth_max: depth_max = d elif d < depth_min: depth_min = d depth = -(abs(depth_min) + abs(depth_max)) depth_position_offset = Vector3.RIGHT * (depth/2) elif extrusion_direction == ExtrudeDirection.UP: for vert in verticies: verts_2d.append(Vector2(vert.x, vert.z)) var d = vert.y if d > depth_max: depth_max = d elif d < depth_min: depth_min = d depth = -(abs(depth_min) + abs(depth_max)) depth_position_offset = (Vector3.UP * (depth/2)) var outer_boudary = Geometry2D.convex_hull(verts_2d) verts2d = outer_boudary func _ready() -> void: if Engine.is_editor_hint(): return outer_polygon = verts2d _draw() func _draw(): var vector_indexes = GeoPolyTriangulization.triangulate(outer_polygon, inner_polygons) var vectors = [] vectors.append_array(outer_polygon) for ip in inner_polygons: vectors.append_array(ip) var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors, extrusion_direction, depth) var commited_mesh = meshGenerator.commit_mesh(meshInstance3d.get_active_material(0)) meshGenerator.free() meshInstance3d.mesh = commited_mesh meshInstance3d.position += depth_position_offset meshInstance3d.create_trimesh_collision() static_body = _find_static_body() func _re_draw(): meshInstance3d.remove_child(static_body) static_body.queue_free() var vector_indexes = GeoPolyTriangulization.triangulate(outer_polygon, inner_polygons) var vectors = [] vectors.append_array(outer_polygon) for ip in inner_polygons: vectors.append_array(ip) var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors, extrusion_direction, depth) var commited_mesh = meshGenerator.commit_mesh(meshInstance3d.get_active_material(0)) meshGenerator.free() meshInstance3d.mesh = commited_mesh meshInstance3d.create_trimesh_collision() static_body = _find_static_body() func hit(position: Vector3): var cutter = Cutter.circleCutter() var hole_position_offset = meshInstance3d.to_local(position) var hole_vector2_offset: Vector2 if extrusion_direction == ExtrudeDirection.BACK: hole_vector2_offset = Vector2(hole_position_offset.x, hole_position_offset.y) elif extrusion_direction == ExtrudeDirection.RIGHT: hole_vector2_offset = Vector2(hole_position_offset.z, hole_position_offset.y) elif extrusion_direction == ExtrudeDirection.UP: hole_vector2_offset = Vector2(hole_position_offset.x, hole_position_offset.z) # translate the cutter var hole_vectors = Transform2D(0, hole_vector2_offset) * cutter var draw = _add_hole(hole_vectors) if draw: _re_draw() func _add_hole(new_hole: PackedVector2Array) -> bool: var values_to_remove = [] # validate that the hole is valid var boundary_polygon = Geometry2D.offset_polygon(outer_polygon, -edge_non_fracture) var boundary_hole = Geometry2D.intersect_polygons(new_hole, boundary_polygon[0]) if !boundary_hole: return false var merged: PackedVector2Array = boundary_hole[0] for index in range(len(self.inner_polygons)): var result = Geometry2D.merge_polygons(merged, self.inner_polygons[index]) if len(result) == 1: merged = result[0] values_to_remove.append(self.inner_polygons[index]) elif len(result) > 1: # need to check that they have no overlapping area if they do clip var overlap = Geometry2D.intersect_polygons(result[0], result[1]) if len(overlap) != 0: merged = Geometry2D.clip_polygons(result[0], result[1])[0] values_to_remove.append(self.inner_polygons[index]) for i in values_to_remove: inner_polygons.erase(i) inner_polygons.append(merged) return true func _find_static_body(): for child in meshInstance3d.get_children(): if child is StaticBody3D: return child func _find_mesh_body(): for child in self.get_children(): if child is MeshInstance3D: return child