broken_seals/game/addons/mesh_data_resource_editor/MIDGizmo.gd
2022-01-25 18:15:38 +01:00

1560 lines
42 KiB
GDScript

tool
extends EditorSpatialGizmo
var MeshOutline = preload("res://addons/mesh_data_resource_editor/utilities/mesh_outline.gd")
var MeshDecompose = preload("res://addons/mesh_data_resource_editor/utilities/mesh_decompose.gd")
var MDRMeshUtils = preload("res://addons/mesh_data_resource_editor/utilities/mdred_mesh_utils.gd")
enum EditMode {
EDIT_MODE_NONE = 0,
EDIT_MODE_TRANSLATE = 1,
EDIT_MODE_SCALE = 2,
EDIT_MODE_ROTATE = 3
}
enum AxisConstraint {
X = 1 << 0,
Y = 1 << 1,
Z = 1 << 2,
}
enum SelectionMode {
SELECTION_MODE_VERTEX = 0,
SELECTION_MODE_EDGE = 1,
SELECTION_MODE_FACE = 2,
}
enum PivotTypes {
PIVOT_TYPE_AVERAGED = 0,
PIVOT_TYPE_MDI_ORIGIN = 1,
PIVOT_TYPE_WORLD_ORIGIN = 2,
}
enum HandleSelectionType {
HANDLE_SELECTION_TYPE_FRONT = 0,
HANDLE_SELECTION_TYPE_BACK = 1,
HANDLE_SELECTION_TYPE_ALL = 2,
}
var gizmo_size = 3.0
var edit_mode : int = EditMode.EDIT_MODE_TRANSLATE
var pivot_type : int = PivotTypes.PIVOT_TYPE_AVERAGED
var axis_constraint : int = AxisConstraint.X | AxisConstraint.Y | AxisConstraint.Z
var selection_mode : int = SelectionMode.SELECTION_MODE_VERTEX
var handle_selection_type : int = HandleSelectionType.HANDLE_SELECTION_TYPE_FRONT
var visual_indicator_outline : bool = true
var visual_indicator_seam : bool= true
var visual_indicator_handle : bool = true
var previous_point : Vector2
var is_dragging : bool = false
var _last_known_camera_facing : Vector3 = Vector3(0, 0, -1)
var _rect_drag : bool = false
var _rect_drag_start_point : Vector2 = Vector2()
var _rect_drag_min_ofset : float = 10
var _mdr : MeshDataResource = null
var _vertices : PoolVector3Array
var _indices : PoolIntArray
var _handle_points : PoolVector3Array
var _handle_to_vertex_map : Array
var _selected_points : PoolIntArray
var _mesh_outline_generator
var _handle_drag_op : bool = false
var _drag_op_orig_verices : PoolVector3Array = PoolVector3Array()
var _editor_plugin : EditorPlugin = null
var _undo_redo : UndoRedo = null
func _init():
_mesh_outline_generator = MeshOutline.new()
func setup() -> void:
get_spatial_node().connect("mesh_data_resource_changed", self, "on_mesh_data_resource_changed")
on_mesh_data_resource_changed(get_spatial_node().mesh_data)
func set_editor_plugin(editor_plugin : EditorPlugin) -> void:
_editor_plugin = editor_plugin
_undo_redo = _editor_plugin.get_undo_redo()
func set_handle(index: int, camera: Camera, point: Vector2):
var relative : Vector2 = point - previous_point
if !is_dragging:
relative = Vector2()
is_dragging = true
_handle_drag_op = true
_drag_op_orig_verices = copy_mdr_verts_array()
if edit_mode == EditMode.EDIT_MODE_NONE:
return
elif edit_mode == EditMode.EDIT_MODE_TRANSLATE:
var ofs : Vector3 = Vector3()
if (axis_constraint & AxisConstraint.X) != 0:
ofs.x = relative.x * 0.001 * sign(camera.get_global_transform().basis.z.z)
if (axis_constraint & AxisConstraint.Y) != 0:
ofs.y = relative.y * -0.001
if (axis_constraint & AxisConstraint.Z) != 0:
ofs.z = relative.x * 0.001 * -sign(camera.get_global_transform().basis.z.x)
add_to_all_selected(ofs)
recalculate_handle_points()
apply()
redraw()
elif edit_mode == EditMode.EDIT_MODE_SCALE:
var r : float = 1.0 + ((relative.x + relative.y) * 0.05)
var vs : Vector3 = Vector3()
if (axis_constraint & AxisConstraint.X) != 0:
vs.x = r
if (axis_constraint & AxisConstraint.Y) != 0:
vs.y = r
if (axis_constraint & AxisConstraint.Z) != 0:
vs.z = r
var b : Basis = Basis().scaled(vs)
mul_all_selected_with_basis(b)
recalculate_handle_points()
apply()
redraw()
elif edit_mode == EditMode.EDIT_MODE_ROTATE:
print("MDR Editor: ROTATE NYI")
previous_point = point
func commit_handle(index: int, restore, cancel: bool = false) -> void:
previous_point = Vector2()
print("MDR Editor: commit_handle test")
func redraw():
clear()
if !_mdr:
return
if _mdr.array.size() != ArrayMesh.ARRAY_MAX:
return
if !get_plugin():
return
var handles_material : SpatialMaterial = get_plugin().get_material("handles", self)
var material = get_plugin().get_material("main", self)
var seam_material = get_plugin().get_material("seam", self)
_mesh_outline_generator.setup(_mdr)
if selection_mode == SelectionMode.SELECTION_MODE_EDGE:
_mesh_outline_generator.generate_mark_edges(visual_indicator_outline, visual_indicator_handle)
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
_mesh_outline_generator.generate_mark_faces(visual_indicator_outline, visual_indicator_handle)
else:
_mesh_outline_generator.generate(visual_indicator_outline, visual_indicator_handle)
if visual_indicator_outline || visual_indicator_handle:
add_lines(_mesh_outline_generator.lines, material, false)
if visual_indicator_seam:
add_lines(_mesh_outline_generator.seam_lines, seam_material, false)
if _selected_points.size() > 0:
var vs : PoolVector3Array = PoolVector3Array()
for i in _selected_points:
vs.append(_handle_points[i])
add_handles(vs, handles_material)
func apply() -> void:
if !_mdr:
return
disable_change_event()
var arrs : Array = _mdr.array
arrs[ArrayMesh.ARRAY_VERTEX] = _vertices
arrs[ArrayMesh.ARRAY_INDEX] = _indices
_mdr.array = arrs
enable_change_event()
func select_all() -> void:
if _selected_points.size() == _handle_points.size():
return
_selected_points.resize(_handle_points.size())
for i in range(_selected_points.size()):
_selected_points[i] = i
redraw()
func selection_click(index, camera, event) -> bool:
if handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_FRONT:
return selection_click_select_front_or_back(index, camera, event)
elif handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_BACK:
return selection_click_select_front_or_back(index, camera, event)
else:
return selection_click_select_through(index, camera, event)
return false
func is_point_visible(point_orig : Vector3, camera_pos : Vector3, gt : Transform) -> bool:
var point : Vector3 = gt.xform(point_orig)
# go from the given point to the origin (camera_pos -> camera)
var dir : Vector3 = camera_pos - point
dir = dir.normalized()
# Might need to reduce z fighting
#point += dir * 0.5
for i in range(0, _indices.size(), 3):
var i0 : int = _indices[i]
var i1 : int = _indices[i + 1]
var i2 : int = _indices[i + 2]
var v0 : Vector3 = _vertices[i0]
var v1 : Vector3 = _vertices[i1]
var v2 : Vector3 = _vertices[i2]
v0 = gt.xform(v0)
v1 = gt.xform(v1)
v2 = gt.xform(v2)
var res = Geometry.ray_intersects_triangle(point, dir, v0, v1, v2)
if res is Vector3:
return false
return true
func selection_click_select_front_or_back(index, camera, event):
var gt : Transform = get_spatial_node().global_transform
var ray_from : Vector3 = camera.global_transform.origin
var gpoint : Vector2 = event.get_position()
var grab_threshold : float = 8
# select vertex
var closest_idx : int = -1
var closest_dist : float = 1e10
for i in range(_handle_points.size()):
var vert_pos_3d : Vector3 = gt.xform(_handle_points[i])
var vert_pos_2d : Vector2 = camera.unproject_position(vert_pos_3d)
var dist_3d : float = ray_from.distance_to(vert_pos_3d)
var dist_2d : float = gpoint.distance_to(vert_pos_2d)
if (dist_2d < grab_threshold && dist_3d < closest_dist):
var point_visible : bool = is_point_visible(_handle_points[i], ray_from, gt)
if handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_FRONT:
if !point_visible:
continue
elif handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_BACK:
if point_visible:
continue
closest_dist = dist_3d
closest_idx = i
if (closest_idx >= 0):
for si in range(_selected_points.size()):
if _selected_points[si] == closest_idx:
if event.alt || event.control:
_selected_points.remove(si)
return true
return false
if event.alt || event.control:
return false
if event.shift:
_selected_points.append(closest_idx)
else:
# Select new point only
_selected_points.resize(0)
_selected_points.append(closest_idx)
apply()
redraw()
else:
# Don't unselect all if either control or shift is held down
if event.shift || event.control || event.alt:
return false
if _selected_points.size() == 0:
return false
#Unselect all
_selected_points.resize(0)
redraw()
func selection_click_select_through(index, camera, event):
var gt : Transform = get_spatial_node().global_transform
var ray_from : Vector3 = camera.global_transform.origin
var gpoint : Vector2 = event.get_position()
var grab_threshold : float = 8
# select vertex
var closest_idx : int = -1
var closest_dist : float = 1e10
for i in range(_handle_points.size()):
var vert_pos_3d : Vector3 = gt.xform(_handle_points[i])
var vert_pos_2d : Vector2 = camera.unproject_position(vert_pos_3d)
var dist_3d : float = ray_from.distance_to(vert_pos_3d)
var dist_2d : float = gpoint.distance_to(vert_pos_2d)
if (dist_2d < grab_threshold && dist_3d < closest_dist):
closest_dist = dist_3d
closest_idx = i
if (closest_idx >= 0):
for si in range(_selected_points.size()):
if _selected_points[si] == closest_idx:
if event.alt || event.control:
_selected_points.remove(si)
return true
return false
if event.alt || event.control:
return false
if event.shift:
_selected_points.append(closest_idx)
else:
# Select new point only
_selected_points.resize(0)
_selected_points.append(closest_idx)
apply()
redraw()
else:
# Don't unselect all if either control or shift is held down
if event.shift || event.control || event.alt:
return false
if _selected_points.size() == 0:
return false
#Unselect all
_selected_points.resize(0)
redraw()
func selection_drag(index, camera, event) -> void:
if handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_FRONT:
selection_drag_rect_select_front_back(index, camera, event)
elif handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_BACK:
selection_drag_rect_select_front_back(index, camera, event)
else:
selection_drag_rect_select_through(index, camera, event)
func selection_drag_rect_select_front_back(index, camera, event):
var gt : Transform = get_spatial_node().global_transform
var ray_from : Vector3 = camera.global_transform.origin
var mouse_pos : Vector2 = event.get_position()
var rect_size : Vector2 = _rect_drag_start_point - mouse_pos
rect_size.x = abs(rect_size.x)
rect_size.y = abs(rect_size.y)
var rect : Rect2 = Rect2(_rect_drag_start_point, rect_size)
# This is needed so selection works even when you drag from bottom to top, and from right to left
var rect_ofs : Vector2 = _rect_drag_start_point - mouse_pos
if rect_ofs.x > 0:
rect.position.x -= rect_ofs.x
if rect_ofs.y > 0:
rect.position.y -= rect_ofs.y
var selected : PoolIntArray = PoolIntArray()
for i in range(_handle_points.size()):
var vert_pos_3d : Vector3 = gt.xform(_handle_points[i])
var vert_pos_2d : Vector2 = camera.unproject_position(vert_pos_3d)
if rect.has_point(vert_pos_2d):
var point_visible : bool = is_point_visible(_handle_points[i], ray_from, gt)
if handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_FRONT:
if !point_visible:
continue
elif handle_selection_type == HandleSelectionType.HANDLE_SELECTION_TYPE_BACK:
if point_visible:
continue
selected.push_back(i)
if event.alt || event.control:
for isel in selected:
for i in range(_selected_points.size()):
if _selected_points[i] == isel:
_selected_points.remove(i)
break
redraw()
return
if event.shift:
for isel in selected:
if !pool_int_arr_contains(_selected_points, isel):
_selected_points.push_back(isel)
redraw()
return
_selected_points.resize(0)
_selected_points.append_array(selected)
redraw()
func selection_drag_rect_select_through(index, camera, event):
var gt : Transform = get_spatial_node().global_transform
var mouse_pos : Vector2 = event.get_position()
var rect_size : Vector2 = _rect_drag_start_point - mouse_pos
rect_size.x = abs(rect_size.x)
rect_size.y = abs(rect_size.y)
var rect : Rect2 = Rect2(_rect_drag_start_point, rect_size)
# This is needed so selection works even when you drag from bottom to top, and from right to left
var rect_ofs : Vector2 = _rect_drag_start_point - mouse_pos
if rect_ofs.x > 0:
rect.position.x -= rect_ofs.x
if rect_ofs.y > 0:
rect.position.y -= rect_ofs.y
var selected : PoolIntArray = PoolIntArray()
for i in range(_handle_points.size()):
var vert_pos_3d : Vector3 = gt.xform(_handle_points[i])
var vert_pos_2d : Vector2 = camera.unproject_position(vert_pos_3d)
if rect.has_point(vert_pos_2d):
selected.push_back(i)
if event.alt || event.control:
for isel in selected:
for i in range(_selected_points.size()):
if _selected_points[i] == isel:
_selected_points.remove(i)
break
redraw()
return
if event.shift:
for isel in selected:
if !pool_int_arr_contains(_selected_points, isel):
_selected_points.push_back(isel)
redraw()
return
_selected_points.resize(0)
_selected_points.append_array(selected)
redraw()
func forward_spatial_gui_input(index, camera, event):
_last_known_camera_facing = camera.transform.basis.xform(Vector3(0, 0, -1))
if event is InputEventMouseButton:
if event.get_button_index() == BUTTON_LEFT:
if !event.is_pressed():
# If a handle was being dragged only run these
is_dragging = false
if _handle_drag_op && _mdr && _mdr.array.size() == ArrayMesh.ARRAY_MAX && _mdr.array[ArrayMesh.ARRAY_VERTEX] != null && _mdr.array[ArrayMesh.ARRAY_VERTEX].size() == _drag_op_orig_verices.size():
_undo_redo.create_action("Drag")
_undo_redo.add_do_method(self, "apply_vertex_array", _mdr, _mdr.array[ArrayMesh.ARRAY_VERTEX])
_undo_redo.add_undo_method(self, "apply_vertex_array", _mdr, _drag_op_orig_verices)
_undo_redo.commit_action()
if _handle_drag_op:
_handle_drag_op = false
return true
# See whether we should check for a click or a selection box
var mouse_pos : Vector2 = event.get_position()
var rect_size : Vector2 = _rect_drag_start_point - mouse_pos
rect_size.x = abs(rect_size.x)
rect_size.y = abs(rect_size.y)
var had_rect_drag : bool = false
if rect_size.x > _rect_drag_min_ofset || rect_size.y > _rect_drag_min_ofset:
had_rect_drag = true
if !had_rect_drag:
return selection_click(index, camera, event)
else:
selection_drag(index, camera, event)
# Always return false here, so the drag rect thing disappears in the editor
return false
else:
# event is pressed
_rect_drag = true
_rect_drag_start_point = event.get_position()
# elif event is InputEventMouseMotion:
# if edit_mode == EditMode.EDIT_MODE_NONE:
# return false
# elif edit_mode == EditMode.EDIT_MODE_TRANSLATE:
# for i in selected_indices:
# var v : Vector3 = vertices[i]
#
# if axis_constraint == AxisConstraint.X:
# v.x += event.relative.x * -0.001
# elif axis_constraint == AxisConstraint.Y:
# v.y += event.relative.y * 0.001
# elif axis_constraint == AxisConstraint.Z:
# v.z += event.relative.x * 0.001
#
# vertices.set(i, v)
#
# redraw()
# elif edit_mode == EditMode.EDIT_MODE_SCALE:
# print("SCALE")
# elif edit_mode == EditMode.EDIT_MODE_ROTATE:
# print("ROTATE")
return false
func add_to_all_selected(ofs : Vector3) -> void:
for i in _selected_points:
var v : Vector3 = _handle_points[i]
v += ofs
_handle_points.set(i, v)
for i in _selected_points:
var ps : PoolIntArray = _handle_to_vertex_map[i]
for j in ps:
var v : Vector3 = _vertices[j]
v += ofs
_vertices.set(j, v)
func mul_all_selected_with_basis(b : Basis) -> void:
for i in _selected_points:
var v : Vector3 = _handle_points[i]
v = b * v
_handle_points.set(i, v)
for i in _selected_points:
var ps : PoolIntArray = _handle_to_vertex_map[i]
for j in ps:
var v : Vector3 = _vertices[j]
v = b * v
_vertices.set(j, v)
func set_translate() -> void:
edit_mode = EditMode.EDIT_MODE_TRANSLATE
func set_scale() -> void:
edit_mode = EditMode.EDIT_MODE_SCALE
func set_rotate() -> void:
edit_mode = EditMode.EDIT_MODE_ROTATE
func set_edit_mode(em : int) -> void:
edit_mode = em
func set_axis_x(on : bool) -> void:
if on:
axis_constraint |= AxisConstraint.X
else:
if (axis_constraint & AxisConstraint.X) != 0:
axis_constraint ^= AxisConstraint.X
func set_axis_y(on : bool) -> void:
if on:
axis_constraint |= AxisConstraint.Y
else:
if (axis_constraint & AxisConstraint.Y) != 0:
axis_constraint ^= AxisConstraint.Y
func set_axis_z(on : bool) -> void:
if on:
axis_constraint |= AxisConstraint.Z
else:
if (axis_constraint & AxisConstraint.Z) != 0:
axis_constraint ^= AxisConstraint.Z
func set_selection_mode_vertex() -> void:
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
return
selection_mode = SelectionMode.SELECTION_MODE_VERTEX
_selected_points.resize(0)
recalculate_handle_points()
redraw()
func set_selection_mode_edge() -> void:
if selection_mode == SelectionMode.SELECTION_MODE_EDGE:
return
selection_mode = SelectionMode.SELECTION_MODE_EDGE
_selected_points.resize(0)
recalculate_handle_points()
redraw()
func set_selection_mode_face() -> void:
if selection_mode == SelectionMode.SELECTION_MODE_FACE:
return
selection_mode = SelectionMode.SELECTION_MODE_FACE
_selected_points.resize(0)
recalculate_handle_points()
redraw()
func _notification(what):
if what == NOTIFICATION_PREDELETE:
if self != null && get_plugin():
get_plugin().unregister_gizmo(self)
func recalculate_handle_points() -> void:
if !_mdr:
_handle_points.resize(0)
_handle_to_vertex_map.resize(0)
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
_handle_points.resize(0)
_handle_to_vertex_map.resize(0)
return
var arr : Array = Array()
arr.resize(ArrayMesh.ARRAY_MAX)
arr[ArrayMesh.ARRAY_VERTEX] = mdr_arr[ArrayMesh.ARRAY_VERTEX]
arr[ArrayMesh.ARRAY_INDEX] = mdr_arr[ArrayMesh.ARRAY_INDEX]
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
var merged_arrays : Array = MeshUtils.merge_mesh_array(arr)
_handle_points = merged_arrays[ArrayMesh.ARRAY_VERTEX]
_handle_to_vertex_map = MeshDecompose.get_handle_vertex_to_vertex_map(mdr_arr, _handle_points)
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
var result : Array = MeshDecompose.get_handle_edge_to_vertex_map(arr)
_handle_points = result[0]
_handle_to_vertex_map = result[1]
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
var result : Array = MeshDecompose.get_handle_face_to_vertex_map(arr)
_handle_points = result[0]
_handle_to_vertex_map = result[1]
func on_mesh_data_resource_changed(mdr : MeshDataResource) -> void:
if _mdr:
_mdr.disconnect("changed", self, "on_mdr_changed")
_mdr = mdr
if _mdr && _mdr.array.size() == ArrayMesh.ARRAY_MAX && _mdr.array[ArrayMesh.ARRAY_VERTEX] != null:
_vertices = _mdr.array[ArrayMesh.ARRAY_VERTEX]
_indices = _mdr.array[ArrayMesh.ARRAY_INDEX]
else:
_vertices.resize(0)
_indices.resize(0)
if _mdr:
_mdr.connect("changed", self, "on_mdr_changed")
recalculate_handle_points()
redraw()
func on_mdr_changed() -> void:
if _mdr && _mdr.array.size() == ArrayMesh.ARRAY_MAX && _mdr.array[ArrayMesh.ARRAY_VERTEX] != null:
_vertices = _mdr.array[ArrayMesh.ARRAY_VERTEX]
_indices = _mdr.array[ArrayMesh.ARRAY_INDEX]
else:
_vertices.resize(0)
_indices.resize(0)
recalculate_handle_points()
redraw()
func disable_change_event() -> void:
_mdr.disconnect("changed", self, "on_mdr_changed")
func enable_change_event(update : bool = true) -> void:
_mdr.connect("changed", self, "on_mdr_changed")
if update:
on_mdr_changed()
func add_triangle() -> void:
if _mdr:
var orig_arr = copy_arrays(_mdr.array)
MDRMeshUtils.add_triangle(_mdr)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Add Triangle")
func add_quad() -> void:
if _mdr:
var orig_arr = copy_arrays(_mdr.array)
MDRMeshUtils.add_quad(_mdr)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Add Quad")
func is_verts_equal(v0 : Vector3, v1 : Vector3) -> bool:
return is_equal_approx(v0.x, v1.x) && is_equal_approx(v0.y, v1.y) && is_equal_approx(v0.z, v1.z)
func find_other_vertex_for_edge(edge : int, v0 : Vector3) -> Vector3:
var ps : PoolIntArray = _handle_to_vertex_map[edge]
var vert : Vector3 = Vector3()
for i in range(ps.size()):
vert = _vertices[ps[i]]
if !is_verts_equal(v0, vert):
return vert
return v0
func split_edge_indices(edge : int) -> Array:
var ps : PoolIntArray = _handle_to_vertex_map[edge]
if ps.size() == 0:
return [ ]
var v0 : Vector3 = _vertices[ps[0]]
var v0ei : PoolIntArray = PoolIntArray()
v0ei.append(ps[0])
var v1ei : PoolIntArray = PoolIntArray()
for i in range(1, ps.size()):
var vert : Vector3 = _vertices[ps[i]]
if is_verts_equal(v0, vert):
v0ei.append(ps[i])
else:
v1ei.append(ps[i])
return [ v0ei, v1ei ]
func pool_int_arr_contains(arr : PoolIntArray, val : int) -> bool:
for a in arr:
if a == val:
return true
return false
func find_triangles_for_edge(edge : int) -> PoolIntArray:
var eisarr : Array = split_edge_indices(edge)
if eisarr.size() == 0:
return PoolIntArray()
# these should have the same size
var v0ei : PoolIntArray = eisarr[0]
var v1ei : PoolIntArray = eisarr[1]
var res : PoolIntArray = PoolIntArray()
for i in range(0, _indices.size(), 3):
var i0 : int = _indices[i]
var i1 : int = _indices[i + 1]
var i2 : int = _indices[i + 2]
if pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2):
if pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2):
res.append(i / 3)
return res
func find_first_triangle_for_edge(edge : int) -> int:
var eisarr : Array = split_edge_indices(edge)
if eisarr.size() == 0:
return -1
# these should have the same size
var v0ei : PoolIntArray = eisarr[0]
var v1ei : PoolIntArray = eisarr[1]
var res : PoolIntArray = PoolIntArray()
for i in range(0, _indices.size(), 3):
var i0 : int = _indices[i]
var i1 : int = _indices[i + 1]
var i2 : int = _indices[i + 2]
if pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2):
if pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2):
return i / 3
return -1
func add_triangle_to_edge(edge : int) -> void:
var triangle_index : int = find_first_triangle_for_edge(edge)
var inds : int = triangle_index * 3
var ti0 : int = _indices[inds]
var ti1 : int = _indices[inds + 1]
var ti2 : int = _indices[inds + 2]
var ps : PoolIntArray = _handle_to_vertex_map[edge]
if ps.size() == 0:
return
var ei0 : int = 0
var ei1 : int = 0
var erefind : int = 0
if !pool_int_arr_contains(ps, ti0):
ei0 = ti1
ei1 = ti2
erefind = ti0
elif !pool_int_arr_contains(ps, ti1):
ei0 = ti0
ei1 = ti2
erefind = ti1
elif !pool_int_arr_contains(ps, ti2):
ei0 = ti0
ei1 = ti1
erefind = ti2
var fo : Vector3 = MDRMeshUtils.get_face_normal(_vertices[ti0], _vertices[ti1], _vertices[ti2])
var fn : Vector3 = MDRMeshUtils.get_face_normal(_vertices[ei0], _vertices[ei1], _vertices[erefind])
if fo.dot(fn) < 0:
var t : int = ei0
ei0 = ei1
ei1 = t
MDRMeshUtils.append_triangle_to_tri_edge(_mdr, _vertices[ei0], _vertices[ei1], _vertices[erefind])
func add_quad_to_edge(edge : int) -> void:
var triangle_index : int = find_first_triangle_for_edge(edge)
var inds : int = triangle_index * 3
var ti0 : int = _indices[inds]
var ti1 : int = _indices[inds + 1]
var ti2 : int = _indices[inds + 2]
var ps : PoolIntArray = _handle_to_vertex_map[edge]
if ps.size() == 0:
return
var ei0 : int = 0
var ei1 : int = 0
var erefind : int = 0
if !pool_int_arr_contains(ps, ti0):
ei0 = ti1
ei1 = ti2
erefind = ti0
elif !pool_int_arr_contains(ps, ti1):
ei0 = ti0
ei1 = ti2
erefind = ti1
elif !pool_int_arr_contains(ps, ti2):
ei0 = ti0
ei1 = ti1
erefind = ti2
var fo : Vector3 = MDRMeshUtils.get_face_normal(_vertices[ti0], _vertices[ti1], _vertices[ti2])
var fn : Vector3 = MDRMeshUtils.get_face_normal(_vertices[ei0], _vertices[ei1], _vertices[erefind])
if fo.dot(fn) < 0:
var t : int = ei0
ei0 = ei1
ei1 = t
MDRMeshUtils.append_quad_to_tri_edge(_mdr, _vertices[ei0], _vertices[ei1], _vertices[erefind])
func add_triangle_at() -> void:
if !_mdr:
return
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
#todo
pass
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
for sp in _selected_points:
add_triangle_to_edge(sp)
_selected_points.resize(0)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Add Triangle At")
enable_change_event()
else:
add_triangle()
func add_quad_at() -> void:
if !_mdr:
return
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
#todo
pass
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
for sp in _selected_points:
add_quad_to_edge(sp)
_selected_points.resize(0)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Add Quad At")
enable_change_event()
else:
add_triangle()
func add_box() -> void:
if _mdr:
var orig_arr = copy_arrays(_mdr.array)
MDRMeshUtils.add_box(_mdr)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Add Box")
func split():
pass
func disconnect_action():
pass
func create_face():
if !_mdr:
return
if _selected_points.size() <= 2:
return
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var points : PoolVector3Array = PoolVector3Array()
for sp in _selected_points:
points.push_back(_handle_points[sp])
MDRMeshUtils.add_triangulated_mesh_from_points(_mdr, points, _last_known_camera_facing)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Create Face")
_selected_points.resize(0)
enable_change_event()
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
pass
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
pass
func split_face_indices(face : int) -> Array:
var ps : PoolIntArray = _handle_to_vertex_map[face]
if ps.size() == 0:
return [ ]
var v0 : Vector3 = _vertices[ps[0]]
var v1 : Vector3 = Vector3()
var v1found : bool = false
var v0ei : PoolIntArray = PoolIntArray()
v0ei.append(ps[0])
var v1ei : PoolIntArray = PoolIntArray()
var v2ei : PoolIntArray = PoolIntArray()
for i in range(1, ps.size()):
var vert : Vector3 = _vertices[ps[i]]
if is_verts_equal(v0, vert):
v0ei.append(ps[i])
else:
if v1found:
if is_verts_equal(v1, vert):
v1ei.append(ps[i])
else:
v2ei.append(ps[i])
else:
v1found = true
v1 = _vertices[ps[i]]
v1ei.append(ps[i])
return [ v0ei, v1ei, v2ei ]
func find_first_triangle_index_for_face(face : int) -> int:
var split_indices_arr : Array = split_face_indices(face)
if split_indices_arr.size() == 0:
return -1
var v0ei : PoolIntArray = split_indices_arr[0]
var v1ei : PoolIntArray = split_indices_arr[1]
var v2ei : PoolIntArray = split_indices_arr[2]
var tri_index : int = -1
for i in range(0, _indices.size(), 3):
var i0 : int = _indices[i]
var i1 : int = _indices[i + 1]
var i2 : int = _indices[i + 2]
if pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2):
if pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2):
if pool_int_arr_contains(v2ei, i0) || pool_int_arr_contains(v2ei, i1) || pool_int_arr_contains(v2ei, i2):
return i / 3
return -1
func delete_selected() -> void:
if !_mdr:
return
if _selected_points.size() == 0:
return
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
#todo
pass
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
#todo
pass
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
for sp in _selected_points:
var triangle_index : int = find_first_triangle_index_for_face(sp)
MDRMeshUtils.remove_triangle(_mdr, triangle_index)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Delete")
_selected_points.resize(0)
enable_change_event()
func generate_normals():
if !_mdr:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var orig_seams = copy_pool_int_array(_mdr.seams)
var seam_points : PoolVector3Array = MDRMeshUtils.seams_to_points(_mdr)
MDRMeshUtils.generate_normals(_mdr)
MDRMeshUtils.points_to_seams(_mdr, seam_points)
add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr.array, _mdr.seams, "Generate Normals")
enable_change_event()
func generate_tangents():
if !_mdr:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var orig_seams = copy_pool_int_array(_mdr.seams)
var seam_points : PoolVector3Array = MDRMeshUtils.seams_to_points(_mdr)
MDRMeshUtils.generate_tangents(_mdr)
MDRMeshUtils.points_to_seams(_mdr, seam_points)
add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr.array, _mdr.seams, "Generate Tangents")
enable_change_event()
func remove_doubles():
if !_mdr:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var orig_seams = copy_pool_int_array(_mdr.seams)
var seam_points : PoolVector3Array = MDRMeshUtils.seams_to_points(_mdr)
var merged_arrays : Array = MeshUtils.remove_doubles(mdr_arr)
_mdr.array = merged_arrays
MDRMeshUtils.points_to_seams(_mdr, seam_points)
add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr.array, _mdr.seams, "Remove Doubles")
enable_change_event()
func merge_optimize():
if !_mdr:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var orig_seams = copy_pool_int_array(_mdr.seams)
var seam_points : PoolVector3Array = MDRMeshUtils.seams_to_points(_mdr)
var merged_arrays : Array = MeshUtils.merge_mesh_array(mdr_arr)
_mdr.array = merged_arrays
MDRMeshUtils.points_to_seams(_mdr, seam_points)
add_mesh_seam_change_undo_redo(orig_arr, orig_seams, _mdr.array, _mdr.seams, "Merge Optimize")
enable_change_event()
func connect_to_first_selected():
if !_mdr:
return
if _selected_points.size() < 2:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var vertices : PoolVector3Array = mdr_arr[ArrayMesh.ARRAY_VERTEX]
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
var mpos : Vector3 = _handle_points[_selected_points[0]]
for i in range(1, _selected_points.size()):
var ps : PoolIntArray = _handle_to_vertex_map[_selected_points[i]]
for indx in ps:
vertices[indx] = mpos
_selected_points.resize(0)
mdr_arr[ArrayMesh.ARRAY_VERTEX] = vertices
_mdr.array = mdr_arr
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Connect to first selected")
enable_change_event()
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
pass
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
pass
func connect_to_avg():
if !_mdr:
return
if _selected_points.size() < 2:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var orig_arr = copy_arrays(_mdr.array)
var vertices : PoolVector3Array = mdr_arr[ArrayMesh.ARRAY_VERTEX]
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
var mpos : Vector3 = Vector3()
for sp in _selected_points:
mpos += _handle_points[sp]
mpos /= _selected_points.size()
for i in range(_selected_points.size()):
var ps : PoolIntArray = _handle_to_vertex_map[_selected_points[i]]
for indx in ps:
vertices[indx] = mpos
_selected_points.resize(0)
mdr_arr[ArrayMesh.ARRAY_VERTEX] = vertices
_mdr.array = mdr_arr
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Connect to average")
enable_change_event()
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
pass
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
pass
func connect_to_last_selected():
if !_mdr:
return
if _selected_points.size() < 2:
return
var orig_arr = copy_arrays(_mdr.array)
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var vertices : PoolVector3Array = mdr_arr[ArrayMesh.ARRAY_VERTEX]
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
var mpos : Vector3 = _handle_points[_selected_points[_selected_points.size() - 1]]
for i in range(0, _selected_points.size() - 1):
var ps : PoolIntArray = _handle_to_vertex_map[_selected_points[i]]
for indx in ps:
vertices[indx] = mpos
_selected_points.resize(0)
mdr_arr[ArrayMesh.ARRAY_VERTEX] = vertices
_mdr.array = mdr_arr
add_mesh_change_undo_redo(orig_arr, _mdr.array, "Connect to last selected")
enable_change_event()
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
pass
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
pass
func get_first_index_pair_for_edge(edge : int) -> PoolIntArray:
var ret : PoolIntArray = PoolIntArray()
var eisarr : Array = split_edge_indices(edge)
if eisarr.size() == 0:
return ret
# these should have the same size
var v0ei : PoolIntArray = eisarr[0]
var v1ei : PoolIntArray = eisarr[1]
var res : PoolIntArray = PoolIntArray()
for i in range(0, _indices.size(), 3):
var i0 : int = _indices[i]
var i1 : int = _indices[i + 1]
var i2 : int = _indices[i + 2]
if pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2):
if pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2):
if pool_int_arr_contains(v0ei, i0):
ret.push_back(i0)
elif pool_int_arr_contains(v0ei, i1):
ret.push_back(i1)
elif pool_int_arr_contains(v0ei, i2):
ret.push_back(i2)
if pool_int_arr_contains(v1ei, i0):
ret.push_back(i0)
elif pool_int_arr_contains(v1ei, i1):
ret.push_back(i1)
elif pool_int_arr_contains(v1ei, i2):
ret.push_back(i2)
return ret
return ret
func get_all_index_pairs_for_edge(edge : int) -> PoolIntArray:
var ret : PoolIntArray = PoolIntArray()
var eisarr : Array = split_edge_indices(edge)
if eisarr.size() == 0:
return ret
# these should have the same size
var v0ei : PoolIntArray = eisarr[0]
var v1ei : PoolIntArray = eisarr[1]
var res : PoolIntArray = PoolIntArray()
for i in range(0, _indices.size(), 3):
var i0 : int = _indices[i]
var i1 : int = _indices[i + 1]
var i2 : int = _indices[i + 2]
if pool_int_arr_contains(v0ei, i0) || pool_int_arr_contains(v0ei, i1) || pool_int_arr_contains(v0ei, i2):
if pool_int_arr_contains(v1ei, i0) || pool_int_arr_contains(v1ei, i1) || pool_int_arr_contains(v1ei, i2):
if pool_int_arr_contains(v0ei, i0):
ret.push_back(i0)
elif pool_int_arr_contains(v0ei, i1):
ret.push_back(i1)
elif pool_int_arr_contains(v0ei, i2):
ret.push_back(i2)
if pool_int_arr_contains(v1ei, i0):
ret.push_back(i0)
elif pool_int_arr_contains(v1ei, i1):
ret.push_back(i1)
elif pool_int_arr_contains(v1ei, i2):
ret.push_back(i2)
return ret
func mark_seam():
if !_mdr:
return
if _selected_points.size() == 0:
return
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
pass
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
disable_change_event()
var prev_seams : PoolIntArray = copy_pool_int_array(_mdr.seams)
for se in _selected_points:
var eis : PoolIntArray = MDRMeshUtils.order_seam_indices(get_first_index_pair_for_edge(se))
if eis.size() == 0:
continue
MDRMeshUtils.add_seam(_mdr, eis[0], eis[1])
_undo_redo.create_action("mark_seam")
_undo_redo.add_do_method(self, "set_seam", _mdr, copy_pool_int_array(_mdr.seams))
_undo_redo.add_undo_method(self, "set_seam", _mdr, prev_seams)
_undo_redo.commit_action()
enable_change_event()
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
pass
func unmark_seam():
if !_mdr:
return
if _selected_points.size() == 0:
return
if selection_mode == SelectionMode.SELECTION_MODE_VERTEX:
pass
elif selection_mode == SelectionMode.SELECTION_MODE_EDGE:
disable_change_event()
var prev_seams : PoolIntArray = copy_pool_int_array(_mdr.seams)
for se in _selected_points:
var eis : PoolIntArray = MDRMeshUtils.order_seam_indices(get_all_index_pairs_for_edge(se))
if eis.size() == 0:
continue
MDRMeshUtils.remove_seam(_mdr, eis[0], eis[1])
_undo_redo.create_action("mark_seam")
_undo_redo.add_do_method(self, "set_seam", _mdr, copy_pool_int_array(_mdr.seams))
_undo_redo.add_undo_method(self, "set_seam", _mdr, prev_seams)
_undo_redo.commit_action()
enable_change_event()
elif selection_mode == SelectionMode.SELECTION_MODE_FACE:
pass
func set_seam(mdr : MeshDataResource, arr : PoolIntArray) -> void:
mdr.seams = arr
func apply_seam():
if !_mdr:
return
disable_change_event()
var orig_arr : Array = copy_arrays(_mdr.array)
MDRMeshUtils.apply_seam(_mdr)
add_mesh_change_undo_redo(orig_arr, _mdr.array, "apply_seam")
enable_change_event()
func uv_unwrap() -> void:
if !_mdr:
return
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return
disable_change_event()
var uvs : PoolVector2Array = MeshUtils.uv_unwrap(mdr_arr)
if uvs.size() != mdr_arr[ArrayMesh.ARRAY_VERTEX].size():
print("Error: Could not unwrap mesh!")
return
var orig_arr : Array = copy_arrays(mdr_arr)
mdr_arr[ArrayMesh.ARRAY_TEX_UV] = uvs
add_mesh_change_undo_redo(orig_arr, mdr_arr, "uv_unwrap")
enable_change_event()
func add_mesh_change_undo_redo(orig_arr : Array, new_arr : Array, action_name : String) -> void:
_undo_redo.create_action(action_name)
var nac : Array = copy_arrays(new_arr)
_undo_redo.add_do_method(self, "apply_mesh_change", _mdr, nac)
_undo_redo.add_undo_method(self, "apply_mesh_change", _mdr, orig_arr)
_undo_redo.commit_action()
func add_mesh_seam_change_undo_redo(orig_arr : Array, orig_seams : PoolIntArray, new_arr : Array, new_seams : PoolIntArray, action_name : String) -> void:
_undo_redo.create_action(action_name)
var nac : Array = copy_arrays(new_arr)
_undo_redo.add_do_method(self, "apply_mesh_change", _mdr, nac)
_undo_redo.add_undo_method(self, "apply_mesh_change", _mdr, orig_arr)
_undo_redo.add_do_method(self, "set_seam", _mdr, copy_pool_int_array(new_seams))
_undo_redo.add_undo_method(self, "set_seam", _mdr, orig_seams)
_undo_redo.commit_action()
func apply_mesh_change(mdr : MeshDataResource, arr : Array) -> void:
if !mdr:
return
mdr.array = copy_arrays(arr)
func apply_vertex_array(mdr : MeshDataResource, verts : PoolVector3Array) -> void:
if !mdr:
return
var mdr_arr : Array = mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX:
return
mdr_arr[ArrayMesh.ARRAY_VERTEX] = verts
mdr.array = mdr_arr
func copy_arrays(arr : Array) -> Array:
return arr.duplicate(true)
func copy_pool_int_array(pia : PoolIntArray) -> PoolIntArray:
var ret : PoolIntArray = PoolIntArray()
ret.resize(pia.size())
for i in range(pia.size()):
ret[i] = pia[i]
return ret
func copy_mdr_verts_array() -> PoolVector3Array:
var ret : PoolVector3Array = PoolVector3Array()
if !_mdr:
return ret
var mdr_arr : Array = _mdr.array
if mdr_arr.size() != ArrayMesh.ARRAY_MAX || mdr_arr[ArrayMesh.ARRAY_VERTEX] == null || mdr_arr[ArrayMesh.ARRAY_VERTEX].size() == 0:
return ret
var vertices : PoolVector3Array = mdr_arr[ArrayMesh.ARRAY_VERTEX]
ret.append_array(vertices)
return ret
func set_pivot_averaged():
pivot_type = PivotTypes.PIVOT_TYPE_AVERAGED
func set_pivot_mdi_origin():
pivot_type = PivotTypes.PIVOT_TYPE_MDI_ORIGIN
func set_pivot_world_origin():
pivot_type = PivotTypes.PIVOT_TYPE_WORLD_ORIGIN
func transfer_state_from(other) -> void:
edit_mode = other.edit_mode
pivot_type = other.pivot_type
axis_constraint = other.axis_constraint
selection_mode = other.selection_mode
handle_selection_type = other.handle_selection_type
visual_indicator_outline = other.visual_indicator_outline
visual_indicator_seam = other.visual_indicator_seam
visual_indicator_handle = other.visual_indicator_handle
func visual_indicator_outline_set(on : bool):
visual_indicator_outline = on
redraw()
func visual_indicator_seam_set(on : bool):
visual_indicator_seam = on
redraw()
func visual_indicator_handle_set(on : bool):
visual_indicator_handle = on
redraw()
func handle_selection_type_front():
handle_selection_type = HandleSelectionType.HANDLE_SELECTION_TYPE_FRONT
func handle_selection_type_back():
handle_selection_type = HandleSelectionType.HANDLE_SELECTION_TYPE_BACK
func handle_selection_type_all():
handle_selection_type = HandleSelectionType.HANDLE_SELECTION_TYPE_ALL