created fog of war

This commit is contained in:
Douwe Ravers
2024-10-05 13:12:42 +02:00
parent 71c3fad482
commit 4c0c57d5ba
40 changed files with 3396 additions and 0 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"]

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Vladi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,2 @@
# Debug_environments
A simple Godot addon that allows to add context (extra scene) to scenes in "Run Current Scene" mode.

View File

@@ -0,0 +1,37 @@
@tool
extends EditorInspectorPlugin
func _can_handle(object):
return object is Node and object == EditorInterface.get_edited_scene_root()
func _parse_begin(object):
add_custom_control(create_ui(object))
func create_ui(node: Node) -> Control:
var path = DebugEnvironmentLib.get_debug_path(node)
var button = Button.new()
button.icon = load("res://addons/godot_debug_environments/flask.svg")
if(FileAccess.file_exists(path)):
button.text = "Edit testing environment"
button.pressed.connect(func(): edit_env(path), CONNECT_DEFERRED)
else:
button.text = "Create testing environment"
button.pressed.connect(func(): create_env(path), CONNECT_DEFERRED)
return button
func edit_env(path: String):
EditorInterface.open_scene_from_path(path)
func create_env(path: String):
if !DirAccess.dir_exists_absolute(path.get_base_dir()):
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
var env = Node.new()
env.name = path.get_file().replace(".tscn", "")
var target = Node.new()
target.name = "$DEBUG_TARGET$"
env.add_child(target)
target.owner = env
var scene = PackedScene.new()
scene.pack(env)
ResourceSaver.save(scene, path)
edit_env(path)

View File

@@ -0,0 +1,6 @@
class_name DebugEnvironmentLib extends Object
const debug_environment_path = "res://editor/debug_environments/debug_env_$SCENE$"
static func get_debug_path(node:Node) -> String:
return debug_environment_path.replace("$SCENE$", node.scene_file_path.get_file())

View File

@@ -0,0 +1,10 @@
extends Node
func _ready() -> void:
# Get loaded scene
var node = get_tree().current_scene
# Check if debug environment is stored
var path = DebugEnvironmentLib.get_debug_path(node)
if not FileAccess.file_exists(path): return
# Replace instance with debug environment
get_tree().call_deferred("change_scene_to_file",path)

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#ffffff" class="icon icon-tabler icons-tabler-filled icon-tabler-flask"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 2a1 1 0 0 1 0 2v4.826l3.932 10.814l.034 .077a1.7 1.7 0 0 1 -.002 1.193l-.07 .162a1.7 1.7 0 0 1 -1.213 .911l-.181 .017h-11l-.181 -.017a1.7 1.7 0 0 1 -1.285 -2.266l.039 -.09l3.927 -10.804v-4.823a1 1 0 1 1 0 -2h6zm-2 2h-2v4h2v-4z" /></svg>

After

Width:  |  Height:  |  Size: 467 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://clfknatufch1h"
path="res://.godot/imported/flask.svg-2fc53ef5b1742277dd2e351a1ac292cf.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/godot_debug_environments/flask.svg"
dest_files=["res://.godot/imported/flask.svg-2fc53ef5b1742277dd2e351a1ac292cf.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=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,19 @@
{
"profiles": {
"Godot Debug": {
"commandName": "Executable",
"executablePath": "$EDITOR$",
"commandLineArgs": "--path . --verbose",
"workingDirectory": ".",
"nativeDebugging": true
},
"Godot Debug $NAME$": {
"commandName": "Executable",
"executablePath": "$EDITOR$",
"commandLineArgs": "--upwards $PATH$ --verbose",
"workingDirectory": ".",
"nativeDebugging": true
}
}
}

View File

@@ -0,0 +1,10 @@
[plugin]
name="Debug environments"
description="
A simple script that loads a given scene in a specified environment.
Good to test scenes in a certain context without having to manually select the right test scene.
"
author="Douwe Ravers"
version="1.0"
script="plugin.gd"

View File

@@ -0,0 +1,13 @@
@tool
extends EditorPlugin
var plugin
func _enter_tree():
plugin = preload("res://addons/godot_debug_environments/debug_environment_editor_inspector.gd").new()
add_autoload_singleton("DebugEnvironmentWatcher", "res://addons/godot_debug_environments/debug_environment_watcher.gd")
add_inspector_plugin(plugin)
func _exit_tree():
remove_autoload_singleton("DebugEnvironmentWatcher")
remove_inspector_plugin(plugin)

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192">
<path fill="#ccc" d="m24.863 51.73 5.274 89.665 72.375 31.894 57.93-37.187 5.038-87.993-74.902-16.453" />
<path fill="none" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="6" d="m24.862 51.731 5.274 89.665 72.375 31.893 57.93-37.186 5.039-87.992-74.903-16.453L24.862 51.73m140.618-3.62-62.372 25.705" />
<path fill="none" stroke="black" stroke-width="6" d="m24.862 51.731 78.242 22.086-.593 99.472" />
</svg>

After

Width:  |  Height:  |  Size: 502 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cqln7j6oifypf"
path="res://.godot/imported/thumb_placeholder.svg-81a80a6d7c59edb9d7ce28b01f213c5c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/scene-library/icons/thumb_placeholder.svg"
dest_files=["res://.godot/imported/thumb_placeholder.svg-81a80a6d7c59edb9d7ce28b01f213c5c.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=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,7 @@
[plugin]
name="Scene Library"
description="A tool for easy work with your scenes"
author="Mansur Isaev and contributors"
version="0.7.0-dev"
script="plugin.gd"

View File

@@ -0,0 +1,65 @@
# Copyright (c) 2023-2024 Mansur Isaev and contributors - MIT License
# See `LICENSE.md` included in the source distribution for details.
@tool
extends EditorPlugin
const SceneLibrary = preload("res://addons/scene-library/scripts/scene_library.gd")
const BUTTON_NAME: String = "Scene Library"
var _editor: SceneLibrary = null
var _button: Button = null
func _enter_tree() -> void:
_editor = SceneLibrary.new()
_button = add_control_to_bottom_panel(_editor, BUTTON_NAME)
_editor.open_asset_request.connect(_on_open_asset_request)
_editor.show_in_file_system_request.connect(_on_show_in_file_system_request)
_editor.show_in_file_manager_request.connect(_on_show_in_file_manager_request)
_editor.library_saved.connect(_on_library_saved)
_editor.library_unsaved.connect(_on_library_unsaved)
get_parent().connect(&"scene_saved", _editor.handle_scene_saved)
EditorInterface.get_file_system_dock().files_moved.connect(_editor.handle_file_moved)
EditorInterface.get_file_system_dock().file_removed.connect(_editor.handle_file_removed)
func _exit_tree() -> void:
remove_control_from_bottom_panel(_editor)
_editor.queue_free()
func _save_external_data() -> void:
if _editor.is_saved():
return
var library_path: String = _editor.get_current_library_path()
if library_path.is_empty():
return
_editor.save_library(library_path)
func _on_library_saved() -> void:
_button.set_text(BUTTON_NAME)
func _on_library_unsaved() -> void:
_button.set_text(BUTTON_NAME + "(*)")
func _on_open_asset_request(path: String) -> void:
EditorInterface.open_scene_from_path(path)
func _on_show_in_file_system_request(path: String) -> void:
EditorInterface.get_file_system_dock().navigate_to_path(path)
func _on_show_in_file_manager_request(path: String) -> void:
var error := OS.shell_show_in_file_manager(ProjectSettings.globalize_path(path), true)
if error:
push_warning(error_string(error))

View File

@@ -0,0 +1,4 @@
library=Array[Dictionary]([{
"assets": Array[Dictionary]([]),
"name": "Props"
}])

File diff suppressed because it is too large Load Diff