Files
agame/addons/global_asset_manager/preview_controller.gd
T
2026-10-05 14:57:41 +08:00

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