feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s
checkpoint / checkpoint (push) Successful in 6m32s
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
class_name StudioViewportCamera
|
||||
extends Camera3D
|
||||
## Professional 3D Studio Camera controller supporting:
|
||||
## - Freecam flying (WASD + Q/E)
|
||||
## - Turntable Orbit & Panning (RMB/MMB drag)
|
||||
## - Selection framing/focus (F key)
|
||||
## - Orthographic view presets (Top, Front, Side, Perspective)
|
||||
|
||||
signal view_preset_changed(preset_name: String)
|
||||
|
||||
enum ViewPreset { PERSPECTIVE, TOP, FRONT, SIDE }
|
||||
|
||||
var current_preset: ViewPreset = ViewPreset.PERSPECTIVE
|
||||
|
||||
# Camera movement parameters
|
||||
var move_speed: float = 12.0
|
||||
var fast_multiplier: float = 2.5
|
||||
var slow_multiplier: float = 0.3
|
||||
var mouse_sensitivity: float = 0.003
|
||||
var zoom_speed: float = 1.5
|
||||
|
||||
# Orbit / focus state
|
||||
var focal_point: Vector3 = Vector3.ZERO
|
||||
var orbit_distance: float = 15.0
|
||||
var _yaw: float = -0.75
|
||||
var _pitch: float = -0.5
|
||||
|
||||
var _rmb_down: bool = false
|
||||
var _mmb_down: bool = false
|
||||
var _move_input: Vector3 = Vector3.ZERO
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
current = true
|
||||
_update_orbit_transform()
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
var mb := event as InputEventMouseButton
|
||||
if mb.button_index == MOUSE_BUTTON_RIGHT:
|
||||
_rmb_down = mb.pressed
|
||||
elif mb.button_index == MOUSE_BUTTON_MIDDLE:
|
||||
_mmb_down = mb.pressed
|
||||
elif mb.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||
zoom_camera(-zoom_speed)
|
||||
elif mb.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||
zoom_camera(zoom_speed)
|
||||
|
||||
elif event is InputEventMouseMotion:
|
||||
var mm := event as InputEventMouseMotion
|
||||
if _rmb_down:
|
||||
# Freecam / FPS mouse look
|
||||
_yaw -= mm.relative.x * mouse_sensitivity
|
||||
_pitch = clampf(_pitch - mm.relative.y * mouse_sensitivity, -PI * 0.49, PI * 0.49)
|
||||
rotation = Vector3(_pitch, _yaw, 0.0)
|
||||
elif _mmb_down:
|
||||
# Pan camera parallel to viewport
|
||||
var pan_speed := orbit_distance * 0.0015
|
||||
var right := global_transform.basis.x
|
||||
var up := global_transform.basis.y
|
||||
var delta := (-right * mm.relative.x + up * mm.relative.y) * pan_speed
|
||||
global_position += delta
|
||||
focal_point += delta
|
||||
|
||||
elif event is InputEventKey:
|
||||
var k := event as InputEventKey
|
||||
if k.pressed and not k.echo:
|
||||
if k.keycode == KEY_F:
|
||||
focus_on(focal_point, 5.0)
|
||||
elif k.keycode == KEY_1:
|
||||
set_view_preset(ViewPreset.FRONT)
|
||||
elif k.keycode == KEY_3:
|
||||
set_view_preset(ViewPreset.SIDE)
|
||||
elif k.keycode == KEY_7:
|
||||
set_view_preset(ViewPreset.TOP)
|
||||
elif k.keycode == KEY_5:
|
||||
toggle_perspective_ortho()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if _rmb_down:
|
||||
_process_keyboard_flight(delta)
|
||||
|
||||
|
||||
func _process_keyboard_flight(delta: float) -> void:
|
||||
var dir := Vector3.ZERO
|
||||
if Input.is_physical_key_pressed(KEY_W):
|
||||
dir -= global_transform.basis.z
|
||||
if Input.is_physical_key_pressed(KEY_S):
|
||||
dir += global_transform.basis.z
|
||||
if Input.is_physical_key_pressed(KEY_A):
|
||||
dir -= global_transform.basis.x
|
||||
if Input.is_physical_key_pressed(KEY_D):
|
||||
dir += global_transform.basis.x
|
||||
if Input.is_physical_key_pressed(KEY_E):
|
||||
dir += Vector3.UP
|
||||
if Input.is_physical_key_pressed(KEY_Q):
|
||||
dir -= Vector3.UP
|
||||
|
||||
if dir.length_squared() > 0.01:
|
||||
var speed := move_speed
|
||||
if Input.is_physical_key_pressed(KEY_SHIFT):
|
||||
speed *= fast_multiplier
|
||||
elif Input.is_physical_key_pressed(KEY_CTRL):
|
||||
speed *= slow_multiplier
|
||||
global_position += dir.normalized() * speed * delta
|
||||
focal_point = global_position - global_transform.basis.z * orbit_distance
|
||||
|
||||
|
||||
func zoom_camera(amount: float) -> void:
|
||||
orbit_distance = clampf(orbit_distance + amount, 1.0, 300.0)
|
||||
if projection == PROJECTION_ORTHOGONAL:
|
||||
size = clampf(size + amount, 1.0, 200.0)
|
||||
else:
|
||||
_update_orbit_transform()
|
||||
|
||||
|
||||
func focus_on(target_pos: Vector3, radius: float = 3.0) -> void:
|
||||
focal_point = target_pos
|
||||
orbit_distance = clampf(radius * 2.5, 3.0, 50.0)
|
||||
_update_orbit_transform()
|
||||
|
||||
|
||||
func _update_orbit_transform() -> void:
|
||||
var rot_basis := Basis.from_euler(Vector3(_pitch, _yaw, 0.0))
|
||||
var offset := rot_basis * Vector3(0.0, 0.0, orbit_distance)
|
||||
_set_camera_pos(focal_point + offset)
|
||||
rotation = Vector3(_pitch, _yaw, 0.0)
|
||||
|
||||
|
||||
func _set_camera_pos(pos: Vector3) -> void:
|
||||
if is_inside_tree():
|
||||
global_position = pos
|
||||
else:
|
||||
position = pos
|
||||
|
||||
|
||||
func set_view_preset(preset: ViewPreset) -> void:
|
||||
current_preset = preset
|
||||
match preset:
|
||||
ViewPreset.TOP:
|
||||
projection = PROJECTION_ORTHOGONAL
|
||||
size = orbit_distance
|
||||
rotation_degrees = Vector3(-90, 0, 0)
|
||||
_set_camera_pos(focal_point + Vector3(0, orbit_distance, 0))
|
||||
view_preset_changed.emit("Top")
|
||||
ViewPreset.FRONT:
|
||||
projection = PROJECTION_ORTHOGONAL
|
||||
size = orbit_distance
|
||||
rotation_degrees = Vector3(0, 0, 0)
|
||||
_set_camera_pos(focal_point + Vector3(0, 0, orbit_distance))
|
||||
view_preset_changed.emit("Front")
|
||||
ViewPreset.SIDE:
|
||||
projection = PROJECTION_ORTHOGONAL
|
||||
size = orbit_distance
|
||||
rotation_degrees = Vector3(0, 90, 0)
|
||||
_set_camera_pos(focal_point + Vector3(orbit_distance, 0, 0))
|
||||
view_preset_changed.emit("Side")
|
||||
ViewPreset.PERSPECTIVE:
|
||||
projection = PROJECTION_PERSPECTIVE
|
||||
_pitch = -0.5
|
||||
_yaw = -0.75
|
||||
_update_orbit_transform()
|
||||
view_preset_changed.emit("Perspective")
|
||||
|
||||
|
||||
func toggle_perspective_ortho() -> void:
|
||||
if projection == PROJECTION_PERSPECTIVE:
|
||||
projection = PROJECTION_ORTHOGONAL
|
||||
size = orbit_distance
|
||||
else:
|
||||
projection = PROJECTION_PERSPECTIVE
|
||||
Reference in New Issue
Block a user