Cutting Holes Into Mesh
We be cutting holes :)
This commit is contained in:
@@ -1,19 +1,24 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://dvgdgi3jrhrvt"]
|
[gd_scene load_steps=4 format=3 uid="uid://dvgdgi3jrhrvt"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dysaws7hlg4td" path="res://scripts/test.gd" id="1_ig7tw"]
|
[ext_resource type="Script" uid="uid://cs057402w7w17" path="res://scripts/camera.gd" id="2_0xm2m"]
|
||||||
|
[ext_resource type="Script" uid="uid://42cwsrh6jyns" path="res://scripts/destructable_wall.gd" id="3_h2yge"]
|
||||||
|
|
||||||
[sub_resource type="Environment" id="Environment_ig7tw"]
|
[sub_resource type="Environment" id="Environment_ig7tw"]
|
||||||
|
|
||||||
[node name="Node3D" type="Node3D"]
|
[node name="Node3D" type="Node3D"]
|
||||||
script = ExtResource("1_ig7tw")
|
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
transform = Transform3D(0.8192554, 0.57171863, 0.04425077, 0.028913066, 0.03588575, -0.9989376, -0.5726993, 0.81966454, 0.012869433, 0, 0.54785013, 6.354968)
|
transform = Transform3D(0.8192555, 0.5717188, 0.044250764, -0.5075839, 0.7589243, -0.40791288, -0.2667944, 0.31172383, 0.91194814, 0, 0.54785013, 6.354968)
|
||||||
shadow_enabled = true
|
shadow_enabled = true
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
environment = SubResource("Environment_ig7tw")
|
environment = SubResource("Environment_ig7tw")
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="."]
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 0.99687606, -0.078981146, 0, 0.078981146, 0.99687606, 1.3097496, 0.96856904, 8.280736)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.546, 1, 1.6508019)
|
||||||
current = true
|
current = true
|
||||||
|
script = ExtResource("2_0xm2m")
|
||||||
|
|
||||||
|
[node name="Wall" type="Node3D" parent="."]
|
||||||
|
script = ExtResource("3_h2yge")
|
||||||
|
outer_polygon = PackedVector2Array(0, 2, 2, 2, 2, 0, 0, 0)
|
||||||
|
|||||||
@@ -18,3 +18,11 @@ config/icon="res://icon.svg"
|
|||||||
[debug_draw_3d]
|
[debug_draw_3d]
|
||||||
|
|
||||||
settings/addon_root_folder="res://addons/debug_draw_3d"
|
settings/addon_root_folder="res://addons/debug_draw_3d"
|
||||||
|
|
||||||
|
[input]
|
||||||
|
|
||||||
|
left_click={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
23
demo/scripts/camera.gd
Normal file
23
demo/scripts/camera.gd
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
extends Camera3D
|
||||||
|
|
||||||
|
signal wall_hit(wall_id, position, cutter)
|
||||||
|
|
||||||
|
func shoot_ray() -> Dictionary:
|
||||||
|
var mouse_position = get_viewport().get_mouse_position()
|
||||||
|
var ray_length = 1000
|
||||||
|
var from = project_ray_origin(mouse_position)
|
||||||
|
var to = from + project_ray_normal(mouse_position) * ray_length
|
||||||
|
var space = get_world_3d().direct_space_state
|
||||||
|
var ray_query = PhysicsRayQueryParameters3D.new()
|
||||||
|
ray_query.from = from
|
||||||
|
ray_query.to = to
|
||||||
|
var raycast_result = space.intersect_ray(ray_query)
|
||||||
|
|
||||||
|
return raycast_result
|
||||||
|
|
||||||
|
func _input(event: InputEvent) -> void:
|
||||||
|
if event.is_action_pressed("left_click"):
|
||||||
|
var query_result = shoot_ray()
|
||||||
|
if query_result:
|
||||||
|
var cutter: PackedVector2Array = Cutter.circleCutter()
|
||||||
|
wall_hit.emit(query_result["collider_id"], query_result["position"], cutter)
|
||||||
1
demo/scripts/camera.gd.uid
Normal file
1
demo/scripts/camera.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cs057402w7w17
|
||||||
24
demo/scripts/cutters.gd
Normal file
24
demo/scripts/cutters.gd
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
class_name Cutter
|
||||||
|
|
||||||
|
static func circleCutter(num_sides = 10, perimerter_length = 0.5) -> PackedVector2Array:
|
||||||
|
var line_length: float = perimerter_length/num_sides
|
||||||
|
var line_angle: float = 360.0/num_sides
|
||||||
|
var current_angle: float = 0.0
|
||||||
|
|
||||||
|
var vectors: PackedVector2Array
|
||||||
|
|
||||||
|
for i in num_sides:
|
||||||
|
var vector: Vector2 = Vector2.ZERO
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
current_angle += line_angle
|
||||||
|
vector = Vector2(line_length * cos(deg_to_rad(current_angle)), line_length * sin(deg_to_rad(current_angle)))
|
||||||
|
vectors.append(vector)
|
||||||
|
else:
|
||||||
|
current_angle += line_angle
|
||||||
|
vector = Vector2(vectors[i-1].x + line_length * cos(deg_to_rad(current_angle)), vectors[i-1].y + line_length * sin(deg_to_rad(current_angle)))
|
||||||
|
vectors.append(vector)
|
||||||
|
|
||||||
|
return vectors
|
||||||
1
demo/scripts/cutters.gd.uid
Normal file
1
demo/scripts/cutters.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cd5la8wwjk34w
|
||||||
103
demo/scripts/destructable_wall.gd
Normal file
103
demo/scripts/destructable_wall.gd
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
@export var outer_polygon: PackedVector2Array
|
||||||
|
@export var inner_polygons: Array[PackedVector2Array]
|
||||||
|
@export var edge_non_fracture: float = 0.1
|
||||||
|
|
||||||
|
@onready var camera: Camera3D = get_node("/root/Node3D/Camera3D")
|
||||||
|
@onready var meshInstance = MeshInstance3D.new()
|
||||||
|
@onready var parentNode = get_node("..")
|
||||||
|
|
||||||
|
var depth: float:
|
||||||
|
set(value):
|
||||||
|
if value > 0:
|
||||||
|
depth = -value
|
||||||
|
depth = value
|
||||||
|
|
||||||
|
var static_body: StaticBody3D
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
assert(camera, "camera was not found")
|
||||||
|
assert(len(outer_polygon) > 2, "outer polygon does not meet the required length")
|
||||||
|
|
||||||
|
camera.connect("wall_hit", _handle_hit_signal)
|
||||||
|
|
||||||
|
self.add_child(meshInstance)
|
||||||
|
_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)
|
||||||
|
var mesh = meshGenerator.commit_mesh()
|
||||||
|
|
||||||
|
meshInstance.mesh = mesh
|
||||||
|
meshInstance.create_trimesh_collision()
|
||||||
|
static_body = _find_static_body()
|
||||||
|
|
||||||
|
func _re_draw():
|
||||||
|
meshInstance.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)
|
||||||
|
var mesh = meshGenerator.commit_mesh()
|
||||||
|
|
||||||
|
meshInstance.mesh = mesh
|
||||||
|
meshInstance.create_trimesh_collision()
|
||||||
|
static_body = _find_static_body()
|
||||||
|
|
||||||
|
|
||||||
|
func _handle_hit_signal(wall_id: int, position: Vector3, cutter: PackedVector2Array):
|
||||||
|
# validate that the hit was to this wall, if not return
|
||||||
|
if wall_id != static_body.get_instance_id():
|
||||||
|
return
|
||||||
|
|
||||||
|
var hole_position_offset = self.position + position
|
||||||
|
var hole_vector2_offset = Vector2(hole_position_offset.x, hole_position_offset.y)
|
||||||
|
|
||||||
|
# translate the cutter
|
||||||
|
var hole_vectors = Transform2D(0, hole_vector2_offset) * cutter
|
||||||
|
|
||||||
|
_add_hole(hole_vectors)
|
||||||
|
|
||||||
|
_re_draw()
|
||||||
|
|
||||||
|
func _add_hole(new_hole: PackedVector2Array):
|
||||||
|
var values_to_remove = []
|
||||||
|
|
||||||
|
var merged: PackedVector2Array = new_hole
|
||||||
|
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:
|
||||||
|
self.inner_polygons.erase(i)
|
||||||
|
|
||||||
|
inner_polygons.append(merged)
|
||||||
|
|
||||||
|
func _find_static_body():
|
||||||
|
for child in meshInstance.get_children():
|
||||||
|
if child is StaticBody3D:
|
||||||
|
return child
|
||||||
1
demo/scripts/destructable_wall.gd.uid
Normal file
1
demo/scripts/destructable_wall.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://42cwsrh6jyns
|
||||||
@@ -14,8 +14,8 @@ var _v_points = []
|
|||||||
|
|
||||||
var edges = {}
|
var edges = {}
|
||||||
|
|
||||||
func _init(vector_indexes: PackedInt32Array, vector_points: PackedVector2Array, depth: float = -2.0):
|
func _init(vector_indexes: PackedInt32Array, vector_points: PackedVector2Array, depth: float = -0.1):
|
||||||
assert(len(vector_indexes) % 3 == 0, "Number of vertex points is not divisible by 3, invalid triangle verticies")
|
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)
|
surface_array.resize(Mesh.ARRAY_MAX)
|
||||||
|
|
||||||
self._v_indexes = vector_indexes
|
self._v_indexes = vector_indexes
|
||||||
@@ -39,7 +39,6 @@ func calculate_area(mesh_vertices: PackedVector2Array) -> float:
|
|||||||
|
|
||||||
return result * 0.5
|
return result * 0.5
|
||||||
|
|
||||||
|
|
||||||
# edges are any verticies that share only on triangle
|
# edges are any verticies that share only on triangle
|
||||||
func _pre_process_edges():
|
func _pre_process_edges():
|
||||||
var faces = UTIL.chunk_array(self._v_indexes, 3)
|
var faces = UTIL.chunk_array(self._v_indexes, 3)
|
||||||
@@ -144,9 +143,6 @@ func _draw_sides(depth: float):
|
|||||||
var v3 = self._v_points[outline_edges_ordered[outline_vector_ind + 1]]+ 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)
|
var n1 = self._calc_triangle_normal(v2, v1, v0)
|
||||||
#
|
|
||||||
#print(v0, ", ", v1, ", ", v2, ", ", v3)
|
|
||||||
#print(n1)
|
|
||||||
|
|
||||||
self.verts.append_array([v0, v1, v2, v1, v3, v2])
|
self.verts.append_array([v0, v1, v2, v1, v3, v2])
|
||||||
self.normals.append_array([n1, n1, n1, n1, n1, n1])
|
self.normals.append_array([n1, n1, n1, n1, n1, n1])
|
||||||
|
|||||||
@@ -4,37 +4,8 @@ extends Node
|
|||||||
# a triangulized Polygon with holes
|
# a triangulized Polygon with holes
|
||||||
class_name GeoPolyTriangulization
|
class_name GeoPolyTriangulization
|
||||||
|
|
||||||
# triangles are made by 3 vector
|
|
||||||
const CHUNK_RESULT: int = 3
|
|
||||||
|
|
||||||
# the boundary polygon
|
|
||||||
var _outer_polygon: PackedVector2Array
|
|
||||||
|
|
||||||
# inner polygons (holes)
|
|
||||||
var _inner_polygons: Array[PackedVector2Array]
|
|
||||||
|
|
||||||
# all vector points defined by outer and inner polygons
|
|
||||||
var vectors: PackedVector2Array = []
|
|
||||||
|
|
||||||
var _edge_safety: float = 0.0
|
|
||||||
|
|
||||||
func _init(outer_polygon: PackedVector2Array, inner_polygons: Array[PackedVector2Array], edge_safety: float):
|
|
||||||
self._outer_polygon = outer_polygon
|
|
||||||
self._edge_safety = edge_safety
|
|
||||||
|
|
||||||
# calulate boundary
|
|
||||||
var hole_boundary = Geometry2D.offset_polygon(outer_polygon, self._edge_safety)[0]
|
|
||||||
|
|
||||||
for ip in inner_polygons:
|
|
||||||
var new_hole = Geometry2D.intersect_polygons(hole_boundary, ip)[0]
|
|
||||||
self._inner_polygons.append(new_hole)
|
|
||||||
|
|
||||||
self.vectors.append_array(self._outer_polygon)
|
|
||||||
|
|
||||||
for inner_polygon in self._inner_polygons:
|
|
||||||
self.vectors.append_array(inner_polygon)
|
|
||||||
|
|
||||||
# returns all triangles generated by ear clipping the outer and inner polygons
|
# returns all triangles generated by ear clipping the outer and inner polygons
|
||||||
func triangulate() -> PackedInt32Array:
|
static func triangulate(outer_polygon: PackedVector2Array, inner_polygons: Array[PackedVector2Array]) -> PackedInt32Array:
|
||||||
var triag = Triangulization.new()
|
var triag = Triangulization.new()
|
||||||
return triag.triangulate_with_holes(self._outer_polygon, self._inner_polygons)
|
var result = triag.triangulate_with_holes(outer_polygon, inner_polygons)
|
||||||
|
return result
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
extends Node
|
|
||||||
|
|
||||||
@onready var meshInstance = MeshInstance3D.new()
|
|
||||||
|
|
||||||
var lines = []
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
|
||||||
for l in lines:
|
|
||||||
DebugDraw3D.draw_lines(l, Color.DARK_RED)
|
|
||||||
|
|
||||||
meshInstance.rotate(Vector3.UP, (0.9 * delta))
|
|
||||||
meshInstance.rotate(Vector3.RIGHT, (0.9 * delta))
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
var p1 = [Vector2(4, 0), Vector2(4, 4), Vector2(0, 4), Vector2(0, 0)]
|
|
||||||
var p2 = [Vector2(0, 2), Vector2(0, 1), Vector2(2, 1), Vector2(2, 2)]
|
|
||||||
var p3 = [Vector2(2, 2), Vector2(2, 1), Vector2(4, 1), Vector2(4, 2)]
|
|
||||||
|
|
||||||
|
|
||||||
var trianglizationInstance = GeoPolyTriangulization.new(PackedVector2Array(p1), [PackedVector2Array(p2), PackedVector2Array(p3)], -0.001)
|
|
||||||
var vector_indexes = trianglizationInstance.triangulate()
|
|
||||||
var vectors_points = trianglizationInstance.vectors
|
|
||||||
|
|
||||||
var meshGenerator = GeoPolyMesh.new(vector_indexes, vectors_points)
|
|
||||||
var mesh = meshGenerator.commit_mesh()
|
|
||||||
|
|
||||||
meshInstance.mesh = mesh
|
|
||||||
|
|
||||||
add_child(meshInstance)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://dysaws7hlg4td
|
|
||||||
Reference in New Issue
Block a user