update
This commit is contained in:
93
scripts/ui/CameraRig.gd
Normal file
93
scripts/ui/CameraRig.gd
Normal file
@@ -0,0 +1,93 @@
|
||||
class_name CameraRig
|
||||
extends Node3D
|
||||
|
||||
## Kamera über dem Brett: passt sich der freien Bildmitte an, lässt sich
|
||||
## zoomen und um die Brettmitte drehen.
|
||||
|
||||
const FOV := 44.0
|
||||
const PITCH := 52.0 ## Grad über der Waagerechten
|
||||
const ZOOM_MIN := 0.6
|
||||
const ZOOM_MAX := 1.5
|
||||
|
||||
var camera: Camera3D
|
||||
var zoom := 1.0
|
||||
var yaw := 0.0
|
||||
var _center := Vector3.ZERO
|
||||
var _dir := Vector3.ZERO
|
||||
var _dist := 20.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
camera = Camera3D.new()
|
||||
camera.fov = FOV
|
||||
camera.look_at_from_position(Vector3(0.0, 16.0, 13.0), Vector3.ZERO, Vector3.UP)
|
||||
add_child(camera)
|
||||
|
||||
|
||||
## Legt die Kamera so, dass alle Punkte in das Zielrechteck passen.
|
||||
func fit(points: Array, target: Rect2) -> void:
|
||||
if target.size.x <= 0.0 or target.size.y <= 0.0 or points.is_empty():
|
||||
return
|
||||
var pitch := deg_to_rad(PITCH)
|
||||
var dir := Vector3(0.0, sin(pitch), cos(pitch))
|
||||
var dist := 20.0
|
||||
var center := Vector3.ZERO
|
||||
for i in 8:
|
||||
camera.look_at_from_position(center + dir * dist, center, Vector3.UP)
|
||||
var r := _project_rect(points)
|
||||
if r.size.x <= 0.0 or r.size.y <= 0.0:
|
||||
return
|
||||
dist *= maxf(r.size.x / target.size.x, r.size.y / target.size.y)
|
||||
camera.look_at_from_position(center + dir * dist, center, Vector3.UP)
|
||||
r = _project_rect(points)
|
||||
var err := r.get_center() - target.get_center()
|
||||
var o := camera.unproject_position(center)
|
||||
var kx := camera.unproject_position(center + Vector3(1.0, 0.0, 0.0)).x - o.x
|
||||
var kz := camera.unproject_position(center + Vector3(0.0, 0.0, 1.0)).y - o.y
|
||||
if absf(kx) < 0.001 or absf(kz) < 0.001:
|
||||
break
|
||||
center += Vector3(err.x / kx, 0.0, err.y / kz)
|
||||
_center = center
|
||||
_dir = dir
|
||||
_dist = dist
|
||||
apply()
|
||||
|
||||
|
||||
## Nutzeranteil: Zoom und Drehung um die Brettmitte.
|
||||
func apply() -> void:
|
||||
var dir := _dir.rotated(Vector3.UP, deg_to_rad(yaw))
|
||||
camera.look_at_from_position(_center + dir * _dist * zoom, _center, Vector3.UP)
|
||||
|
||||
|
||||
func zoom_by(delta: float) -> void:
|
||||
zoom = clampf(zoom + delta, ZOOM_MIN, ZOOM_MAX)
|
||||
apply()
|
||||
|
||||
|
||||
func rotate_by(deg: float) -> void:
|
||||
yaw = fmod(yaw + deg, 360.0)
|
||||
apply()
|
||||
|
||||
|
||||
func _project_rect(points: Array) -> Rect2:
|
||||
var lo := Vector2(INF, INF)
|
||||
var hi := Vector2(-INF, -INF)
|
||||
for p in points:
|
||||
if camera.is_position_behind(p):
|
||||
return Rect2()
|
||||
var q := camera.unproject_position(p)
|
||||
lo = Vector2(minf(lo.x, q.x), minf(lo.y, q.y))
|
||||
hi = Vector2(maxf(hi.x, q.x), maxf(hi.y, q.y))
|
||||
return Rect2(lo, hi - lo)
|
||||
|
||||
|
||||
## Punkt auf der Feldebene unter einer Bildschirmposition, oder null.
|
||||
func ground_point(screen_pos: Vector2, plane_y: float):
|
||||
var from := camera.project_ray_origin(screen_pos)
|
||||
var dir := camera.project_ray_normal(screen_pos)
|
||||
if absf(dir.y) < 0.0001:
|
||||
return null
|
||||
var t := (plane_y - from.y) / dir.y
|
||||
if t < 0.0:
|
||||
return null
|
||||
return from + dir * t
|
||||
Reference in New Issue
Block a user