mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-18 17:19:50 +00:00
Replace Camera2D with a custom CanvasCamera to reduce idle GPU usage
Necessary workaround because of https://github.com/godotengine/godot/issues/89514
This commit is contained in:
parent
63469e9c78
commit
f1ef01bb87
|
@ -491,11 +491,11 @@ var cel_button_scene: PackedScene = load("res://src/UI/Timeline/CelButton.tscn")
|
|||
"PreviewViewportContainer"
|
||||
)
|
||||
## Camera of the main canvas. It has the [param CameraMovement.gd] script attached.
|
||||
@onready var camera: Camera2D = main_viewport.find_child("Camera2D")
|
||||
@onready var camera: CanvasCamera = main_viewport.find_child("Camera2D")
|
||||
## Camera of the second canvas preview. It has the [param CameraMovement.gd] script attached.
|
||||
@onready var camera2: Camera2D = second_viewport.find_child("Camera2D2")
|
||||
@onready var camera2: CanvasCamera = second_viewport.find_child("Camera2D2")
|
||||
## Camera of the canvas preview. It has the [param CameraMovement.gd] script attached.
|
||||
@onready var camera_preview: Camera2D = control.find_child("CameraPreview")
|
||||
@onready var camera_preview: CanvasCamera = control.find_child("CameraPreview")
|
||||
## Array of cameras used in Pixelorama.
|
||||
@onready var cameras := [camera, camera2, camera_preview]
|
||||
## Horizontal ruler of the main canvas. It has the [param HorizontalRuler.gd] script attached.
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
extends Camera2D
|
||||
class_name CanvasCamera
|
||||
extends Node2D
|
||||
|
||||
signal zoom_changed
|
||||
signal rotation_changed
|
||||
signal offset_changed
|
||||
|
||||
enum Cameras { MAIN, SECOND, SMALL }
|
||||
|
||||
|
@ -9,6 +11,24 @@ const CAMERA_SPEED_RATE := 15.0
|
|||
|
||||
@export var index := 0
|
||||
|
||||
var zoom := Vector2.ONE:
|
||||
set(value):
|
||||
zoom = value
|
||||
zoom_changed.emit()
|
||||
_update_viewport_transform()
|
||||
var camera_angle := 0.0:
|
||||
set(value):
|
||||
camera_angle = wrapf(value, -PI, PI)
|
||||
camera_angle_degrees = rad_to_deg(camera_angle)
|
||||
rotation_changed.emit()
|
||||
_update_viewport_transform()
|
||||
var camera_angle_degrees := 0.0
|
||||
var offset := Vector2.ZERO:
|
||||
set(value):
|
||||
offset = value
|
||||
offset_changed.emit()
|
||||
_update_viewport_transform()
|
||||
var camera_screen_center := Vector2.ZERO
|
||||
var zoom_in_max := Vector2(500, 500)
|
||||
var zoom_out_max := Vector2(0.01, 0.01)
|
||||
var viewport_container: SubViewportContainer
|
||||
|
@ -19,55 +39,24 @@ var rotation_slider: ValueSlider
|
|||
var zoom_slider: ValueSlider
|
||||
var should_tween := true
|
||||
|
||||
@onready var viewport := get_viewport()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not DisplayServer.is_touchscreen_available():
|
||||
set_process_input(false)
|
||||
if index == Cameras.MAIN:
|
||||
rotation_slider = Global.top_menu_container.get_node("%RotationSlider")
|
||||
rotation_slider.value_changed.connect(_rotation_value_changed)
|
||||
rotation_slider.value_changed.connect(_rotation_slider_value_changed)
|
||||
zoom_slider = Global.top_menu_container.get_node("%ZoomSlider")
|
||||
zoom_slider.value_changed.connect(_zoom_value_changed)
|
||||
zoom_slider.value_changed.connect(_zoom_slider_value_changed)
|
||||
zoom_changed.connect(_zoom_changed)
|
||||
rotation_changed.connect(_rotation_changed)
|
||||
ignore_rotation = false
|
||||
viewport_container = get_parent().get_parent()
|
||||
transparent_checker = get_parent().get_node("TransparentChecker")
|
||||
update_transparent_checker_offset()
|
||||
|
||||
|
||||
func _rotation_value_changed(value: float) -> void:
|
||||
# Negative makes going up rotate clockwise
|
||||
var degrees := -value
|
||||
var difference := degrees - rotation_degrees
|
||||
var canvas_center: Vector2 = Global.current_project.size / 2
|
||||
offset = (offset - canvas_center).rotated(deg_to_rad(difference)) + canvas_center
|
||||
rotation_degrees = wrapf(degrees, -180, 180)
|
||||
rotation_changed.emit()
|
||||
|
||||
|
||||
func _zoom_value_changed(value: float) -> void:
|
||||
if value <= 0:
|
||||
value = 1
|
||||
var new_zoom := Vector2(value, value) / 100.0
|
||||
if zoom == new_zoom:
|
||||
return
|
||||
if Global.smooth_zoom and should_tween:
|
||||
var tween := create_tween().set_trans(Tween.TRANS_LINEAR).set_ease(Tween.EASE_IN)
|
||||
tween.step_finished.connect(_on_tween_step)
|
||||
tween.tween_property(self, "zoom", new_zoom, 0.05)
|
||||
else:
|
||||
zoom = new_zoom
|
||||
zoom_changed.emit()
|
||||
|
||||
|
||||
func update_transparent_checker_offset() -> void:
|
||||
var o := get_global_transform_with_canvas().get_origin()
|
||||
var s := get_global_transform_with_canvas().get_scale()
|
||||
o.y = get_viewport_rect().size.y - o.y
|
||||
transparent_checker.update_offset(o, s)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if !Global.can_draw:
|
||||
drag = false
|
||||
|
@ -89,43 +78,19 @@ func _input(event: InputEvent) -> void:
|
|||
zoom_camera(-1)
|
||||
elif event is InputEventPanGesture:
|
||||
# Pan gesture on touchscreens
|
||||
offset = offset + event.delta.rotated(rotation) * 7.0 / zoom
|
||||
offset = offset + event.delta.rotated(camera_angle) * 7.0 / zoom
|
||||
elif event is InputEventMouseMotion:
|
||||
if drag:
|
||||
offset = offset - event.relative.rotated(rotation) / zoom
|
||||
offset = offset - event.relative.rotated(camera_angle) / zoom
|
||||
update_transparent_checker_offset()
|
||||
_update_rulers()
|
||||
else:
|
||||
var dir := Input.get_vector(&"camera_left", &"camera_right", &"camera_up", &"camera_down")
|
||||
if dir != Vector2.ZERO and !_has_selection_tool():
|
||||
offset += (dir.rotated(rotation) / zoom) * CAMERA_SPEED_RATE
|
||||
_update_rulers()
|
||||
offset += (dir.rotated(camera_angle) / zoom) * CAMERA_SPEED_RATE
|
||||
|
||||
save_values_to_project()
|
||||
|
||||
|
||||
func _has_selection_tool() -> bool:
|
||||
for slot in Tools._slots.values():
|
||||
if slot.tool_node is BaseSelectionTool:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# Rotate Camera
|
||||
func _rotate_camera_around_point(degrees: float, point: Vector2) -> void:
|
||||
offset = (offset - point).rotated(deg_to_rad(degrees)) + point
|
||||
rotation_degrees = wrapf(rotation_degrees + degrees, -180, 180)
|
||||
rotation_changed.emit()
|
||||
|
||||
|
||||
func _rotation_changed() -> void:
|
||||
if index == Cameras.MAIN:
|
||||
# Negative to make going up in value clockwise, and match the spinbox which does the same
|
||||
var degrees := wrapf(-rotation_degrees, -180, 180)
|
||||
rotation_slider.value = degrees
|
||||
_update_rulers()
|
||||
|
||||
|
||||
func zoom_camera(dir: int) -> void:
|
||||
var viewport_size := viewport_container.size
|
||||
if Global.smooth_zoom:
|
||||
|
@ -137,13 +102,12 @@ func zoom_camera(dir: int) -> void:
|
|||
var new_offset := (
|
||||
offset
|
||||
+ (
|
||||
(-0.5 * viewport_size + mouse_pos).rotated(rotation)
|
||||
(-0.5 * viewport_size + mouse_pos).rotated(camera_angle)
|
||||
* (Vector2.ONE / zoom - Vector2.ONE / new_zoom)
|
||||
)
|
||||
)
|
||||
var tween := create_tween().set_parallel()
|
||||
tween.set_trans(Tween.TRANS_LINEAR).set_ease(Tween.EASE_IN)
|
||||
tween.step_finished.connect(_on_tween_step)
|
||||
tween.tween_property(self, "zoom", new_zoom, 0.05)
|
||||
tween.tween_property(self, "offset", new_offset, 0.05)
|
||||
else:
|
||||
|
@ -158,37 +122,15 @@ func zoom_camera(dir: int) -> void:
|
|||
offset = (
|
||||
offset
|
||||
+ (
|
||||
(-0.5 * viewport_size + mouse_pos).rotated(rotation)
|
||||
(-0.5 * viewport_size + mouse_pos).rotated(camera_angle)
|
||||
* (Vector2.ONE / prev_zoom - Vector2.ONE / zoom)
|
||||
)
|
||||
)
|
||||
zoom_changed.emit()
|
||||
|
||||
|
||||
func _zoom_changed() -> void:
|
||||
update_transparent_checker_offset()
|
||||
if index == Cameras.MAIN:
|
||||
zoom_slider.value = zoom.x * 100.0
|
||||
_update_rulers()
|
||||
for guide in Global.current_project.guides:
|
||||
guide.width = 1.0 / zoom.x * 2
|
||||
|
||||
|
||||
func _update_rulers() -> void:
|
||||
Global.horizontal_ruler.queue_redraw()
|
||||
Global.vertical_ruler.queue_redraw()
|
||||
|
||||
|
||||
func _on_tween_step(_idx: int) -> void:
|
||||
should_tween = false
|
||||
zoom_changed.emit()
|
||||
should_tween = true
|
||||
|
||||
|
||||
func zoom_100() -> void:
|
||||
zoom = Vector2.ONE
|
||||
offset = Global.current_project.size / 2
|
||||
zoom_changed.emit()
|
||||
|
||||
|
||||
func fit_to_frame(size: Vector2) -> void:
|
||||
|
@ -199,12 +141,12 @@ func fit_to_frame(size: Vector2) -> void:
|
|||
offset = size / 2
|
||||
|
||||
# Adjust to the rotated size:
|
||||
if rotation != 0.0:
|
||||
if camera_angle != 0.0:
|
||||
# Calculating the rotated corners of the frame to find its rotated size
|
||||
var a := Vector2.ZERO # Top left
|
||||
var b := Vector2(size.x, 0).rotated(rotation) # Top right
|
||||
var c := Vector2(0, size.y).rotated(rotation) # Bottom left
|
||||
var d := Vector2(size.x, size.y).rotated(rotation) # Bottom right
|
||||
var b := Vector2(size.x, 0).rotated(camera_angle) # Top right
|
||||
var c := Vector2(0, size.y).rotated(camera_angle) # Bottom left
|
||||
var d := Vector2(size.x, size.y).rotated(camera_angle) # Bottom right
|
||||
|
||||
# Find how far apart each opposite point is on each axis, and take the longer one
|
||||
size.x = maxf(absf(a.x - d.x), absf(b.x - c.x))
|
||||
|
@ -229,12 +171,85 @@ func fit_to_frame(size: Vector2) -> void:
|
|||
|
||||
ratio = clampf(ratio, 0.1, ratio)
|
||||
zoom = Vector2(ratio, ratio)
|
||||
zoom_changed.emit()
|
||||
if reset_integer_zoom:
|
||||
Global.integer_zoom = !Global.integer_zoom
|
||||
|
||||
|
||||
func save_values_to_project() -> void:
|
||||
Global.current_project.cameras_rotation[index] = rotation
|
||||
Global.current_project.cameras_rotation[index] = camera_angle
|
||||
Global.current_project.cameras_zoom[index] = zoom
|
||||
Global.current_project.cameras_offset[index] = offset
|
||||
|
||||
|
||||
func update_transparent_checker_offset() -> void:
|
||||
var o := get_global_transform_with_canvas().get_origin()
|
||||
var s := get_global_transform_with_canvas().get_scale()
|
||||
o.y = get_viewport_rect().size.y - o.y
|
||||
transparent_checker.update_offset(o, s)
|
||||
|
||||
|
||||
## Updates the viewport's canvas transform, which is the area of the canvas that is
|
||||
## currently visible. Called every time the camera's zoom, rotation or origin changes.
|
||||
func _update_viewport_transform() -> void:
|
||||
if not is_instance_valid(viewport):
|
||||
return
|
||||
var zoom_scale := Vector2.ONE / zoom
|
||||
var viewport_size := get_viewport_rect().size
|
||||
var screen_offset := viewport_size * 0.5 * zoom_scale
|
||||
screen_offset = screen_offset.rotated(camera_angle)
|
||||
var screen_rect := Rect2(-screen_offset, viewport_size * zoom_scale)
|
||||
screen_rect.position += offset
|
||||
var xform := Transform2D(camera_angle, zoom_scale, 0, screen_rect.position)
|
||||
camera_screen_center = xform * (viewport_size * 0.5)
|
||||
viewport.canvas_transform = xform.affine_inverse()
|
||||
|
||||
|
||||
func _zoom_changed() -> void:
|
||||
update_transparent_checker_offset()
|
||||
if index == Cameras.MAIN:
|
||||
should_tween = false
|
||||
zoom_slider.value = zoom.x * 100.0
|
||||
should_tween = true
|
||||
for guide in Global.current_project.guides:
|
||||
guide.width = 1.0 / zoom.x * 2
|
||||
|
||||
|
||||
func _rotation_changed() -> void:
|
||||
if index == Cameras.MAIN:
|
||||
# Negative to make going up in value clockwise, and match the spinbox which does the same
|
||||
rotation_slider.value = -camera_angle_degrees
|
||||
|
||||
|
||||
func _zoom_slider_value_changed(value: float) -> void:
|
||||
if value <= 0:
|
||||
value = 1
|
||||
var new_zoom := Vector2(value, value) / 100.0
|
||||
if zoom.is_equal_approx(new_zoom):
|
||||
return
|
||||
if Global.smooth_zoom and should_tween:
|
||||
var tween := create_tween().set_trans(Tween.TRANS_LINEAR).set_ease(Tween.EASE_IN)
|
||||
tween.tween_property(self, "zoom", new_zoom, 0.05)
|
||||
else:
|
||||
zoom = new_zoom
|
||||
|
||||
|
||||
func _rotation_slider_value_changed(value: float) -> void:
|
||||
# Negative makes going up rotate clockwise
|
||||
var angle := deg_to_rad(-value)
|
||||
var difference := angle - camera_angle
|
||||
var canvas_center: Vector2 = Global.current_project.size / 2
|
||||
offset = (offset - canvas_center).rotated(difference) + canvas_center
|
||||
camera_angle = angle
|
||||
|
||||
|
||||
func _has_selection_tool() -> bool:
|
||||
for slot in Tools._slots.values():
|
||||
if slot.tool_node is BaseSelectionTool:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
func _rotate_camera_around_point(degrees: float, point: Vector2) -> void:
|
||||
var angle := deg_to_rad(degrees)
|
||||
offset = (offset - point).rotated(angle) + point
|
||||
camera_angle = camera_angle + angle
|
|
@ -11,6 +11,9 @@ var last: Vector2
|
|||
|
||||
func _ready() -> void:
|
||||
Global.project_switched.connect(queue_redraw)
|
||||
Global.camera.zoom_changed.connect(queue_redraw)
|
||||
Global.camera.rotation_changed.connect(queue_redraw)
|
||||
Global.camera.offset_changed.connect(queue_redraw)
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
|
|
|
@ -11,6 +11,9 @@ var last: Vector2
|
|||
|
||||
func _ready() -> void:
|
||||
Global.project_switched.connect(queue_redraw)
|
||||
Global.camera.zoom_changed.connect(queue_redraw)
|
||||
Global.camera.rotation_changed.connect(queue_redraw)
|
||||
Global.camera.offset_changed.connect(queue_redraw)
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
|
|
|
@ -742,7 +742,7 @@ func paste(in_place := false) -> void:
|
|||
project.selection_offset = clipboard.selection_offset
|
||||
big_bounding_rectangle = clipboard.big_bounding_rectangle
|
||||
if not in_place: # If "Paste" is selected, and not "Paste in Place"
|
||||
var camera_center := Global.camera.get_screen_center_position()
|
||||
var camera_center := Global.camera.camera_screen_center
|
||||
camera_center -= Vector2(big_bounding_rectangle.size) / 2.0
|
||||
var max_pos := project.size - big_bounding_rectangle.size
|
||||
if max_pos.x >= 0:
|
||||
|
|
|
@ -2,7 +2,7 @@ extends PanelContainer
|
|||
|
||||
@onready var preview_zoom_slider := $VBox/HBox/VBoxContainer/PreviewZoomSlider as VSlider
|
||||
@onready var canvas_preview := $"%CanvasPreview" as Node2D
|
||||
@onready var camera := $"%CameraPreview" as Camera2D
|
||||
@onready var camera := $"%CameraPreview" as CanvasCamera
|
||||
@onready var play_button := $"%PlayButton" as Button
|
||||
@onready var start_frame := $"%StartFrame" as ValueSlider
|
||||
@onready var end_frame := $"%EndFrame" as ValueSlider
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
[ext_resource type="PackedScene" uid="uid://3pmb60gpst7b" path="res://src/UI/Nodes/TransparentChecker.tscn" id="1"]
|
||||
[ext_resource type="Shader" path="res://src/Shaders/TransparentChecker.gdshader" id="2"]
|
||||
[ext_resource type="Script" path="res://src/UI/Canvas/CameraMovement.gd" id="3"]
|
||||
[ext_resource type="Script" path="res://src/UI/CanvasPreviewContainer/CanvasPreviewContainer.gd" id="4"]
|
||||
[ext_resource type="PackedScene" uid="uid://c546tskdu53j1" path="res://src/UI/Canvas/CanvasPreview.tscn" id="5"]
|
||||
[ext_resource type="Script" path="res://src/UI/Canvas/CanvasCamera.gd" id="5_ge2km"]
|
||||
[ext_resource type="Texture2D" uid="uid://c7smxwfa8826j" path="res://assets/graphics/timeline/play.png" id="6"]
|
||||
[ext_resource type="Script" path="res://src/UI/Nodes/CollapsibleContainer.gd" id="7_o7sn3"]
|
||||
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="8"]
|
||||
|
@ -77,11 +77,9 @@ anchors_preset = 0
|
|||
[node name="CanvasPreview" parent="VBox/HBox/PreviewViewportContainer/SubViewport" instance=ExtResource("5")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="CameraPreview" type="Camera2D" parent="VBox/HBox/PreviewViewportContainer/SubViewport"]
|
||||
[node name="CameraPreview" type="Node2D" parent="VBox/HBox/PreviewViewportContainer/SubViewport"]
|
||||
unique_name_in_owner = true
|
||||
offset = Vector2(32, 32)
|
||||
zoom = Vector2(0.15, 0.15)
|
||||
script = ExtResource("3")
|
||||
script = ExtResource("5_ge2km")
|
||||
index = 2
|
||||
|
||||
[node name="Animation" type="HBoxContainer" parent="VBox"]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
[ext_resource type="Script" path="res://src/UI/Canvas/Rulers/VerticalRuler.gd" id="4"]
|
||||
[ext_resource type="PackedScene" uid="uid://3pmb60gpst7b" path="res://src/UI/Nodes/TransparentChecker.tscn" id="5"]
|
||||
[ext_resource type="Script" path="res://src/UI/Canvas/Rulers/HorizontalRuler.gd" id="6"]
|
||||
[ext_resource type="Script" path="res://src/UI/Canvas/CameraMovement.gd" id="7"]
|
||||
[ext_resource type="Script" path="res://src/UI/Canvas/CanvasCamera.gd" id="7"]
|
||||
[ext_resource type="Shader" path="res://src/Shaders/Greyscale.gdshader" id="8"]
|
||||
[ext_resource type="Shader" path="res://src/Shaders/TransparentChecker.gdshader" id="9"]
|
||||
[ext_resource type="PackedScene" uid="uid://wo0hqxkst808" path="res://src/UI/GlobalToolOptions/GlobalToolOptions.tscn" id="10"]
|
||||
|
@ -322,8 +322,7 @@ anchors_preset = 0
|
|||
|
||||
[node name="Canvas" parent="DockableContainer/Main Canvas/ViewportandVerticalRuler/SubViewportContainer/SubViewport" instance=ExtResource("19")]
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="DockableContainer/Main Canvas/ViewportandVerticalRuler/SubViewportContainer/SubViewport"]
|
||||
zoom = Vector2(0.15, 0.15)
|
||||
[node name="Camera2D" type="Node2D" parent="DockableContainer/Main Canvas/ViewportandVerticalRuler/SubViewportContainer/SubViewport"]
|
||||
script = ExtResource("7")
|
||||
|
||||
[node name="CanvasLayer" type="CanvasLayer" parent="DockableContainer/Main Canvas/ViewportandVerticalRuler/SubViewportContainer/SubViewport"]
|
||||
|
@ -359,8 +358,7 @@ anchors_preset = 0
|
|||
|
||||
[node name="CanvasPreview" parent="DockableContainer/Second Canvas/SubViewport" instance=ExtResource("2")]
|
||||
|
||||
[node name="Camera2D2" type="Camera2D" parent="DockableContainer/Second Canvas/SubViewport"]
|
||||
zoom = Vector2(0.15, 0.15)
|
||||
[node name="Camera2D2" type="Node2D" parent="DockableContainer/Second Canvas/SubViewport"]
|
||||
script = ExtResource("7")
|
||||
index = 1
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ extends SubViewportContainer
|
|||
|
||||
@export var camera_path: NodePath
|
||||
|
||||
@onready var camera := get_node(camera_path) as Camera2D
|
||||
@onready var camera := get_node(camera_path) as CanvasCamera
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
Loading…
Reference in a new issue