FPS Controller
This commit is contained in:
23
demo/assets/scripts/destructable/camera.gd
Normal file
23
demo/assets/scripts/destructable/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/assets/scripts/destructable/camera.gd.uid
Normal file
1
demo/assets/scripts/destructable/camera.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cs057402w7w17
|
||||
24
demo/assets/scripts/destructable/cutters.gd
Normal file
24
demo/assets/scripts/destructable/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/assets/scripts/destructable/cutters.gd.uid
Normal file
1
demo/assets/scripts/destructable/cutters.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cd5la8wwjk34w
|
||||
103
demo/assets/scripts/destructable/destructable_wall.gd
Normal file
103
demo/assets/scripts/destructable/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
|
||||
@@ -0,0 +1 @@
|
||||
uid://42cwsrh6jyns
|
||||
201
demo/assets/scripts/destructable/geometry/geopolymesh.gd
Normal file
201
demo/assets/scripts/destructable/geometry/geopolymesh.gd
Normal file
@@ -0,0 +1,201 @@
|
||||
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
|
||||
@@ -0,0 +1 @@
|
||||
uid://c61g8fppm366c
|
||||
@@ -0,0 +1,11 @@
|
||||
extends Node
|
||||
|
||||
# provides all the utility functions for generating
|
||||
# a triangulized Polygon with holes
|
||||
class_name GeoPolyTriangulization
|
||||
|
||||
# returns all triangles generated by ear clipping the outer and inner polygons
|
||||
static func triangulate(outer_polygon: PackedVector2Array, inner_polygons: Array[PackedVector2Array]) -> PackedInt32Array:
|
||||
var triag = Triangulization.new()
|
||||
var result = triag.triangulate_with_holes(outer_polygon, inner_polygons)
|
||||
return result
|
||||
@@ -0,0 +1 @@
|
||||
uid://comj14x4uckt2
|
||||
16
demo/assets/scripts/destructable/util.gd
Normal file
16
demo/assets/scripts/destructable/util.gd
Normal file
@@ -0,0 +1,16 @@
|
||||
extends Object
|
||||
|
||||
class_name UTIL
|
||||
|
||||
# chunks array into 'chunk_size' pieces
|
||||
static func chunk_array(arr: Array, chunk_size: int) -> Array:
|
||||
var result_chunks = []
|
||||
var i = 0
|
||||
while i < arr.size():
|
||||
var chunk = arr.slice(i, i + chunk_size)
|
||||
result_chunks.append(chunk)
|
||||
i += chunk_size
|
||||
return result_chunks
|
||||
|
||||
static func vector2_to_vector3(v: Vector2) -> Vector3:
|
||||
return Vector3(v.x, v.y, 0)
|
||||
1
demo/assets/scripts/destructable/util.gd.uid
Normal file
1
demo/assets/scripts/destructable/util.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bjv35mc6440ea
|
||||
Reference in New Issue
Block a user