|
|
@@ -0,0 +1,147 @@ |
|
1 |
+ |
@tool |
|
2 |
+ |
extends EditorNode3DGizmoPlugin |
|
3 |
+ |
|
|
4 |
+ |
const ONE_VEC = Vector3(1.0, 1.0, 1.0) |
|
5 |
+ |
|
|
6 |
+ |
var undo_redo: EditorUndoRedoManager |
|
7 |
+ |
|
|
8 |
+ |
var handles: PackedVector3Array = [ |
|
9 |
+ |
Vector3.UP, |
|
10 |
+ |
-Vector3.FORWARD, |
|
11 |
+ |
Vector3.RIGHT, |
|
12 |
+ |
|
|
13 |
+ |
-Vector3.UP, |
|
14 |
+ |
Vector3.FORWARD, |
|
15 |
+ |
-Vector3.RIGHT |
|
16 |
+ |
] |
|
17 |
+ |
|
|
18 |
+ |
var directions: Array[String] = [ |
|
19 |
+ |
"+Y", "+Z", "+X", "-Y", "-Z", "-X" |
|
20 |
+ |
] |
|
21 |
+ |
|
|
22 |
+ |
var first_pos: Variant |
|
23 |
+ |
var first_scale: Vector3 |
|
24 |
+ |
var first_position: Vector3 |
|
25 |
+ |
var first_transform: Transform3D |
|
26 |
+ |
|
|
27 |
+ |
func _has_gizmo(node): |
|
28 |
+ |
return node is Node3D |
|
29 |
+ |
|
|
30 |
+ |
func _get_gizmo_name() -> String: |
|
31 |
+ |
return "Rodot gizmo" |
|
32 |
+ |
|
|
33 |
+ |
func _init(undo_redo: EditorUndoRedoManager): |
|
34 |
+ |
self.undo_redo = undo_redo |
|
35 |
+ |
create_handle_material("handles") |
|
36 |
+ |
|
|
37 |
+ |
func _redraw(gizmo): |
|
38 |
+ |
gizmo.clear() |
|
39 |
+ |
gizmo.add_handles(handles, get_material("handles", gizmo), []) |
|
40 |
+ |
|
|
41 |
+ |
func _get_handle_name(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> String: |
|
42 |
+ |
return "Resize " + directions[handle_id] |
|
43 |
+ |
|
|
44 |
+ |
func _get_handle_value(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> Variant: |
|
45 |
+ |
return handles[handle_id] |
|
46 |
+ |
|
|
47 |
+ |
func _begin_handle_action(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> void: |
|
48 |
+ |
var node = gizmo.get_node_3d() |
|
49 |
+ |
first_pos = null |
|
50 |
+ |
first_scale = node.scale |
|
51 |
+ |
first_position = node.position |
|
52 |
+ |
first_transform = Transform3D(node.transform) |
|
53 |
+ |
|
|
54 |
+ |
func _commit_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, restore: Variant, cancel: bool) -> void: |
|
55 |
+ |
var node = gizmo.get_node_3d() |
|
56 |
+ |
if cancel: |
|
57 |
+ |
node.scale = first_scale |
|
58 |
+ |
node.position = first_position |
|
59 |
+ |
return |
|
60 |
+ |
|
|
61 |
+ |
if node.scale.is_equal_approx(first_scale) and node.position.is_equal_approx(first_position): |
|
62 |
+ |
return |
|
63 |
+ |
|
|
64 |
+ |
undo_redo.create_action("Resize Node3D") |
|
65 |
+ |
undo_redo.add_do_property(node, "position", node.position) |
|
66 |
+ |
undo_redo.add_undo_property(node, "position", first_position) |
|
67 |
+ |
undo_redo.add_do_property(node, "scale", node.scale) |
|
68 |
+ |
undo_redo.add_undo_property(node, "scale", first_scale) |
|
69 |
+ |
undo_redo.commit_action() |
|
70 |
+ |
|
|
71 |
+ |
func _update_node(node: Node3D, change: Vector3, axis: Vector3, minimum_deflection = 1.0) -> void: |
|
72 |
+ |
if change.length() == 0: |
|
73 |
+ |
# Reset to default, no size change |
|
74 |
+ |
node.position = first_position |
|
75 |
+ |
node.scale = first_scale |
|
76 |
+ |
return |
|
77 |
+ |
|
|
78 |
+ |
var axis_direction = sign(change.dot(axis)) |
|
79 |
+ |
var direction = axis_direction * sign(axis.dot(axis.abs())) |
|
80 |
+ |
var amount_to_add = change.length() * direction |
|
81 |
+ |
var current_scale = (first_scale * axis.abs()).length() |
|
82 |
+ |
|
|
83 |
+ |
# If user attempts to go negative or zero, force them back to the minimum amount |
|
84 |
+ |
var deflection = current_scale + amount_to_add |
|
85 |
+ |
if deflection < minimum_deflection: |
|
86 |
+ |
amount_to_add = amount_to_add - deflection + minimum_deflection |
|
87 |
+ |
|
|
88 |
+ |
var abs_axis: Vector3 = axis.abs() |
|
89 |
+ |
var scale_vec: Vector3 = ONE_VEC - abs_axis |
|
90 |
+ |
var factor: float = (current_scale + amount_to_add) / current_scale |
|
91 |
+ |
var axis_factor: Vector3 = abs_axis * factor |
|
92 |
+ |
var apply_scale: Vector3 = scale_vec + axis_factor |
|
93 |
+ |
|
|
94 |
+ |
var pos_factor = ((apply_scale - ONE_VEC) / 2) * axis |
|
95 |
+ |
|
|
96 |
+ |
node.position = first_position |
|
97 |
+ |
node.scale = first_scale |
|
98 |
+ |
node.translate_object_local(pos_factor) |
|
99 |
+ |
node.scale_object_local(apply_scale) |
|
100 |
+ |
|
|
101 |
+ |
func _set_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, camera: Camera3D, screen_pos: Vector2) -> void: |
|
102 |
+ |
var node := gizmo.get_node_3d() |
|
103 |
+ |
var pos = _get_projected_pos(gizmo, handle_id, camera, screen_pos) |
|
104 |
+ |
|
|
105 |
+ |
if not pos is Vector3: |
|
106 |
+ |
return |
|
107 |
+ |
|
|
108 |
+ |
if not first_pos: |
|
109 |
+ |
first_pos = pos |
|
110 |
+ |
return |
|
111 |
+ |
|
|
112 |
+ |
var o_pos := first_pos as Vector3 |
|
113 |
+ |
var m_pos := pos as Vector3 |
|
114 |
+ |
var axis := handles[handle_id] |
|
115 |
+ |
|
|
116 |
+ |
var offset := (m_pos - o_pos) * axis |
|
117 |
+ |
|
|
118 |
+ |
var scale_snap: float = EditorInterface.get_node_3d_translate_snap() |
|
119 |
+ |
|
|
120 |
+ |
var handle_diff := offset.length() |
|
121 |
+ |
var snap_diff = handle_diff |
|
122 |
+ |
|
|
123 |
+ |
if EditorInterface.is_node_3d_snap_enabled(): |
|
124 |
+ |
snap_diff = roundf(handle_diff / scale_snap) * scale_snap |
|
125 |
+ |
|
|
126 |
+ |
var handle_dot := offset.normalized().dot(axis) |
|
127 |
+ |
var handle_dir = sign(handle_dot) |
|
128 |
+ |
|
|
129 |
+ |
var handle_diff_vec = axis * snap_diff * handle_dir |
|
130 |
+ |
|
|
131 |
+ |
_update_node(node, handle_diff_vec, axis, scale_snap) |
|
132 |
+ |
|
|
133 |
+ |
node.update_gizmos() |
|
134 |
+ |
|
|
135 |
+ |
func _get_projected_pos(gizmo: EditorNode3DGizmo, handle_id: int, camera: Camera3D, screen_pos: Vector2) -> Variant: |
|
136 |
+ |
var node := gizmo.get_node_3d() |
|
137 |
+ |
var pos_vector = Vector3(0, node.global_position.y, 0) |
|
138 |
+ |
var plane = Plane(node.global_basis.y, pos_vector) |
|
139 |
+ |
if handle_id == 0 or handle_id == 3: |
|
140 |
+ |
pos_vector = Vector3(node.global_position.x, 0, node.global_position.z) |
|
141 |
+ |
plane = Plane(node.global_basis.z, pos_vector) |
|
142 |
+ |
|
|
143 |
+ |
var from := camera.project_ray_origin(screen_pos) |
|
144 |
+ |
var pos = plane.intersects_ray(from, camera.project_ray_normal(screen_pos) * 4096.0) |
|
145 |
+ |
|
|
146 |
+ |
return pos |
|
147 |
+ |
|