added weapon wheel and model texture creator

This commit is contained in:
derek
2024-12-11 16:11:29 -06:00
parent 0ec82d84aa
commit 5ceaa00785
45 changed files with 1337 additions and 25 deletions

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M4 11V4h7" fill="none" stroke="#fff" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" stroke-opacity=".6"/></svg>

After

Width:  |  Height:  |  Size: 221 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://btc01wc11tiid"
path="res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg"
dest_files=["res://.godot/imported/GuiResizerTopLeft.svg-eb563f557424c74239e878a1213a5bf4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M12 11V4h-7" fill="none" stroke="#fff" stroke-opacity=".6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>

After

Width:  |  Height:  |  Size: 223 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://04l05jxuyt7k"
path="res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg"
dest_files=["res://.godot/imported/GuiResizerTopRight.svg-cc1dc8556d51357c5eb0b01d09d8f049.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4 1v1l1 1v3h6v-3l1-1v-1zm1 6-2 3h10l-2-3zm2 4v2l1 2 1-2v-2z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 177 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://do6d60od41vmg"
path="res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/anthonyec.camera_preview/Pin.svg"
dest_files=["res://.godot/imported/Pin.svg-83b09f5c00a829c5d8b136bf5bae65bc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,7 @@
[plugin]
name="Little Camera Preview"
description="Shows a picture-in-picture preview of the selected 2D or 3D camera"
author="Anthony Cossins"
version="1.3"
script="plugin.gd"

View File

@@ -0,0 +1,87 @@
@tool
extends EditorPlugin
const preview_scene = preload("res://addons/anthonyec.camera_preview/preview.tscn")
var preview: CameraPreview
var current_main_screen_name: String
func _enter_tree() -> void:
main_screen_changed.connect(_on_main_screen_changed)
EditorInterface.get_selection().selection_changed.connect(_on_editor_selection_changed)
# Initialise preview panel and add to main screen.
preview = preview_scene.instantiate() as CameraPreview
preview.request_hide()
var main_screen = EditorInterface.get_editor_main_screen()
main_screen.add_child(preview)
func _exit_tree() -> void:
if preview:
preview.queue_free()
func _ready() -> void:
# TODO: Currently there is no API to get the main screen name without
# listening to the `EditorPlugin.main_screen_changed` signal:
# https://github.com/godotengine/godot-proposals/issues/2081
EditorInterface.set_main_screen_editor("Script")
EditorInterface.set_main_screen_editor("3D")
func _on_main_screen_changed(screen_name: String) -> void:
current_main_screen_name = screen_name
# TODO: Bit of a hack to prevent pinned staying between view changes on the same scene.
preview.unlink_camera()
_on_editor_selection_changed()
func _on_editor_selection_changed() -> void:
if not is_main_screen_viewport():
# This hides the preview "container" and not the preview itself, allowing
# any locked previews to remain visible once switching back to 3D tab.
preview.visible = false
return
preview.visible = true
var selected_nodes = EditorInterface.get_selection().get_selected_nodes()
var selected_camera_3d: Camera3D = find_camera_3d_or_null(selected_nodes)
var selected_camera_2d: Camera2D = find_camera_2d_or_null(selected_nodes)
if selected_camera_3d and current_main_screen_name == "3D":
preview.link_with_camera_3d(selected_camera_3d)
preview.request_show()
elif selected_camera_2d and current_main_screen_name == "2D":
preview.link_with_camera_2d(selected_camera_2d)
preview.request_show()
else:
preview.request_hide()
func is_main_screen_viewport() -> bool:
return current_main_screen_name == "3D" or current_main_screen_name == "2D"
func find_camera_3d_or_null(nodes: Array[Node]) -> Camera3D:
var camera: Camera3D
for node in nodes:
if node is Camera3D:
camera = node as Camera3D
break
return camera
func find_camera_2d_or_null(nodes: Array[Node]) -> Camera2D:
var camera: Camera2D
for node in nodes:
if node is Camera2D:
camera = node as Camera2D
break
return camera
func _on_selected_camera_3d_tree_exiting() -> void:
preview.unlink_camera()

View File

@@ -0,0 +1,404 @@
@tool
class_name CameraPreview
extends Control
enum CameraType {
CAMERA_2D,
CAMERA_3D
}
enum PinnedPosition {
LEFT,
RIGHT,
}
enum InteractionState {
NONE,
RESIZE,
DRAG,
# Animation is split into 2 seperate states so that the tween is only
# invoked once in the "start" state.
START_ANIMATE_INTO_PLACE,
ANIMATE_INTO_PLACE,
}
const margin_3d: Vector2 = Vector2(10, 10)
const margin_2d: Vector2 = Vector2(20, 15)
const panel_margin: float = 2
const min_panel_size: float = 250
@onready var panel: Panel = %Panel
@onready var placeholder: Panel = %Placeholder
@onready var preview_camera_3d: Camera3D = %Camera3D
@onready var preview_camera_2d: Camera2D = %Camera2D
@onready var sub_viewport: SubViewport = %SubViewport
@onready var sub_viewport_text_rect: TextureRect = %TextureRect
@onready var resize_left_handle: Button = %ResizeLeftHandle
@onready var resize_right_handle: Button = %ResizeRightHandle
@onready var lock_button: Button = %LockButton
@onready var gradient: TextureRect = %Gradient
@onready var viewport_margin_container: MarginContainer = %ViewportMarginContainer
@onready var overlay_margin_container: MarginContainer = %OverlayMarginContainer
@onready var overlay_container: Control = %OverlayContainer
var camera_type: CameraType = CameraType.CAMERA_3D
var pinned_position: PinnedPosition = PinnedPosition.RIGHT
var viewport_ratio: float = 1
var editor_scale: float = EditorInterface.get_editor_scale()
var is_locked: bool
var show_controls: bool
var selected_camera_3d: Camera3D
var selected_camera_2d: Camera2D
var state: InteractionState = InteractionState.NONE
var initial_mouse_position: Vector2
var initial_panel_size: Vector2
var initial_panel_position: Vector2
func _ready() -> void:
# Set initial width.
panel.size.x = min_panel_size * editor_scale
# Setting texture to viewport in code instead of directly in the editor
# because otherwise an error "Path to node is invalid: Panel/SubViewport"
# on first load. This is harmless but doesn't look great.
#
# This is a known issue:
# https://github.com/godotengine/godot/issues/27790#issuecomment-499740220
sub_viewport_text_rect.texture = sub_viewport.get_texture()
# From what I can tell there's something wrong with how an editor theme
# scales when used within a plugin. It seems to ignore the screen scale.
# For instance, a 30x30px button will appear tiny on a retina display.
#
# Someone else had the issue with no luck:
# https://forum.godotengine.org/t/how-to-scale-plugin-controls-to-look-the-same-in-4k-as-1080p/36151
#
# And seems Dialogic also scales buttons manually:
# https://github.com/dialogic-godot/dialogic/blob/master/addons/dialogic/Editor/Common/sidebar.gd#L25C6-L38
#
# Maybe I don't know the correct way to do it, so for now the workaround is
# to set the correct size in code using screen scale.
var button_size = Vector2(30, 30) * editor_scale
var margin_size: float = panel_margin * editor_scale
resize_left_handle.size = button_size
resize_left_handle.pivot_offset = Vector2(0, 0) * editor_scale
resize_right_handle.size = button_size
resize_right_handle.pivot_offset = Vector2(30, 30) * editor_scale
lock_button.size = button_size
lock_button.pivot_offset = Vector2(0, 30) * editor_scale
viewport_margin_container.add_theme_constant_override("margin_left", margin_size)
viewport_margin_container.add_theme_constant_override("margin_top", margin_size)
viewport_margin_container.add_theme_constant_override("margin_right", margin_size)
viewport_margin_container.add_theme_constant_override("margin_bottom", margin_size)
overlay_margin_container.add_theme_constant_override("margin_left", margin_size)
overlay_margin_container.add_theme_constant_override("margin_top", margin_size)
overlay_margin_container.add_theme_constant_override("margin_right", margin_size)
overlay_margin_container.add_theme_constant_override("margin_bottom", margin_size)
# Parent node overlay size is not available on first ready, need to wait a
# frame for it to be drawn.
await get_tree().process_frame
# Anchors are set in code because setting them in the editor UI doesn't take
# editor scale into account.
resize_left_handle.position = Vector2(0, 0)
resize_right_handle.set_anchors_preset(Control.PRESET_TOP_LEFT)
resize_right_handle.position = Vector2(overlay_container.size.x - button_size.x, 0)
resize_right_handle.set_anchors_preset(Control.PRESET_TOP_RIGHT)
lock_button.position = Vector2(0, overlay_container.size.y - button_size.y)
lock_button.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
func _process(_delta: float) -> void:
if not visible: return
match state:
InteractionState.NONE:
panel.size = get_clamped_size(panel.size)
panel.position = get_pinned_position(pinned_position)
InteractionState.RESIZE:
var delta_mouse_position = initial_mouse_position - get_global_mouse_position()
var resized_size = panel.size
if pinned_position == PinnedPosition.LEFT:
resized_size = initial_panel_size - delta_mouse_position
if pinned_position == PinnedPosition.RIGHT:
resized_size = initial_panel_size + delta_mouse_position
panel.size = get_clamped_size(resized_size)
panel.position = get_pinned_position(pinned_position)
InteractionState.DRAG:
placeholder.size = panel.size
var global_mouse_position = get_global_mouse_position()
var offset = initial_mouse_position - initial_panel_position
panel.global_position = global_mouse_position - offset
if global_mouse_position.x < global_position.x + size.x / 2:
pinned_position = PinnedPosition.LEFT
else:
pinned_position = PinnedPosition.RIGHT
placeholder.position = get_pinned_position(pinned_position)
InteractionState.START_ANIMATE_INTO_PLACE:
var final_position: Vector2 = get_pinned_position(pinned_position)
var tween = get_tree().create_tween()
tween.set_ease(Tween.EASE_OUT)
tween.set_trans(Tween.TRANS_CUBIC)
tween.tween_property(panel, "position", final_position, 0.3)
tween.finished.connect(func():
panel.position = final_position
state = InteractionState.NONE
)
state = InteractionState.ANIMATE_INTO_PLACE
# I couldn't get `mouse_entered` and `mouse_exited` events to work
# nicely, so I use rect method instead. Plus using this method it's easy to
# grow the hit area size.
var panel_hover_rect = Rect2(panel.global_position, panel.size)
panel_hover_rect = panel_hover_rect.grow(40)
var mouse_position = get_global_mouse_position()
show_controls = state != InteractionState.NONE or panel_hover_rect.has_point(mouse_position)
# UI visibility.
resize_left_handle.visible = show_controls and pinned_position == PinnedPosition.RIGHT
resize_right_handle.visible = show_controls and pinned_position == PinnedPosition.LEFT
lock_button.visible = show_controls or is_locked
placeholder.visible = state == InteractionState.DRAG or state == InteractionState.ANIMATE_INTO_PLACE
gradient.visible = show_controls
# Sync camera settings.
if camera_type == CameraType.CAMERA_3D and selected_camera_3d:
sub_viewport.size = panel.size
# Sync position and rotation without using a `RemoteTransform` node
# because if you save a camera as a scene, the remote transform node will
# be stored within the scene. Also it's harder to keep the remote
# transform `remote_path` up-to-date with scene changes, which causes
# many errors.
preview_camera_3d.global_position = selected_camera_3d.global_position
preview_camera_3d.global_rotation = selected_camera_3d.global_rotation
preview_camera_3d.fov = selected_camera_3d.fov
preview_camera_3d.projection = selected_camera_3d.projection
preview_camera_3d.size = selected_camera_3d.size
preview_camera_3d.cull_mask = selected_camera_3d.cull_mask
preview_camera_3d.keep_aspect = selected_camera_3d.keep_aspect
preview_camera_3d.near = selected_camera_3d.near
preview_camera_3d.far = selected_camera_3d.far
preview_camera_3d.h_offset = selected_camera_3d.h_offset
preview_camera_3d.v_offset = selected_camera_3d.v_offset
preview_camera_3d.attributes = selected_camera_3d.attributes
preview_camera_3d.environment = selected_camera_3d.environment
if camera_type == CameraType.CAMERA_2D and selected_camera_2d:
var project_window_size = get_project_window_size()
var ratio = project_window_size.x / panel.size.x
# TODO: Is there a better way to fix this?
# The camera border is visible sometimes due to pixel rounding.
# Subtract 1px from right and bottom to hide this.
var hide_camera_border_fix = Vector2(1, 1)
sub_viewport.size = panel.size
sub_viewport.size_2d_override = (panel.size - hide_camera_border_fix) * ratio
sub_viewport.size_2d_override_stretch = true
preview_camera_2d.global_position = selected_camera_2d.global_position
preview_camera_2d.global_rotation = selected_camera_2d.global_rotation
preview_camera_2d.offset = selected_camera_2d.offset
preview_camera_2d.zoom = selected_camera_2d.zoom
preview_camera_2d.ignore_rotation = selected_camera_2d.ignore_rotation
preview_camera_2d.anchor_mode = selected_camera_2d.anchor_mode
preview_camera_2d.limit_left = selected_camera_2d.limit_left
preview_camera_2d.limit_right = selected_camera_2d.limit_right
preview_camera_2d.limit_top = selected_camera_2d.limit_top
preview_camera_2d.limit_bottom = selected_camera_2d.limit_bottom
func link_with_camera_3d(camera_3d: Camera3D) -> void:
# TODO: Camera may not be ready since this method is called in `_enter_tree`
# in the plugin because of a workaround for:
# https://github.com/godotengine/godot-proposals/issues/2081
if not preview_camera_3d:
return request_hide()
var is_different_camera = camera_3d != preview_camera_3d
# TODO: A bit messy.
if is_different_camera:
if preview_camera_3d.tree_exiting.is_connected(unlink_camera):
preview_camera_3d.tree_exiting.disconnect(unlink_camera)
if not camera_3d.tree_exiting.is_connected(unlink_camera):
camera_3d.tree_exiting.connect(unlink_camera)
sub_viewport.disable_3d = false
sub_viewport.world_3d = camera_3d.get_world_3d()
selected_camera_3d = camera_3d
camera_type = CameraType.CAMERA_3D
func link_with_camera_2d(camera_2d: Camera2D) -> void:
if not preview_camera_2d:
return request_hide()
var is_different_camera = camera_2d != preview_camera_2d
# TODO: A bit messy.
if is_different_camera:
if preview_camera_2d.tree_exiting.is_connected(unlink_camera):
preview_camera_2d.tree_exiting.disconnect(unlink_camera)
if not camera_2d.tree_exiting.is_connected(unlink_camera):
camera_2d.tree_exiting.connect(unlink_camera)
sub_viewport.disable_3d = true
sub_viewport.world_2d = camera_2d.get_world_2d()
selected_camera_2d = camera_2d
camera_type = CameraType.CAMERA_2D
func unlink_camera() -> void:
if selected_camera_3d:
selected_camera_3d = null
if selected_camera_2d:
selected_camera_2d = null
is_locked = false
lock_button.button_pressed = false
func request_hide() -> void:
if is_locked: return
visible = false
func request_show() -> void:
visible = true
func get_pinned_position(pinned_position: PinnedPosition) -> Vector2:
var margin: Vector2 = margin_3d * editor_scale
if camera_type == CameraType.CAMERA_2D:
margin = margin_2d * editor_scale
match pinned_position:
PinnedPosition.LEFT:
return Vector2.ZERO - Vector2(0, panel.size.y) - Vector2(-margin.x, margin.y)
PinnedPosition.RIGHT:
return size - panel.size - margin
_:
assert(false, "Unknown pinned position %s" % str(pinned_position))
return Vector2.ZERO
func get_clamped_size(desired_size: Vector2) -> Vector2:
var viewport_ratio = get_project_window_ratio()
var editor_viewport_size = get_editor_viewport_size()
var max_bounds = Vector2(
editor_viewport_size.x * 0.6,
editor_viewport_size.y * 0.8
)
var clamped_size = desired_size
# Apply aspect ratio.
clamped_size = Vector2(clamped_size.x, clamped_size.x * viewport_ratio)
# Clamp the max size while respecting the aspect ratio.
if clamped_size.y >= max_bounds.y:
clamped_size.x = max_bounds.y / viewport_ratio
clamped_size.y = max_bounds.y
if clamped_size.x >= max_bounds.x:
clamped_size.x = max_bounds.x
clamped_size.y = max_bounds.x * viewport_ratio
# Clamp the min size based on if it's portrait or landscape. Portrait min
# size should be based on it's height. Landscape min size is based on it's
# width instead. Applying min width to a portrait size would make it too big.
var is_portrait = viewport_ratio > 1
if is_portrait and clamped_size.y <= min_panel_size * editor_scale:
clamped_size.x = min_panel_size / viewport_ratio
clamped_size.y = min_panel_size
clamped_size = clamped_size * editor_scale
if not is_portrait and clamped_size.x <= min_panel_size * editor_scale:
clamped_size.x = min_panel_size
clamped_size.y = min_panel_size * viewport_ratio
clamped_size = clamped_size * editor_scale
# Round down to avoid sub-pixel artifacts, mainly seen around the margins.
return clamped_size.floor()
func get_project_window_size() -> Vector2:
var window_width = float(ProjectSettings.get_setting("display/window/size/viewport_width"))
var window_height = float(ProjectSettings.get_setting("display/window/size/viewport_height"))
return Vector2(window_width, window_height)
func get_project_window_ratio() -> float:
var project_window_size = get_project_window_size()
return project_window_size.y / project_window_size.x
func get_editor_viewport_size() -> Vector2:
var fallback_size = EditorInterface.get_editor_main_screen().size
# There isn't an API for getting the viewport node. Instead it has to be
# found by checking the parent's parent of the subviewport and find
# the correct node based on name and class.
var editor_sub_viewport_3d = EditorInterface.get_editor_viewport_3d(0)
var editor_viewport_container = editor_sub_viewport_3d.get_parent().get_parent().get_parent()
# Early return incase editor tree structure has changed.
if editor_viewport_container.get_class() != "Node3DEditorViewportContainer":
return fallback_size
return editor_viewport_container.size
func _on_resize_handle_button_down() -> void:
if state != InteractionState.NONE: return
state = InteractionState.RESIZE
initial_mouse_position = get_global_mouse_position()
initial_panel_size = panel.size
func _on_resize_handle_button_up() -> void:
state = InteractionState.NONE
func _on_drag_handle_button_down() -> void:
if state != InteractionState.NONE: return
state = InteractionState.DRAG
initial_mouse_position = get_global_mouse_position()
initial_panel_position = panel.global_position
func _on_drag_handle_button_up() -> void:
if state != InteractionState.DRAG: return
state = InteractionState.START_ANIMATE_INTO_PLACE
func _on_lock_button_pressed() -> void:
is_locked = !is_locked

View File

@@ -0,0 +1,200 @@
[gd_scene load_steps=8 format=3 uid="uid://xybmfvufjuv"]
[ext_resource type="Script" path="res://addons/anthonyec.camera_preview/preview.gd" id="1_6b32r"]
[ext_resource type="Texture2D" uid="uid://do6d60od41vmg" path="res://addons/anthonyec.camera_preview/Pin.svg" id="2_p0pa8"]
[ext_resource type="Texture2D" uid="uid://btc01wc11tiid" path="res://addons/anthonyec.camera_preview/GuiResizerTopLeft.svg" id="2_t64ej"]
[ext_resource type="Texture2D" uid="uid://04l05jxuyt7k" path="res://addons/anthonyec.camera_preview/GuiResizerTopRight.svg" id="3_6yuab"]
[sub_resource type="ViewportTexture" id="ViewportTexture_hchdq"]
viewport_path = NodePath("Panel/SubViewport")
[sub_resource type="Gradient" id="Gradient_11p6r"]
offsets = PackedFloat32Array(0, 0.3, 0.6, 1)
colors = PackedColorArray(0, 0, 0, 0.235294, 0, 0, 0, 0.0784314, 0, 0, 0, 0.0784314, 0, 0, 0, 0.235294)
[sub_resource type="GradientTexture2D" id="GradientTexture2D_4dkve"]
gradient = SubResource("Gradient_11p6r")
width = 256
height = 256
fill_to = Vector2(2.08165e-12, 1)
[node name="Preview" type="Control"]
z_index = 999
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_6b32r")
[node name="Placeholder" type="Panel" parent="."]
unique_name_in_owner = true
visible = false
modulate = Color(1, 1, 1, 0.705882)
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -40.0
offset_top = -40.0
offset_right = 410.0
offset_bottom = 410.0
grow_horizontal = 0
grow_vertical = 0
[node name="Panel" type="Panel" parent="."]
unique_name_in_owner = true
clip_contents = true
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -520.0
offset_top = -908.889
offset_right = -20.0
offset_bottom = -20.0
grow_horizontal = 0
grow_vertical = 0
pivot_offset = Vector2(450, 300)
[node name="SubViewport" type="SubViewport" parent="Panel"]
unique_name_in_owner = true
handle_input_locally = false
gui_disable_input = true
size_2d_override_stretch = true
[node name="Camera3D" type="Camera3D" parent="Panel/SubViewport"]
unique_name_in_owner = true
current = true
[node name="Camera2D" type="Camera2D" parent="Panel/SubViewport"]
unique_name_in_owner = true
ignore_rotation = false
[node name="ViewportMarginContainer" type="MarginContainer" parent="Panel"]
unique_name_in_owner = true
clip_contents = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 4
theme_override_constants/margin_top = 4
theme_override_constants/margin_right = 4
theme_override_constants/margin_bottom = 4
[node name="TextureRect" type="TextureRect" parent="Panel/ViewportMarginContainer"]
unique_name_in_owner = true
layout_mode = 2
texture = SubResource("ViewportTexture_hchdq")
expand_mode = 1
[node name="Gradient" type="TextureRect" parent="Panel"]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
texture = SubResource("GradientTexture2D_4dkve")
[node name="OverlayMarginContainer" type="MarginContainer" parent="Panel"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_override_constants/margin_left = 4
theme_override_constants/margin_top = 4
theme_override_constants/margin_right = 4
theme_override_constants/margin_bottom = 4
[node name="OverlayContainer" type="Control" parent="Panel/OverlayMarginContainer"]
unique_name_in_owner = true
clip_contents = true
layout_mode = 2
mouse_filter = 2
[node name="DragHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_mode = 0
flat = true
[node name="ResizeLeftHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 1
offset_right = 60.0
offset_bottom = 60.0
size_flags_horizontal = 0
size_flags_vertical = 0
mouse_default_cursor_shape = 12
icon = ExtResource("2_t64ej")
flat = true
icon_alignment = 1
expand_icon = true
[node name="ResizeRightHandle" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -60.0
offset_bottom = 60.0
pivot_offset = Vector2(60, 60)
size_flags_horizontal = 8
size_flags_vertical = 0
mouse_default_cursor_shape = 11
icon = ExtResource("3_6yuab")
flat = true
icon_alignment = 1
expand_icon = true
[node name="LockButton" type="Button" parent="Panel/OverlayMarginContainer/OverlayContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_top = -60.0
offset_right = 60.0
pivot_offset = Vector2(0, 60)
size_flags_horizontal = 0
size_flags_vertical = 8
tooltip_text = "Always Show Preview"
toggle_mode = true
icon = ExtResource("2_p0pa8")
flat = true
icon_alignment = 1
expand_icon = true
[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_down"]
[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_button_up"]
[connection signal="renamed" from="Panel/OverlayMarginContainer/OverlayContainer/DragHandle" to="." method="_on_drag_handle_renamed"]
[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_down"]
[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeLeftHandle" to="." method="_on_resize_handle_button_up"]
[connection signal="button_down" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_down"]
[connection signal="button_up" from="Panel/OverlayMarginContainer/OverlayContainer/ResizeRightHandle" to="." method="_on_resize_handle_button_up"]
[connection signal="pressed" from="Panel/OverlayMarginContainer/OverlayContainer/LockButton" to="." method="_on_lock_button_pressed"]