117 lines
4.1 KiB
GDScript
117 lines
4.1 KiB
GDScript
@tool
|
|
class_name PreviewController
|
|
extends RefCounted
|
|
## Handles 2D and 3D preview manipulations (orbit, pan, zoom).
|
|
|
|
var pivot: Node3D
|
|
var camera: Camera3D
|
|
var texture_rect: TextureRect
|
|
|
|
var _is_orbiting: bool = false
|
|
var _is_panning_3d: bool = false
|
|
var _is_panning_2d: bool = false
|
|
var _last_mouse_pos: Vector2 = Vector2.ZERO
|
|
|
|
var _yaw: float = 0.0
|
|
var _pitch: float = 0.0
|
|
var _initial_cam_transform: Transform3D
|
|
|
|
func reset_views() -> void:
|
|
_yaw = 0.0
|
|
_pitch = 0.0
|
|
if is_instance_valid(pivot):
|
|
pivot.transform.basis = Basis.IDENTITY
|
|
pivot.position = Vector3.ZERO
|
|
if is_instance_valid(camera):
|
|
if _initial_cam_transform == Transform3D():
|
|
_initial_cam_transform = camera.transform
|
|
camera.transform = _initial_cam_transform
|
|
|
|
if is_instance_valid(texture_rect):
|
|
texture_rect.scale = Vector2.ONE
|
|
texture_rect.position = Vector2.ZERO
|
|
|
|
func reset_origin() -> void:
|
|
_yaw = 0.0
|
|
_pitch = 0.0
|
|
if is_instance_valid(pivot):
|
|
pivot.transform.basis = Basis.IDENTITY
|
|
pivot.position = Vector3.ZERO
|
|
|
|
func reset_zoom() -> void:
|
|
if is_instance_valid(camera) and _initial_cam_transform != Transform3D():
|
|
# Preserve the camera's angle, but snap its distance back to default
|
|
camera.global_position = _initial_cam_transform.origin
|
|
|
|
func handle_3d_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton:
|
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
|
_is_orbiting = event.pressed
|
|
_last_mouse_pos = event.position
|
|
elif event.button_index == MOUSE_BUTTON_RIGHT or event.button_index == MOUSE_BUTTON_MIDDLE:
|
|
_is_panning_3d = event.pressed
|
|
_last_mouse_pos = event.position
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
|
|
# Zoom in along the camera's actual line of sight
|
|
camera.global_position -= camera.global_transform.basis.z * 0.5
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
|
|
# Zoom out along the camera's actual line of sight
|
|
camera.global_position += camera.global_transform.basis.z * 0.5
|
|
|
|
elif event is InputEventMouseMotion:
|
|
var motion := event as InputEventMouseMotion
|
|
var delta: Vector2 = motion.position - _last_mouse_pos
|
|
_last_mouse_pos = motion.position
|
|
|
|
if _is_orbiting:
|
|
_yaw += delta.x * 0.01
|
|
_pitch += delta.y * 0.01
|
|
_pitch = clampf(_pitch, -PI / 2.5, PI / 2.5)
|
|
|
|
pivot.transform.basis = Basis.IDENTITY
|
|
|
|
# Rotate relative to the camera's orientation so screen-space movement always feels perfectly vertical/horizontal
|
|
var cam_up := camera.global_transform.basis.y.normalized()
|
|
var cam_right := camera.global_transform.basis.x.normalized()
|
|
|
|
pivot.global_rotate(cam_up, _yaw)
|
|
pivot.global_rotate(cam_right, _pitch)
|
|
|
|
elif _is_panning_3d:
|
|
var right := camera.global_transform.basis.x
|
|
var up := camera.global_transform.basis.y
|
|
|
|
# Calculate distance from camera to pivot to dynamically adjust pan speed
|
|
var dist := camera.global_position.distance_to(pivot.global_position)
|
|
var pan_speed := maxf(0.01, dist * 0.003)
|
|
|
|
pivot.global_position += right * delta.x * pan_speed
|
|
pivot.global_position -= up * delta.y * pan_speed
|
|
|
|
func handle_2d_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton:
|
|
if event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT or event.button_index == MOUSE_BUTTON_MIDDLE:
|
|
_is_panning_2d = event.pressed
|
|
_last_mouse_pos = event.position
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
|
|
_zoom_2d(event.position, 1.1)
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
|
|
_zoom_2d(event.position, 1.0 / 1.1)
|
|
|
|
elif event is InputEventMouseMotion and _is_panning_2d:
|
|
var motion := event as InputEventMouseMotion
|
|
var delta: Vector2 = motion.position - _last_mouse_pos
|
|
_last_mouse_pos = motion.position
|
|
texture_rect.position += delta
|
|
|
|
func _zoom_2d(mouse_pos: Vector2, factor: float) -> void:
|
|
var old_scale := texture_rect.scale
|
|
var new_scale := old_scale * factor
|
|
|
|
new_scale.x = clampf(new_scale.x, 0.1, 50.0)
|
|
new_scale.y = clampf(new_scale.y, 0.1, 50.0)
|
|
|
|
var mouse_local := mouse_pos - texture_rect.position
|
|
texture_rect.position -= mouse_local * ((new_scale / old_scale) - Vector2.ONE)
|
|
texture_rect.scale = new_scale
|