From dda8eac39acc993c2214bc2ddbe5179699e95377 Mon Sep 17 00:00:00 2001 From: midnight Date: Fri, 9 May 2025 23:06:36 +0200 Subject: [PATCH] stuff --- .../.idea/.gitignore | 13 + .../.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 8 + .../.idea/vcs.xml | 6 + addons/debug_menu/debug_menu.gd | 22 +- addons/debug_menu/debug_menu.gd.uid | 1 + addons/debug_menu/debug_menu.tscn | 40 +- addons/debug_menu/plugin.gd.uid | 1 + addons/lightmap_probe_grid/Depth.gdshader | 9 + addons/lightmap_probe_grid/Depth.gdshader.uid | 1 + addons/lightmap_probe_grid/LICENSE.txt | 21 + addons/lightmap_probe_grid/README.md | 79 + addons/lightmap_probe_grid/UI.gd | 89 + addons/lightmap_probe_grid/UI.gd.uid | 1 + .../lightmap_probe_grid/controls_scene.tscn | 198 + .../editor_inspector_plugin.gd | 16 + .../editor_inspector_plugin.gd.uid | 1 + addons/lightmap_probe_grid/gizmo.gd | 159 + addons/lightmap_probe_grid/gizmo.gd.uid | 1 + .../lightmap_probe_grid.gd | 453 ++ .../lightmap_probe_grid.gd.uid | 1 + .../lightmap_probe_grid_icon.svg | 4 + .../lightmap_probe_grid_icon.svg.import | 37 + addons/lightmap_probe_grid/plugin.cfg | 7 + addons/lightmap_probe_grid/plugin.gd | 25 + addons/lightmap_probe_grid/plugin.gd.uid | 1 + .../lightmap_probe_grid/style_box_flat.tres | 13 + default_bus_layout.tres | 27 + export_presets.cfg | 106 +- fonts/AvQest.ttf | Bin 0 -> 51536 bytes fonts/AvQest.ttf.import | 35 + fonts/metamorphous.ttf | Bin 0 -> 135740 bytes fonts/metamorphous.ttf.import | 35 + icon.svg.import | 2 +- materials/highlight.tres | 6 + prefabs/box.tscn | 24 + prefabs/character.tscn | 11 +- prefabs/crosshair001.png | Bin 0 -> 174 bytes prefabs/crosshair001.png.import | 34 + prefabs/interactor.tscn | 33 + prefabs/npc.tscn | 191 + project.godot | 79 +- scenes/blockout/level_blockout.glb | Bin 0 -> 4649144 bytes scenes/blockout/level_blockout.glb.import | 37 + .../blockout/level_blockout.glb.unwrap_cache | Bin 0 -> 148128 bytes scenes/blockout/level_blockout_0.png | Bin 0 -> 2114683 bytes scenes/blockout/level_blockout_0.png.import | 38 + scenes/blockout/level_blockout_1.png | Bin 0 -> 2079432 bytes scenes/blockout/level_blockout_1.png.import | 38 + scenes/blockout/world2.exr | Bin 0 -> 9560049 bytes scenes/blockout/world2.exr.import | 28 + scenes/blockout/world2.lmbake | Bin 0 -> 124253 bytes scenes/blockout/world2_shadow.png | Bin 0 -> 384781 bytes scenes/blockout/world2_shadow.png.import | 29 + scenes/blockout/world3.VoxelGI_data.res | Bin 0 -> 369378 bytes scenes/kloofendal_43d_clear_1k.exr | Bin 0 -> 6097621 bytes scenes/kloofendal_43d_clear_1k.exr.import | 35 + scenes/main.tscn | 64 - scenes/main/main_environment.tres | 49 +- scenes/test.tscn | 288 ++ scenes/world.tscn | 242 + scenes/world2.tscn | 537 +++ scenes/world3.tscn | 171 + shaders/cam.gdshader | 52 + shaders/cam.gdshader.uid | 1 + sounds/quack.mp3 | Bin 0 -> 2256 bytes sounds/quack.mp3.import | 19 + src/Actions/Action.gd | 4 + src/Actions/Action.gd.uid | 1 + src/Actions/SceneChangeAction.gd | 4 + src/Actions/SceneChangeAction.gd.uid | 1 + src/CompositorEffects/new_script.gd | 6 + src/CompositorEffects/new_script.gd.uid | 1 + src/character/camera_controller.gd | 4 +- src/character/camera_controller.gd.uid | 1 + src/character/character.gd | 5 + src/character/character.gd.uid | 1 + src/character/character_controller.gd.uid | 1 + ...interpolate.gd => physics_interpolator.gd} | 2 +- src/character/physics_interpolator.gd.uid | 1 + src/character/player_input.gd | 26 + src/character/player_input.gd.uid | 1 + src/game_manager.gd | 15 +- src/game_manager.gd.uid | 1 + src/interactable.gd | 25 + src/interactable.gd.uid | 1 + src/interactor.gd | 52 + src/interactor.gd.uid | 1 + src/loader/SceneLoader.gd | 47 + src/loader/SceneLoader.gd.uid | 1 + src/npc.gd | 37 + src/npc.gd.uid | 1 + src/shaders/depth.gdshader | 3 + src/shaders/depth.gdshader.uid | 1 + src/shaders/lensflare.gdshader | 35 + src/shaders/lensflare.gdshader.uid | 1 + src/shaders/lib/clr.gdshaderinc | 7 + src/shaders/lib/clr.gdshaderinc.uid | 1 + src/shaders/shadows.gdshader | 67 + src/shaders/shadows.gdshader.uid | 1 + src/shaders/ui/blur.gdshader.uid | 1 + src/shaders/vhs.gdshader | 98 + src/shaders/vhs.gdshader.uid | 1 + src/spawner.gd | 1 + src/spawner.gd.uid | 1 + src/world.gd | 6 + src/world.gd.uid | 1 + ui/8_white.png | Bin 0 -> 200 bytes ui/8_white.png.import | 34 + ui/Background.jpg | Bin 0 -> 134454 bytes ui/Background.jpg.import | 34 + ui/loaD11A.tmp | 93 + ui/loading_screen.tscn | 99 + ui/main_menu.tscn | 86 + ui/pause_menu.tscn | 8 +- ui/src/grab_focus_on_enter.gd | 4 + ui/src/grab_focus_on_enter.gd.uid | 1 + ui/src/loading_screen.gd | 14 + ui/src/loading_screen.gd.uid | 1 + ui/src/main_menu.gd | 13 + ui/src/main_menu.gd.uid | 1 + ui/src/pause_menu.gd | 7 +- ui/src/pause_menu.gd.uid | 1 + ui/theme/borderStylebox.tres | 10 + ui/theme/borderStylebox_disabled.tres | 11 + ui/theme/borderStylebox_selected.tres | 10 + ui/theme/fantasy-ui-borders.svg | 4016 +++++++++++++++++ ui/theme/fantasy-ui-borders.svg.import | 37 + ui/theme/textures/panel-010.png | Bin 0 -> 168 bytes ui/theme/textures/panel-010.png.import | 34 + ui/theme/textures/panel-border-010.png | Bin 0 -> 154 bytes ui/theme/textures/panel-border-010.png.import | 34 + .../textures/panel-transparent-center-010.png | Bin 0 -> 182 bytes .../panel-transparent-center-010.png.import | 34 + ui/theme/ui_theme.tres | 14 + 135 files changed, 8297 insertions(+), 186 deletions(-) create mode 100644 .idea/.idea.3D-Character-Controller-Template.dir/.idea/.gitignore create mode 100644 .idea/.idea.3D-Character-Controller-Template.dir/.idea/encodings.xml create mode 100644 .idea/.idea.3D-Character-Controller-Template.dir/.idea/indexLayout.xml create mode 100644 .idea/.idea.3D-Character-Controller-Template.dir/.idea/vcs.xml create mode 100644 addons/debug_menu/debug_menu.gd.uid create mode 100644 addons/debug_menu/plugin.gd.uid create mode 100644 addons/lightmap_probe_grid/Depth.gdshader create mode 100644 addons/lightmap_probe_grid/Depth.gdshader.uid create mode 100644 addons/lightmap_probe_grid/LICENSE.txt create mode 100644 addons/lightmap_probe_grid/README.md create mode 100644 addons/lightmap_probe_grid/UI.gd create mode 100644 addons/lightmap_probe_grid/UI.gd.uid create mode 100644 addons/lightmap_probe_grid/controls_scene.tscn create mode 100644 addons/lightmap_probe_grid/editor_inspector_plugin.gd create mode 100644 addons/lightmap_probe_grid/editor_inspector_plugin.gd.uid create mode 100644 addons/lightmap_probe_grid/gizmo.gd create mode 100644 addons/lightmap_probe_grid/gizmo.gd.uid create mode 100644 addons/lightmap_probe_grid/lightmap_probe_grid.gd create mode 100644 addons/lightmap_probe_grid/lightmap_probe_grid.gd.uid create mode 100644 addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg create mode 100644 addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg.import create mode 100644 addons/lightmap_probe_grid/plugin.cfg create mode 100644 addons/lightmap_probe_grid/plugin.gd create mode 100644 addons/lightmap_probe_grid/plugin.gd.uid create mode 100644 addons/lightmap_probe_grid/style_box_flat.tres create mode 100644 default_bus_layout.tres create mode 100644 fonts/AvQest.ttf create mode 100644 fonts/AvQest.ttf.import create mode 100644 fonts/metamorphous.ttf create mode 100644 fonts/metamorphous.ttf.import create mode 100644 materials/highlight.tres create mode 100644 prefabs/box.tscn create mode 100644 prefabs/crosshair001.png create mode 100644 prefabs/crosshair001.png.import create mode 100644 prefabs/interactor.tscn create mode 100644 prefabs/npc.tscn create mode 100644 scenes/blockout/level_blockout.glb create mode 100644 scenes/blockout/level_blockout.glb.import create mode 100644 scenes/blockout/level_blockout.glb.unwrap_cache create mode 100644 scenes/blockout/level_blockout_0.png create mode 100644 scenes/blockout/level_blockout_0.png.import create mode 100644 scenes/blockout/level_blockout_1.png create mode 100644 scenes/blockout/level_blockout_1.png.import create mode 100644 scenes/blockout/world2.exr create mode 100644 scenes/blockout/world2.exr.import create mode 100644 scenes/blockout/world2.lmbake create mode 100644 scenes/blockout/world2_shadow.png create mode 100644 scenes/blockout/world2_shadow.png.import create mode 100644 scenes/blockout/world3.VoxelGI_data.res create mode 100644 scenes/kloofendal_43d_clear_1k.exr create mode 100644 scenes/kloofendal_43d_clear_1k.exr.import delete mode 100644 scenes/main.tscn create mode 100644 scenes/test.tscn create mode 100644 scenes/world.tscn create mode 100644 scenes/world2.tscn create mode 100644 scenes/world3.tscn create mode 100644 shaders/cam.gdshader create mode 100644 shaders/cam.gdshader.uid create mode 100644 sounds/quack.mp3 create mode 100644 sounds/quack.mp3.import create mode 100644 src/Actions/Action.gd create mode 100644 src/Actions/Action.gd.uid create mode 100644 src/Actions/SceneChangeAction.gd create mode 100644 src/Actions/SceneChangeAction.gd.uid create mode 100644 src/CompositorEffects/new_script.gd create mode 100644 src/CompositorEffects/new_script.gd.uid create mode 100644 src/character/camera_controller.gd.uid create mode 100644 src/character/character.gd create mode 100644 src/character/character.gd.uid create mode 100644 src/character/character_controller.gd.uid rename src/character/{physics_interpolate.gd => physics_interpolator.gd} (91%) create mode 100644 src/character/physics_interpolator.gd.uid create mode 100644 src/character/player_input.gd create mode 100644 src/character/player_input.gd.uid create mode 100644 src/game_manager.gd.uid create mode 100644 src/interactable.gd create mode 100644 src/interactable.gd.uid create mode 100644 src/interactor.gd create mode 100644 src/interactor.gd.uid create mode 100644 src/loader/SceneLoader.gd create mode 100644 src/loader/SceneLoader.gd.uid create mode 100644 src/npc.gd create mode 100644 src/npc.gd.uid create mode 100644 src/shaders/depth.gdshader create mode 100644 src/shaders/depth.gdshader.uid create mode 100644 src/shaders/lensflare.gdshader create mode 100644 src/shaders/lensflare.gdshader.uid create mode 100644 src/shaders/lib/clr.gdshaderinc create mode 100644 src/shaders/lib/clr.gdshaderinc.uid create mode 100644 src/shaders/shadows.gdshader create mode 100644 src/shaders/shadows.gdshader.uid create mode 100644 src/shaders/ui/blur.gdshader.uid create mode 100644 src/shaders/vhs.gdshader create mode 100644 src/shaders/vhs.gdshader.uid create mode 100644 src/spawner.gd.uid create mode 100644 src/world.gd create mode 100644 src/world.gd.uid create mode 100644 ui/8_white.png create mode 100644 ui/8_white.png.import create mode 100644 ui/Background.jpg create mode 100644 ui/Background.jpg.import create mode 100644 ui/loaD11A.tmp create mode 100644 ui/loading_screen.tscn create mode 100644 ui/main_menu.tscn create mode 100644 ui/src/grab_focus_on_enter.gd create mode 100644 ui/src/grab_focus_on_enter.gd.uid create mode 100644 ui/src/loading_screen.gd create mode 100644 ui/src/loading_screen.gd.uid create mode 100644 ui/src/main_menu.gd create mode 100644 ui/src/main_menu.gd.uid create mode 100644 ui/src/pause_menu.gd.uid create mode 100644 ui/theme/borderStylebox.tres create mode 100644 ui/theme/borderStylebox_disabled.tres create mode 100644 ui/theme/borderStylebox_selected.tres create mode 100644 ui/theme/fantasy-ui-borders.svg create mode 100644 ui/theme/fantasy-ui-borders.svg.import create mode 100644 ui/theme/textures/panel-010.png create mode 100644 ui/theme/textures/panel-010.png.import create mode 100644 ui/theme/textures/panel-border-010.png create mode 100644 ui/theme/textures/panel-border-010.png.import create mode 100644 ui/theme/textures/panel-transparent-center-010.png create mode 100644 ui/theme/textures/panel-transparent-center-010.png.import create mode 100644 ui/theme/ui_theme.tres diff --git a/.idea/.idea.3D-Character-Controller-Template.dir/.idea/.gitignore b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/.gitignore new file mode 100644 index 0000000..bacdacd --- /dev/null +++ b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.3D-Character-Controller-Template.iml +/contentModel.xml +/modules.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.3D-Character-Controller-Template.dir/.idea/encodings.xml b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.3D-Character-Controller-Template.dir/.idea/indexLayout.xml b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.3D-Character-Controller-Template.dir/.idea/vcs.xml b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.3D-Character-Controller-Template.dir/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/addons/debug_menu/debug_menu.gd b/addons/debug_menu/debug_menu.gd index adce78e..727e405 100644 --- a/addons/debug_menu/debug_menu.gd +++ b/addons/debug_menu/debug_menu.gd @@ -34,10 +34,10 @@ const GRAPH_MAX_FRAMETIME = 1.0 / GRAPH_MAX_FPS ## Debug menu display style. enum Style { - HIDDEN, ## Debug menu is hidden. - VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame. - VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs. - MAX, ## Represents the size of the Style enum. + HIDDEN, ## Debug menu is hidden. + VISIBLE_COMPACT, ## Debug menu is visible, with only the FPS, FPS cap (if any) and time taken to render the last frame. + VISIBLE_DETAILED, ## Debug menu is visible with full information, including graphs. + MAX, ## Represents the size of the Style enum. } ## The style to use when drawing the debug menu. @@ -70,7 +70,7 @@ var sum_func := func avg(accum: float, number: float) -> float: return accum + n var frame_history_total: Array[float] = [] var frame_history_cpu: Array[float] = [] var frame_history_gpu: Array[float] = [] -var fps_history: Array[float] = [] # Only used for graphs. +var fps_history: Array[float] = [] # Only used for graphs. var frametime_avg := GRAPH_MIN_FRAMETIME var frametime_cpu_avg := GRAPH_MAX_FRAMETIME @@ -106,10 +106,10 @@ func _ready() -> void: # (red = 10 FPS, yellow = 60 FPS, green = 110 FPS, cyan = 160 FPS). # This makes the color gradient non-linear. # Colors are taken from . - frame_time_gradient.set_color(0, Color8(239, 68, 68)) # red-500 - frame_time_gradient.set_color(1, Color8(56, 189, 248)) # light-blue-400 - frame_time_gradient.add_point(0.3333, Color8(250, 204, 21)) # yellow-400 - frame_time_gradient.add_point(0.6667, Color8(128, 226, 95)) # 50-50 mix of lime-400 and green-400 + frame_time_gradient.set_color(0, Color(239, 68, 68)) # red-500 + frame_time_gradient.set_color(1, Color(56, 189, 248)) # light-blue-400 + frame_time_gradient.add_point(0.3333, Color(250, 204, 21)) # yellow-400 + frame_time_gradient.add_point(0.6667, Color(128, 226, 95)) # 50-50 mix of lime-400 and green-400 get_viewport().size_changed.connect(update_settings_label) @@ -274,8 +274,8 @@ func update_information_label() -> void: information.text = ( "%s, %d threads\n" % [OS.get_processor_name().replace("(R)", "").replace("(TM)", ""), OS.get_processor_count()] - + "%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()] - + "%s, %s" % [adapter_string, driver_info_string] + +"%s %s (%s %s), %s %s\n" % [OS.get_name(), "64-bit" if OS.has_feature("64") else "32-bit", release_string, "double" if OS.has_feature("double") else "single", graphics_api_string, RenderingServer.get_video_adapter_api_version()] + +"%s, %s" % [adapter_string, driver_info_string] ) diff --git a/addons/debug_menu/debug_menu.gd.uid b/addons/debug_menu/debug_menu.gd.uid new file mode 100644 index 0000000..c731934 --- /dev/null +++ b/addons/debug_menu/debug_menu.gd.uid @@ -0,0 +1 @@ +uid://uy4ruqo71tqe diff --git a/addons/debug_menu/debug_menu.tscn b/addons/debug_menu/debug_menu.tscn index 2aaf0c1..b0d99c7 100644 --- a/addons/debug_menu/debug_menu.tscn +++ b/addons/debug_menu/debug_menu.tscn @@ -1,7 +1,20 @@ -[gd_scene load_steps=3 format=3 uid="uid://cggqb75a8w8r"] +[gd_scene load_steps=6 format=3 uid="uid://cggqb75a8w8r"] [ext_resource type="Script" path="res://addons/debug_menu/debug_menu.gd" id="1_p440y"] +[sub_resource type="SystemFont" id="SystemFont_f56mb"] +font_names = PackedStringArray("Sans-Serif", "", "", "", "", "", "", "", "", "") + +[sub_resource type="Theme" id="Theme_qdnc2"] +default_font = SubResource("SystemFont_f56mb") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6qqxv"] +bg_color = Color(0, 0, 0, 0.498039) +expand_margin_left = 8.0 +expand_margin_top = 8.0 +expand_margin_right = 8.0 +expand_margin_bottom = 8.0 + [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ki0n8"] bg_color = Color(0, 0, 0, 0.25098) @@ -9,20 +22,19 @@ bg_color = Color(0, 0, 0, 0.25098) process_mode = 3 layer = 128 -[node name="DebugMenu" type="Control" parent="." node_paths=PackedStringArray("fps", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")] +[node name="DebugMenu" type="PanelContainer" parent="." node_paths=PackedStringArray("fps", "frame_time", "frame_number", "frame_history_total_avg", "frame_history_total_min", "frame_history_total_max", "frame_history_total_last", "frame_history_cpu_avg", "frame_history_cpu_min", "frame_history_cpu_max", "frame_history_cpu_last", "frame_history_gpu_avg", "frame_history_gpu_min", "frame_history_gpu_max", "frame_history_gpu_last", "fps_graph", "total_graph", "cpu_graph", "gpu_graph", "information", "settings")] custom_minimum_size = Vector2(400, 400) -layout_mode = 3 -anchors_preset = 1 +anchors_preset = 11 anchor_left = 1.0 anchor_right = 1.0 -offset_left = -416.0 -offset_top = 8.0 -offset_right = -16.0 -offset_bottom = 408.0 +anchor_bottom = 1.0 +offset_left = -277.0 grow_horizontal = 0 +grow_vertical = 2 size_flags_horizontal = 8 -size_flags_vertical = 4 mouse_filter = 2 +theme = SubResource("Theme_qdnc2") +theme_override_styles/panel = SubResource("StyleBoxFlat_6qqxv") script = ExtResource("1_p440y") fps = NodePath("VBoxContainer/FPS") frame_time = NodePath("VBoxContainer/FrameTime") @@ -47,13 +59,7 @@ information = NodePath("VBoxContainer/Information") settings = NodePath("VBoxContainer/Settings") [node name="VBoxContainer" type="VBoxContainer" parent="DebugMenu"] -layout_mode = 1 -anchors_preset = 1 -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -300.0 -offset_bottom = 374.0 -grow_horizontal = 0 +layout_mode = 2 mouse_filter = 2 theme_override_constants/separation = 0 @@ -61,8 +67,8 @@ theme_override_constants/separation = 0 modulate = Color(0, 1, 0, 1) layout_mode = 2 theme_override_colors/font_outline_color = Color(0, 0, 0, 1) -theme_override_constants/outline_size = 5 theme_override_constants/line_spacing = 0 +theme_override_constants/outline_size = 5 theme_override_font_sizes/font_size = 18 text = "60 FPS" horizontal_alignment = 2 diff --git a/addons/debug_menu/plugin.gd.uid b/addons/debug_menu/plugin.gd.uid new file mode 100644 index 0000000..8a3ad24 --- /dev/null +++ b/addons/debug_menu/plugin.gd.uid @@ -0,0 +1 @@ +uid://c15ljqrjuqgvh diff --git a/addons/lightmap_probe_grid/Depth.gdshader b/addons/lightmap_probe_grid/Depth.gdshader new file mode 100644 index 0000000..f0362ff --- /dev/null +++ b/addons/lightmap_probe_grid/Depth.gdshader @@ -0,0 +1,9 @@ +shader_type spatial; +render_mode unshaded; + +uniform sampler2D depth_texture : source_color, hint_depth_texture, filter_nearest, repeat_disable; + +void fragment() { + float depth = texture(depth_texture, SCREEN_UV).x; + ALBEDO = vec3(depth); +} diff --git a/addons/lightmap_probe_grid/Depth.gdshader.uid b/addons/lightmap_probe_grid/Depth.gdshader.uid new file mode 100644 index 0000000..eec8b96 --- /dev/null +++ b/addons/lightmap_probe_grid/Depth.gdshader.uid @@ -0,0 +1 @@ +uid://0dexnhbddxfl diff --git a/addons/lightmap_probe_grid/LICENSE.txt b/addons/lightmap_probe_grid/LICENSE.txt new file mode 100644 index 0000000..5db5b6c --- /dev/null +++ b/addons/lightmap_probe_grid/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 SpockBauru + +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. diff --git a/addons/lightmap_probe_grid/README.md b/addons/lightmap_probe_grid/README.md new file mode 100644 index 0000000..f6d652e --- /dev/null +++ b/addons/lightmap_probe_grid/README.md @@ -0,0 +1,79 @@ +# LightmapProbeGrid +Create a grid of Lightmap Probes and cut unwanted ones! + +LightmapProbeGrid is an extension for [Godot Engine](https://godotengine.org/) that helps on the demanding task of placing Lightmap Probes where LightmapGI fails to do it. + +**Disclaimer:** If you are getting the error `scene/3d/lightmap_gi.cpp:529 - Inconsistency found in triangulation...` is because [Godot Issue 82642](https://github.com/godotengine/godot/issues/82642). If you have the knowledge, would you kindly contrubute to fix the issue please? \o/ + +### Video Tutorial +https://www.youtube.com/watch?v=HzZSQ0BPpuk + +# Index + * [What's new](#whats-new) + * [How to install](#how-to-install) + * [Making a grid of Light Probes](#making-a-grid-of-light-probes) + * [Cut Obstructed Probes](#cut-obstructed-probes) + * [Cut probes inside objects](#cut-probes-inside-objects) + * [Cut probes far from objects](#cut-probes-far-from-objects) + * [Limitations](#limitations) + * [Compatibility](#compatibility) + * [Ending notes](#ending-notes) + * [Changelog](#changelog) + +# What's New +Thanks to [dwarfses](https://twitter.com/dwarfses/status/1731691097263362513), LightmapProbeGrid v2.0 now uses GPU raycast instead of the regular Physics raycast. It may be slower but means that colliders are not needed anymore! If the camera can see, it should work! + +NOTE: The Cull Mask from v1.0 is not compatible with v2.0. + +# How to install +1) Download the file `LightmapProbeGrid_v2.0.zip` from the [Download Page](https://github.com/SpockBauru/LightmapProbeGrid/releases) +2) Extract the `addons` folder on the root of your project (`res://`). Other files/folders are optional. +3) Go to Godot's "Project" menu -> "Project Settings" -> "Plugins" tab -> enable "LightmapProbeGrid". +4) Restart Godot. + +You can also open the `DemoScene` to see how it works. + +# Making a grid of Light Probes +- Place the LightmapProbeGrid Node in the scene. It's located at "Add Node" -> Node3D -> LightmapProbeGrid. +- Use the handles (red dots) to resize the grid. +- In the LightmapProbeGrid Inspector you can set the number of Light Probes on each axis with the minimum of 2. Press "Generate Probes" to apply the settings and place your grid of Light Probes in the Scene. + +Now you can cut unwanted probes with the methods bellow. + +## Cut Obstructed Probes +This method is designed to cut probes that are placed beyond visual limits such as the ground or the walls of a cave. + +On LightmapProbeGrid Inscpector click on "Cut Obstructed Probes". It will test each Light Probe from the center of the grid to the probe and see if the line intercepts an object. The probe will be cut if there's something blocking the line. + +## Cut probes inside objects +This method is designed to delete probes that are inside objects. It will test all 6 axis of each Light Probe: Up, Down, Left, Right, Forward and Backward by the distance indicated in `Max Object Size`. + +If at least 4 of these lines hit something the probe will be cut. It considers only 4 hits to cut probes on long objects like pillars and trees. + +## Cut probes far from objects +This method is designed to delete probes that are far away from any object. Normally these probes don't contain any relevant light information but use with care in places that have a high usage of spotlights. + +When you click the button the area around the Light Probe is tested on various directions by the distance indicated in `Max Distance`. The probe will be cut if none of the rays intercept an object. + +## Using masks +You can select which 3D render layers LightmapProbeGrid will interact on the section Visual Cull Mask. Only selected layers will be used on detection for the Cut methods above. + +Use masks to filter out objects to not interact with the rays, like characters or moving objects. + +# Limitations +LightmapProbeGrid is not designed to work with a huge number of Light Probes at once covering a vast area. It is designed to be placed multiple times in a scene with relatively small grids (less than 1,000 probes). + +# Compatibility +LightmapProbeGrid is compatible with Godot 4.2 and there are plans to continue supporting onward. + +# Ending notes +This tool was entirely made on my free time. If you want to support me, please make an awesome asset and publish for free to the community! + +# Changelog +v2.0: +- Major changes: now uses GPU Raycast instead of Physics raycast +- This means that colliders are not needed anymore! +- The Cull Mask from v1.0 is not compatible with v2.0 + +v1.0: +- First release. diff --git a/addons/lightmap_probe_grid/UI.gd b/addons/lightmap_probe_grid/UI.gd new file mode 100644 index 0000000..b19f5c1 --- /dev/null +++ b/addons/lightmap_probe_grid/UI.gd @@ -0,0 +1,89 @@ +@tool +extends Node + +@export var probes_x: SpinBox +@export var probes_y: SpinBox +@export var probes_z: SpinBox +@export var planned_probes: RichTextLabel + +@export var generate_button: Button + +@export var far_distance: SpinBox +@export var object_size: SpinBox + +var root_node: Node +var probe_grid: LightmapProbeGrid + + +func _ready() -> void: + root_node = EditorInterface.get_edited_scene_root() + if EditorInterface.get_selection().get_selected_nodes().size() == 1: + probe_grid = EditorInterface.get_selection().get_selected_nodes()[0] as LightmapProbeGrid + else: + return + + # connecting signals + if not probe_grid.probes_changed.is_connected(_get_probes): + probe_grid.probes_changed.connect(_get_probes) + if not probe_grid.probes_changed.is_connected(planned_probes_text): + probe_grid.probes_changed.connect(planned_probes_text) + + # initializing values + far_distance.value = probe_grid.far_distance + object_size.value = probe_grid.object_size + _get_probes() + planned_probes_text() + planned_probes.tooltip_text = "Maximum is %s" % probe_grid.max_probes + + +func planned_probes_text() -> void: + var total: int = probe_grid.planned_probes + var current: int = probe_grid.current_probes + var max_probes: int = probe_grid.max_probes + + if total <= max_probes: + planned_probes.text = "Probes Planned/Current: " + str(total) + " / " + str(current) + generate_button.disabled = false + else: + planned_probes.text = "[color=red]Planned Probes: %s [/color] \ + \nWarning: Max number of probes is %s" % [total, max_probes] + generate_button.disabled = true + + +func _set_probes_number(_value: float) -> void: + var number_of_probes: Vector3i = Vector3i.ONE + number_of_probes.x = int(probes_x.value) + number_of_probes.y = int(probes_y.value) + number_of_probes.z = int(probes_z.value) + probe_grid.probes_number = number_of_probes + + +func _on_generate_probes_pressed() -> void: + probe_grid.generate_probes() + + +func _get_probes() -> void: + var number: Vector3i = probe_grid.probes_number + probes_x.value = number.x + probes_y.value = number.y + probes_z.value = number.z + + +func _on_cut_by_mask_pressed() -> void: + probe_grid.cut_obstructed() + + +func _cut_far_probes(): + probe_grid.cut_far() + + +func _set_far_distance(value): + probe_grid.far_distance = value + + +func _cut_inside(): + probe_grid.cut_inside() + + +func _set_object_size(value): + probe_grid.object_size = value diff --git a/addons/lightmap_probe_grid/UI.gd.uid b/addons/lightmap_probe_grid/UI.gd.uid new file mode 100644 index 0000000..e220008 --- /dev/null +++ b/addons/lightmap_probe_grid/UI.gd.uid @@ -0,0 +1 @@ +uid://l34dvbnsrsna diff --git a/addons/lightmap_probe_grid/controls_scene.tscn b/addons/lightmap_probe_grid/controls_scene.tscn new file mode 100644 index 0000000..6e096e0 --- /dev/null +++ b/addons/lightmap_probe_grid/controls_scene.tscn @@ -0,0 +1,198 @@ +[gd_scene load_steps=3 format=3 uid="uid://xp1820rv0uy"] + +[ext_resource type="Script" path="res://addons/lightmap_probe_grid/UI.gd" id="1_4a60v"] +[ext_resource type="StyleBox" uid="uid://4nfudftbnexo" path="res://addons/lightmap_probe_grid/style_box_flat.tres" id="2_rdj4j"] + +[node name="RootContainer" type="VBoxContainer" node_paths=PackedStringArray("probes_x", "probes_y", "probes_z", "planned_probes", "generate_button", "far_distance", "object_size")] +offset_right = 257.0 +offset_bottom = 346.0 +script = ExtResource("1_4a60v") +probes_x = NodePath("ProbesContainer/ProbesX") +probes_y = NodePath("ProbesContainer/ProbesY") +probes_z = NodePath("ProbesContainer/ProbesZ") +planned_probes = NodePath("PlannedLabel") +generate_button = NodePath("GenerateProbes") +far_distance = NodePath("CutFarProbesContainer/Distance") +object_size = NodePath("CutInsideContainer/Size") + +[node name="ProbesLabel" type="Label" parent="."] +clip_contents = true +layout_mode = 2 +tooltip_text = "Number of probes on each axis" +mouse_filter = 0 +text = "Number of Probes" +vertical_alignment = 1 + +[node name="ProbesContainer" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="ProbesX" type="SpinBox" parent="ProbesContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "Minimum is 2" +min_value = 2.0 +max_value = 250.0 +value = 2.0 +alignment = 2 +prefix = "x:" +select_all_on_focus = true + +[node name="ProbesY" type="SpinBox" parent="ProbesContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "Minimum is 2" +min_value = 2.0 +max_value = 250.0 +value = 2.0 +alignment = 2 +prefix = "y:" +select_all_on_focus = true + +[node name="ProbesZ" type="SpinBox" parent="ProbesContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "Minimum is 2" +min_value = 2.0 +max_value = 250.0 +value = 2.0 +alignment = 2 +prefix = "z:" +select_all_on_focus = true + +[node name="PlannedLabel" type="RichTextLabel" parent="."] +layout_mode = 2 +tooltip_text = "Maximum is xxxx" +bbcode_enabled = true +text = "Planned Probes: xxxx" +fit_content = true + +[node name="GenerateProbes" type="Button" parent="."] +layout_mode = 2 +theme_override_styles/normal = ExtResource("2_rdj4j") +text = "Generate Probes" + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="ObscuredLabel" type="Label" parent="."] +layout_mode = 2 +tooltip_text = "Test from center to each probe, +and cut the probe if the path is obstructed by an object. + +Use Collision Mask to filter objects." +mouse_filter = 0 +text = "Probes obscured from center" +vertical_alignment = 1 + +[node name="Cut by Mask" type="Button" parent="."] +layout_mode = 2 +tooltip_text = "Test from center to each probe, +and cut the probe if the path is obstructed by an object. + +Use Collision Mask to filter objects." +theme_override_styles/normal = ExtResource("2_rdj4j") +text = "Cut Obstructed Probes" + +[node name="HSeparator2" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="Label" type="Label" parent="."] +layout_mode = 2 +tooltip_text = "For each probe, test if there is any object within an Max distance. +If there isn't any object the probe is cut. + +Use Collision Mask to filter objects." +mouse_filter = 0 +text = "Probes far from any object" + +[node name="CutFarProbesContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +alignment = 1 + +[node name="DistanceLabel" type="Label" parent="CutFarProbesContainer"] +custom_minimum_size = Vector2(67, 0) +layout_mode = 2 +tooltip_text = "The distance from the probe that objects will be detected" +mouse_filter = 0 +text = "Max distance" + +[node name="Distance" type="SpinBox" parent="CutFarProbesContainer"] +layout_mode = 2 +tooltip_text = "The distance from the probe that objects will be detected" +step = 0.0 +value = 1.0 +allow_greater = true +suffix = "m" +custom_arrow_step = 1.0 +select_all_on_focus = true + +[node name="CutFarProbes" type="Button" parent="."] +custom_minimum_size = Vector2(148, 0) +layout_mode = 2 +tooltip_text = "For each probe, test if there is any object within an Max distance. +If there isn't any object the probe is cut. + +Use Collision Mask to filter objects." +theme_override_styles/normal = ExtResource("2_rdj4j") +text = "Cut Far Probes" + +[node name="HSeparator3" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="Label2" type="Label" parent="."] +layout_mode = 2 +tooltip_text = "For each probe, test if the same object is intercepted by the yellow lines. + +If the same object is detected 4 times or more, +the probe is considered to be inside and will be cut. + +Only 4 lines are tested instead of 6 because is common for +objects to not have 2 faces, like pillars. + +Use Collision Mask to filter objects." +mouse_filter = 0 +text = "Probes inside objects" + +[node name="CutInsideContainer" type="HBoxContainer" parent="."] +layout_mode = 2 +alignment = 1 + +[node name="SizeLabel" type="Label" parent="CutInsideContainer"] +layout_mode = 2 +tooltip_text = "Distance tested from the probes (size of yellow lines)" +mouse_filter = 0 +text = "Max object size" + +[node name="Size" type="SpinBox" parent="CutInsideContainer"] +layout_mode = 2 +tooltip_text = "Distance tested from the probes (size of yellow lines)" +step = 0.0 +value = 1.0 +allow_greater = true +suffix = "m" +custom_arrow_step = 1.0 + +[node name="CutInsideObjects" type="Button" parent="."] +custom_minimum_size = Vector2(148, 0) +layout_mode = 2 +tooltip_text = "For each probe, test if the same object is intercepted by the yellow lines. + +If the same object is detected 4 times or more, +the probe is considered to be inside and will be cut. + +Only 4 lines are tested instead of 6 because is common for +objects to not have 2 faces, like pillars. + +Use Collision Mask to filter objects." +theme_override_styles/normal = ExtResource("2_rdj4j") +text = "Cut Inside Objects" + +[connection signal="value_changed" from="ProbesContainer/ProbesX" to="." method="_set_probes_number"] +[connection signal="value_changed" from="ProbesContainer/ProbesY" to="." method="_set_probes_number"] +[connection signal="value_changed" from="ProbesContainer/ProbesZ" to="." method="_set_probes_number"] +[connection signal="pressed" from="GenerateProbes" to="." method="_on_generate_probes_pressed"] +[connection signal="pressed" from="Cut by Mask" to="." method="_on_cut_by_mask_pressed"] +[connection signal="value_changed" from="CutFarProbesContainer/Distance" to="." method="_set_far_distance"] +[connection signal="pressed" from="CutFarProbes" to="." method="_cut_far_probes"] +[connection signal="value_changed" from="CutInsideContainer/Size" to="." method="_set_object_size"] +[connection signal="pressed" from="CutInsideObjects" to="." method="_cut_inside"] diff --git a/addons/lightmap_probe_grid/editor_inspector_plugin.gd b/addons/lightmap_probe_grid/editor_inspector_plugin.gd new file mode 100644 index 0000000..759110e --- /dev/null +++ b/addons/lightmap_probe_grid/editor_inspector_plugin.gd @@ -0,0 +1,16 @@ +extends EditorInspectorPlugin + +var ui_control: PackedScene = preload("controls_scene.tscn") +var ui: Control = null + +func _can_handle(object: Object) -> bool: + if object is LightmapProbeGrid: + return true + else: + return false + +func _parse_category(_object: Object, category: String) -> void: + if category.begins_with("lightmap_probe_grid"): + if ui == null: + ui = ui_control.instantiate() + add_property_editor("LighmapProbeGrid", ui) diff --git a/addons/lightmap_probe_grid/editor_inspector_plugin.gd.uid b/addons/lightmap_probe_grid/editor_inspector_plugin.gd.uid new file mode 100644 index 0000000..436a3f9 --- /dev/null +++ b/addons/lightmap_probe_grid/editor_inspector_plugin.gd.uid @@ -0,0 +1 @@ +uid://cyh1thwnyn2ra diff --git a/addons/lightmap_probe_grid/gizmo.gd b/addons/lightmap_probe_grid/gizmo.gd new file mode 100644 index 0000000..8d7eac8 --- /dev/null +++ b/addons/lightmap_probe_grid/gizmo.gd @@ -0,0 +1,159 @@ +# my_custom_gizmo_plugin.gd +extends EditorNode3DGizmoPlugin + +const handles_axis: PackedVector3Array = [ + Vector3(1, 0, 0), + Vector3(0, 1, 0), + Vector3(0, 0, 1), + Vector3(-1, 0, 0), + Vector3(0, -1, 0), + Vector3(0, 0, -1) +] + +const box_lines: PackedVector3Array = [ + # plane -x + Vector3(-1, -1, -1), Vector3(-1, -1, 1), + Vector3(-1, -1, 1), Vector3(-1, 1, 1), + Vector3(-1, 1, 1), Vector3(-1, 1, -1), + Vector3(-1, 1, -1), Vector3(-1, -1, -1), + # plane +x + Vector3(1, -1, -1), Vector3(1, -1, 1), + Vector3(1, -1, 1), Vector3(1, 1, 1), + Vector3(1, 1, 1), Vector3(1, 1, -1), + Vector3(1, 1, -1), Vector3(1, -1, -1), + # connecting plane x with -x + Vector3(1, -1, -1), Vector3(-1, -1, -1), + Vector3(1, -1, 1), Vector3(-1, -1, 1), + Vector3(1, 1, -1), Vector3(-1, 1, -1), + Vector3(1, 1, 1), Vector3(-1, 1, 1), +] + +var icon: Texture2D = preload("lightmap_probe_grid_icon.svg") + +var timer: Timer = Timer.new() +var is_awayting: bool = false + + +func _get_gizmo_name() -> String: + return "LightmapProbeGrid" + + +func _init() -> void: + create_material("main_material", Color(0,0,0)) + create_material("tool_material", Color(1, 0.9, 0)) + create_handle_material("handles_material") + create_icon_material("icon_material", icon) + + +func _has_gizmo(node: Node3D) -> bool: + if node is LightmapProbeGrid: + if not node.size_changed.is_connected(node.update_gizmos): + node.size_changed.connect(node.update_gizmos) + if not node.probes_changed.is_connected(node.update_gizmos): + node.probes_changed.connect(node.update_gizmos) + return true + else: + return false + + +func _redraw(gizmo: EditorNode3DGizmo) -> void: + gizmo.clear() + var box: LightmapProbeGrid = gizmo.get_node_3d() as LightmapProbeGrid + var size: Vector3 = box.size + + var icon_gizmo: Material = get_material("icon_material") + gizmo.add_unscaled_billboard(icon_gizmo, 0.05) + + # Setting box lines + var lines: PackedVector3Array = [] + for pos: Vector3 in box_lines: + var scaled: Vector3 = 0.5 * pos * size + lines.append(scaled) + gizmo.add_lines(lines, get_material("main_material", gizmo)) + + # Setting handles + var handles: PackedVector3Array = [] + for pos: Vector3 in handles_axis: + var scaled: Vector3 = 0.5 * pos * size + handles.append(scaled) + gizmo.add_handles(handles, get_material("handles_material", gizmo), []) + + # Setting extra tool lines from main script + var tool_lines: PackedVector3Array = box.gizmo_lines + if not tool_lines.is_empty(): + gizmo.add_lines(tool_lines, get_material("tool_material", gizmo)) + clear_tool_await(box) + + +# Wait 3 seconds before clear the main script gizmos. If called twice the timer is reset +func clear_tool_await(box: LightmapProbeGrid): + if timer == null: + timer = Timer.new() + # Add timer to the scene + if timer.get_parent() == null: + var root_node = EditorInterface.get_edited_scene_root() + root_node.add_child(timer) + timer.name = "lightmap_probe_grid_timer" + + timer.wait_time = 3.0 + timer.start() + + if is_awayting: + return + + is_awayting = true + await timer.timeout + is_awayting = false + + timer.stop + box.gizmo_lines.clear() + box.update_gizmos() + + +# Based on github.com/godotengine/godot/blob/master/editor/plugins/gizmos/gizmo_3d_helper.cpp +# please, make it available to GDScript plugin developers... +func _set_handle(gizmo: EditorNode3DGizmo, index: int, _sec: bool, camera: Camera3D, point: Vector2) -> void: + var box: LightmapProbeGrid = gizmo.get_node_3d() as LightmapProbeGrid + var axis: Vector3 = handles_axis[index] + var axis_index: int = axis.abs().max_axis_index() + + var inverse: Transform3D = box.global_transform.affine_inverse() + var ray_from: Vector3 = camera.project_ray_origin(point) + var ray_to: Vector3 = camera.project_ray_normal(point) + var camera_position: Vector3 = inverse * ray_from + var camera_to_mouse: Vector3 = inverse * (ray_from + ray_to * 5000) + + var segment1: Vector3 = axis * 5000 + var segment2: Vector3 = axis * -5000 + + var intersection: PackedVector3Array = Geometry3D.get_closest_points_between_segments(segment2, + segment1, camera_position, camera_to_mouse) + + # Distance between the center and the handle (without scale) + var distance: float = intersection[0][axis_index] + # multiply axis signal to cancel distance signal + distance *= axis[axis_index] + + var old_distance: float = 0.5 * box.size[axis_index] + + # Defining new size and positions + var new_size: float = distance + old_distance + + # Translate halfway through the size difference + var translate: Vector3 = 0.5 * (distance - old_distance) * axis + + # Updating size and position + box.size[axis_index] = new_size + box.translate(translate) + + # Update Gizmo + box.update_gizmos() + + +func _get_handle_name(_gizmo: EditorNode3DGizmo, _handle_id: int, _sec: bool) -> String: + return "Probe Grid Size" + + +func _get_handle_value(gizmo: EditorNode3DGizmo, _id: int, _sec: bool) -> Vector3: + var box: LightmapProbeGrid = gizmo.get_node_3d() as LightmapProbeGrid + return box.size diff --git a/addons/lightmap_probe_grid/gizmo.gd.uid b/addons/lightmap_probe_grid/gizmo.gd.uid new file mode 100644 index 0000000..16a50bd --- /dev/null +++ b/addons/lightmap_probe_grid/gizmo.gd.uid @@ -0,0 +1 @@ +uid://cww446exifamp diff --git a/addons/lightmap_probe_grid/lightmap_probe_grid.gd b/addons/lightmap_probe_grid/lightmap_probe_grid.gd new file mode 100644 index 0000000..77262ad --- /dev/null +++ b/addons/lightmap_probe_grid/lightmap_probe_grid.gd @@ -0,0 +1,453 @@ +@tool +extends Node3D +class_name LightmapProbeGrid + +signal size_changed +signal probes_changed + +const max_probes: int = 1000 + +## Only selected layers will be seen by LightmapProbeGrid. Works like Camera3D Cull Mask.[br][br] +## NOTE: NOT compatible with LightmapProbeGrid v1.0 +@export_flags_3d_render var visual_cull_mask: int = 1048575 + +@export var size: Vector3 = Vector3.ONE: + set(value): + # size cannot be zero or negative + size = value.clamp(Vector3(1E-6, 1E-6, 1E-6), Vector3.INF) + size_changed.emit() + get: + return size + +@onready var depth_shader: Shader = preload("Depth.gdshader") + +var probes_number: Vector3i = Vector3i(2, 2, 2): + set(value): + probes_number = value + set_probes_number() + get: + return probes_number + +var planned_probes: int = 8 +var current_probes: int = 8 + +var old_size: Vector3 = Vector3.ONE +var old_scale: Vector3 = Vector3.ONE +var warned: bool = false + +var far_distance: float = 1 +var object_size: float = 1 + +var gizmo_lines: PackedVector3Array = [] + + +func _get_property_list() -> Array[Dictionary]: + var properties: Array[Dictionary] = [] + + properties.append({ + "name": "probes_number", + "type": TYPE_VECTOR3I, + "usage": PROPERTY_USAGE_STORAGE + }) + + properties.append({ + "name": "far_distance", + "type": TYPE_FLOAT, + "usage": PROPERTY_USAGE_STORAGE + }) + + properties.append({ + "name": "object_size", + "type": TYPE_FLOAT, + "usage": PROPERTY_USAGE_STORAGE + }) + + return properties + + +func _enter_tree() -> void: + if get_child_count() < 1: + generate_probes() + + +func _ready() -> void: + size_changed.connect(scale_probes) + set_notify_local_transform(true) + old_size = size + old_scale = scale + current_probes = get_child_count() + + +# Keep local scale fixed. Reflect in "size" if the user try to scale +func _notification(what: int) -> void: + if (what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) and not scale.is_equal_approx(Vector3.ONE): + if not warned: + printerr("LightmapProbeGrid: Resetting Scale, please use the handles (red dots) or ", + "the property \"Size\" in LightmapProbeGridsection") + warned = true + + if(scale.x <= 0): + scale = Vector3.ONE + return + + # TODO take a look on this workaround + var scale_diff: Vector3 = abs(scale - Vector3.ONE) + var size_sign: Vector3 = sign(scale - old_scale) + size += size_sign * scale_diff / 10.0 + + old_scale = scale + scale = Vector3.ONE + + +func _get_configuration_warnings() -> PackedStringArray: + var warnings: PackedStringArray = [] + + if planned_probes > max_probes: + var text: String = "LightmapProbeGrid: The maximum number of Probes must be " + \ + str(max_probes) + ". Please consider add more instances of LightmapProbeGrid" + warnings.append(text) + printerr(text) + + # Returning an empty array means "no warning". + return warnings + + +func set_probes_number() -> void: + planned_probes = probes_number.x * probes_number.y * probes_number.z + update_configuration_warnings() + probes_changed.emit() + + +func scale_probes() -> void: + var new_size: Vector3 = size / old_size + + # Scaling all probes + for probe: Node3D in get_children(): + probe.position *= new_size + + old_size = size + + +func generate_probes() -> void: + # check number of probes + if planned_probes > max_probes: + return + + # Clear all previews probes + for i: int in get_child_count(): + get_child(i).queue_free() + # Wait for the last one to be cleaned + if i == get_child_count() -1: + await get_child(i).tree_exited + + # Defining probe arrays + var probes_positions: Array[Vector3] = [] + var probes_names: Array[String] = [] + + var probes_x: float = probes_number.x + var probes_y: float = probes_number.y + var probes_z: float = probes_number.z + + # Distance between probes + var step_x: float = size.x / (probes_x - 1) + var step_y: float = size.y / (probes_y - 1) + var step_z: float = size.z / (probes_z - 1) + + # Starting relative positions + var start_position: Vector3 = Vector3.ONE * size / 2.0 + var current_position: Vector3 = Vector3.ZERO + + # Defining Probes relative positions and names + for x: float in probes_x: + for y: float in probes_y: + for z: float in probes_z: + current_position.x = start_position.x - step_x * x + current_position.y = start_position.y - step_y * y + current_position.z = start_position.z - step_z * z + probes_positions.append(current_position) + probes_names.append("LightmapProbe %.f, %.f, %.f" % [x, y, z]) + + # Generating probes + var root_node: Node = get_tree().edited_scene_root + + for i: int in range(probes_positions.size()): + var probe: LightmapProbe = LightmapProbe.new() + probe.position = probes_positions[i] + probe.name = probes_names[i] + add_child(probe) + probe.set_owner(root_node) + + current_probes = probes_number.x * probes_number.y * probes_number.z + set_probes_number() + + +# Workaround to raycast without colliders. Consists in a camera with a filter in front that shows +# the depth texture. The camera.far is the "ray" lenght and camera rotation is the "ray" orientation +# https://docs.godotengine.org/en/stable/tutorials/shaders/advanced_postprocessing.html#depth-texture +func add_GPU_raycaster(probe: Node3D) -> void: + var root_node: Node = get_tree().edited_scene_root + + # SubViewport that will host the camera + var sub_viewport: SubViewport = SubViewport.new() + sub_viewport.name = "GPUraycast" + sub_viewport.size = Vector2(2, 2) + sub_viewport.render_target_update_mode = SubViewport.UPDATE_DISABLED + sub_viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_NEVER + sub_viewport.handle_input_locally = false + sub_viewport.debug_draw = Viewport.DEBUG_DRAW_UNSHADED + + sub_viewport.positional_shadow_atlas_size = 0 + sub_viewport.positional_shadow_atlas_quad_0 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED + sub_viewport.positional_shadow_atlas_quad_1 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED + sub_viewport.positional_shadow_atlas_quad_2 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED + sub_viewport.positional_shadow_atlas_quad_3 = Viewport.SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED + + probe.add_child(sub_viewport) + sub_viewport.set_owner(root_node) + + # Camera for the viewport + var camera_3d: Camera3D = Camera3D.new() + camera_3d.projection = Camera3D.PROJECTION_ORTHOGONAL + camera_3d.size = 0.001 + camera_3d.near = 0.001 + camera_3d.far = 1.0 + sub_viewport.add_child(camera_3d) + camera_3d.set_owner(root_node) + camera_3d.position = probe.global_position + camera_3d.rotation = Vector3.ZERO + camera_3d.cull_mask = visual_cull_mask + + # Depth filter: A quad with a material that shows the Depth texture. This goes in front of the + # camera + var depth_material: ShaderMaterial = ShaderMaterial.new() + depth_material.shader = depth_shader + + var depth_filter: MeshInstance3D = MeshInstance3D.new() + var depth_mesh: QuadMesh = QuadMesh.new() + depth_filter.mesh = depth_mesh + depth_mesh.material = depth_material + depth_mesh.size = Vector2.ONE * 0.001 + camera_3d.add_child(depth_filter) + depth_filter.set_owner(root_node) + depth_filter.position = Vector3(0, 0, -0.002) + depth_filter.rotation = Vector3.ZERO + + +func generate_probes_raycasters(distance: float) -> void: + for probe in get_children(): + add_GPU_raycaster(probe) + + +func remove_probes_raycasters() -> void: + for probe in get_children(): + for child in probe.get_children(): + child.queue_free(); + + +# The function look_at not always work. Exceptions are handled here +func rotate_camera(camera: Camera3D, to: Vector3) -> void: + var from: Vector3 = camera.position + # look_at don't work if the node and target have the same position. You cannot look at yourself + if from == to: + return + + # look_at don't work if the direction and rotation axix have same orientation. In this case, + # change the rotation axis + var direction: Vector3 = abs(to - from) + var mag = (direction.normalized() - Vector3.UP).length() + if mag > 0.001: + camera.look_at(to) + else: + camera.look_at(to, Vector3.RIGHT) + + +# Shoot rays from the center to all the probes. If any object is detected so the probe is +# obstructed and will be cut +func cut_obstructed() -> void: + await generate_probes_raycasters(far_distance) + + var probes_array: Array[LightmapProbe] = [] + var camera_array: Array[Camera3D] = [] + var subViewport_array: Array[SubViewport] = [] + var results_array: Array[float] = [] + + # Populating arrays + for probe: LightmapProbe in get_children(): + probes_array.append(probe) + gizmo_lines.append_array([Vector3.ZERO, probe.position]) + var sub_viewport: SubViewport = probe.get_child(0) + subViewport_array.append(sub_viewport) + var camera: Camera3D = sub_viewport.get_child(0) + camera_array.append(camera) + + # Rotating cameras and updating sub_viewports + for i in range(camera_array.size()): + var camera: Camera3D = camera_array[i] + var probe_pos: Vector3 = probes_array[i].global_position + var sub_viewport: SubViewport = subViewport_array[i] + + camera.position = position + # The lenght of the "Ray" + camera.far = (probe_pos - position).length() + # The direction of the "Ray" + rotate_camera(camera, probe_pos) + sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + # Getting the values + await RenderingServer.frame_post_draw + for i in range(subViewport_array.size()): + var sub_viewport = subViewport_array[i] + var texture: Image = sub_viewport.get_texture().get_image() + var color: Color = texture.get_pixel(0,0) + var colorValue: float = color.r + var result: float = colorValue + results_array.append(result) + + # Cutting probes + for i in range(subViewport_array.size()): + var result: float = results_array[i] + if result < 1.0: + var probe = probes_array[i] + probe.queue_free() + current_probes -= 1 + + set_probes_number() + remove_probes_raycasters() + + +# Detect if the probe is far from any object. It will shoot rays on all 6 axis and 8 quadrants. +# If there aren't any objects the probe will be cut +func cut_far() -> void: + await generate_probes_raycasters(far_distance) + + # 6 axis and 8 quadrants + var directions: Array[Vector3] = [ + # 6 Axis + Vector3(0, 0, 1), Vector3(0, 1, 0), Vector3(1, 0, 0), + Vector3(0, 0, -1), Vector3(0, -1, 0), Vector3(-1, 0, 0), + # 8 Quadrants + Vector3(1, 1, 1).normalized(), Vector3(1, 1, -1).normalized(), + Vector3(1, -1, 1).normalized(), Vector3(1, -1, -1).normalized(), + Vector3(-1, 1, 1).normalized(), Vector3(-1, 1, -1).normalized(), + Vector3(-1, -1, 1).normalized(), Vector3(-1, -1, -1).normalized() + ] + + var probes_array: Array[LightmapProbe] = [] + var camera_array: Array[Camera3D] = [] + var subViewport_array: Array[SubViewport] = [] + var collisions_number: Array[int] = [] + + # Populating arrays + for probe: LightmapProbe in get_children(): + probes_array.append(probe) + var sub_viewport: SubViewport = probe.get_child(0) + subViewport_array.append(sub_viewport) + var camera: Camera3D = sub_viewport.get_child(0) + camera_array.append(camera) + collisions_number.resize(camera_array.size()) + collisions_number.fill(0) + + # Getting data for all cameras on each direction + for dir in directions: + # Rotating all cameras to the same direction, and updating viewport + for i in camera_array.size(): + var probe: LightmapProbe = probes_array[i] + var sub_viewport: SubViewport = subViewport_array[i] + var camera: Camera3D = camera_array[i] + + camera.position = probe.global_position + # The lenght of the "Ray" + camera.far = far_distance + # The direction of the "Ray" + rotate_camera(camera, probe.global_position + dir) + sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + gizmo_lines.append_array([probe.position, probe.position + dir * far_distance]) + + # Getting all values for the current direction + await RenderingServer.frame_post_draw + for i in range(subViewport_array.size()): + var sub_viewport = subViewport_array[i] + var texture: Image = sub_viewport.get_texture().get_image() + var color: Color = texture.get_pixel(0,0) + var colorValue: float = color.r + var result: float = colorValue + if result < 1.0: + collisions_number[i] += 1 + + # Cut probes if there are no collisions + for i in probes_array.size(): + if collisions_number[i] < 1: + var probe = probes_array[i] + probe.queue_free() + current_probes -= 1 + + set_probes_number() + remove_probes_raycasters() + + +# Detect if probe is inside an object. It will shoot rays from all 6 axis to the probe. If at least +# 4 are obstructed, the probe will be cut +func cut_inside() -> void: + await generate_probes_raycasters(far_distance) + + # 6 Axis + var axis: Array[Vector3] = [ + Vector3(0, 0, 1), Vector3(0, 1, 0), Vector3(1, 0, 0), + Vector3(0, 0, -1), Vector3(0, -1, 0), Vector3(-1, 0, 0), + ] + + var probes_array: Array[LightmapProbe] = [] + var camera_array: Array[Camera3D] = [] + var subViewport_array: Array[SubViewport] = [] + var collisions_number: Array[int] = [] + + # Populating arrays + for probe: LightmapProbe in get_children(): + probes_array.append(probe) + var sub_viewport: SubViewport = probe.get_child(0) + subViewport_array.append(sub_viewport) + var camera: Camera3D = sub_viewport.get_child(0) + camera_array.append(camera) + collisions_number.resize(camera_array.size()) + collisions_number.fill(0) + + # Getting data for all cameras on each axis + for dir in axis: + # For each direction, position all cameras to look from outside + # to each probe in object_size distance + for i in camera_array.size(): + var probe: LightmapProbe = probes_array[i] + var sub_viewport: SubViewport = subViewport_array[i] + var camera: Camera3D = camera_array[i] + + camera.position = probe.global_position + dir * object_size + # The lenght of the "Ray" + camera.far = object_size + # The direction of the "Ray" + rotate_camera(camera, probe.global_position) + sub_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE + + gizmo_lines.append_array([probe.position, probe.position + dir * object_size]) + + # Getting all values for the current direction + await RenderingServer.frame_post_draw + for i in range(subViewport_array.size()): + var sub_viewport = subViewport_array[i] + var texture: Image = sub_viewport.get_texture().get_image() + var color: Color = texture.get_pixel(0,0) + var colorValue: float = color.r + var result: float = colorValue + if result < 1.0: + collisions_number[i] += 1 + + # Cut probes if there are more than 4 collisions + for i in probes_array.size(): + if collisions_number[i] > 3: + var probe = probes_array[i] + probe.queue_free() + current_probes -= 1 + + set_probes_number() + remove_probes_raycasters() + diff --git a/addons/lightmap_probe_grid/lightmap_probe_grid.gd.uid b/addons/lightmap_probe_grid/lightmap_probe_grid.gd.uid new file mode 100644 index 0000000..4dbecef --- /dev/null +++ b/addons/lightmap_probe_grid/lightmap_probe_grid.gd.uid @@ -0,0 +1 @@ +uid://dy5lhlfkbk5t6 diff --git a/addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg b/addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg new file mode 100644 index 0000000..c6d864a --- /dev/null +++ b/addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg.import b/addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg.import new file mode 100644 index 0000000..eb73175 --- /dev/null +++ b/addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dtmcycli3j3j1" +path="res://.godot/imported/lightmap_probe_grid_icon.svg-71f84c7339e4d62a065aeef79112e123.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/lightmap_probe_grid/lightmap_probe_grid_icon.svg" +dest_files=["res://.godot/imported/lightmap_probe_grid_icon.svg-71f84c7339e4d62a065aeef79112e123.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=0 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/lightmap_probe_grid/plugin.cfg b/addons/lightmap_probe_grid/plugin.cfg new file mode 100644 index 0000000..e50f745 --- /dev/null +++ b/addons/lightmap_probe_grid/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="LightmapProbeGrid" +description="Create a grid of LightmapProbes, and cut unwanted ones!" +author="SpockBauru" +version="2.0" +script="plugin.gd" diff --git a/addons/lightmap_probe_grid/plugin.gd b/addons/lightmap_probe_grid/plugin.gd new file mode 100644 index 0000000..6ea43d8 --- /dev/null +++ b/addons/lightmap_probe_grid/plugin.gd @@ -0,0 +1,25 @@ +@tool +extends EditorPlugin + +var custom_node: Script = preload("lightmap_probe_grid.gd") +var icon: Texture2D = preload("lightmap_probe_grid_icon.svg") + +var inspector_script: Script = preload("editor_inspector_plugin.gd") +var inspector_plugin: EditorInspectorPlugin = inspector_script.new() + +var gizmo_script: Script = preload("gizmo.gd") +var gizmo_plugin: EditorNode3DGizmoPlugin = gizmo_script.new() + +func _get_plugin_name() -> String: + return "LightmapProbeGrid" + +func _enter_tree() -> void: + add_custom_type("LightmapProbeGrid", "Node3D", custom_node, icon) + add_inspector_plugin(inspector_plugin) + add_node_3d_gizmo_plugin(gizmo_plugin) + + +func _exit_tree() -> void: + remove_custom_type("LightmapProbeGrid") + remove_inspector_plugin(inspector_plugin) + remove_node_3d_gizmo_plugin(gizmo_plugin) diff --git a/addons/lightmap_probe_grid/plugin.gd.uid b/addons/lightmap_probe_grid/plugin.gd.uid new file mode 100644 index 0000000..d5f61d3 --- /dev/null +++ b/addons/lightmap_probe_grid/plugin.gd.uid @@ -0,0 +1 @@ +uid://df4lt60aa1ac8 diff --git a/addons/lightmap_probe_grid/style_box_flat.tres b/addons/lightmap_probe_grid/style_box_flat.tres new file mode 100644 index 0000000..867120c --- /dev/null +++ b/addons/lightmap_probe_grid/style_box_flat.tres @@ -0,0 +1,13 @@ +[gd_resource type="StyleBoxFlat" format=3 uid="uid://4nfudftbnexo"] + +[resource] +draw_center = false +border_width_left = 1 +border_width_top = 1 +border_width_right = 1 +border_width_bottom = 1 +border_color = Color(0.5, 0.5, 0.5, 1) +corner_radius_top_left = 2 +corner_radius_top_right = 2 +corner_radius_bottom_right = 2 +corner_radius_bottom_left = 2 diff --git a/default_bus_layout.tres b/default_bus_layout.tres new file mode 100644 index 0000000..8ccabe6 --- /dev/null +++ b/default_bus_layout.tres @@ -0,0 +1,27 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://dykupbfl42kqe"] + +[resource] +bus/1/name = &"Music" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" +bus/2/name = &"Voice" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = 0.0 +bus/2/send = &"Master" +bus/3/name = &"Effects" +bus/3/solo = false +bus/3/mute = false +bus/3/bypass_fx = false +bus/3/volume_db = 0.0 +bus/3/send = &"Master" +bus/4/name = &"Environment" +bus/4/solo = false +bus/4/mute = false +bus/4/bypass_fx = false +bus/4/volume_db = 0.0 +bus/4/send = &"Master" diff --git a/export_presets.cfg b/export_presets.cfg index b65518b..eff03d5 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -3,16 +3,21 @@ name="Windows Desktop" platform="Windows Desktop" runnable=true +advanced_options=false dedicated_server=false custom_features="" -export_filter="all_resources" +export_filter="exclude" +export_files=PackedStringArray("res://meshes/tailandia.glb") include_filter="" exclude_filter="" -export_path="C:/Users/Midnight/Desktop/export2/not_bar.exe" +export_path="C:/Users/Midnight/Desktop/export2/quacc.exe" +patches=PackedStringArray() encryption_include_filters="" encryption_exclude_filters="" +seed=0 encrypt_pck=false encrypt_directory=false +script_export_mode=2 [preset.0.options] @@ -20,14 +25,12 @@ custom_template/debug="" custom_template/release="" debug/export_console_wrapper=1 binary_format/embed_pck=false -texture_format/bptc=true -texture_format/s3tc=true -texture_format/etc=false -texture_format/etc2=false +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false binary_format/architecture="x86_64" -codesign/enable=false +codesign/enable=true codesign/timestamp=true -codesign/timestamp_server_url="" +codesign/timestamp_server_url="http://timestamp.digicert.com" codesign/digest_algorithm=1 codesign/description="" codesign/custom_options=PackedStringArray() @@ -37,12 +40,14 @@ application/console_wrapper_icon="" application/icon_interpolation=4 application/file_version="" application/product_version="" -application/company_name="" -application/product_name="" +application/company_name="MNXS" +application/product_name="Quacc" application/file_description="" application/copyright="" application/trademarks="" application/export_angle=0 +application/export_d3d12=0 +application/d3d12_agility_sdk_multiarch=true ssh_remote_deploy/enabled=false ssh_remote_deploy/host="user@host_ip" ssh_remote_deploy/port="22" @@ -60,43 +65,52 @@ Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorActi ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue Remove-Item -Recurse -Force '{temp_dir}'" - -[preset.1] - -name="Linux/X11" -platform="Linux/X11" -runnable=true -dedicated_server=false -custom_features="" -export_filter="all_resources" -include_filter="" -exclude_filter="" -export_path="C:/Users/Midnight/Desktop/export2/not_bar.x86_64" -encryption_include_filters="" -encryption_exclude_filters="" -encrypt_pck=false -encrypt_directory=false - -[preset.1.options] - -custom_template/debug="" -custom_template/release="" -debug/export_console_wrapper=1 -binary_format/embed_pck=false texture_format/bptc=true texture_format/s3tc=true texture_format/etc=false texture_format/etc2=false -binary_format/architecture="x86_64" -ssh_remote_deploy/enabled=false -ssh_remote_deploy/host="user@host_ip" -ssh_remote_deploy/port="22" -ssh_remote_deploy/extra_args_ssh="" -ssh_remote_deploy/extra_args_scp="" -ssh_remote_deploy/run_script="#!/usr/bin/env bash -export DISPLAY=:0 -unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" -\"{temp_dir}/{exe_name}\" {cmd_args}" -ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash -kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") -rm -rf \"{temp_dir}\"" + +[preset.1] + +name="Web" +platform="Web" +runnable=true +advanced_options=true +dedicated_server=false +custom_features="" +export_filter="scenes" +export_files=PackedStringArray("res://cloud_sky/clouds_material.tres", "res://meshes/characters/animations/idle.res", "res://cloud_sky/worlnoise.bmp", "res://cloud_sky/weather.bmp", "res://cloud_sky/transmittance_lut.tres", "res://cloud_sky/transmittance_lut.gd", "res://cloud_sky/transmittance-lut.glsl", "res://cloud_sky/sun.gd", "res://cloud_sky/sky_lut.tres", "res://cloud_sky/sky_lut.gd", "res://cloud_sky/sky-lut.glsl", "res://cloud_sky/perlworlnoise.tga", "res://cloud_sky/cloud_sky.gd", "res://cloud_sky/clouds_sky.tres", "res://meshes/characters/animations/Fast Run.fbx", "res://cloud_sky/clouds.gdshader", "res://cloud_sky/clouds.glsl", "res://meshes/characters/animations/Breathing Idle.fbx", "res://ui/main_menu.tscn", "res://ui/pause_menu.tscn", "res://ui/loading_screen.tscn", "res://scenes/world2.tscn") +include_filter="" +exclude_filter="" +export_path="../../export/index.html" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.1.options] + +custom_template/debug="E:/godot/bin/godot.web.template_debug.wasm32.zip" +custom_template/release="E:/godot/bin/godot.web.template_release.wasm32.zip" +variant/extensions_support=false +variant/thread_support=true +vram_texture_compression/for_desktop=true +vram_texture_compression/for_mobile=false +html/export_icon=true +html/custom_html_shell="" +html/head_include="" +html/canvas_resize_policy=2 +html/focus_canvas_on_start=true +html/experimental_virtual_keyboard=false +progressive_web_app/enabled=false +progressive_web_app/ensure_cross_origin_isolation_headers=true +progressive_web_app/offline_page="" +progressive_web_app/display=1 +progressive_web_app/orientation=0 +progressive_web_app/icon_144x144="" +progressive_web_app/icon_180x180="" +progressive_web_app/icon_512x512="" +progressive_web_app/background_color=Color(0, 0, 0, 1) diff --git a/fonts/AvQest.ttf b/fonts/AvQest.ttf new file mode 100644 index 0000000000000000000000000000000000000000..5d2b94b984b75ce5c11b0e53ad6e52be1a80b41e GIT binary patch literal 51536 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