forked from GodotVR/godot-xr-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added gizmos to help align interactable joints
This PR resolves request GodotVR#584 by adding gizmos to align interactable hinges, sliders, and joysticks. This does cause minor breakage in that: - New origin nodes have been introduced where the ranges are visualized - Testing showed Y-Axis inversion on the slider and joystick
- Loading branch information
1 parent
3123a3d
commit 5164a96
Showing
21 changed files
with
996 additions
and
216 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
extends EditorNode3DGizmoPlugin | ||
|
||
|
||
## Editor Gizmo for [XRToolsInteractableHingeOrigin] | ||
## | ||
## This editor gizmo helps align interactable hinge joints. | ||
|
||
|
||
var undo_redo : EditorUndoRedoManager | ||
|
||
|
||
func _init() -> void: | ||
create_material("axis", Color(1, 1, 0)) | ||
create_material("extent", Color(1, 1, 0, 0.3), false, true) | ||
create_handle_material("handles") | ||
|
||
|
||
func _get_gizmo_name() -> String: | ||
return "InteractableHingeOrigin" | ||
|
||
|
||
func _get_handle_name( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool) -> String: | ||
# Return minimum or maximum handle name | ||
return "Minimum" if p_handle_id == 0 else "Maximum" | ||
|
||
|
||
func _get_handle_value( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool) -> Variant: | ||
# Return limit | ||
return _get_limit(p_gizmo.get_node_3d(), p_handle_id) | ||
|
||
|
||
func _set_handle( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool, | ||
p_camera : Camera3D, | ||
p_screen_pos : Vector2) -> void: | ||
# Get the hinge origin node | ||
var origin : XRToolsInteractableHingeOrigin = p_gizmo.get_node_3d() | ||
var origin_pos := origin.global_position | ||
var origin_dir := origin.global_transform.basis.x | ||
|
||
# Construct the plane | ||
var plane := Plane(origin_dir, origin_pos) | ||
|
||
# Find the intersection between the ray and the plane | ||
var v_intersect = plane.intersects_ray( | ||
p_camera.global_position, | ||
p_camera.project_ray_normal(p_screen_pos)) | ||
if not v_intersect: | ||
return | ||
|
||
# Find the local position and the delta in angle | ||
var local := origin.to_local(v_intersect) | ||
var old_local := _get_limit_pos(origin, p_handle_id) | ||
var delta := rad_to_deg(old_local.signed_angle_to(local, Vector3.LEFT)) | ||
|
||
# Adjust the current limit | ||
var limit := _get_limit(origin, p_handle_id) | ||
limit = snappedf(limit + delta, 5) | ||
_set_limit(origin, p_handle_id, limit) | ||
|
||
|
||
func _commit_handle( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool, | ||
p_restore : Variant, | ||
p_cancel : bool) -> void: | ||
# Get the slider origin node | ||
var origin : XRToolsInteractableHingeOrigin = p_gizmo.get_node_3d() | ||
|
||
# If canceling then restore limit | ||
if p_cancel: | ||
_set_limit(origin, p_handle_id, p_restore) | ||
return | ||
|
||
# Commit the handle change | ||
match p_handle_id: | ||
0: | ||
undo_redo.create_action("Set interactable hinge limit_minimum") | ||
undo_redo.add_do_method(origin, "set_limit_minimum", origin.limit_minimum) | ||
undo_redo.add_undo_method(origin, "set_limit_minimum", p_restore) | ||
undo_redo.commit_action() | ||
1: | ||
undo_redo.create_action("Set interactable hinge limit_maximum") | ||
undo_redo.add_do_method(origin, "set_limit_maximum", origin.limit_maximum) | ||
undo_redo.add_undo_method(origin, "set_limit_maximum", p_restore) | ||
undo_redo.commit_action() | ||
|
||
|
||
func _has_gizmo(node : Node3D) -> bool: | ||
return node is XRToolsInteractableHingeOrigin | ||
|
||
|
||
func _redraw(gizmo : EditorNode3DGizmo) -> void: | ||
# Clear the current gizmo contents | ||
gizmo.clear() | ||
|
||
# Get the hinge origin and its extents | ||
var origin : XRToolsInteractableHingeOrigin = gizmo.get_node_3d() | ||
var min_angle := deg_to_rad(origin.limit_minimum) | ||
var max_angle := deg_to_rad(origin.limit_maximum) | ||
|
||
# Draw the lines (for the axis) | ||
var lines := PackedVector3Array() | ||
lines.push_back(Vector3(-0.2, 0, 0)) | ||
lines.push_back(Vector3(0.2, 0, 0)) | ||
gizmo.add_lines(lines, get_material("axis", gizmo)) | ||
|
||
# Construct an immediate mesh for the extent | ||
var mesh := ImmediateMesh.new() | ||
mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP) | ||
var steps := int(abs(max_angle - min_angle) / 0.1) | ||
for i in steps + 1: | ||
if i != 0: | ||
mesh.surface_add_vertex(Vector3.ZERO) | ||
var angle := lerpf(min_angle, max_angle, i / float(steps)) | ||
mesh.surface_add_vertex( | ||
Vector3(0, sin(angle) * 0.2, cos(angle) * 0.2)) | ||
mesh.surface_end() | ||
|
||
# Draw the extent mesh | ||
gizmo.add_mesh(mesh, get_material("extent", gizmo)) | ||
|
||
# Add the handles | ||
var handles := PackedVector3Array() | ||
handles.push_back(_get_limit_pos(origin, 0)) | ||
handles.push_back(_get_limit_pos(origin, 1)) | ||
gizmo.add_handles(handles, get_material("handles", gizmo), []) | ||
|
||
|
||
# Get the limit of a hinge by handle | ||
func _get_limit( | ||
origin : XRToolsInteractableHingeOrigin, | ||
p_handle_id : int) -> float: | ||
# Read the limit | ||
match p_handle_id: | ||
0: | ||
return origin.limit_minimum | ||
1: | ||
return origin.limit_maximum | ||
_: | ||
return 0.0 | ||
|
||
|
||
# Get the limit position of a slider by handle | ||
func _get_limit_pos( | ||
origin : XRToolsInteractableHingeOrigin, | ||
p_handle_id : int) -> Vector3: | ||
# Return the limit position | ||
var angle := deg_to_rad(_get_limit(origin, p_handle_id)) | ||
return Vector3(0, sin(angle) * 0.2, cos(angle) * 0.2) | ||
|
||
|
||
# Set the limit of a hinge by handle | ||
func _set_limit( | ||
origin : XRToolsInteractableHingeOrigin, | ||
p_handle_id : int, | ||
limit : float) -> void: | ||
# Apply the limit | ||
match p_handle_id: | ||
0: | ||
origin.limit_minimum = limit | ||
1: | ||
origin.limit_maximum = limit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
extends EditorNode3DGizmoPlugin | ||
|
||
|
||
## Editor Gizmo for [XRToolsInteractableJoystickOrigin] | ||
## | ||
## This editor gizmo helps align interactable joystick joints. | ||
|
||
|
||
var undo_redo : EditorUndoRedoManager | ||
|
||
|
||
func _init() -> void: | ||
create_material("extent", Color(1, 1, 0, 0.3), false, true) | ||
create_handle_material("handles") | ||
|
||
|
||
func _get_gizmo_name() -> String: | ||
return "InteractableJoystickOrigin" | ||
|
||
|
||
func _get_handle_name( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool) -> String: | ||
# Return handle name | ||
if p_handle_id == 0: return "Minimum X" | ||
if p_handle_id == 1: return "Maximum X" | ||
if p_handle_id == 2: return "Minimum Y" | ||
return "Maximum Y" | ||
|
||
|
||
func _get_handle_value( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool) -> Variant: | ||
# Return limit | ||
return _get_limit(p_gizmo.get_node_3d(), p_handle_id) | ||
|
||
|
||
func _set_handle( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool, | ||
p_camera : Camera3D, | ||
p_screen_pos : Vector2) -> void: | ||
# Get the hinge origin node | ||
var origin : XRToolsInteractableJoystickOrigin = p_gizmo.get_node_3d() | ||
var origin_pos := origin.global_position | ||
var origin_dir_x := origin.global_transform.basis.y | ||
var origin_dir_y := -origin.global_transform.basis.x | ||
|
||
# Construct the plane | ||
var plane : Plane | ||
if p_handle_id < 2: | ||
plane = Plane(origin_dir_x, origin_pos) | ||
else: | ||
plane = Plane(origin_dir_y, origin_pos) | ||
|
||
# Find the intersection between the ray and the plane | ||
var v_intersect = plane.intersects_ray( | ||
p_camera.global_position, | ||
p_camera.project_ray_normal(p_screen_pos)) | ||
if not v_intersect: | ||
return | ||
|
||
# Find the local position | ||
var local := origin.to_local(v_intersect) | ||
var old_local := _get_limit_pos(origin, p_handle_id) | ||
var delta : float | ||
if p_handle_id < 2: | ||
delta = rad_to_deg(old_local.signed_angle_to(local, Vector3.UP)) | ||
else: | ||
delta = rad_to_deg(old_local.signed_angle_to(local, Vector3.LEFT)) | ||
|
||
# Adjust the current limit | ||
var limit := _get_limit(origin, p_handle_id) | ||
limit = snappedf(limit + delta, 5) | ||
_set_limit(origin, p_handle_id, limit) | ||
|
||
|
||
func _commit_handle( | ||
p_gizmo : EditorNode3DGizmo, | ||
p_handle_id : int, | ||
_secondary : bool, | ||
p_restore : Variant, | ||
p_cancel : bool) -> void: | ||
# Get the slider origin node | ||
var origin : XRToolsInteractableJoystickOrigin = p_gizmo.get_node_3d() | ||
|
||
# If canceling then restore limit | ||
if p_cancel: | ||
_set_limit(origin, p_handle_id, p_restore) | ||
return | ||
|
||
# Commit the handle change | ||
match p_handle_id: | ||
0: | ||
undo_redo.create_action("Set interactable joystick limit_x_minimum") | ||
undo_redo.add_do_method(origin, "set_limit_x_minimum", origin.limit_x_minimum) | ||
undo_redo.add_undo_method(origin, "set_limit_x_minimum", p_restore) | ||
undo_redo.commit_action() | ||
1: | ||
undo_redo.create_action("Set interactable joystick limit_x_maximum") | ||
undo_redo.add_do_method(origin, "set_limit_x_maximum", origin.limit_x_maximum) | ||
undo_redo.add_undo_method(origin, "set_limit_x_maximum", p_restore) | ||
undo_redo.commit_action() | ||
2: | ||
undo_redo.create_action("Set interactable joystick limit_y_minimum") | ||
undo_redo.add_do_method(origin, "set_limit_y_minimum", origin.limit_y_minimum) | ||
undo_redo.add_undo_method(origin, "set_limit_y_minimum", p_restore) | ||
undo_redo.commit_action() | ||
3: | ||
undo_redo.create_action("Set interactable joystick limit_y_maximum") | ||
undo_redo.add_do_method(origin, "set_limit_y_maximum", origin.limit_y_maximum) | ||
undo_redo.add_undo_method(origin, "set_limit_y_maximum", p_restore) | ||
undo_redo.commit_action() | ||
|
||
|
||
func _has_gizmo(node : Node3D) -> bool: | ||
return node is XRToolsInteractableJoystickOrigin | ||
|
||
|
||
func _redraw(gizmo : EditorNode3DGizmo) -> void: | ||
# Clear the current gizmo contents | ||
gizmo.clear() | ||
|
||
# Get the joystick origin and its extents | ||
var origin : XRToolsInteractableJoystickOrigin = gizmo.get_node_3d() | ||
var min_x_angle := deg_to_rad(origin.limit_x_minimum) | ||
var max_x_angle := deg_to_rad(origin.limit_x_maximum) | ||
var min_y_angle := deg_to_rad(origin.limit_y_minimum) | ||
var max_y_angle := deg_to_rad(origin.limit_y_maximum) | ||
|
||
# Construct an immediate mesh for the extent | ||
var mesh := ImmediateMesh.new() | ||
mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP) | ||
for i in 33: | ||
if i != 0: | ||
mesh.surface_add_vertex(Vector3.ZERO) | ||
var angle := lerpf(min_x_angle, max_x_angle, i / 32.0) | ||
mesh.surface_add_vertex( | ||
Vector3(sin(angle) * 0.2, 0, cos(angle) * 0.2)) | ||
mesh.surface_end() | ||
mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP) | ||
for i in 33: | ||
if i != 0: | ||
mesh.surface_add_vertex(Vector3.ZERO) | ||
var angle := lerpf(min_y_angle, max_y_angle, i / 32.0) | ||
mesh.surface_add_vertex( | ||
Vector3(0, sin(angle) * 0.2, cos(angle) * 0.2)) | ||
mesh.surface_end() | ||
|
||
# Draw the extent mesh | ||
gizmo.add_mesh(mesh, get_material("extent", gizmo)) | ||
|
||
# Add the handles | ||
var handles := PackedVector3Array() | ||
handles.push_back(_get_limit_pos(origin, 0)) | ||
handles.push_back(_get_limit_pos(origin, 1)) | ||
handles.push_back(_get_limit_pos(origin, 2)) | ||
handles.push_back(_get_limit_pos(origin, 3)) | ||
gizmo.add_handles(handles, get_material("handles", gizmo), []) | ||
|
||
|
||
# Get the limit of a joystick by handle | ||
func _get_limit( | ||
origin : XRToolsInteractableJoystickOrigin, | ||
p_handle_id : int) -> float: | ||
# Read the limit | ||
match p_handle_id: | ||
0: | ||
return origin.limit_x_minimum | ||
1: | ||
return origin.limit_x_maximum | ||
2: | ||
return origin.limit_y_minimum | ||
3: | ||
return origin.limit_y_maximum | ||
_: | ||
return 0.0 | ||
|
||
|
||
# Get the limit position of a slider by handle | ||
func _get_limit_pos( | ||
origin : XRToolsInteractableJoystickOrigin, | ||
p_handle_id : int) -> Vector3: | ||
# Return the limit position | ||
var angle := deg_to_rad(_get_limit(origin, p_handle_id)) | ||
match p_handle_id: | ||
0, 1: | ||
return Vector3(sin(angle) * 0.2, 0, cos(angle) * 0.2) | ||
2, 3: | ||
return Vector3(0, sin(angle) * 0.2, cos(angle) * 0.2) | ||
_: | ||
return Vector3.ZERO | ||
|
||
|
||
# Set the limit of a joystick by handle | ||
func _set_limit( | ||
origin : XRToolsInteractableJoystickOrigin, | ||
p_handle_id : int, | ||
limit : float) -> void: | ||
# Apply the limit | ||
match p_handle_id: | ||
0: | ||
origin.limit_x_minimum = limit | ||
1: | ||
origin.limit_x_maximum = limit | ||
2: | ||
origin.limit_y_minimum = limit | ||
3: | ||
origin.limit_y_maximum = limit |
Oops, something went wrong.