mirror of
https://github.com/Cuteminded/jellyframe.git
synced 2026-10-19 05:00:30 +02:00
This change replaces the old playback button strip with a transparent overlay attached to the movie screen. It adds a seekable timeline, duration reporting, and proper seek routing for resumed/transcoded streams while preserving paused state and overlay behavior across settings and recenter actions. Pointer and scroll handling were also hardened to improve controller input stability, including hover tracking, exit handling, aim smoothing, and synthetic wheel release events. Regression tests and testing notes were extended to cover overlay toggling, timeline formatting, seek flows, recenter behavior, and pointer/scroll edge cases.
90 lines
3.1 KiB
GDScript
90 lines
3.1 KiB
GDScript
class_name LaserPointer
|
|
extends XRController3D
|
|
|
|
var panel: Node3D
|
|
var viewport: SubViewport
|
|
var panel_size := Vector2(4.4, 2.75)
|
|
var beam: MeshInstance3D
|
|
var dot: MeshInstance3D
|
|
var input := PointerInput.new()
|
|
var aim := PointerAim.new()
|
|
static var active_pointer: LaserPointer
|
|
|
|
func _ready() -> void:
|
|
input.viewport = viewport
|
|
if tracker == &"right_hand": active_pointer = self
|
|
var material := StandardMaterial3D.new()
|
|
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
material.albedo_color = Color("6de3cf")
|
|
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
material.no_depth_test = true
|
|
material.render_priority = 127
|
|
beam = MeshInstance3D.new()
|
|
var shape := CylinderMesh.new()
|
|
shape.top_radius = 0.002
|
|
shape.bottom_radius = 0.002
|
|
shape.height = 1.0
|
|
beam.mesh = shape
|
|
beam.rotation.x = PI / 2
|
|
beam.material_override = material
|
|
add_child(beam)
|
|
dot = MeshInstance3D.new()
|
|
var sphere := SphereMesh.new()
|
|
sphere.radius = 0.009
|
|
sphere.height = 0.018
|
|
dot.mesh = sphere
|
|
dot.material_override = material
|
|
add_child(dot)
|
|
|
|
func set_target(target: Node3D, target_viewport: SubViewport, target_size: Vector2) -> void:
|
|
if panel != target:
|
|
_cancel()
|
|
panel = target
|
|
viewport = target_viewport
|
|
input.viewport = viewport
|
|
panel_size = target_size
|
|
|
|
func _process(delta: float) -> void:
|
|
input.sample_trigger(get_float("trigger"))
|
|
if input.just_pressed and get_is_active():
|
|
if is_instance_valid(active_pointer) and active_pointer != self:
|
|
active_pointer._cancel()
|
|
active_pointer = self
|
|
if (is_instance_valid(active_pointer) and active_pointer != self) or not get_is_active() or not panel.is_visible_in_tree():
|
|
_cancel()
|
|
return
|
|
var inverse := panel.global_transform.affine_inverse()
|
|
var origin := inverse * global_position
|
|
var direction := inverse.basis * -global_basis.z
|
|
var distance := -origin.z / direction.z if absf(direction.z) > 0.00001 else -1.0
|
|
var hit := origin + direction * distance
|
|
var inside := distance > 0 and absf(hit.x) < panel_size.x / 2 and absf(hit.y) < panel_size.y / 2
|
|
beam.visible = inside
|
|
dot.visible = inside
|
|
var point := Vector2(hit.x / panel_size.x + 0.5, 0.5 - hit.y / panel_size.y) * Vector2(viewport.size)
|
|
if not inside:
|
|
input.aim(point, false)
|
|
aim.reset()
|
|
return
|
|
point = aim.update(point, delta, input.just_pressed)
|
|
input.aim(point, true)
|
|
var uv := point / Vector2(viewport.size)
|
|
var destination := panel.to_global(Vector3((uv.x - 0.5) * panel_size.x, (0.5 - uv.y) * panel_size.y, 0.008))
|
|
dot.global_position = destination
|
|
var direction_to_dot := destination - global_position
|
|
var up := Vector3.FORWARD if absf(direction_to_dot.normalized().dot(Vector3.UP)) > 0.99 else Vector3.UP
|
|
beam.global_transform = Transform3D(Basis.looking_at(direction_to_dot, up) * Basis(Vector3.RIGHT, PI / 2), (global_position + destination) / 2)
|
|
beam.scale.y = direction_to_dot.length()
|
|
if not input.trigger_down: _scroll(point, delta)
|
|
|
|
func _scroll(point: Vector2, delta: float) -> void:
|
|
var scroll := get_vector2("primary").y
|
|
if absf(scroll) <= 0.5: return
|
|
input.scroll(point, scroll * delta * 12.0)
|
|
|
|
func _cancel() -> void:
|
|
beam.hide()
|
|
dot.hide()
|
|
input.cancel()
|
|
aim.reset()
|