diff --git a/addons/.DS_Store b/addons/.DS_Store
deleted file mode 100644
index a8b392c..0000000
Binary files a/addons/.DS_Store and /dev/null differ
diff --git a/addons/scatter-4/.DS_Store b/addons/scatter-4/.DS_Store
deleted file mode 100644
index de125fb..0000000
Binary files a/addons/scatter-4/.DS_Store and /dev/null differ
diff --git a/addons/scatter-4/.gitignore b/addons/scatter-4/.gitignore
deleted file mode 100755
index 3063d8d..0000000
--- a/addons/scatter-4/.gitignore
+++ /dev/null
@@ -1,9 +0,0 @@
-
-# Godot-specific ignores
-.import/
-export.cfg
-export_presets.cfg
-
-# Mono-specific ignores
-.mono/
-*.import
diff --git a/addons/scatter-4/LICENSE b/addons/scatter-4/LICENSE
deleted file mode 100755
index ba6eddb..0000000
--- a/addons/scatter-4/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019 HungryProton
-
-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/scatter-4/README.md b/addons/scatter-4/README.md
deleted file mode 100644
index 15624bf..0000000
--- a/addons/scatter-4/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Scatter
-
-![scatter](https://user-images.githubusercontent.com/52043844/103457605-08084d80-4d01-11eb-98a3-3cdb523d5410.png)
-Addon for the Godot game engine to randomly place anything in your scenes.
-[See it in action here](https://twitter.com/HungryProton/status/1344623041620402176)
-
-## THIS IS A WORK IN PROGRESS
-
-**This is the version 4 of ProtonScatter. It's only compatible with Godot 4.**
-
-If you're looking for a version compatible with Godot 3.x, check the releases
-page or the legacy branch.
-
-V4 is currently being ported from v2.8.x, but it's **breaking compatibility**
-due to core changes. You will have to redo your scatter nodes when
-moving to v4. (I might write a conversion script if it's technically possible
-and not too time consuming but that will have to wait until v4 is stable)
-
-
-## How to use it
-
-Demos and documentation will return as soon as the addon is in a usable state
-
-## Licence
-- This addon is published under the MIT licence.
-- Most 2D and 3D assets in the demos folder are MIT, with a few exceptions.
-Refer the the LICENCE.md in the assets folder for more details.
diff --git a/addons/scatter-4/demos/assets/brick.tscn b/addons/scatter-4/demos/assets/brick.tscn
deleted file mode 100644
index ed27fb7..0000000
--- a/addons/scatter-4/demos/assets/brick.tscn
+++ /dev/null
@@ -1,23 +0,0 @@
-[gd_scene load_steps=5 format=3 uid="uid://b4ted6l27vuyd"]
-
-[ext_resource type="PackedScene" uid="uid://d1d1fag0m04yc" path="res://addons/proton_scatter/demos/assets/models/brick.glb" id="1_bkmk2"]
-[ext_resource type="Texture2D" uid="uid://dqa2jfs1jy0hq" path="res://addons/proton_scatter/demos/assets/textures/t_rock.jpg" id="2_235bd"]
-
-[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nwvh2"]
-albedo_color = Color(0.678431, 0.596078, 0.466667, 1)
-albedo_texture = ExtResource("2_235bd")
-uv1_scale = Vector3(0.75, 0.75, 0.75)
-uv1_triplanar = true
-
-[sub_resource type="BoxShape3D" id="BoxShape3D_0rrnn"]
-size = Vector3(0.4, 0.4, 0.4)
-
-[node name="brick" instance=ExtResource("1_bkmk2")]
-
-[node name="Cube" parent="." index="0"]
-material_override = SubResource("StandardMaterial3D_nwvh2")
-
-[node name="StaticBody3D" type="StaticBody3D" parent="." index="1"]
-
-[node name="CollisionShape3D" type="CollisionShape3D" parent="." index="2"]
-shape = SubResource("BoxShape3D_0rrnn")
diff --git a/addons/scatter-4/demos/assets/bush.tscn b/addons/scatter-4/demos/assets/bush.tscn
deleted file mode 100644
index 7e0d3a8..0000000
--- a/addons/scatter-4/demos/assets/bush.tscn
+++ /dev/null
@@ -1,10 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://b8abs8me7ckgo"]
-
-[ext_resource type="PackedScene" uid="uid://dbb4culid55v5" path="res://addons/proton_scatter/demos/assets/models/bush.glb" id="1_kv8tm"]
-[ext_resource type="Material" uid="uid://bn3fr3m3glrnp" path="res://addons/proton_scatter/demos/assets/materials/m_bush.tres" id="2_bkwoq"]
-
-[node name="bush" instance=ExtResource("1_kv8tm")]
-
-[node name="Bush" parent="." index="0"]
-material_override = ExtResource("2_bkwoq")
-instance_shader_parameters/camera_bend_strength = 0.0
diff --git a/addons/scatter-4/demos/assets/dead_branch.tscn b/addons/scatter-4/demos/assets/dead_branch.tscn
deleted file mode 100644
index 956a667..0000000
--- a/addons/scatter-4/demos/assets/dead_branch.tscn
+++ /dev/null
@@ -1,9 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://ctkii8aivl17n"]
-
-[ext_resource type="PackedScene" uid="uid://cmqlv88xp71tw" path="res://addons/proton_scatter/demos/assets/models/dead_branch.glb" id="1_5foyv"]
-[ext_resource type="Material" uid="uid://d01d0h08lqqn6" path="res://addons/proton_scatter/demos/assets/materials/m_trunk.tres" id="2_tldro"]
-
-[node name="dead_branch" instance=ExtResource("1_5foyv")]
-
-[node name="DeadBranch" parent="." index="0"]
-surface_material_override/0 = ExtResource("2_tldro")
diff --git a/addons/scatter-4/demos/assets/fence_planks.tscn b/addons/scatter-4/demos/assets/fence_planks.tscn
deleted file mode 100644
index 7a60b84..0000000
--- a/addons/scatter-4/demos/assets/fence_planks.tscn
+++ /dev/null
@@ -1,18 +0,0 @@
-[gd_scene load_steps=4 format=3 uid="uid://bfcjigq0vdl4d"]
-
-[ext_resource type="PackedScene" uid="uid://6gxiul1pw13t" path="res://addons/proton_scatter/demos/assets/models/fence_planks.glb" id="1"]
-[ext_resource type="Material" path="res://addons/proton_scatter/demos/assets/materials/m_fence.tres" id="2"]
-
-[sub_resource type="BoxShape3D" id="BoxShape3D_fesk1"]
-size = Vector3(1, 0.5, 0.1)
-
-[node name="fence_planks" instance=ExtResource("1")]
-
-[node name="fence_planks2" parent="." index="0"]
-surface_material_override/0 = ExtResource("2")
-
-[node name="StaticBody3D" type="StaticBody3D" parent="." index="1"]
-
-[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" index="0"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.197684, 0.0236663)
-shape = SubResource("BoxShape3D_fesk1")
diff --git a/addons/scatter-4/demos/assets/gobot.tscn b/addons/scatter-4/demos/assets/gobot.tscn
deleted file mode 100644
index 5b005eb..0000000
--- a/addons/scatter-4/demos/assets/gobot.tscn
+++ /dev/null
@@ -1,16 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://bmglbfn5jaubp"]
-
-[ext_resource type="PackedScene" uid="uid://d3f4d3m7n8tpr" path="res://addons/proton_scatter/demos/assets/models/gobot.glb" id="1_gfyx7"]
-
-[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_rhuea"]
-albedo_color = Color(0.278431, 0.54902, 0.74902, 1)
-metallic = 0.4
-metallic_specular = 0.2
-roughness = 0.15
-rim_enabled = true
-rim = 0.3
-
-[node name="gobot" instance=ExtResource("1_gfyx7")]
-
-[node name="Sphere001" parent="." index="0"]
-surface_material_override/0 = SubResource("StandardMaterial3D_rhuea")
diff --git a/addons/scatter-4/demos/assets/grass.tscn b/addons/scatter-4/demos/assets/grass.tscn
deleted file mode 100644
index 36260f3..0000000
--- a/addons/scatter-4/demos/assets/grass.tscn
+++ /dev/null
@@ -1,10 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://c3c76je2y6vfj"]
-
-[ext_resource type="PackedScene" uid="uid://018flajgtx7t" path="res://addons/proton_scatter/demos/assets/models/grass.glb" id="1_203fe"]
-[ext_resource type="Material" uid="uid://c4mot1fo3siox" path="res://addons/proton_scatter/demos/assets/materials/m_grass.tres" id="2_sv1ar"]
-
-[node name="grass" instance=ExtResource("1_203fe")]
-
-[node name="Plane011" parent="." index="0"]
-instance_shader_parameters/camera_bend_strength = 0.35
-surface_material_override/0 = ExtResource("2_sv1ar")
diff --git a/addons/scatter-4/demos/assets/grass_2.tscn b/addons/scatter-4/demos/assets/grass_2.tscn
deleted file mode 100644
index 6810785..0000000
--- a/addons/scatter-4/demos/assets/grass_2.tscn
+++ /dev/null
@@ -1,9 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://cia3jakp3wj1d"]
-
-[ext_resource type="PackedScene" uid="uid://dcnm2ijk7hj4j" path="res://addons/proton_scatter/demos/assets/models/grass_2.glb" id="1_xyqky"]
-[ext_resource type="Material" uid="uid://c4mot1fo3siox" path="res://addons/proton_scatter/demos/assets/materials/m_grass.tres" id="2_63qe5"]
-
-[node name="grass_2" instance=ExtResource("1_xyqky")]
-
-[node name="Grass" parent="." index="0"]
-surface_material_override/0 = ExtResource("2_63qe5")
diff --git a/addons/scatter-4/demos/assets/large_rock.tscn b/addons/scatter-4/demos/assets/large_rock.tscn
deleted file mode 100644
index 2234439..0000000
--- a/addons/scatter-4/demos/assets/large_rock.tscn
+++ /dev/null
@@ -1,17 +0,0 @@
-[gd_scene load_steps=4 format=3 uid="uid://8ay5rlrutcmx"]
-
-[ext_resource type="PackedScene" uid="uid://bxqkgplyoipsx" path="res://addons/proton_scatter/demos/assets/models/large_rock.glb" id="1_etns1"]
-[ext_resource type="Material" uid="uid://i0jgjmbbl2m5" path="res://addons/proton_scatter/demos/assets/materials/m_rock.tres" id="2_fes1d"]
-
-[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_6ssqc"]
-data = PackedVector3Array(0.0629, 0.1755, -1.8335, -0.0082, 0.0041, -1.6822, 0.2717, -0.1032, -1.6837, 0.6881, 0.5954, 1.4818, 1.1748, -0.0814, 1.7311, 0.3761, 0.3877, 1.5951, 0.8004, 0.2889, -1.6673, 0.453, 0.787, -1.6234, 0.2234, 0.3394, -1.8428, 0.0438, -0.3535, -1.5059, 0.0958, -0.4313, -1.5075, 0.2334, -0.4206, -1.5131, 1.7542, -0.4854, 0.9086, 1.6573, -0.2166, 1.2501, 1.8018, -0.1166, 1.0465, -0.9001, 0.6982, -1.3812, 0.4904, 0.8361, -0.4902, -1.2997, 0.5397, -0.7858, -1.3066, -0.6146, -0.0152, -1.3934, -0.7148, -0.3785, -1.6779, -0.4855, -0.1607, -1.6422, -0.27, -0.3156, -1.3474, 0.367, -0.2501, -1.5688, -0.3115, 0.1105, 0.7765, -0.6276, -1.2561, 0.9699, -0.5671, -1.2091, 0.6074, -0.5308, -1.4001, -0.9001, 0.6982, -1.3812, -0.534, 0.3122, -1.6595, -0.4893, 0.652, -1.605, 1.5141, -0.6082, 1.2539, 1.502, -0.4979, 1.4563, 1.5644, -0.4551, 1.31, 1.6136, 0.2882, 1.3723, 1.2715, 0.1425, 1.7004, 1.307, 0.4153, 1.6132, -0.0974, 0.1771, -1.7741, -0.0692, 0.1191, -1.7434, -0.0832, 0.1572, -1.7751, -0.0258, 0.0213, -1.688, -0.3113, -0.1613, -1.5779, 0.0157, -0.0532, -1.6436, -0.0864, 0.2978, -1.7808, 0.0784, 0.5885, -1.8004, -0.0895, 0.5831, -1.6991, 1.8072, -0.4701, -0.0799, 1.571, 0.5779, 0.2372, 1.7035, -0.1804, -0.405, 0.7935, 0.8229, -1.3081, 0.9762, 0.8279, -1.1273, 0.6719, 0.9108, -1.1199, -1.2328, -0.714, -0.1886, -1.3066, -0.6146, -0.0152, -1.0843, -0.5896, 0.1569, -1.6727, -0.3783, -0.0804, -1.6583, -0.5073, -0.3071, -1.6422, -0.27, -0.3156, 1.7542, -0.4854, 0.9086, 1.4359, -0.8794, 0.8237, 1.5663, -0.6718, 1.1167, 0.6096, -0.219, -1.5025, 0.6415, -0.1642, -1.5814, 0.2717, -0.1032, -1.6837, -0.0258, 0.0213, -1.688, 0.0157, -0.0532, -1.6436, -0.0082, 0.0041, -1.6822, 0.4631, 0.9672, -1.1988, 0.6719, 0.9108, -1.1199, 0.5883, 0.9628, -1.0063, 1.6466, 0.5513, 1.0625, 1.6113, 0.4774, 1.1526, 1.487, 0.6285, 1.1421, 1.259, -0.746, -0.7881, 0.8809, -0.6144, -1.19, -0.8459, -0.6965, -0.1217, 1.2972, 0.6831, -0.5577, 1.3496, 0.6378, -0.4421, 1.3469, 0.6517, -0.3691, 1.0299, -0.8113, 1.3675, 1.1519, -0.8005, 1.3807, 1.0338, -0.9119, 1.2882, 0.1273, -0.231, -1.5631, 0.2717, -0.1032, -1.6837, 0.0157, -0.0532, -1.6436, 0.0157, -0.0532, -1.6436, 0.2717, -0.1032, -1.6837, -0.0082, 0.0041, -1.6822, -0.0082, 0.0041, -1.6822, 0.0629, 0.1755, -1.8335, -0.0258, 0.0213, -1.688, -0.0258, 0.0213, -1.688, 0.0629, 0.1755, -1.8335, -0.0692, 0.1191, -1.7434, -0.0832, 0.1572, -1.7751, -0.0864, 0.2978, -1.7808, -0.0974, 0.1771, -1.7741, -0.0692, 0.1191, -1.7434, 0.0629, 0.1755, -1.8335, -0.0832, 0.1572, -1.7751, -0.0864, 0.2978, -1.7808, -0.0832, 0.1572, -1.7751, 0.0629, 0.1755, -1.8335, 0.1504, 0.3249, 1.5256, 0.2321, -0.5546, 1.2968, -0.1811, 0.3534, 1.33, -0.1811, 0.3534, 1.33, -0.7713, -0.4525, 0.8629, -0.8882, 0.3173, 0.8972, -0.8882, 0.3173, 0.8972, -0.7713, -0.4525, 0.8629, -1.057, 0.126, 0.9512, -1.057, 0.126, 0.9512, -0.7713, -0.4525, 0.8629, -1.1208, -0.2211, 0.8364, -0.7713, -0.4525, 0.8629, -0.1811, 0.3534, 1.33, 0.2321, -0.5546, 1.2968, 0.2321, -0.5546, 1.2968, 0.5577, -0.3719, 1.5982, 0.5902, -0.5067, 1.4887, 0.6927, -0.351, 1.6834, 1.1748, -0.0814, 1.7311, 1.0161, -0.3973, 1.7034, 0.2321, -0.5546, 1.2968, 0.1504, 0.3249, 1.5256, 0.5577, -0.3719, 1.5982, 0.1504, 0.3249, 1.5256, 0.3761, 0.3877, 1.5951, 0.5577, -0.3719, 1.5982, 0.5577, -0.3719, 1.5982, 0.3761, 0.3877, 1.5951, 0.6927, -0.351, 1.6834, 0.6927, -0.351, 1.6834, 0.3761, 0.3877, 1.5951, 1.1748, -0.0814, 1.7311, 1.1748, -0.0814, 1.7311, 0.6881, 0.5954, 1.4818, 1.2715, 0.1425, 1.7004, 1.2715, 0.1425, 1.7004, 0.6881, 0.5954, 1.4818, 1.307, 0.4153, 1.6132, 1.307, 0.4153, 1.6132, 1.0721, 0.6892, 1.1619, 1.3943, 0.5306, 1.4472, 1.0721, 0.6892, 1.1619, 1.307, 0.4153, 1.6132, 0.6881, 0.5954, 1.4818, 0.5536, 0.8275, -1.4892, 0.7935, 0.8229, -1.3081, 0.606, 0.847, -1.3151, 0.5536, 0.8275, -1.4892, 0.8004, 0.2889, -1.6673, 0.7935, 0.8229, -1.3081, 0.7935, 0.8229, -1.3081, 0.8004, 0.2889, -1.6673, 0.9175, 0.7849, -1.3125, 0.0784, 0.5885, -1.8004, 0.2234, 0.3394, -1.8428, 0.2304, 0.721, -1.7333, 0.2304, 0.721, -1.7333, 0.2234, 0.3394, -1.8428, 0.453, 0.787, -1.6234, 0.453, 0.787, -1.6234, 0.8004, 0.2889, -1.6673, 0.5536, 0.8275, -1.4892, 0.9625, 0.7976, -1.2398, 1.1721, 0.7244, -1.0147, 0.9762, 0.8279, -1.1273, 0.8004, 0.2889, -1.6673, 1.0764, 0.3168, -1.4944, 0.9175, 0.7849, -1.3125, 0.9175, 0.7849, -1.3125, 1.0764, 0.3168, -1.4944, 0.9625, 0.7976, -1.2398, 0.9625, 0.7976, -1.2398, 1.1928, 0.2492, -1.3592, 1.1721, 0.7244, -1.0147, 1.0764, 0.3168, -1.4944, 1.1928, 0.2492, -1.3592, 0.9625, 0.7976, -1.2398, 1.1721, 0.7244, -1.0147, 1.1928, 0.2492, -1.3592, 1.3553, 0.3188, -1.0845, 0.0438, -0.3535, -1.5059, 0.2334, -0.4206, -1.5131, 0.1273, -0.231, -1.5631, 1.8018, -0.1166, 1.0465, 1.6936, -0.0517, 1.195, 1.7358, 0.1659, 1.068, 1.7358, 0.1659, 1.068, 1.6944, 0.1451, 1.1403, 1.7079, 0.2069, 1.098, 1.6936, -0.0517, 1.195, 1.6944, 0.1451, 1.1403, 1.7358, 0.1659, 1.068, 1.6936, -0.0517, 1.195, 1.8018, -0.1166, 1.0465, 1.6573, -0.2166, 1.2501, 1.6573, -0.2166, 1.2501, 1.7542, -0.4854, 0.9086, 1.5644, -0.4551, 1.31, 1.7542, -0.4854, 0.9086, 1.5663, -0.6718, 1.1167, 1.5644, -0.4551, 1.31, 1.5644, -0.4551, 1.31, 1.5663, -0.6718, 1.1167, 1.5141, -0.6082, 1.2539, 0.3565, 0.8862, -1.3307, 0.5536, 0.8275, -1.4892, 0.606, 0.847, -1.3151, 0.5536, 0.8275, -1.4892, 0.3565, 0.8862, -1.3307, 0.453, 0.787, -1.6234, 0.453, 0.787, -1.6234, 0.3565, 0.8862, -1.3307, 0.2304, 0.721, -1.7333, 0.2304, 0.721, -1.7333, -0.0895, 0.5831, -1.6991, 0.0784, 0.5885, -1.8004, -0.2181, 0.7012, -1.5976, -0.9001, 0.6982, -1.3812, -0.4893, 0.652, -1.605, 0.2304, 0.721, -1.7333, 0.3565, 0.8862, -1.3307, -0.0895, 0.5831, -1.6991, -0.0895, 0.5831, -1.6991, 0.3565, 0.8862, -1.3307, -0.2181, 0.7012, -1.5976, 0.5883, 0.9628, -1.0063, 0.4904, 0.8361, -0.4902, 0.4631, 0.9672, -1.1988, 0.4631, 0.9672, -1.1988, 0.4904, 0.8361, -0.4902, 0.3565, 0.8862, -1.3307, 0.3127, 0.6878, 0.2041, -1.2997, 0.5397, -0.7858, 0.4904, 0.8361, -0.4902, 1.0721, 0.6892, 1.1619, 0.6881, 0.5954, 1.4818, 0.6961, 0.7351, 0.3218, 0.6961, 0.7351, 0.3218, 0.6881, 0.5954, 1.4818, 0.3127, 0.6878, 0.2041, 0.3761, 0.3877, 1.5951, 0.1504, 0.3249, 1.5256, 0.6881, 0.5954, 1.4818, -0.1811, 0.3534, 1.33, 0.3127, 0.6878, 0.2041, 0.1504, 0.3249, 1.5256, 0.3127, 0.6878, 0.2041, 0.6881, 0.5954, 1.4818, 0.1504, 0.3249, 1.5256, -1.2352, 0.3268, 0.2088, 0.3127, 0.6878, 0.2041, -0.8882, 0.3173, 0.8972, -0.8882, 0.3173, 0.8972, 0.3127, 0.6878, 0.2041, -0.1811, 0.3534, 1.33, -1.2997, 0.5397, -0.7858, 0.3127, 0.6878, 0.2041, -1.3474, 0.367, -0.2501, -1.3474, 0.367, -0.2501, 0.3127, 0.6878, 0.2041, -1.2352, 0.3268, 0.2088, -0.9001, 0.6982, -1.3812, -1.2997, 0.5397, -0.7858, -1.1636, 0.6589, -1.169, -0.2181, 0.7012, -1.5976, 0.4904, 0.8361, -0.4902, -0.9001, 0.6982, -1.3812, 0.3565, 0.8862, -1.3307, 0.4904, 0.8361, -0.4902, -0.2181, 0.7012, -1.5976, -1.1208, -0.2211, 0.8364, -1.0843, -0.5896, 0.1569, -1.4177, -0.2864, 0.367, -1.4177, -0.2864, 0.367, -1.3066, -0.6146, -0.0152, -1.5688, -0.3115, 0.1105, -1.0843, -0.5896, 0.1569, -1.3066, -0.6146, -0.0152, -1.4177, -0.2864, 0.367, -1.5688, -0.3115, 0.1105, -1.3066, -0.6146, -0.0152, -1.6727, -0.3783, -0.0804, -1.6727, -0.3783, -0.0804, -1.3066, -0.6146, -0.0152, -1.6779, -0.4855, -0.1607, -1.6779, -0.4855, -0.1607, -1.3934, -0.7148, -0.3785, -1.6583, -0.5073, -0.3071, -1.6583, -0.5073, -0.3071, -1.3934, -0.7148, -0.3785, -1.5776, -0.5652, -0.4479, -1.5776, -0.5652, -0.4479, -1.3934, -0.7148, -0.3785, -1.4282, -0.6229, -0.5707, -1.3934, -0.7148, -0.3785, -1.3066, -0.6146, -0.0152, -1.2328, -0.714, -0.1886, -1.0843, -0.5896, 0.1569, -1.1208, -0.2211, 0.8364, -0.7713, -0.4525, 0.8629, -1.4177, -0.2864, 0.367, -1.2352, 0.3268, 0.2088, -1.1208, -0.2211, 0.8364, -1.1208, -0.2211, 0.8364, -1.2352, 0.3268, 0.2088, -1.057, 0.126, 0.9512, -1.057, 0.126, 0.9512, -1.2352, 0.3268, 0.2088, -0.8882, 0.3173, 0.8972, -1.2352, 0.3268, 0.2088, -1.5688, -0.3115, 0.1105, -1.3474, 0.367, -0.2501, -1.5688, -0.3115, 0.1105, -1.2352, 0.3268, 0.2088, -1.4177, -0.2864, 0.367, -1.6422, -0.27, -0.3156, -1.5688, -0.3115, 0.1105, -1.6727, -0.3783, -0.0804, -1.387, 0.0341, -0.9932, -1.2997, 0.5397, -0.7858, -1.4997, -0.1393, -0.7307, -1.4997, -0.1393, -0.7307, -1.3474, 0.367, -0.2501, -1.6422, -0.27, -0.3156, -1.2722, 0.4609, -1.2479, -1.387, 0.0341, -0.9932, -1.1493, 0.3036, -1.3146, -1.1636, 0.6589, -1.169, -1.2997, 0.5397, -0.7858, -1.2362, 0.5774, -1.237, -1.2362, 0.5774, -1.237, -1.2997, 0.5397, -0.7858, -1.2722, 0.4609, -1.2479, -1.2722, 0.4609, -1.2479, -1.2997, 0.5397, -0.7858, -1.387, 0.0341, -0.9932, -1.4997, -0.1393, -0.7307, -1.2997, 0.5397, -0.7858, -1.3474, 0.367, -0.2501, 1.1928, 0.2492, -1.3592, 1.0502, -0.6021, -1.1682, 1.3553, 0.3188, -1.0845, 1.3553, 0.3188, -1.0845, 1.259, -0.746, -0.7881, 1.7035, -0.1804, -0.405, 1.3553, 0.3188, -1.0845, 1.1109, -0.6863, -1.0196, 1.259, -0.746, -0.7881, 1.7035, -0.1804, -0.405, 1.5814, -0.7117, -0.4108, 1.8072, -0.4701, -0.0799, 1.8072, -0.4701, -0.0799, 1.6645, -0.7082, -0.1973, 1.7652, -0.6177, 0.216, 1.5814, -0.7117, -0.4108, 1.6645, -0.7082, -0.1973, 1.8072, -0.4701, -0.0799, 1.7652, -0.6177, 0.216, 1.6645, -0.7082, -0.1973, 1.6461, -0.7592, 0.1617, 1.436, -0.7221, -0.624, 1.5814, -0.7117, -0.4108, 1.7035, -0.1804, -0.405, 0.8004, 0.2889, -1.6673, 0.6415, -0.1642, -1.5814, 1.0764, 0.3168, -1.4944, 1.0764, 0.3168, -1.4944, 0.6415, -0.1642, -1.5814, 1.1928, 0.2492, -1.3592, 0.6415, -0.1642, -1.5814, 0.6096, -0.219, -1.5025, 1.1928, 0.2492, -1.3592, 0.0629, 0.1755, -1.8335, 0.2717, -0.1032, -1.6837, 0.2234, 0.3394, -1.8428, 0.2234, 0.3394, -1.8428, 0.2717, -0.1032, -1.6837, 0.8004, 0.2889, -1.6673, 0.6415, -0.1642, -1.5814, 0.8004, 0.2889, -1.6673, 0.2717, -0.1032, -1.6837, 0.2334, -0.4206, -1.5131, 0.6096, -0.219, -1.5025, 0.1273, -0.231, -1.5631, 0.6074, -0.5308, -1.4001, 0.6096, -0.219, -1.5025, 0.2334, -0.4206, -1.5131, 1.7035, -0.1804, -0.405, 1.259, -0.746, -0.7881, 1.436, -0.7221, -0.624, 1.1928, 0.2492, -1.3592, 0.9699, -0.5671, -1.2091, 1.0502, -0.6021, -1.1682, 1.1109, -0.6863, -1.0196, 1.3553, 0.3188, -1.0845, 1.0502, -0.6021, -1.1682, 0.6074, -0.5308, -1.4001, 0.9699, -0.5671, -1.2091, 0.6096, -0.219, -1.5025, 0.9699, -0.5671, -1.2091, 1.1928, 0.2492, -1.3592, 0.6096, -0.219, -1.5025, 0.8809, -0.6144, -1.19, 0.9699, -0.5671, -1.2091, 0.7765, -0.6276, -1.2561, -0.534, 0.3122, -1.6595, -0.9001, 0.6982, -1.3812, -1.1493, 0.3036, -1.3146, -1.1493, 0.3036, -1.3146, -1.2362, 0.5774, -1.237, -1.2722, 0.4609, -1.2479, -0.9001, 0.6982, -1.3812, -1.2362, 0.5774, -1.237, -1.1493, 0.3036, -1.3146, -1.2362, 0.5774, -1.237, -0.9001, 0.6982, -1.3812, -1.1636, 0.6589, -1.169, 1.502, -0.4979, 1.4563, 1.5141, -0.6082, 1.2539, 1.3317, -0.6356, 1.518, 1.3317, -0.6356, 1.518, 1.5141, -0.6082, 1.2539, 1.2689, -0.7439, 1.3995, 1.2689, -0.7439, 1.3995, 1.124, -0.9978, 1.0499, 1.1519, -0.8005, 1.3807, 1.1519, -0.8005, 1.3807, 1.124, -0.9978, 1.0499, 1.0338, -0.9119, 1.2882, 1.0338, -0.9119, 1.2882, 1.124, -0.9978, 1.0499, 1.0169, -0.9992, 1.153, 1.124, -0.9978, 1.0499, 1.5663, -0.6718, 1.1167, 1.2368, -0.9984, 0.9293, 1.2368, -0.9984, 0.9293, 1.5663, -0.6718, 1.1167, 1.4359, -0.8794, 0.8237, 1.5663, -0.6718, 1.1167, 1.2689, -0.7439, 1.3995, 1.5141, -0.6082, 1.2539, 1.124, -0.9978, 1.0499, 1.2689, -0.7439, 1.3995, 1.5663, -0.6718, 1.1167, 1.2715, 0.1425, 1.7004, 1.6307, -0.0932, 1.3908, 1.1748, -0.0814, 1.7311, 1.1748, -0.0814, 1.7311, 1.1624, -0.5844, 1.6321, 1.0161, -0.3973, 1.7034, 1.502, -0.4979, 1.4563, 1.3317, -0.6356, 1.518, 1.1624, -0.5844, 1.6321, 1.1748, -0.0814, 1.7311, 1.502, -0.4979, 1.4563, 1.1624, -0.5844, 1.6321, 1.502, -0.4979, 1.4563, 1.1748, -0.0814, 1.7311, 1.5954, -0.2805, 1.4511, 1.5954, -0.2805, 1.4511, 1.1748, -0.0814, 1.7311, 1.6307, -0.0932, 1.3908, 1.6307, -0.0932, 1.3908, 1.2715, 0.1425, 1.7004, 1.6136, 0.2882, 1.3723, 1.6136, 0.2882, 1.3723, 1.307, 0.4153, 1.6132, 1.3943, 0.5306, 1.4472, -0.534, 0.3122, -1.6595, -0.2181, 0.7012, -1.5976, -0.4893, 0.652, -1.605, -0.2181, 0.7012, -1.5976, -0.534, 0.3122, -1.6595, -0.0895, 0.5831, -1.6991, -0.0895, 0.5831, -1.6991, -0.534, 0.3122, -1.6595, -0.0864, 0.2978, -1.7808, -0.0974, 0.1771, -1.7741, -0.3113, -0.1613, -1.5779, -0.0692, 0.1191, -1.7434, -0.0692, 0.1191, -1.7434, -0.3113, -0.1613, -1.5779, -0.0258, 0.0213, -1.688, -0.0864, 0.2978, -1.7808, -0.534, 0.3122, -1.6595, -0.0974, 0.1771, -1.7741, -0.3113, -0.1613, -1.5779, -0.0974, 0.1771, -1.7741, -0.534, 0.3122, -1.6595, 0.0157, -0.0532, -1.6436, -0.3113, -0.1613, -1.5779, 0.1273, -0.231, -1.5631, 0.0784, 0.5885, -1.8004, -0.0864, 0.2978, -1.7808, 0.2234, 0.3394, -1.8428, 0.2234, 0.3394, -1.8428, -0.0864, 0.2978, -1.7808, 0.0629, 0.1755, -1.8335, 1.7035, -0.1804, -0.405, 1.3496, 0.6378, -0.4421, 1.3553, 0.3188, -1.0845, 1.3553, 0.3188, -1.0845, 1.2972, 0.6831, -0.5577, 1.1721, 0.7244, -1.0147, 1.3496, 0.6378, -0.4421, 1.2972, 0.6831, -0.5577, 1.3553, 0.3188, -1.0845, 1.3496, 0.6378, -0.4421, 1.5036, 0.5972, -0.0994, 1.3469, 0.6517, -0.3691, 1.571, 0.5779, 0.2372, 1.8018, -0.1166, 1.0465, 1.5632, 0.6125, 0.4668, 1.5632, 0.6125, 0.4668, 1.6466, 0.5513, 1.0625, 1.559, 0.6543, 0.9634, 1.5632, 0.6125, 0.4668, 1.7358, 0.1659, 1.068, 1.6466, 0.5513, 1.0625, 1.7358, 0.1659, 1.068, 1.7079, 0.2069, 1.098, 1.6466, 0.5513, 1.0625, 1.6466, 0.5513, 1.0625, 1.7079, 0.2069, 1.098, 1.6113, 0.4774, 1.1526, 1.6113, 0.4774, 1.1526, 1.7079, 0.2069, 1.098, 1.6616, 0.3586, 1.2649, 1.6616, 0.3586, 1.2649, 1.6944, 0.1451, 1.1403, 1.6136, 0.2882, 1.3723, 1.6136, 0.2882, 1.3723, 1.6944, 0.1451, 1.1403, 1.6307, -0.0932, 1.3908, 1.6307, -0.0932, 1.3908, 1.6573, -0.2166, 1.2501, 1.5954, -0.2805, 1.4511, 1.5954, -0.2805, 1.4511, 1.5644, -0.4551, 1.31, 1.502, -0.4979, 1.4563, 1.6573, -0.2166, 1.2501, 1.5644, -0.4551, 1.31, 1.5954, -0.2805, 1.4511, 1.6573, -0.2166, 1.2501, 1.6307, -0.0932, 1.3908, 1.6936, -0.0517, 1.195, 1.6936, -0.0517, 1.195, 1.6307, -0.0932, 1.3908, 1.6944, 0.1451, 1.1403, 1.5036, 0.5972, -0.0994, 1.7035, -0.1804, -0.405, 1.571, 0.5779, 0.2372, 1.6616, 0.3586, 1.2649, 1.7079, 0.2069, 1.098, 1.6944, 0.1451, 1.1403, 1.5632, 0.6125, 0.4668, 1.8018, -0.1166, 1.0465, 1.7358, 0.1659, 1.068, 1.8018, -0.1166, 1.0465, 1.7652, -0.6177, 0.216, 1.7542, -0.4854, 0.9086, 1.7652, -0.6177, 0.216, 1.8018, -0.1166, 1.0465, 1.571, 0.5779, 0.2372, 1.3496, 0.6378, -0.4421, 1.7035, -0.1804, -0.405, 1.5036, 0.5972, -0.0994, 1.571, 0.5779, 0.2372, 1.8072, -0.4701, -0.0799, 1.7652, -0.6177, 0.216, 1.487, 0.6285, 1.1421, 1.0721, 0.6892, 1.1619, 1.559, 0.6543, 0.9634, 1.559, 0.6543, 0.9634, 1.0721, 0.6892, 1.1619, 1.5632, 0.6125, 0.4668, 1.5632, 0.6125, 0.4668, 0.6961, 0.7351, 0.3218, 1.571, 0.5779, 0.2372, 1.571, 0.5779, 0.2372, 0.6961, 0.7351, 0.3218, 1.5036, 0.5972, -0.0994, 1.5036, 0.5972, -0.0994, 0.6961, 0.7351, 0.3218, 1.3469, 0.6517, -0.3691, 1.6616, 0.3586, 1.2649, 1.6136, 0.2882, 1.3723, 1.6113, 0.4774, 1.1526, 1.6113, 0.4774, 1.1526, 1.6136, 0.2882, 1.3723, 1.487, 0.6285, 1.1421, 1.2972, 0.6831, -0.5577, 0.4904, 0.8361, -0.4902, 1.1721, 0.7244, -1.0147, 1.1721, 0.7244, -1.0147, 0.4904, 0.8361, -0.4902, 0.9762, 0.8279, -1.1273, 1.5632, 0.6125, 0.4668, 1.0721, 0.6892, 1.1619, 0.6961, 0.7351, 0.3218, 1.3469, 0.6517, -0.3691, 0.4904, 0.8361, -0.4902, 1.2972, 0.6831, -0.5577, 0.6961, 0.7351, 0.3218, 0.4904, 0.8361, -0.4902, 1.3469, 0.6517, -0.3691, 1.0721, 0.6892, 1.1619, 1.487, 0.6285, 1.1421, 1.3943, 0.5306, 1.4472, 1.3943, 0.5306, 1.4472, 1.487, 0.6285, 1.1421, 1.6136, 0.2882, 1.3723, 0.4904, 0.8361, -0.4902, 0.6961, 0.7351, 0.3218, 0.3127, 0.6878, 0.2041, 0.5883, 0.9628, -1.0063, 0.9762, 0.8279, -1.1273, 0.4904, 0.8361, -0.4902, 0.7935, 0.8229, -1.3081, 0.6719, 0.9108, -1.1199, 0.606, 0.847, -1.3151, 0.6719, 0.9108, -1.1199, 0.9762, 0.8279, -1.1273, 0.5883, 0.9628, -1.0063, 0.9762, 0.8279, -1.1273, 0.7935, 0.8229, -1.3081, 0.9625, 0.7976, -1.2398, 0.9625, 0.7976, -1.2398, 0.7935, 0.8229, -1.3081, 0.9175, 0.7849, -1.3125, -1.2328, -0.714, -0.1886, -1.0843, -0.5896, 0.1569, -0.8459, -0.6965, -0.1217, -1.1493, 0.3036, -1.3146, -0.8122, -0.4144, -1.2617, -0.534, 0.3122, -1.6595, -0.534, 0.3122, -1.6595, -0.8122, -0.4144, -1.2617, -0.3113, -0.1613, -1.5779, -0.8122, -0.4144, -1.2617, -1.387, 0.0341, -0.9932, -1.1401, -0.5073, -0.9935, -1.1493, 0.3036, -1.3146, -1.387, 0.0341, -0.9932, -0.8122, -0.4144, -1.2617, -1.1401, -0.5073, -0.9935, -1.4997, -0.1393, -0.7307, -1.4282, -0.6229, -0.5707, -1.4282, -0.6229, -0.5707, -1.4997, -0.1393, -0.7307, -1.5776, -0.5652, -0.4479, -1.6583, -0.5073, -0.3071, -1.6727, -0.3783, -0.0804, -1.6779, -0.4855, -0.1607, -1.5776, -0.5652, -0.4479, -1.6422, -0.27, -0.3156, -1.6583, -0.5073, -0.3071, -1.4997, -0.1393, -0.7307, -1.6422, -0.27, -0.3156, -1.5776, -0.5652, -0.4479, -1.4997, -0.1393, -0.7307, -1.1401, -0.5073, -0.9935, -1.387, 0.0341, -0.9932, 1.6461, -0.7592, 0.1617, 1.4359, -0.8794, 0.8237, 1.7652, -0.6177, 0.216, 1.7652, -0.6177, 0.216, 1.4359, -0.8794, 0.8237, 1.7542, -0.4854, 0.9086, 0.6096, -0.219, -1.5025, 0.2717, -0.1032, -1.6837, 0.1273, -0.231, -1.5631, 0.6719, 0.9108, -1.1199, 0.4631, 0.9672, -1.1988, 0.606, 0.847, -1.3151, 0.606, 0.847, -1.3151, 0.4631, 0.9672, -1.1988, 0.3565, 0.8862, -1.3307, 1.6466, 0.5513, 1.0625, 1.487, 0.6285, 1.1421, 1.559, 0.6543, 0.9634, 1.1624, -0.5844, 1.6321, 1.0299, -0.8113, 1.3675, 1.0161, -0.3973, 1.7034, 1.0161, -0.3973, 1.7034, 1.0299, -0.8113, 1.3675, 0.6927, -0.351, 1.6834, 0.6927, -0.351, 1.6834, 0.5902, -0.5067, 1.4887, 0.5577, -0.3719, 1.5982, 0.2321, -0.5546, 1.2968, -0.8459, -0.6965, -0.1217, -0.7713, -0.4525, 0.8629, -0.7713, -0.4525, 0.8629, -0.8459, -0.6965, -0.1217, -1.0843, -0.5896, 0.1569, 0.6927, -0.351, 1.6834, 1.0299, -0.8113, 1.3675, 0.5902, -0.5067, 1.4887, 0.5902, -0.5067, 1.4887, 1.0169, -0.9992, 1.153, 0.2321, -0.5546, 1.2968, -0.8459, -0.6965, -0.1217, -1.3934, -0.7148, -0.3785, -1.2328, -0.714, -0.1886, 0.2321, -0.5546, 1.2968, 1.259, -0.746, -0.7881, -0.8459, -0.6965, -0.1217, 1.2368, -0.9984, 0.9293, 1.259, -0.746, -0.7881, 0.2321, -0.5546, 1.2968, 1.3317, -0.6356, 1.518, 1.1519, -0.8005, 1.3807, 1.1624, -0.5844, 1.6321, 1.1519, -0.8005, 1.3807, 1.3317, -0.6356, 1.518, 1.2689, -0.7439, 1.3995, 1.0169, -0.9992, 1.153, 0.5902, -0.5067, 1.4887, 1.0338, -0.9119, 1.2882, 1.0338, -0.9119, 1.2882, 0.5902, -0.5067, 1.4887, 1.0299, -0.8113, 1.3675, 1.0299, -0.8113, 1.3675, 1.1624, -0.5844, 1.6321, 1.1519, -0.8005, 1.3807, -0.8459, -0.6965, -0.1217, -1.4282, -0.6229, -0.5707, -1.3934, -0.7148, -0.3785, -1.4282, -0.6229, -0.5707, -0.8459, -0.6965, -0.1217, -1.1401, -0.5073, -0.9935, -1.1401, -0.5073, -0.9935, -0.8459, -0.6965, -0.1217, -0.8122, -0.4144, -1.2617, -0.8122, -0.4144, -1.2617, 0.0438, -0.3535, -1.5059, -0.3113, -0.1613, -1.5779, -0.3113, -0.1613, -1.5779, 0.0438, -0.3535, -1.5059, 0.1273, -0.231, -1.5631, 1.2368, -0.9984, 0.9293, 0.2321, -0.5546, 1.2968, 1.124, -0.9978, 1.0499, 1.124, -0.9978, 1.0499, 0.2321, -0.5546, 1.2968, 1.0169, -0.9992, 1.153, -0.8459, -0.6965, -0.1217, 0.0438, -0.3535, -1.5059, -0.8122, -0.4144, -1.2617, 0.0958, -0.4313, -1.5075, 0.0438, -0.3535, -1.5059, -0.8459, -0.6965, -0.1217, 0.0958, -0.4313, -1.5075, 0.6074, -0.5308, -1.4001, 0.2334, -0.4206, -1.5131, 0.0958, -0.4313, -1.5075, 0.7765, -0.6276, -1.2561, 0.6074, -0.5308, -1.4001, -0.8459, -0.6965, -0.1217, 0.7765, -0.6276, -1.2561, 0.0958, -0.4313, -1.5075, 1.6461, -0.7592, 0.1617, 1.2368, -0.9984, 0.9293, 1.4359, -0.8794, 0.8237, 1.5814, -0.7117, -0.4108, 1.6461, -0.7592, 0.1617, 1.6645, -0.7082, -0.1973, 1.259, -0.746, -0.7881, 1.6461, -0.7592, 0.1617, 1.436, -0.7221, -0.624, 1.436, -0.7221, -0.624, 1.6461, -0.7592, 0.1617, 1.5814, -0.7117, -0.4108, 0.9699, -0.5671, -1.2091, 0.8809, -0.6144, -1.19, 1.0502, -0.6021, -1.1682, 1.0502, -0.6021, -1.1682, 0.8809, -0.6144, -1.19, 1.1109, -0.6863, -1.0196, 1.1109, -0.6863, -1.0196, 0.8809, -0.6144, -1.19, 1.259, -0.746, -0.7881, 0.7765, -0.6276, -1.2561, -0.8459, -0.6965, -0.1217, 0.8809, -0.6144, -1.19, 1.259, -0.746, -0.7881, 1.2368, -0.9984, 0.9293, 1.6461, -0.7592, 0.1617)
-
-[node name="large_rock" instance=ExtResource("1_etns1")]
-
-[node name="LargeRock" parent="." index="0"]
-surface_material_override/0 = ExtResource("2_fes1d")
-
-[node name="StaticBody3D" type="StaticBody3D" parent="LargeRock" index="0"]
-
-[node name="CollisionShape3D" type="CollisionShape3D" parent="LargeRock/StaticBody3D" index="0"]
-shape = SubResource("ConcavePolygonShape3D_6ssqc")
diff --git a/addons/scatter-4/demos/assets/materials/grass.gdshader b/addons/scatter-4/demos/assets/materials/grass.gdshader
deleted file mode 100644
index 9850b66..0000000
--- a/addons/scatter-4/demos/assets/materials/grass.gdshader
+++ /dev/null
@@ -1,62 +0,0 @@
-shader_type spatial;
-
-render_mode depth_draw_opaque, cull_disabled;
-
-// Texture settings
-uniform sampler2D texture_albedo : hint_default_white, repeat_disable;
-uniform sampler2D texture_gradient : hint_default_white, repeat_disable;
-uniform sampler2D texture_noise : hint_default_white;
-uniform float alpha_scissor_threshold : hint_range(0.0, 1.0);
-uniform vec4 transmission : source_color;
-uniform vec4 secondary_color : source_color;
-uniform float secondary_attenuation = 0.2;
-uniform float grass_height = 1.0;
-
-// Wind settings
-uniform vec2 wind_direction = vec2(1, -0.5);
-uniform float wind_speed = 1.0;
-uniform float wind_strength = 2.0;
-uniform float noise_scale = 20.0;
-
-instance uniform float camera_bend_strength : hint_range(0.0, 3.0) = 0.2;
-
-varying float color;
-varying float height;
-
-void vertex() {
- height = VERTEX.y;
- float influence = smoothstep(0, 1, height / 2.0);
- vec4 world_pos = MODEL_MATRIX * vec4(VERTEX, 1.0);
- vec2 uv = world_pos.xz / (noise_scale + 1e-2);
- vec2 panning_uv = uv + fract(TIME * wind_direction * wind_speed);
- float wind = texture(texture_noise, panning_uv).r * 2.0 - 0.4;
- color = texture(texture_noise, uv).r;
-
- vec2 wind_offset = -wind_direction * wind_strength * influence * wind;
- world_pos.xz += wind_offset;
- world_pos.y -= wind * influence * smoothstep(0.0, height, wind_strength);
-
- //Push the top vertex away from the camera to bend the grass clump
- float ndotv = 1.0 - dot(vec3(0.0, 1.0, 0.0), normalize(INV_VIEW_MATRIX[1].xyz));
- world_pos.xz += INV_VIEW_MATRIX[1].xz * camera_bend_strength * height * ndotv;
-
- vec4 local_pos = inverse(MODEL_MATRIX) * world_pos;
- local_pos.x += wind_strength * influence * cos(TIME * 1.0) / 8.0;
- local_pos.z += wind_strength * influence * sin(TIME * 1.5) / 8.0;
-
- VERTEX = local_pos.xyz;
- //NORMAL = vec3(0.0, 1.0, 0.0);
-}
-
-void fragment() {
- vec4 tex = texture(texture_albedo, UV);
- if (tex.a < alpha_scissor_threshold) {
- discard;
- }
-
- BACKLIGHT = transmission.rgb;
- vec4 gradient = texture(texture_gradient, vec2(height / grass_height, 0.0));
- float secondary_weight = smoothstep(0.0, 1.0, color - secondary_attenuation);
- ALBEDO = tex.rbg * gradient.rgb;
- //ALBEDO = mix(ALBEDO, secondary_color.rgb, secondary_weight);
-}
diff --git a/addons/scatter-4/demos/assets/materials/leaves.gdshader b/addons/scatter-4/demos/assets/materials/leaves.gdshader
deleted file mode 100644
index ca84300..0000000
--- a/addons/scatter-4/demos/assets/materials/leaves.gdshader
+++ /dev/null
@@ -1,51 +0,0 @@
-shader_type spatial;
-
-render_mode depth_draw_opaque, cull_disabled;
-
-// Texture settings
-uniform sampler2D texture_albedo : hint_default_white, repeat_disable;
-uniform sampler2D texture_gradient : hint_default_white;
-uniform sampler2D texture_noise : hint_default_white;
-uniform float alpha_scissor_threshold : hint_range(0.0, 1.0);
-uniform vec4 transmission : source_color;
-uniform float total_height = 1.0;
-
-// Wind settings
-uniform vec2 wind_direction = vec2(1, -0.5);
-uniform float wind_speed = 1.0;
-uniform float wind_strength = 2.0;
-uniform float noise_scale = 20.0;
-
-varying float color;
-varying float height;
-
-void vertex() {
- height = VERTEX.y;
-
- vec4 world_pos = MODEL_MATRIX * vec4(VERTEX, 1.0);
- vec2 uv = (world_pos.xz + VERTEX.yy) / (noise_scale + 1e-2) ;
- vec2 panning_uv = uv + fract(TIME * wind_direction * wind_speed);
- float wind = texture(texture_noise, panning_uv).r * 2.0 - 0.4;
- color = texture(texture_noise, uv).r;
-
- float wind_influence = smoothstep(0, 1, 1.0 - UV.y);
- vec2 wind_offset = -wind_direction * wind_strength * wind_influence * wind;
- world_pos.xz += wind_offset;
- world_pos.y -= wind * wind_influence * wind_strength * 0.45;
-
- vec4 local_pos = inverse(MODEL_MATRIX) * world_pos;
-
- VERTEX = local_pos.xyz;
- //NORMAL = vec3(0.0, 1.0, 0.0);
-}
-
-void fragment() {
- vec4 tex = texture(texture_albedo, UV);
- if (tex.a < alpha_scissor_threshold) {
- discard;
- }
-
- BACKLIGHT = transmission.rgb;
- vec4 gradient = texture(texture_gradient, vec2(height / total_height, 0.0));
- ALBEDO = tex.rbg * gradient.rgb;
-}
diff --git a/addons/scatter-4/demos/assets/materials/m_bush.tres b/addons/scatter-4/demos/assets/materials/m_bush.tres
deleted file mode 100644
index d38f6e1..0000000
--- a/addons/scatter-4/demos/assets/materials/m_bush.tres
+++ /dev/null
@@ -1,37 +0,0 @@
-[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://bn3fr3m3glrnp"]
-
-[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/grass.gdshader" id="1_peshr"]
-[ext_resource type="Texture2D" uid="uid://b2a6ylo2enm4g" path="res://addons/proton_scatter/demos/assets/textures/t_bush.png" id="2_mbhvd"]
-
-[sub_resource type="Gradient" id="Gradient_122hb"]
-offsets = PackedFloat32Array(0, 0.5, 1)
-colors = PackedColorArray(0.179688, 0.0759602, 0.0183228, 1, 0.386532, 0.390625, 0.0230687, 1, 1, 0.693237, 0.0687054, 1)
-
-[sub_resource type="GradientTexture1D" id="GradientTexture1D_i0bw2"]
-gradient = SubResource("Gradient_122hb")
-
-[sub_resource type="FastNoiseLite" id="FastNoiseLite_eeqpx"]
-seed = 1
-frequency = 0.002
-
-[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7l0n1"]
-in_3d_space = true
-seamless = true
-seamless_blend_skirt = 0.65
-noise = SubResource("FastNoiseLite_eeqpx")
-
-[resource]
-render_priority = 0
-shader = ExtResource("1_peshr")
-shader_parameter/alpha_scissor_threshold = 0.25
-shader_parameter/transmission = Color(0.619608, 0.541176, 0.101961, 1)
-shader_parameter/secondary_color = Color(0, 0, 0, 1)
-shader_parameter/secondary_attenuation = 0.2
-shader_parameter/grass_height = 0.829
-shader_parameter/wind_direction = Vector2(1, -0.5)
-shader_parameter/wind_speed = 0.5
-shader_parameter/wind_strength = 0.15
-shader_parameter/noise_scale = 6.0
-shader_parameter/texture_albedo = ExtResource("2_mbhvd")
-shader_parameter/texture_gradient = SubResource("GradientTexture1D_i0bw2")
-shader_parameter/texture_noise = SubResource("NoiseTexture2D_7l0n1")
diff --git a/addons/scatter-4/demos/assets/materials/m_fence.tres b/addons/scatter-4/demos/assets/materials/m_fence.tres
deleted file mode 100644
index d2cedf3..0000000
--- a/addons/scatter-4/demos/assets/materials/m_fence.tres
+++ /dev/null
@@ -1,6 +0,0 @@
-[gd_resource type="SpatialMaterial" format=2]
-
-[resource]
-resource_name = "wood"
-vertex_color_use_as_albedo = true
-albedo_color = Color( 0.568627, 0.466667, 0.372549, 1 )
diff --git a/addons/scatter-4/demos/assets/materials/m_grass.tres b/addons/scatter-4/demos/assets/materials/m_grass.tres
deleted file mode 100644
index 3cf26c6..0000000
--- a/addons/scatter-4/demos/assets/materials/m_grass.tres
+++ /dev/null
@@ -1,38 +0,0 @@
-[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://c4mot1fo3siox"]
-
-[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/grass.gdshader" id="1_fntgl"]
-[ext_resource type="Texture2D" uid="uid://d23p13yi7asw0" path="res://addons/proton_scatter/demos/assets/textures/t_grass_2.png" id="2_1odx0"]
-
-[sub_resource type="Gradient" id="Gradient_122hb"]
-offsets = PackedFloat32Array(0, 0.473451, 1)
-colors = PackedColorArray(0.179688, 0.0855483, 0.00322032, 1, 0.251693, 0.390625, 0.0117187, 1, 1, 0.964706, 0.129412, 1)
-
-[sub_resource type="GradientTexture1D" id="GradientTexture1D_i0bw2"]
-gradient = SubResource("Gradient_122hb")
-
-[sub_resource type="FastNoiseLite" id="FastNoiseLite_eeqpx"]
-seed = 1
-frequency = 0.002
-
-[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_7l0n1"]
-in_3d_space = true
-seamless = true
-seamless_blend_skirt = 0.65
-noise = SubResource("FastNoiseLite_eeqpx")
-
-[resource]
-render_priority = 0
-shader = ExtResource("1_fntgl")
-shader_parameter/alpha_scissor_threshold = 0.3
-shader_parameter/transmission = Color(0.737255, 0.72549, 0, 1)
-shader_parameter/secondary_color = Color(0, 0, 0, 1)
-shader_parameter/secondary_attenuation = 0.2
-shader_parameter/grass_height = 0.6
-shader_parameter/wind_direction = Vector2(1, -0.5)
-shader_parameter/wind_speed = 0.5
-shader_parameter/wind_strength = 0.15
-shader_parameter/noise_scale = 6.0
-shader_parameter/camera_bend_strength = 0.3
-shader_parameter/texture_albedo = ExtResource("2_1odx0")
-shader_parameter/texture_gradient = SubResource("GradientTexture1D_i0bw2")
-shader_parameter/texture_noise = SubResource("NoiseTexture2D_7l0n1")
diff --git a/addons/scatter-4/demos/assets/materials/m_leaves.tres b/addons/scatter-4/demos/assets/materials/m_leaves.tres
deleted file mode 100644
index a73f1c1..0000000
--- a/addons/scatter-4/demos/assets/materials/m_leaves.tres
+++ /dev/null
@@ -1,38 +0,0 @@
-[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://djo80ucamk643"]
-
-[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/grass.gdshader" id="1_8py1k"]
-[ext_resource type="Texture2D" uid="uid://cgenco43aneod" path="res://addons/proton_scatter/demos/assets/textures/t_leaves_1.png" id="2_l2uea"]
-
-[sub_resource type="Gradient" id="Gradient_yy7fg"]
-offsets = PackedFloat32Array(0, 0.726872, 0.934272)
-colors = PackedColorArray(0.333333, 0.486275, 0.556863, 1, 0.496467, 0.55, 0.1485, 1, 0.898039, 0.670588, 0.0196078, 1)
-
-[sub_resource type="GradientTexture1D" id="GradientTexture1D_rwvaq"]
-gradient = SubResource("Gradient_yy7fg")
-
-[sub_resource type="FastNoiseLite" id="FastNoiseLite_wpihy"]
-seed = 1
-frequency = 0.002
-
-[sub_resource type="NoiseTexture2D" id="NoiseTexture_tgrrr"]
-in_3d_space = true
-seamless = true
-seamless_blend_skirt = 0.65
-noise = SubResource("FastNoiseLite_wpihy")
-
-[resource]
-render_priority = 0
-shader = ExtResource("1_8py1k")
-shader_parameter/alpha_scissor_threshold = 0.5
-shader_parameter/camera_bend_strength = 0.0
-shader_parameter/grass_height = 1.0
-shader_parameter/noise_scale = 12.0
-shader_parameter/secondary_attenuation = 0.2
-shader_parameter/secondary_color = Color(0, 0.305882, 0.211765, 1)
-shader_parameter/texture_albedo = ExtResource("2_l2uea")
-shader_parameter/texture_gradient = SubResource("GradientTexture1D_rwvaq")
-shader_parameter/texture_noise = SubResource("NoiseTexture_tgrrr")
-shader_parameter/transmission = Color(1, 0.975296, 0.943663, 1)
-shader_parameter/wind_direction = Vector2(1, -0.5)
-shader_parameter/wind_speed = 0.5
-shader_parameter/wind_strength = 0.05
diff --git a/addons/scatter-4/demos/assets/materials/m_mushroom.tres b/addons/scatter-4/demos/assets/materials/m_mushroom.tres
deleted file mode 100644
index 04ca9b8..0000000
--- a/addons/scatter-4/demos/assets/materials/m_mushroom.tres
+++ /dev/null
@@ -1,6 +0,0 @@
-[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://ds2hjlo70hglg"]
-
-[ext_resource type="Texture2D" uid="uid://bay670hln3ji5" path="res://addons/proton_scatter/demos/assets/textures/mushroom.png" id="1_y0tuv"]
-
-[resource]
-albedo_texture = ExtResource("1_y0tuv")
diff --git a/addons/scatter-4/demos/assets/materials/m_pine_leaves.tres b/addons/scatter-4/demos/assets/materials/m_pine_leaves.tres
deleted file mode 100644
index 6815dae..0000000
--- a/addons/scatter-4/demos/assets/materials/m_pine_leaves.tres
+++ /dev/null
@@ -1,35 +0,0 @@
-[gd_resource type="ShaderMaterial" load_steps=7 format=3 uid="uid://d28lq2qtgdyie"]
-
-[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/leaves.gdshader" id="1_hlncd"]
-[ext_resource type="Texture2D" uid="uid://ctpb1w0cr8tqc" path="res://addons/proton_scatter/demos/assets/textures/t_pine_branch.png" id="2_yef44"]
-
-[sub_resource type="Gradient" id="Gradient_pookg"]
-offsets = PackedFloat32Array(0.38342, 0.694301, 1)
-colors = PackedColorArray(0.059375, 0.078125, 0.07, 1, 0.628287, 0.73, 0.1752, 1, 0.897921, 1, 0, 1)
-
-[sub_resource type="GradientTexture1D" id="GradientTexture1D_n86jv"]
-gradient = SubResource("Gradient_pookg")
-
-[sub_resource type="FastNoiseLite" id="FastNoiseLite_t7o5y"]
-seed = 1
-frequency = 0.002
-
-[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_03p8g"]
-in_3d_space = true
-seamless = true
-seamless_blend_skirt = 0.65
-noise = SubResource("FastNoiseLite_t7o5y")
-
-[resource]
-render_priority = 0
-shader = ExtResource("1_hlncd")
-shader_parameter/alpha_scissor_threshold = 0.3
-shader_parameter/transmission = Color(0.745098, 0.741176, 0, 1)
-shader_parameter/total_height = 4.046
-shader_parameter/wind_direction = Vector2(1, -0.5)
-shader_parameter/wind_speed = 0.2
-shader_parameter/wind_strength = 0.05
-shader_parameter/noise_scale = 12.0
-shader_parameter/texture_albedo = ExtResource("2_yef44")
-shader_parameter/texture_gradient = SubResource("GradientTexture1D_n86jv")
-shader_parameter/texture_noise = SubResource("NoiseTexture2D_03p8g")
diff --git a/addons/scatter-4/demos/assets/materials/m_rock.tres b/addons/scatter-4/demos/assets/materials/m_rock.tres
deleted file mode 100644
index 7b165cb..0000000
--- a/addons/scatter-4/demos/assets/materials/m_rock.tres
+++ /dev/null
@@ -1,10 +0,0 @@
-[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://i0jgjmbbl2m5"]
-
-[ext_resource type="Texture2D" uid="uid://fdcwowhvwiia" path="res://addons/proton_scatter/demos/assets/textures/t_rock_dirty.png" id="1_hx37f"]
-
-[resource]
-albedo_color = Color(0.439216, 0.407843, 0.388235, 1)
-albedo_texture = ExtResource("1_hx37f")
-metallic_specular = 0.3
-uv1_triplanar = true
-uv1_world_triplanar = true
diff --git a/addons/scatter-4/demos/assets/materials/m_trunk.tres b/addons/scatter-4/demos/assets/materials/m_trunk.tres
deleted file mode 100644
index b1a2ecc..0000000
--- a/addons/scatter-4/demos/assets/materials/m_trunk.tres
+++ /dev/null
@@ -1,7 +0,0 @@
-[gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://d01d0h08lqqn6"]
-
-[ext_resource type="Texture2D" uid="uid://c72s4mvxck84w" path="res://addons/proton_scatter/demos/assets/textures/t_tree_bark_rough.png" id="1_g4son"]
-
-[resource]
-albedo_color = Color(0.470588, 0.376471, 0.309804, 1)
-albedo_texture = ExtResource("1_g4son")
diff --git a/addons/scatter-4/demos/assets/materials/m_water.gdshader b/addons/scatter-4/demos/assets/materials/m_water.gdshader
deleted file mode 100644
index cff66d1..0000000
--- a/addons/scatter-4/demos/assets/materials/m_water.gdshader
+++ /dev/null
@@ -1,90 +0,0 @@
-// Source: https://godotshaders.com/shader/toon-water-shader/
-
-shader_type spatial;
-
-const float SMOOTHSTEP_AA = 0.01;
-uniform sampler2D surfaceNoise;
-uniform sampler2D distortNoise;
-uniform sampler2D DEPTH_TEXTURE : hint_depth_texture, filter_linear_mipmap;
-
-uniform float beer_factor = 0.8;
-
-uniform float foam_distance = 0.01;
-uniform float foam_max_distance = 0.4;
-uniform float foam_min_distance = 0.04;
-uniform vec4 foam_color: source_color = vec4(1.0);
-
-uniform vec2 surface_noise_tiling = vec2(1.0, 4.0);
-uniform vec3 surface_noise_scroll = vec3(0.03, 0.03, 0.0);
-uniform float surface_noise_cutoff: hint_range(0, 1) = 0.777;
-uniform float surface_distortion_amount: hint_range(0, 1) = 0.27;
-
-uniform vec4 _DepthGradientShallow: source_color = vec4(0.325, 0.807, 0.971, 0.725);
-uniform vec4 _DepthGradientDeep: source_color = vec4(0.086, 0.407, 1, 0.749);
-uniform float _DepthMaxDistance: hint_range(0, 1) = 1.0;
-uniform float _DepthFactor = 1.0;
-
-uniform float roughness = 0.25;
-uniform float specular = 0.75;
-
-varying vec2 noiseUV;
-varying vec2 distortUV;
-varying vec3 viewNormal;
-
-
-vec4 alphaBlend(vec4 top, vec4 bottom)
-{
- vec3 color = (top.rgb * top.a) + (bottom.rgb * (1.0 - top.a));
- float alpha = top.a + bottom.a * (1.0 - top.a);
-
- return vec4(color, alpha);
-}
-
-void vertex() {
- viewNormal = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;
- noiseUV = UV * surface_noise_tiling;
- distortUV = UV;
-}
-
-void fragment(){
- // https://www.youtube.com/watch?v=Jq3he9Lbj7M
- float depthVal = texture(DEPTH_TEXTURE, SCREEN_UV).r;
- float depth = PROJECTION_MATRIX[3][2] / (depthVal + PROJECTION_MATRIX[2][2]);
- depth = depth + VERTEX.z;
- depth = exp(-depth * beer_factor);
- depth = 1.0 - depth;
-
- // Still unsure how to get properly the NORMAL from the camera
- // This was generated by ChatGPT xD
- vec4 view_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depthVal, 1.0);
- view_pos /= view_pos.w;
- vec3 existingNormal = normalize(cross( dFdx(view_pos.xyz), dFdy(view_pos.xyz)));
-
- float normalDot = clamp(dot(existingNormal.xyz, viewNormal), 0.0, 1.0);
- float foamDistance = mix(foam_max_distance, foam_min_distance, normalDot);
-
- float foamDepth = clamp(depth / foamDistance, 0.0, 1.0);
- float surfaceNoiseCutoff = foamDepth * surface_noise_cutoff;
-
- vec4 distortNoiseSample = texture(distortNoise, distortUV);
- vec2 distortAmount = (distortNoiseSample.xy * 2.0 -1.0) * surface_distortion_amount;
-
- vec2 noise_uv = vec2(
- (noiseUV.x + TIME * surface_noise_scroll.x) + distortAmount.x ,
- (noiseUV.y + TIME * surface_noise_scroll.y + distortAmount.y)
- );
- float surfaceNoiseSample = texture(surfaceNoise, noise_uv).r;
- float surfaceNoiseAmount = smoothstep(surfaceNoiseCutoff - SMOOTHSTEP_AA, surfaceNoiseCutoff + SMOOTHSTEP_AA, surfaceNoiseSample);
-
- float waterDepth = clamp(depth / _DepthMaxDistance, 0.0, 1.0) * _DepthFactor;
- vec4 waterColor = mix(_DepthGradientShallow, _DepthGradientDeep, waterDepth);
-
- vec4 surfaceNoiseColor = foam_color;
- surfaceNoiseColor.a *= surfaceNoiseAmount;
- vec4 color = alphaBlend(surfaceNoiseColor, waterColor);
-
- ALBEDO = color.rgb;
- ALPHA = color.a;
- ROUGHNESS = roughness;
- SPECULAR = specular;
-}
\ No newline at end of file
diff --git a/addons/scatter-4/demos/assets/materials/m_water.tres b/addons/scatter-4/demos/assets/materials/m_water.tres
deleted file mode 100644
index 9dcd264..0000000
--- a/addons/scatter-4/demos/assets/materials/m_water.tres
+++ /dev/null
@@ -1,40 +0,0 @@
-[gd_resource type="ShaderMaterial" load_steps=6 format=3 uid="uid://c7mw5tryqfggw"]
-
-[ext_resource type="Shader" path="res://addons/proton_scatter/demos/assets/materials/m_water.gdshader" id="1_j8rl3"]
-
-[sub_resource type="FastNoiseLite" id="FastNoiseLite_7bjdc"]
-noise_type = 2
-fractal_type = 3
-
-[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_wxuht"]
-seamless = true
-noise = SubResource("FastNoiseLite_7bjdc")
-
-[sub_resource type="FastNoiseLite" id="FastNoiseLite_dx86n"]
-noise_type = 2
-domain_warp_enabled = true
-
-[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_1j0ob"]
-seamless = true
-noise = SubResource("FastNoiseLite_dx86n")
-
-[resource]
-render_priority = 0
-shader = ExtResource("1_j8rl3")
-shader_parameter/beer_factor = 4.0
-shader_parameter/foam_distance = 0.01
-shader_parameter/foam_max_distance = 0.345
-shader_parameter/foam_min_distance = 0.05
-shader_parameter/foam_color = Color(1, 1, 1, 0.784314)
-shader_parameter/surface_noise_tiling = Vector2(1, 4)
-shader_parameter/surface_noise_scroll = Vector3(0.03, 0.03, 0)
-shader_parameter/surface_noise_cutoff = 0.875
-shader_parameter/surface_distortion_amount = 0.65
-shader_parameter/_DepthGradientShallow = Color(0.435294, 0.647059, 0.972549, 0.72549)
-shader_parameter/_DepthGradientDeep = Color(0.0823529, 0.392157, 0.701961, 0.862745)
-shader_parameter/_DepthMaxDistance = 1.0
-shader_parameter/_DepthFactor = 1.0
-shader_parameter/roughness = 0.001
-shader_parameter/specular = 0.5
-shader_parameter/surfaceNoise = SubResource("NoiseTexture2D_1j0ob")
-shader_parameter/distortNoise = SubResource("NoiseTexture2D_wxuht")
diff --git a/addons/scatter-4/demos/assets/models/brick.glb b/addons/scatter-4/demos/assets/models/brick.glb
deleted file mode 100644
index 0748793..0000000
Binary files a/addons/scatter-4/demos/assets/models/brick.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/bush.glb b/addons/scatter-4/demos/assets/models/bush.glb
deleted file mode 100644
index e25bf36..0000000
Binary files a/addons/scatter-4/demos/assets/models/bush.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/dead_branch.glb b/addons/scatter-4/demos/assets/models/dead_branch.glb
deleted file mode 100644
index de35d32..0000000
Binary files a/addons/scatter-4/demos/assets/models/dead_branch.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/fence_planks.glb b/addons/scatter-4/demos/assets/models/fence_planks.glb
deleted file mode 100644
index 2ac79fd..0000000
Binary files a/addons/scatter-4/demos/assets/models/fence_planks.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/gobot.glb b/addons/scatter-4/demos/assets/models/gobot.glb
deleted file mode 100644
index f8a83fe..0000000
Binary files a/addons/scatter-4/demos/assets/models/gobot.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/grass.glb b/addons/scatter-4/demos/assets/models/grass.glb
deleted file mode 100644
index f3a2f57..0000000
Binary files a/addons/scatter-4/demos/assets/models/grass.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/grass_2.glb b/addons/scatter-4/demos/assets/models/grass_2.glb
deleted file mode 100644
index 8c20845..0000000
Binary files a/addons/scatter-4/demos/assets/models/grass_2.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/large_rock.glb b/addons/scatter-4/demos/assets/models/large_rock.glb
deleted file mode 100644
index e88cd6c..0000000
Binary files a/addons/scatter-4/demos/assets/models/large_rock.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/mushrooms.glb b/addons/scatter-4/demos/assets/models/mushrooms.glb
deleted file mode 100644
index 6fc402a..0000000
Binary files a/addons/scatter-4/demos/assets/models/mushrooms.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/pine_tree.glb b/addons/scatter-4/demos/assets/models/pine_tree.glb
deleted file mode 100644
index 2081573..0000000
Binary files a/addons/scatter-4/demos/assets/models/pine_tree.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/small_rock.glb b/addons/scatter-4/demos/assets/models/small_rock.glb
deleted file mode 100644
index f30897f..0000000
Binary files a/addons/scatter-4/demos/assets/models/small_rock.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/models/tree.glb b/addons/scatter-4/demos/assets/models/tree.glb
deleted file mode 100644
index 92e9012..0000000
Binary files a/addons/scatter-4/demos/assets/models/tree.glb and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/mushroom.tscn b/addons/scatter-4/demos/assets/mushroom.tscn
deleted file mode 100644
index 43d1923..0000000
--- a/addons/scatter-4/demos/assets/mushroom.tscn
+++ /dev/null
@@ -1,9 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://bodkixm8bubes"]
-
-[ext_resource type="PackedScene" uid="uid://c38uugpgw7hjm" path="res://addons/proton_scatter/demos/assets/models/mushrooms.glb" id="1_spmys"]
-[ext_resource type="Material" uid="uid://ds2hjlo70hglg" path="res://addons/proton_scatter/demos/assets/materials/m_mushroom.tres" id="2_y6jw1"]
-
-[node name="mushrooms" instance=ExtResource("1_spmys")]
-
-[node name="Sphere001" parent="." index="0"]
-surface_material_override/0 = ExtResource("2_y6jw1")
diff --git a/addons/scatter-4/demos/assets/pine_tree.tscn b/addons/scatter-4/demos/assets/pine_tree.tscn
deleted file mode 100644
index 9f1b08c..0000000
--- a/addons/scatter-4/demos/assets/pine_tree.tscn
+++ /dev/null
@@ -1,31 +0,0 @@
-[gd_scene load_steps=6 format=3 uid="uid://caqxfqurbp3ku"]
-
-[ext_resource type="PackedScene" uid="uid://bhums0j31gm5n" path="res://addons/proton_scatter/demos/assets/models/pine_tree.glb" id="1_hw1e5"]
-[ext_resource type="Material" uid="uid://d01d0h08lqqn6" path="res://addons/proton_scatter/demos/assets/materials/m_trunk.tres" id="2_cgtpc"]
-[ext_resource type="Material" uid="uid://d28lq2qtgdyie" path="res://addons/proton_scatter/demos/assets/materials/m_pine_leaves.tres" id="2_xnytt"]
-
-[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_2xqpo"]
-radius = 0.0750397
-height = 1.3553
-
-[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_uhp33"]
-radius = 0.101768
-height = 0.489166
-
-[node name="pine_tree" instance=ExtResource("1_hw1e5")]
-
-[node name="Trunk" parent="." index="0"]
-surface_material_override/0 = ExtResource("2_cgtpc")
-
-[node name="Leaves" parent="." index="1"]
-surface_material_override/0 = ExtResource("2_xnytt")
-
-[node name="StaticBody3D" type="StaticBody3D" parent="." index="2"]
-
-[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" index="0"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.661681, 0)
-shape = SubResource("CapsuleShape3D_2xqpo")
-
-[node name="CollisionShape3D2" type="CollisionShape3D" parent="StaticBody3D" index="1"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.015189, 0)
-shape = SubResource("CapsuleShape3D_uhp33")
diff --git a/addons/scatter-4/demos/assets/small_rock.tscn b/addons/scatter-4/demos/assets/small_rock.tscn
deleted file mode 100644
index 2025b28..0000000
--- a/addons/scatter-4/demos/assets/small_rock.tscn
+++ /dev/null
@@ -1,9 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://bltmr2xgs8nq1"]
-
-[ext_resource type="PackedScene" uid="uid://b81l25tbebki4" path="res://addons/proton_scatter/demos/assets/models/small_rock.glb" id="1_e2qk6"]
-[ext_resource type="Material" uid="uid://i0jgjmbbl2m5" path="res://addons/proton_scatter/demos/assets/materials/m_rock.tres" id="2_clsfy"]
-
-[node name="small_rock" instance=ExtResource("1_e2qk6")]
-
-[node name="SmallRock" parent="." index="0"]
-surface_material_override/0 = ExtResource("2_clsfy")
diff --git a/addons/scatter-4/demos/assets/source.blend b/addons/scatter-4/demos/assets/source.blend
deleted file mode 100644
index f973cf9..0000000
Binary files a/addons/scatter-4/demos/assets/source.blend and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/mushroom.png b/addons/scatter-4/demos/assets/textures/mushroom.png
deleted file mode 100644
index f1f960d..0000000
Binary files a/addons/scatter-4/demos/assets/textures/mushroom.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/sky_2.png b/addons/scatter-4/demos/assets/textures/sky_2.png
deleted file mode 100644
index 921ae63..0000000
Binary files a/addons/scatter-4/demos/assets/textures/sky_2.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_bush.png b/addons/scatter-4/demos/assets/textures/t_bush.png
deleted file mode 100644
index 3afb9f9..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_bush.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_grass.png b/addons/scatter-4/demos/assets/textures/t_grass.png
deleted file mode 100644
index 2f712d4..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_grass.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_grass_2.png b/addons/scatter-4/demos/assets/textures/t_grass_2.png
deleted file mode 100644
index 346a5c1..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_grass_2.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_leaves_1.png b/addons/scatter-4/demos/assets/textures/t_leaves_1.png
deleted file mode 100644
index 1913781..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_leaves_1.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_pine_branch.png b/addons/scatter-4/demos/assets/textures/t_pine_branch.png
deleted file mode 100644
index 26ea314..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_pine_branch.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_rock.jpg b/addons/scatter-4/demos/assets/textures/t_rock.jpg
deleted file mode 100644
index d709604..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_rock.jpg and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_rock_dirty.png b/addons/scatter-4/demos/assets/textures/t_rock_dirty.png
deleted file mode 100644
index 69ef74c..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_rock_dirty.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_sand.png b/addons/scatter-4/demos/assets/textures/t_sand.png
deleted file mode 100644
index 244c60b..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_sand.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_tree_bark.png b/addons/scatter-4/demos/assets/textures/t_tree_bark.png
deleted file mode 100755
index 73529aa..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_tree_bark.png and /dev/null differ
diff --git a/addons/scatter-4/demos/assets/textures/t_tree_bark_rough.png b/addons/scatter-4/demos/assets/textures/t_tree_bark_rough.png
deleted file mode 100644
index 80823c2..0000000
Binary files a/addons/scatter-4/demos/assets/textures/t_tree_bark_rough.png and /dev/null differ
diff --git a/addons/scatter-4/demos/showcase.tscn b/addons/scatter-4/demos/showcase.tscn
deleted file mode 100644
index fde7ff4..0000000
--- a/addons/scatter-4/demos/showcase.tscn
+++ /dev/null
@@ -1,715 +0,0 @@
-[gd_scene load_steps=78 format=3 uid="uid://dga4klregd82"]
-
-[ext_resource type="Texture2D" uid="uid://bgc5rl13dopuj" path="res://addons/proton_scatter/demos/assets/textures/sky_2.png" id="1_bp1wy"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/scatter.gd" id="1_odnwj"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/modifier_stack.gd" id="2_wdwa6"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/scatter_item.gd" id="3_tn31i"]
-[ext_resource type="Material" uid="uid://c7mw5tryqfggw" path="res://addons/proton_scatter/demos/assets/materials/m_water.tres" id="3_yrj3o"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/scatter_shape.gd" id="4_5klvy"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/create_inside_grid.gd" id="4_cnevb"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/randomize_transforms.gd" id="5_h0430"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/shapes/path_shape.gd" id="8_vjeqj"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/shapes/sphere_shape.gd" id="9_mhcwm"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/shapes/box_shape.gd" id="11_lv5tc"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/relax.gd" id="12_04tbd"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/project_on_geometry.gd" id="13_s5uny"]
-[ext_resource type="PackedScene" uid="uid://bmglbfn5jaubp" path="res://addons/proton_scatter/demos/assets/gobot.tscn" id="15_mnk3f"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/create_inside_random.gd" id="15_terd0"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/randomize_rotation.gd" id="16_qmwrn"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/array.gd" id="17_2af2s"]
-
-[sub_resource type="PanoramaSkyMaterial" id="PanoramaSkyMaterial_bxgb5"]
-panorama = ExtResource("1_bp1wy")
-
-[sub_resource type="Sky" id="Sky_ju840"]
-sky_material = SubResource("PanoramaSkyMaterial_bxgb5")
-
-[sub_resource type="Environment" id="Environment_1kod5"]
-background_mode = 2
-background_energy_multiplier = 1.25
-sky = SubResource("Sky_ju840")
-sky_rotation = Vector3(0, 1.13446, 0)
-ambient_light_color = Color(0.352941, 0.215686, 0.529412, 1)
-ambient_light_sky_contribution = 0.2
-ambient_light_energy = 0.5
-tonemap_mode = 3
-tonemap_white = 1.2
-glow_enabled = true
-fog_enabled = true
-fog_light_color = Color(0.243137, 0.411765, 0.607843, 1)
-fog_light_energy = 1.8
-fog_density = 0.02
-fog_sky_affect = 0.05
-fog_height = 4.0
-fog_height_density = 0.02
-volumetric_fog_temporal_reprojection_enabled = false
-adjustment_enabled = true
-adjustment_contrast = 1.15
-adjustment_saturation = 1.1
-
-[sub_resource type="PlaneMesh" id="PlaneMesh_84bvf"]
-size = Vector2(300, 300)
-
-[sub_resource type="Resource" id="Resource_6c8re"]
-script = ExtResource("4_cnevb")
-spacing = Vector3(1.5, 2, 1)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_js1jv"]
-script = ExtResource("5_h0430")
-position = Vector3(0.173, 0.222, 0.918)
-rotation = Vector3(10, 360, 10)
-scale = Vector3(1.201, 0.399, 1.183)
-enabled = true
-override_global_seed = true
-custom_seed = 4
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_cp6c8"]
-script = ExtResource("16_qmwrn")
-rotation = Vector3(360, 0, 360)
-snap_angle = Vector3(180, 0, 180)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_8xlqi"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_6c8re"), SubResource("Resource_js1jv"), SubResource("Resource_cp6c8")])
-
-[sub_resource type="Curve3D" id="Curve3D_ew6qf"]
-_data = {
-"points": PackedVector3Array(3.80872, 0, 0.938568, -3.80872, 0, -0.938568, -5.23002, 0, 0.969148, -4.01273, -4.76837e-07, 0.319402, 4.01273, 4.76837e-07, -0.319402, -4.0201, 0, -3.87154, 0.483762, 0, -2.35326, -0.483762, 0, 2.35326, 2.27396, 0, -0.779812),
-"tilts": PackedFloat32Array(0, 0, 0)
-}
-point_count = 3
-
-[sub_resource type="Resource" id="Resource_ftiyl"]
-script = ExtResource("8_vjeqj")
-closed = true
-thickness = 0.0
-curve = SubResource("Curve3D_ew6qf")
-
-[sub_resource type="Resource" id="Resource_kskpi"]
-script = ExtResource("4_cnevb")
-spacing = Vector3(4, 4, 4)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_solyr"]
-script = ExtResource("5_h0430")
-position = Vector3(10, 4, 10)
-rotation = Vector3(10, 180, 10)
-scale = Vector3(5, 5, 5)
-enabled = true
-override_global_seed = true
-custom_seed = 25
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_j43hv"]
-script = ExtResource("16_qmwrn")
-rotation = Vector3(360, 0, 360)
-snap_angle = Vector3(180, 0, 180)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_33b0f"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_kskpi"), SubResource("Resource_solyr"), SubResource("Resource_j43hv")])
-
-[sub_resource type="Resource" id="Resource_1jvqx"]
-script = ExtResource("11_lv5tc")
-size = Vector3(29.1644, 1, 11.2962)
-
-[sub_resource type="Resource" id="Resource_2jkyh"]
-script = ExtResource("15_terd0")
-amount = 14
-enabled = true
-override_global_seed = true
-custom_seed = 30
-restrict_height = false
-reference_frame = 1
-
-[sub_resource type="Resource" id="Resource_lh4ex"]
-script = ExtResource("12_04tbd")
-iterations = 5
-offset_step = 0.1
-consecutive_step_multiplier = 0.5
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_rgn8a"]
-script = ExtResource("13_s5uny")
-ray_direction = Vector3(0, -1, 0)
-ray_length = 10.0
-ray_offset = 1.0
-remove_points_on_miss = true
-align_with_collision_normal = false
-max_slope = 90.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_7pgn6"]
-script = ExtResource("5_h0430")
-position = Vector3(0, 0, 0)
-rotation = Vector3(0, 0, 0)
-scale = Vector3(3, 3, 3)
-enabled = true
-override_global_seed = true
-custom_seed = 30
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_bwydp"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_2jkyh"), SubResource("Resource_lh4ex"), SubResource("Resource_rgn8a"), SubResource("Resource_7pgn6")])
-
-[sub_resource type="Curve3D" id="Curve3D_p31po"]
-_data = {
-"points": PackedVector3Array(0, 0, 0, 0, 0, 0, -5.18137, -4.76837e-07, 0.742086, 0, 0, 0, 0, 0, 0, -6.91097, -2.38419e-07, -2.62414, 0, 0, 0, 0, 0, 0, 4.23263, 0, -2.10494, 0, 0, 0, 0, 0, 0, 4.13477, 0, -0.306146, 0, 0, 0, 0, 0, 0, -1.36184, 0, 1.16488),
-"tilts": PackedFloat32Array(0, 0, 0, 0, 0)
-}
-point_count = 5
-
-[sub_resource type="Resource" id="Resource_jto6x"]
-script = ExtResource("8_vjeqj")
-closed = true
-thickness = 0.0
-curve = SubResource("Curve3D_p31po")
-
-[sub_resource type="Resource" id="Resource_ae4op"]
-script = ExtResource("15_terd0")
-amount = 500
-enabled = true
-override_global_seed = true
-custom_seed = 7
-restrict_height = true
-reference_frame = 1
-
-[sub_resource type="Resource" id="Resource_g4wmd"]
-script = ExtResource("5_h0430")
-position = Vector3(0, 0, 0)
-rotation = Vector3(360, 360, 360)
-scale = Vector3(1.5, 1.5, 1.5)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_jagld"]
-script = ExtResource("13_s5uny")
-ray_direction = Vector3(0, -1, 0)
-ray_length = 10.0
-ray_offset = 1.0
-remove_points_on_miss = true
-align_with_collision_normal = false
-max_slope = 90.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_4tdre"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_ae4op"), SubResource("Resource_g4wmd"), SubResource("Resource_jagld")])
-
-[sub_resource type="Resource" id="Resource_0weea"]
-script = ExtResource("11_lv5tc")
-size = Vector3(11.3737, 0.642154, 5.57444)
-
-[sub_resource type="Resource" id="Resource_2o5oc"]
-script = ExtResource("15_terd0")
-amount = 10000
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 1
-
-[sub_resource type="Resource" id="Resource_8ser0"]
-script = ExtResource("5_h0430")
-position = Vector3(0.2, 0, 0.2)
-rotation = Vector3(20, 360, 20)
-scale = Vector3(6, 5, 6)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_75nf0"]
-script = ExtResource("13_s5uny")
-ray_direction = Vector3(0, -1, 0)
-ray_length = 10.0
-ray_offset = 1.0
-remove_points_on_miss = true
-align_with_collision_normal = false
-max_slope = 20.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_v2tsa"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_2o5oc"), SubResource("Resource_8ser0"), SubResource("Resource_75nf0")])
-
-[sub_resource type="Resource" id="Resource_iou5c"]
-script = ExtResource("9_mhcwm")
-radius = 3.03782
-
-[sub_resource type="Resource" id="Resource_llvm5"]
-script = ExtResource("9_mhcwm")
-radius = 2.15656
-
-[sub_resource type="Resource" id="Resource_5qcqk"]
-script = ExtResource("4_cnevb")
-spacing = Vector3(0.2, 0.3, 0.2)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_fxfw2"]
-script = ExtResource("16_qmwrn")
-rotation = Vector3(0, 360, 0)
-snap_angle = Vector3(0, 0, 0)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_6jn28"]
-script = ExtResource("5_h0430")
-position = Vector3(0, 0.107, 0)
-rotation = Vector3(0, 360, 0)
-scale = Vector3(0.75, 0.75, 0.75)
-enabled = true
-override_global_seed = true
-custom_seed = 20
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_hs61a"]
-script = ExtResource("13_s5uny")
-ray_direction = Vector3(0, 0, 1)
-ray_length = 1.0
-ray_offset = 0.5
-remove_points_on_miss = true
-align_with_collision_normal = false
-max_slope = 90.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_u4rnr"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_5qcqk"), SubResource("Resource_fxfw2"), SubResource("Resource_6jn28"), SubResource("Resource_hs61a")])
-
-[sub_resource type="Resource" id="Resource_272xj"]
-script = ExtResource("11_lv5tc")
-size = Vector3(1.64858, 0.539851, 0.847638)
-
-[sub_resource type="Resource" id="Resource_p1ngd"]
-script = ExtResource("11_lv5tc")
-size = Vector3(1.07363, 0.537276, 1.1214)
-
-[sub_resource type="Resource" id="Resource_is18q"]
-script = ExtResource("11_lv5tc")
-size = Vector3(2.15312, 0.683458, 0.916096)
-
-[sub_resource type="Resource" id="Resource_jheb7"]
-script = ExtResource("15_terd0")
-amount = 150
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 1
-
-[sub_resource type="Resource" id="Resource_p6u0x"]
-script = ExtResource("5_h0430")
-position = Vector3(0.058, 0, 0.086)
-rotation = Vector3(360, 360, 360)
-scale = Vector3(4, 4, 4)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_mijan"]
-script = ExtResource("13_s5uny")
-ray_direction = Vector3(0, -1, 0)
-ray_length = 10.0
-ray_offset = 1.0
-remove_points_on_miss = true
-align_with_collision_normal = true
-max_slope = 90.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_3cg3d"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_jheb7"), SubResource("Resource_p6u0x"), SubResource("Resource_mijan")])
-
-[sub_resource type="Curve3D" id="Curve3D_lorvn"]
-_data = {
-"points": PackedVector3Array(0, 0, 0, 0, 0, 0, -3.37811, 0, 0.414886, 0, 0, 0, 0, 0, 0, -6.85248, 0, 0.176656, 0, 0, 0, 0, 0, 0, -6.65984, 0, -2.15071, 0, 0, 0, 0, 0, 0, 0.0761006, 0, -2.44185, 0, 0, 0, 0, 0, 0, 0.0166965, 0, -0.474161),
-"tilts": PackedFloat32Array(0, 0, 0, 0, 0)
-}
-point_count = 5
-
-[sub_resource type="Resource" id="Resource_ojpr0"]
-script = ExtResource("8_vjeqj")
-closed = true
-thickness = 0.0
-curve = SubResource("Curve3D_lorvn")
-
-[sub_resource type="Resource" id="Resource_ubw23"]
-script = ExtResource("15_terd0")
-amount = 13
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 1
-
-[sub_resource type="Resource" id="Resource_pwqrf"]
-script = ExtResource("12_04tbd")
-iterations = 5
-offset_step = 0.02
-consecutive_step_multiplier = 0.35
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_v58t8"]
-script = ExtResource("5_h0430")
-position = Vector3(1, 1, 1)
-rotation = Vector3(0, 360, 0)
-scale = Vector3(0.2, 0.2, 0.2)
-enabled = true
-override_global_seed = true
-custom_seed = 9
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_pdeyv"]
-script = ExtResource("13_s5uny")
-ray_direction = Vector3(0, -1, 0)
-ray_length = 10.0
-ray_offset = 1.0
-remove_points_on_miss = true
-align_with_collision_normal = false
-max_slope = 90.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_kuroc"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_ubw23"), SubResource("Resource_pwqrf"), SubResource("Resource_v58t8"), SubResource("Resource_pdeyv")])
-
-[sub_resource type="Resource" id="Resource_bpfxh"]
-script = ExtResource("9_mhcwm")
-radius = 1.57673
-
-[sub_resource type="Resource" id="Resource_rspgw"]
-script = ExtResource("9_mhcwm")
-radius = 1.57673
-
-[sub_resource type="Resource" id="Resource_q8na6"]
-script = ExtResource("4_cnevb")
-spacing = Vector3(0.78, 2, 0.78)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-reference_frame = 1
-
-[sub_resource type="Resource" id="Resource_2nnk7"]
-script = ExtResource("17_2af2s")
-amount = 1
-min_amount = -1
-local_offset = true
-offset = Vector3(0.39, 0, 0)
-local_rotation = false
-rotation = Vector3(0, 0, 0)
-individual_rotation_pivots = true
-rotation_pivot = Vector3(0, 0, 0)
-local_scale = true
-scale = Vector3(1, 1, 1)
-randomize_indices = false
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_gtgt8"]
-script = ExtResource("17_2af2s")
-amount = 1
-min_amount = -1
-local_offset = true
-offset = Vector3(0.195, 0, 0.39)
-local_rotation = false
-rotation = Vector3(0, 0, 0)
-individual_rotation_pivots = true
-rotation_pivot = Vector3(0, 0, 0)
-local_scale = true
-scale = Vector3(1, 1, 1)
-randomize_indices = true
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 0
-
-[sub_resource type="Resource" id="Resource_jlbar"]
-script = ExtResource("5_h0430")
-position = Vector3(0, 0.02, 0)
-rotation = Vector3(0, 0, 0)
-scale = Vector3(0, 0, 0)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-reference_frame = 2
-
-[sub_resource type="Resource" id="Resource_nle4h"]
-script = ExtResource("2_wdwa6")
-stack = Array[Resource]([SubResource("Resource_q8na6"), SubResource("Resource_2nnk7"), SubResource("Resource_gtgt8"), SubResource("Resource_jlbar")])
-
-[sub_resource type="Resource" id="Resource_3agf6"]
-script = ExtResource("11_lv5tc")
-size = Vector3(3.74466, 1, 3.51889)
-
-[node name="ProtonScatterShowcase" type="Node3D"]
-
-[node name="Lighting" type="Node3D" parent="."]
-
-[node name="DirectionalLight3D" type="DirectionalLight3D" parent="Lighting"]
-transform = Transform3D(0.422618, -0.383022, 0.821394, 0, 0.906308, 0.422618, -0.906308, -0.178606, 0.383022, 0, 2.29496, 0)
-light_color = Color(1, 0.941176, 0.921569, 1)
-light_energy = 2.0
-light_indirect_energy = 0.5
-shadow_enabled = true
-shadow_opacity = 0.85
-shadow_blur = 0.2
-directional_shadow_max_distance = 20.0
-
-[node name="WorldEnvironment" type="WorldEnvironment" parent="Lighting"]
-environment = SubResource("Environment_1kod5")
-
-[node name="Camera3D" type="Camera3D" parent="Lighting"]
-transform = Transform3D(1, 0, 0, 0, 0.98872, 0.149777, 0, -0.149777, 0.98872, -3.319, 2.435, 4.146)
-
-[node name="gobot" parent="." instance=ExtResource("15_mnk3f")]
-transform = Transform3D(0.269842, -0.00767393, 0.420864, -0.0348094, 0.497798, 0.0313953, -0.419492, -0.0462436, 0.26812, -5.95266, 0.505731, 0.83356)
-
-[node name="Water" type="MeshInstance3D" parent="."]
-material_override = ExtResource("3_yrj3o")
-cast_shadow = 0
-mesh = SubResource("PlaneMesh_84bvf")
-metadata/_edit_lock_ = true
-metadata/_edit_group_ = true
-
-[node name="MainGround" type="Node3D" parent="."]
-script = ExtResource("1_odnwj")
-use_instancing = false
-modifier_stack = SubResource("Resource_8xlqi")
-
-[node name="ScatterItem" type="Node3D" parent="MainGround"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.4
-path = "res://addons/proton_scatter/demos/assets/large_rock.tscn"
-
-[node name="PathShape" type="Node3D" parent="MainGround"]
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_ftiyl")
-
-[node name="BackgroundMountain" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 68.323, 0, -80.067)
-script = ExtResource("1_odnwj")
-modifier_stack = SubResource("Resource_33b0f")
-
-[node name="ScatterItem" type="Node3D" parent="BackgroundMountain"]
-script = ExtResource("3_tn31i")
-path = "res://addons/proton_scatter/demos/assets/large_rock.tscn"
-
-[node name="BoxShape" type="Node3D" parent="BackgroundMountain"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.41334, -0.086071, -0.341324)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_1jvqx")
-
-[node name="Trees" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.64893, 0, -0.774464)
-script = ExtResource("1_odnwj")
-use_instancing = false
-scatter_parent = NodePath("../MainGround")
-modifier_stack = SubResource("Resource_bwydp")
-
-[node name="ScatterItem" type="Node3D" parent="Trees"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.4
-path = "res://addons/proton_scatter/demos/assets/pine_tree.tscn"
-
-[node name="PathShape" type="Node3D" parent="Trees"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0376822, 0, -0.0782702)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_jto6x")
-
-[node name="SmallRocks" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.58903, 0.624608, -1.05701)
-script = ExtResource("1_odnwj")
-scatter_parent = NodePath("../MainGround")
-modifier_stack = SubResource("Resource_4tdre")
-
-[node name="ScatterItem" type="Node3D" parent="SmallRocks"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.2
-path = "res://addons/proton_scatter/demos/assets/small_rock.tscn"
-
-[node name="BoxShape" type="Node3D" parent="SmallRocks"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.279, -0.69077, 0.16165)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_0weea")
-
-[node name="Grass" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.53694, 0.590527, -0.866904)
-script = ExtResource("1_odnwj")
-scatter_parent = NodePath("../MainGround")
-modifier_stack = SubResource("Resource_v2tsa")
-
-[node name="ScatterItem" type="Node3D" parent="Grass"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.15
-path = "res://addons/proton_scatter/demos/assets/grass_2.tscn"
-
-[node name="SphereShape" type="Node3D" parent="Grass"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3.66162, 1.19209e-07, -0.977096)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_iou5c")
-
-[node name="SphereShape2" type="Node3D" parent="Grass"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.21329, 0.0320051, -0.71306)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_llvm5")
-
-[node name="Mushrooms" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.48012, 0.850601, -0.986201)
-script = ExtResource("1_odnwj")
-scatter_parent = NodePath("../Trees")
-dbg_disable_thread = true
-modifier_stack = SubResource("Resource_u4rnr")
-
-[node name="ScatterItem" type="Node3D" parent="Mushrooms"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.2
-path = "res://addons/proton_scatter/demos/assets/mushroom.tscn"
-
-[node name="ScatterShape" type="Node3D" parent="Mushrooms"]
-transform = Transform3D(0.992366, 0, 0.123324, -0.00806148, 0.997861, 0.0648693, -0.12306, -0.0653683, 0.990244, -0.545904, 0.200069, -0.0382232)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_272xj")
-
-[node name="ScatterShape2" type="Node3D" parent="Mushrooms"]
-transform = Transform3D(0.702501, 0, 0.711683, 0, 1, 0, -0.711683, 0, 0.702501, -2.18823, 0.0800232, 0.866356)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_p1ngd")
-
-[node name="ScatterShape3" type="Node3D" parent="Mushrooms"]
-transform = Transform3D(0.82657, 0, 0.562834, 0, 1, 0, -0.562834, 0, 0.82657, -2.31684, 0.0552569, -1.67132)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_is18q")
-
-[node name="DeadBranches" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.572101, 0)
-script = ExtResource("1_odnwj")
-scatter_parent = NodePath("../MainGround")
-modifier_stack = SubResource("Resource_3cg3d")
-
-[node name="ScatterItem" type="Node3D" parent="DeadBranches"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.6
-path = "res://addons/proton_scatter/demos/assets/dead_branch.tscn"
-
-[node name="ScatterShape" type="Node3D" parent="DeadBranches"]
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_ojpr0")
-
-[node name="Bushes" type="Node3D" parent="."]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.222831, 0.534039, -1.07373)
-script = ExtResource("1_odnwj")
-modifier_stack = SubResource("Resource_kuroc")
-
-[node name="ScatterItem" type="Node3D" parent="Bushes"]
-script = ExtResource("3_tn31i")
-source_scale_multiplier = 0.7
-path = "res://addons/proton_scatter/demos/assets/bush.tscn"
-
-[node name="ScatterShape" type="Node3D" parent="Bushes"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.300213, 4.76837e-07, -0.398241)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_bpfxh")
-
-[node name="ScatterShape2" type="Node3D" parent="Bushes"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.9127, 4.76837e-07, -1.13497)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_rspgw")
-
-[node name="Platform" type="Node3D" parent="."]
-transform = Transform3D(0.330514, 0, 0.943801, 0, 1, 0, -0.943801, 0, 0.330514, -12.248, 0, -5.402)
-script = ExtResource("1_odnwj")
-dbg_disable_thread = true
-modifier_stack = SubResource("Resource_nle4h")
-
-[node name="ScatterItem" type="Node3D" parent="Platform"]
-script = ExtResource("3_tn31i")
-path = "res://addons/proton_scatter/demos/assets/brick.tscn"
-
-[node name="ScatterShape" type="Node3D" parent="Platform"]
-transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.248102, 0, 0.914603)
-script = ExtResource("4_5klvy")
-shape = SubResource("Resource_3agf6")
diff --git a/addons/scatter-4/icons/add.svg b/addons/scatter-4/icons/add.svg
deleted file mode 100644
index a241829..0000000
--- a/addons/scatter-4/icons/add.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/arrow_down.svg b/addons/scatter-4/icons/arrow_down.svg
deleted file mode 100644
index fac4a77..0000000
--- a/addons/scatter-4/icons/arrow_down.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/arrow_exp.svg b/addons/scatter-4/icons/arrow_exp.svg
deleted file mode 100644
index 41a4cf0..0000000
--- a/addons/scatter-4/icons/arrow_exp.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/arrow_linear.svg b/addons/scatter-4/icons/arrow_linear.svg
deleted file mode 100644
index caacb92..0000000
--- a/addons/scatter-4/icons/arrow_linear.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/arrow_log.svg b/addons/scatter-4/icons/arrow_log.svg
deleted file mode 100644
index 4de912c..0000000
--- a/addons/scatter-4/icons/arrow_log.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/arrow_right.svg b/addons/scatter-4/icons/arrow_right.svg
deleted file mode 100644
index 3316c55..0000000
--- a/addons/scatter-4/icons/arrow_right.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/checker.png b/addons/scatter-4/icons/checker.png
deleted file mode 100644
index 9abdc75..0000000
Binary files a/addons/scatter-4/icons/checker.png and /dev/null differ
diff --git a/addons/scatter-4/icons/clear.svg b/addons/scatter-4/icons/clear.svg
deleted file mode 100644
index 43c0031..0000000
--- a/addons/scatter-4/icons/clear.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/close.svg b/addons/scatter-4/icons/close.svg
deleted file mode 100644
index 4147c7b..0000000
--- a/addons/scatter-4/icons/close.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/curve_close.svg b/addons/scatter-4/icons/curve_close.svg
deleted file mode 100644
index 032f1c6..0000000
--- a/addons/scatter-4/icons/curve_close.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/curve_create.svg b/addons/scatter-4/icons/curve_create.svg
deleted file mode 100644
index 1181111..0000000
--- a/addons/scatter-4/icons/curve_create.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/addons/scatter-4/icons/curve_delete.svg b/addons/scatter-4/icons/curve_delete.svg
deleted file mode 100644
index 901a08e..0000000
--- a/addons/scatter-4/icons/curve_delete.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/addons/scatter-4/icons/curve_select.svg b/addons/scatter-4/icons/curve_select.svg
deleted file mode 100644
index 8f09ca6..0000000
--- a/addons/scatter-4/icons/curve_select.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/addons/scatter-4/icons/dice.svg b/addons/scatter-4/icons/dice.svg
deleted file mode 100644
index 214a745..0000000
--- a/addons/scatter-4/icons/dice.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/doc.svg b/addons/scatter-4/icons/doc.svg
deleted file mode 100644
index 89c8735..0000000
--- a/addons/scatter-4/icons/doc.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/documentation.svg b/addons/scatter-4/icons/documentation.svg
deleted file mode 100644
index 113612b..0000000
--- a/addons/scatter-4/icons/documentation.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/drag_area.svg b/addons/scatter-4/icons/drag_area.svg
deleted file mode 100644
index 43efb22..0000000
--- a/addons/scatter-4/icons/drag_area.svg
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
-
diff --git a/addons/scatter-4/icons/duplicate.svg b/addons/scatter-4/icons/duplicate.svg
deleted file mode 100644
index d258f5a..0000000
--- a/addons/scatter-4/icons/duplicate.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/exclude_path.svg b/addons/scatter-4/icons/exclude_path.svg
deleted file mode 100644
index 05866e3..0000000
--- a/addons/scatter-4/icons/exclude_path.svg
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/global.svg b/addons/scatter-4/icons/global.svg
deleted file mode 100644
index d23299e..0000000
--- a/addons/scatter-4/icons/global.svg
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/group.svg b/addons/scatter-4/icons/group.svg
deleted file mode 100644
index 5ec0350..0000000
--- a/addons/scatter-4/icons/group.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/individual_instances.svg b/addons/scatter-4/icons/individual_instances.svg
deleted file mode 100644
index 2d9f63b..0000000
--- a/addons/scatter-4/icons/individual_instances.svg
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/item.svg b/addons/scatter-4/icons/item.svg
deleted file mode 100644
index 8bc141c..0000000
--- a/addons/scatter-4/icons/item.svg
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/link.svg b/addons/scatter-4/icons/link.svg
deleted file mode 100644
index 909c3af..0000000
--- a/addons/scatter-4/icons/link.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/load.svg b/addons/scatter-4/icons/load.svg
deleted file mode 100644
index 7ee6ae2..0000000
--- a/addons/scatter-4/icons/load.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/loading/m_loading.tres b/addons/scatter-4/icons/loading/m_loading.tres
deleted file mode 100644
index 357aac7..0000000
--- a/addons/scatter-4/icons/loading/m_loading.tres
+++ /dev/null
@@ -1,20 +0,0 @@
-[gd_resource type="StandardMaterial3D" load_steps=2 format=3]
-
-[ext_resource type="Texture2D" uid="uid://il8i8280eew6" path="res://addons/proton_scatter/icons/loading/t_loading.tres" id="1_7rsk1"]
-
-[resource]
-render_priority = 120
-transparency = 1
-blend_mode = 1
-depth_draw_mode = 2
-no_depth_test = true
-disable_ambient_light = true
-albedo_color = Color(1, 0.498039, 0, 1)
-albedo_texture = ExtResource("1_7rsk1")
-emission_enabled = true
-emission = Color(1, 0.411765, 0, 1)
-emission_energy_multiplier = 3.5
-disable_receive_shadows = true
-billboard_mode = 1
-fixed_size = true
-point_size = 24.8
diff --git a/addons/scatter-4/icons/loading/progress1.svg b/addons/scatter-4/icons/loading/progress1.svg
deleted file mode 100644
index 07505dd..0000000
--- a/addons/scatter-4/icons/loading/progress1.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress2.svg b/addons/scatter-4/icons/loading/progress2.svg
deleted file mode 100644
index 0a48f7d..0000000
--- a/addons/scatter-4/icons/loading/progress2.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress3.svg b/addons/scatter-4/icons/loading/progress3.svg
deleted file mode 100644
index a7f0f9c..0000000
--- a/addons/scatter-4/icons/loading/progress3.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress4.svg b/addons/scatter-4/icons/loading/progress4.svg
deleted file mode 100644
index 1719209..0000000
--- a/addons/scatter-4/icons/loading/progress4.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress5.svg b/addons/scatter-4/icons/loading/progress5.svg
deleted file mode 100644
index 7289b7b..0000000
--- a/addons/scatter-4/icons/loading/progress5.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress6.svg b/addons/scatter-4/icons/loading/progress6.svg
deleted file mode 100644
index 3deba6d..0000000
--- a/addons/scatter-4/icons/loading/progress6.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress7.svg b/addons/scatter-4/icons/loading/progress7.svg
deleted file mode 100644
index 546155d..0000000
--- a/addons/scatter-4/icons/loading/progress7.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/progress8.svg b/addons/scatter-4/icons/loading/progress8.svg
deleted file mode 100644
index b56ffcb..0000000
--- a/addons/scatter-4/icons/loading/progress8.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/loading/t_loading.tres b/addons/scatter-4/icons/loading/t_loading.tres
deleted file mode 100644
index 1907238..0000000
--- a/addons/scatter-4/icons/loading/t_loading.tres
+++ /dev/null
@@ -1,29 +0,0 @@
-[gd_resource type="AnimatedTexture" load_steps=9 format=3]
-
-[ext_resource type="Texture2D" uid="uid://bkdk5ka8f2eyn" path="res://addons/proton_scatter/icons/loading/progress1.svg" id="1_n051c"]
-[ext_resource type="Texture2D" uid="uid://yinrmiasphf0" path="res://addons/proton_scatter/icons/loading/progress2.svg" id="2_huiva"]
-[ext_resource type="Texture2D" uid="uid://cddfi3mmjlhfa" path="res://addons/proton_scatter/icons/loading/progress3.svg" id="3_5gmad"]
-[ext_resource type="Texture2D" uid="uid://bgrh6kl71mjxp" path="res://addons/proton_scatter/icons/loading/progress4.svg" id="4_ypilv"]
-[ext_resource type="Texture2D" uid="uid://cu5lshwm0g08o" path="res://addons/proton_scatter/icons/loading/progress5.svg" id="5_o3w57"]
-[ext_resource type="Texture2D" uid="uid://cwfbjg2q2j1b1" path="res://addons/proton_scatter/icons/loading/progress6.svg" id="6_475ws"]
-[ext_resource type="Texture2D" uid="uid://d0f2b7boq8n1j" path="res://addons/proton_scatter/icons/loading/progress7.svg" id="7_kuufk"]
-[ext_resource type="Texture2D" uid="uid://c0cn8qjmctb2n" path="res://addons/proton_scatter/icons/loading/progress8.svg" id="8_spub1"]
-
-[resource]
-frames = 8
-speed_scale = 8.0
-frame_0/texture = ExtResource("1_n051c")
-frame_1/texture = ExtResource("2_huiva")
-frame_1/duration = 1.0
-frame_2/texture = ExtResource("3_5gmad")
-frame_2/duration = 1.0
-frame_3/texture = ExtResource("4_ypilv")
-frame_3/duration = 1.0
-frame_4/texture = ExtResource("5_o3w57")
-frame_4/duration = 1.0
-frame_5/texture = ExtResource("6_475ws")
-frame_5/duration = 1.0
-frame_6/texture = ExtResource("7_kuufk")
-frame_6/duration = 1.0
-frame_7/texture = ExtResource("8_spub1")
-frame_7/duration = 1.0
diff --git a/addons/scatter-4/icons/local.svg b/addons/scatter-4/icons/local.svg
deleted file mode 100644
index 2d5e554..0000000
--- a/addons/scatter-4/icons/local.svg
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/menu.svg b/addons/scatter-4/icons/menu.svg
deleted file mode 100644
index 20c5300..0000000
--- a/addons/scatter-4/icons/menu.svg
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/point.svg b/addons/scatter-4/icons/point.svg
deleted file mode 100644
index 1447124..0000000
--- a/addons/scatter-4/icons/point.svg
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/rebuild.svg b/addons/scatter-4/icons/rebuild.svg
deleted file mode 100644
index d69e6a7..0000000
--- a/addons/scatter-4/icons/rebuild.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/remove.svg b/addons/scatter-4/icons/remove.svg
deleted file mode 100644
index eb8e244..0000000
--- a/addons/scatter-4/icons/remove.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/restrict_volume.svg b/addons/scatter-4/icons/restrict_volume.svg
deleted file mode 100644
index af4ffbf..0000000
--- a/addons/scatter-4/icons/restrict_volume.svg
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/restrict_volume_lock.svg b/addons/scatter-4/icons/restrict_volume_lock.svg
deleted file mode 100644
index 88bdfb7..0000000
--- a/addons/scatter-4/icons/restrict_volume_lock.svg
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/save.svg b/addons/scatter-4/icons/save.svg
deleted file mode 100644
index be5d3ef..0000000
--- a/addons/scatter-4/icons/save.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/scatter.svg b/addons/scatter-4/icons/scatter.svg
deleted file mode 100644
index 9092f4a..0000000
--- a/addons/scatter-4/icons/scatter.svg
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/select_all.svg b/addons/scatter-4/icons/select_all.svg
deleted file mode 100644
index 4440149..0000000
--- a/addons/scatter-4/icons/select_all.svg
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/shape.svg b/addons/scatter-4/icons/shape.svg
deleted file mode 100644
index 1335f5a..0000000
--- a/addons/scatter-4/icons/shape.svg
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
diff --git a/addons/scatter-4/icons/square_handle.svg b/addons/scatter-4/icons/square_handle.svg
deleted file mode 100644
index 4df41e2..0000000
--- a/addons/scatter-4/icons/square_handle.svg
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
diff --git a/addons/scatter-4/icons/types/bool.svg b/addons/scatter-4/icons/types/bool.svg
deleted file mode 100644
index 674cbc9..0000000
--- a/addons/scatter-4/icons/types/bool.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/curve.svg b/addons/scatter-4/icons/types/curve.svg
deleted file mode 100644
index 8b330b7..0000000
--- a/addons/scatter-4/icons/types/curve.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/types/float.svg b/addons/scatter-4/icons/types/float.svg
deleted file mode 100644
index b941332..0000000
--- a/addons/scatter-4/icons/types/float.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/int.svg b/addons/scatter-4/icons/types/int.svg
deleted file mode 100644
index b943822..0000000
--- a/addons/scatter-4/icons/types/int.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/string.svg b/addons/scatter-4/icons/types/string.svg
deleted file mode 100644
index abcb92d..0000000
--- a/addons/scatter-4/icons/types/string.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/texture.svg b/addons/scatter-4/icons/types/texture.svg
deleted file mode 100644
index bb7831e..0000000
--- a/addons/scatter-4/icons/types/texture.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/addons/scatter-4/icons/types/vector2.svg b/addons/scatter-4/icons/types/vector2.svg
deleted file mode 100644
index 2bab922..0000000
--- a/addons/scatter-4/icons/types/vector2.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/vector2i.svg b/addons/scatter-4/icons/types/vector2i.svg
deleted file mode 100644
index f292354..0000000
--- a/addons/scatter-4/icons/types/vector2i.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/vector3.svg b/addons/scatter-4/icons/types/vector3.svg
deleted file mode 100644
index 85cac57..0000000
--- a/addons/scatter-4/icons/types/vector3.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/types/vector3i.svg b/addons/scatter-4/icons/types/vector3i.svg
deleted file mode 100644
index 26e9c1b..0000000
--- a/addons/scatter-4/icons/types/vector3i.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/icons/warning.svg b/addons/scatter-4/icons/warning.svg
deleted file mode 100644
index f40d539..0000000
--- a/addons/scatter-4/icons/warning.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/masks/bars.png b/addons/scatter-4/masks/bars.png
deleted file mode 100644
index 99ce23b..0000000
Binary files a/addons/scatter-4/masks/bars.png and /dev/null differ
diff --git a/addons/scatter-4/masks/blinds.png b/addons/scatter-4/masks/blinds.png
deleted file mode 100644
index a15a038..0000000
Binary files a/addons/scatter-4/masks/blinds.png and /dev/null differ
diff --git a/addons/scatter-4/masks/checker.png b/addons/scatter-4/masks/checker.png
deleted file mode 100644
index ca58baf..0000000
Binary files a/addons/scatter-4/masks/checker.png and /dev/null differ
diff --git a/addons/scatter-4/masks/wave.png b/addons/scatter-4/masks/wave.png
deleted file mode 100644
index 3e5f218..0000000
Binary files a/addons/scatter-4/masks/wave.png and /dev/null differ
diff --git a/addons/scatter-4/plugin.cfg b/addons/scatter-4/plugin.cfg
deleted file mode 100644
index 29069d3..0000000
--- a/addons/scatter-4/plugin.cfg
+++ /dev/null
@@ -1,7 +0,0 @@
-[plugin]
-
-name="ProtonScatter"
-description="Scatter props or entire scenes across any space in a non destructive way"
-author="HungryProton"
-version="4.0"
-script="plugin.gd"
diff --git a/addons/scatter-4/plugin.gd b/addons/scatter-4/plugin.gd
deleted file mode 100644
index ea85271..0000000
--- a/addons/scatter-4/plugin.gd
+++ /dev/null
@@ -1,97 +0,0 @@
-@tool
-extends EditorPlugin
-
-
-const ProtonScatter := preload("./src/scatter.gd")
-const ProtonScatterShape := preload("./src/scatter_shape.gd")
-const ModifierStackPlugin := preload("./src/stack/inspector_plugin/modifier_stack_plugin.gd")
-const ScatterGizmoPlugin := preload("./src/scatter_gizmo_plugin.gd")
-const ShapeGizmoPlugin := preload("./src/shapes/gizmos_plugin/shape_gizmo_plugin.gd")
-const PathPanel := preload("./src/shapes/gizmos_plugin/components/path_panel.tscn")
-
-
-var _modifier_stack_plugin: EditorInspectorPlugin = ModifierStackPlugin.new()
-var _scatter_gizmo_plugin: ScatterGizmoPlugin = ScatterGizmoPlugin.new()
-var _shape_gizmo_plugin: EditorNode3DGizmoPlugin = ShapeGizmoPlugin.new()
-var _path_panel
-
-
-func get_name():
- return "ProtonScatter"
-
-
-func _enter_tree():
- add_inspector_plugin(_modifier_stack_plugin)
-
- _path_panel = PathPanel.instantiate()
- add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, _path_panel)
- _path_panel.visible = false
-
- add_node_3d_gizmo_plugin(_scatter_gizmo_plugin)
-
- add_node_3d_gizmo_plugin(_shape_gizmo_plugin)
- _shape_gizmo_plugin.set_undo_redo(get_undo_redo())
- _shape_gizmo_plugin.set_path_gizmo_panel(_path_panel)
- _shape_gizmo_plugin.set_editor_plugin(self)
-
- add_custom_type(
- "ProtonScatter",
- "Node3D",
- preload("./src/scatter.gd"),
- preload("./icons/scatter.svg")
- )
- add_custom_type(
- "ScatterItem",
- "Node3D",
- preload("./src/scatter_item.gd"),
- preload("./icons/item.svg")
- )
- add_custom_type(
- "ScatterShape",
- "Node3D",
- preload("./src/scatter_shape.gd"),
- preload("./icons/shape.svg")
- )
-
- var editor_selection = get_editor_interface().get_selection()
- editor_selection.selection_changed.connect(_on_selection_changed)
-
- scene_changed.connect(_on_scene_changed)
-
-
-func _exit_tree():
- remove_custom_type("ProtonScatter")
- remove_custom_type("ScatterItem")
- remove_custom_type("ScatterShape")
- remove_inspector_plugin(_modifier_stack_plugin)
- remove_node_3d_gizmo_plugin(_shape_gizmo_plugin)
- remove_node_3d_gizmo_plugin(_scatter_gizmo_plugin)
- if _path_panel:
- remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, _path_panel)
- _path_panel.queue_free()
- _path_panel = null
-
-
-func _handles(node) -> bool:
- return node is ProtonScatterShape
-
-
-func _forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> int:
- return _shape_gizmo_plugin.forward_3d_gui_input(viewport_camera, event)
-
-
-func _on_selection_changed() -> void:
- var selected = get_editor_interface().get_selection().get_selected_nodes()
- _path_panel.selection_changed(selected)
-
- if selected.is_empty():
- return
-
- var selected_node = selected[0]
- if selected_node is ProtonScatter:
- selected_node.undo_redo = get_undo_redo()
- selected_node.editor_plugin = self
-
-
-func _on_scene_changed(_scene_root) -> void:
- pass
diff --git a/addons/scatter-4/presets/default.tscn b/addons/scatter-4/presets/default.tscn
deleted file mode 100644
index 240adc5..0000000
--- a/addons/scatter-4/presets/default.tscn
+++ /dev/null
@@ -1,69 +0,0 @@
-[gd_scene load_steps=14 format=3]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/scatter.gd" id="1_e0kty"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/modifier_stack.gd" id="2_lt5xy"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/create_inside_grid.gd" id="3_nry4c"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/randomize_transforms.gd" id="4_5a045"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/modifiers/project_on_geometry.gd" id="5_gkw57"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/scatter_item.gd" id="6_3iwkw"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/scatter_shape.gd" id="7_jofmq"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/shapes/sphere_shape.gd" id="8_gnbkw"]
-
-[sub_resource type="Resource" id="Resource_741ly"]
-script = ExtResource("3_nry4c")
-spacing = Vector3(0.2, 1, 0.2)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = true
-use_local_space = false
-
-[sub_resource type="Resource" id="Resource_n0177"]
-script = ExtResource("4_5a045")
-position = Vector3(0.15, 0.15, 0.15)
-rotation = Vector3(20, 360, 20)
-scale = Vector3(2, 2, 2)
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-use_local_space = true
-
-[sub_resource type="Resource" id="Resource_hqvoo"]
-script = ExtResource("5_gkw57")
-ray_direction = Vector3(0, -1, 0)
-ray_length = 5.0
-ray_offset = 5.0
-remove_points_on_miss = true
-align_with_collision_normal = false
-max_slope = 90.0
-collision_mask = 1
-enabled = true
-override_global_seed = false
-custom_seed = 0
-restrict_height = false
-use_local_space = false
-
-[sub_resource type="Resource" id="Resource_asuhn"]
-script = ExtResource("2_lt5xy")
-stack = [SubResource("Resource_741ly"), SubResource("Resource_n0177"), SubResource("Resource_hqvoo")]
-
-[sub_resource type="Resource" id="Resource_g8bsm"]
-script = ExtResource("8_gnbkw")
-radius = 2.0
-
-[node name="ProtonScatter3" type="Node3D"]
-script = ExtResource("1_e0kty")
-scatter_parent = NodePath("")
-modifier_stack = SubResource("Resource_asuhn")
-
-[node name="Grass" type="Node3D" parent="."]
-script = ExtResource("6_3iwkw")
-path = "res://addons/proton_scatter/demos/assets/grass_2.tscn"
-
-[node name="ScatterShape2" type="Node3D" parent="."]
-transform = Transform3D(1, 0, -2.98023e-08, 0, 1, 0, 2.98023e-08, 0, 1, 0, 0, 0)
-script = ExtResource("7_jofmq")
-shape = SubResource("Resource_g8bsm")
-
-[node name="ScatterOutput" type="Marker3D" parent="."]
diff --git a/addons/scatter-4/src/common/bounds.gd b/addons/scatter-4/src/common/bounds.gd
deleted file mode 100644
index 52d0ed4..0000000
--- a/addons/scatter-4/src/common/bounds.gd
+++ /dev/null
@@ -1,51 +0,0 @@
-@tool
-extends Resource
-
-# Essentially a Rect3
-# Used by the Domain class
-
-
-var size: Vector3
-var center: Vector3
-var min: Vector3
-var max: Vector3
-
-var _points := 0
-
-
-func clear() -> void:
- size = Vector3.ZERO
- center = Vector3.ZERO
- min = Vector3.ZERO
- max = Vector3.ZERO
- _points = 0
-
-
-func feed(point: Vector3) -> void:
- if _points == 0:
- min = point
- max = point
-
- min = _minv(min, point)
- max = _maxv(max, point)
- _points += 1
-
-
-# Call this after you've called feed() with all the points in your data set
-func compute_bounds() -> void:
- if min == null or max == null:
- return
-
- size = max - min
- center = min + (size / 2.0)
-
-
-# Returns a vector with the smallest values in each of the 2 input vectors
-func _minv(v1: Vector3, v2: Vector3) -> Vector3:
- return Vector3(min(v1.x, v2.x), min(v1.y, v2.y), min(v1.z, v2.z))
-
-
-# Returns a vector with the highest values in each of the 2 input vectors
-func _maxv(v1: Vector3, v2: Vector3) -> Vector3:
- return Vector3(max(v1.x, v2.x), max(v1.y, v2.y), max(v1.z, v2.z))
-
diff --git a/addons/scatter-4/src/common/domain.gd b/addons/scatter-4/src/common/domain.gd
deleted file mode 100644
index dcdd45d..0000000
--- a/addons/scatter-4/src/common/domain.gd
+++ /dev/null
@@ -1,295 +0,0 @@
-@tool
-extends RefCounted
-
-# A domain is the complete area where transforms can (and can't) be placed.
-# A Scatter node has one single domain, a domain has one or more shape nodes.
-#
-# It's the combination of every shape defined under a Scatter node, grouped in
-# a single class that exposes utility functions (check if a point is inside, or
-# along the surface etc).
-#
-# An instance of this class is passed to the modifiers during a rebuild.
-
-
-const ProtonScatter := preload("../scatter.gd")
-const ProtonScatterShape := preload("../scatter_shape.gd")
-const BaseShape := preload("../shapes/base_shape.gd")
-const Bounds := preload("../common/bounds.gd")
-
-
-class DomainShapeInfo:
- var transform: Transform3D
- var shape: BaseShape
-
- func is_point_inside(point: Vector3) -> bool:
- return shape.is_point_inside(point, transform)
-
- func get_corners_global() -> Array:
- return shape.get_corners_global(transform)
-
-# A polygon made of one outer boundary and one or multiple holes (inner polygons)
-class ComplexPolygon:
- var inner: Array[PackedVector2Array] = []
- var outer: PackedVector2Array
-
- func add(polygon: PackedVector2Array) -> void:
- if polygon.is_empty(): return
- if Geometry2D.is_polygon_clockwise(polygon):
- inner.push_back(polygon)
- else:
- if not outer.is_empty():
- print_debug("ProtonScatter error: Replacing polygon's existing outer boundary. This should not happen, please report.")
- outer = polygon
-
- func add_array(array: Array, reverse := false) -> void:
- for p in array:
- if reverse:
- p.reverse()
- add(p)
-
- func get_all() -> Array[PackedVector2Array]:
- var res = inner.duplicate()
- res.push_back(outer)
- return res
-
- func _to_string() -> String:
- var res = "o: " + var_to_str(outer.size()) + ", i: ["
- for i in inner:
- res += var_to_str(i.size()) + ", "
- res += "]"
- return res
-
-
-var root: ProtonScatter
-var positive_shapes: Array[DomainShapeInfo]
-var negative_shapes: Array[DomainShapeInfo]
-var bounds: Bounds = Bounds.new()
-var bounds_local: Bounds = Bounds.new()
-var edges: Array[Curve3D] = []
-
-
-func is_empty() -> bool:
- return positive_shapes.is_empty()
-
-
-# If a point is in an exclusion shape, returns false
-# If a point is in an inclusion shape (but not in an exclusion one), returns true
-# If a point is in neither, returns false
-func is_point_inside(point: Vector3) -> bool:
- for s in negative_shapes:
- if s.is_point_inside(point):
- return false
-
- for s in positive_shapes:
- if s.is_point_inside(point):
- return true
-
- return false
-
-
-# If a point is inside an exclusion shape, returns true
-# Returns false in every other case
-func is_point_excluded(point: Vector3) -> bool:
- for s in negative_shapes:
- if s.is_point_inside(point):
- return true
-
- return false
-
-
-# Recursively find all ScatterShape nodes under the provided root. In case of
-# nested Scatter nodes, shapes under these other Scatter nodes will be ignored
-func discover_shapes(root_node: Node3D) -> void:
- root = root_node
- positive_shapes.clear()
- negative_shapes.clear()
- for c in root.get_children():
- _discover_shapes_recursive(c)
- compute_bounds()
- compute_edges()
-
-
-func compute_bounds() -> void:
- bounds.clear()
- bounds_local.clear()
-
- var gt: Transform3D = root.get_global_transform().affine_inverse()
-
- for info in positive_shapes:
- for point in info.get_corners_global():
- bounds.feed(point)
- bounds_local.feed(gt * point)
-
- bounds.compute_bounds()
- bounds_local.compute_bounds()
-
-
-func compute_edges() -> void:
- edges.clear()
- var source_polygons: Array[ComplexPolygon] = []
- var root_gt: Transform3D = root.get_global_transform()
-
- ## Retrieve all polygons
- for info in positive_shapes:
- # Store all closed polygons in a specific array
- var polygon := ComplexPolygon.new()
- polygon.add_array(info.shape.get_closed_edges(root_gt, info.transform))
-
- # Polygons with holes must be merged together first
- if not polygon.inner.is_empty():
- source_polygons.push_back(polygon)
- else:
- source_polygons.push_front(polygon)
-
- # Store open edges directly since they are already Curve3D and we
- # don't apply boolean operations to them.
- var open_edges = info.shape.get_open_edges(root_gt, info.transform)
- edges.append_array(open_edges)
-
- if source_polygons.is_empty():
- return
-
- ## Merge all closed polygons together
- var merged_polygons: Array[ComplexPolygon] = []
-
- while not source_polygons.is_empty():
- var merged := false
- var p1: ComplexPolygon = source_polygons.pop_back()
- var max_steps: int = source_polygons.size()
- var i = 0
-
- # Test p1 against every other polygon from source_polygon until a
- # successful merge. If no merge happened, put it in the final array.
- while i < max_steps and not merged:
- i += 1
-
- # Get the next polygon in the list
- var p2: ComplexPolygon = source_polygons.pop_back()
-
- # If the outer boundary of any of the two polygons is completely
- # enclosed in one of the other polygon's hole, we don't try to
- # merge them and go the next iteration.
- var full_overlap = false
- for ip1 in p1.inner:
- var res = Geometry2D.clip_polygons(p2.outer, ip1)
- if res.is_empty():
- full_overlap = true
- break
-
- for ip2 in p2.inner:
- var res = Geometry2D.clip_polygons(p1.outer, ip2)
- if res.is_empty():
- full_overlap = true
- break
-
- if full_overlap:
- source_polygons.push_front(p2)
- continue
-
- # Try to merge the two polygons p1 and p2
- var res = Geometry2D.merge_polygons(p1.outer, p2.outer)
- var outer_polygons := 0
- for p in res:
- if not Geometry2D.is_polygon_clockwise(p):
- outer_polygons += 1
-
- # If the merge generated a new polygon, process the holes data from
- # the two original polygons and store in the new_polygon
- # P1 and P2 are then discarded and replaced by the new polygon.
- if outer_polygons == 1:
- var new_polygon = ComplexPolygon.new()
- new_polygon.add_array(res)
-
- # Process the holes data from p1 and p2
- for ip1 in p1.inner:
- for ip2 in p2.inner:
- new_polygon.add_array(Geometry2D.intersect_polygons(ip1, ip2), true)
- new_polygon.add_array(Geometry2D.clip_polygons(ip2, p1.outer), true)
-
- new_polygon.add_array(Geometry2D.clip_polygons(ip1, p2.outer), true)
-
- source_polygons.push_back(new_polygon)
- merged = true
-
- # If the polygons don't overlap, return it to the pool to be tested
- # against other polygons
- else:
- source_polygons.push_front(p2)
-
- # If p1 is not overlapping any other polygon, add it to the final list
- if not merged:
- merged_polygons.push_back(p1)
-
- var gt_inverse := root_gt.affine_inverse()
-
- ## For each polygons from the previous step, create a corresponding Curve3D
- for cp in merged_polygons:
- for polygon in cp.get_all():
- if polygon.size() < 2: # Ignore polygons too small to form a loop
- continue
-
- var curve := Curve3D.new()
- for point in polygon:
- var p = Vector3(point.x, 0.0, point.y)
- curve.add_point(root_gt * p)
-
- curve.add_point(curve.get_point_position(0)) # Close the loop
- edges.push_back(curve)
-
-
-func get_root() -> ProtonScatter:
- return root
-
-
-func get_global_transform() -> Transform3D:
- return root.get_global_transform()
-
-
-func get_local_transform() -> Transform3D:
- return root.get_transform()
-
-
-func get_edges() -> Array[Curve3D]:
- if edges.is_empty():
- compute_edges()
- return edges
-
-
-func get_copy():
- var copy = get_script().new()
-
- copy.root = root
- copy.bounds = bounds
- copy.bounds_local = bounds_local
-
- for s in positive_shapes:
- var s_copy = DomainShapeInfo.new()
- s_copy.transform = s.transform
- s_copy.shape = s.shape.get_copy()
- copy.positive_shapes.push_back(s_copy)
-
- for s in negative_shapes:
- var s_copy = DomainShapeInfo.new()
- s_copy.transform = s.transform
- s_copy.shape = s.shape.get_copy()
- copy.negative_shapes.push_back(s_copy)
-
- return copy
-
-
-func _discover_shapes_recursive(node: Node) -> void:
- if node is ProtonScatter: # Ignore shapes under nested Scatter nodes
- return
-
- if node is ProtonScatterShape and node.shape != null:
- var info := DomainShapeInfo.new()
- info.transform = node.get_global_transform()
- info.shape = node.shape
-
- if node.negative:
- negative_shapes.push_back(info)
- else:
- positive_shapes.push_back(info)
-
- for c in node.get_children():
- _discover_shapes_recursive(c)
diff --git a/addons/scatter-4/src/common/event_helper.gd b/addons/scatter-4/src/common/event_helper.gd
deleted file mode 100644
index 912d18c..0000000
--- a/addons/scatter-4/src/common/event_helper.gd
+++ /dev/null
@@ -1,72 +0,0 @@
-extends RefCounted
-
-# Utility class that mimics the Input class behavior
-#
-# This only useful when using actions from the Input class isn't possible,
-# like in _unhandled_input or forward_3d_gui_input for example, where you don't
-# have a native way to detect if a key was just pressed or released.
-#
-# How to use:
-# Call the feed() method first with the latest event you received, then call
-# either of the is_key_* function
-#
-# If you don't call feed() on the same frame before calling any of these two,
-# the behavior is undefined.
-
-
-var _actions := {}
-
-
-func feed(event: InputEvent) -> void:
- var key
- if event is InputEventMouseButton:
- key = event.button_index
- elif event is InputEventKey:
- key = event.keycode
- else:
- _cleanup_states()
- return
-
- if not key in _actions:
- _actions[key] = {
- pressed = event.pressed,
- just_released = not event.pressed,
- just_pressed = event.pressed,
- }
- return
-
- var pressed = _actions[key].pressed
-
- if pressed and not event.pressed:
- _actions[key].just_released = true
- _actions[key].just_pressed = false
-
- if not pressed and event.pressed:
- _actions[key].just_pressed = true
- _actions[key].just_released = false
-
- if pressed and event.pressed:
- _actions[key].just_pressed = false
- _actions[key].just_released = false
-
- _actions[key].pressed = event.pressed
-
-
-func _cleanup_states() -> void:
- for key in _actions:
- _actions[key].just_released = false
- _actions[key].just_pressed = false
-
-
-func is_key_just_pressed(key) -> bool:
- if key in _actions:
- return _actions[key].just_pressed
-
- return false
-
-
-func is_key_just_released(key) -> bool:
- if key in _actions:
- return _actions[key].just_released
-
- return false
diff --git a/addons/scatter-4/src/common/physics_helper.gd b/addons/scatter-4/src/common/physics_helper.gd
deleted file mode 100644
index 8ec4a0c..0000000
--- a/addons/scatter-4/src/common/physics_helper.gd
+++ /dev/null
@@ -1,58 +0,0 @@
-@tool
-extends Node
-
-# Runs jobs during the physics step.
-# Only supports raycast for now, but can easilly be adapted to handle
-# the other types of queries.
-
-
-signal job_canceled
-signal job_completed
-
-const MAX_QUERIES_PER_FRAME = 400 # TODO: Expose in user settings
-
-
-var _queries: Array
-var _results: Array[Dictionary]
-var _job_in_progress := false
-var _cancel_current := false
-
-
-func execute(queries: Array) -> Array[Dictionary]:
- if _job_in_progress:
- _cancel_current = true
- await job_canceled
-
- _results.clear()
- _queries = queries
- _queries.reverse()
- _job_in_progress = true
-
- await job_completed
-
- _job_in_progress = false
- return _results.duplicate()
-
-
-func _physics_process(_delta: float) -> void:
- if _cancel_current:
- _cancel_current = false
- _job_in_progress = false
- _results.clear()
- job_canceled.emit()
- return
-
- if not _job_in_progress:
- return
-
- var space_state: PhysicsDirectSpaceState3D = get_tree().get_root().get_world_3d().get_direct_space_state()
- var steps = min(MAX_QUERIES_PER_FRAME, _queries.size())
-
- for i in steps:
- var q = _queries.pop_back()
- var hit := space_state.intersect_ray(q) # TODO: Support other operations
- _results.push_back(hit)
-
- if _queries.is_empty():
- _job_in_progress = false
- job_completed.emit()
diff --git a/addons/scatter-4/src/common/scatter_util.gd b/addons/scatter-4/src/common/scatter_util.gd
deleted file mode 100644
index 22d897c..0000000
--- a/addons/scatter-4/src/common/scatter_util.gd
+++ /dev/null
@@ -1,209 +0,0 @@
-extends Node
-
-# To prevent the other core scripts from becoming too large, some of their
-# utility functions are written here (only the functions that don't disturb
-# reading the core code, mostly data validation and other verbose checks).
-
-
-const ProtonScatter := preload("../scatter.gd")
-const ProtonScatterItem := preload("../scatter_item.gd")
-const ModifierStack := preload("../stack/modifier_stack.gd")
-
-### SCATTER UTILITY FUNCTIONS ###
-
-
-# Make sure the output node exists. This is the parent node to
-# everything generated by the scatter mesh
-static func ensure_output_root_exists(s: ProtonScatter) -> void:
- # Check if the node exists in the tree
- if not s.output_root:
- s.output_root = s.get_node_or_null("ScatterOutput")
-
- # If the node is valid, end here
- if is_instance_valid(s.output_root) and s.has_node(NodePath(s.output_root.name)):
- enforce_output_root_owner(s)
- return
-
- # Some conditions are not met, cleanup and recreate the root
- if s.output_root:
- if s.has_node(NodePath(s.output_root.name)):
- s.remove_node(s.output_root.name)
- s.output_root.queue_free()
- s.output_root = null
-
- s.output_root = Marker3D.new()
- s.output_root.name = "ScatterOutput"
- s.add_child(s.output_root, true)
- enforce_output_root_owner(s)
-
-
-static func enforce_output_root_owner(s: ProtonScatter) -> void:
- if is_instance_valid(s.output_root) and s.is_inside_tree():
- if s.show_output_in_tree:
- set_owner_recursive(s.output_root, s.get_tree().get_edited_scene_root())
- else:
- set_owner_recursive(s.output_root, null)
-
- # TMP: Workaround to force the scene tree to update and take in account
- # the owner changes. Otherwise it doesn't show until much later.
- s.output_root.update_configuration_warnings()
-
-
-# Item root is a Node3D placed as a child of the ScatterOutput node.
-# Each ScatterItem has a corresponding output node, serving as a parent for
-# the Multimeshes or duplicates generated by the Scatter node.
-static func get_or_create_item_root(item: ProtonScatterItem) -> Node3D:
- var s: ProtonScatter = item.get_parent()
- ensure_output_root_exists(s)
- var item_root: Node3D = s.output_root.get_node_or_null(NodePath(item.name))
-
- if not item_root:
- item_root = Node3D.new()
- s.output_root.add_child(item_root)
- item_root.name = item.name
- if Engine.is_editor_hint():
- item_root.owner = item_root.get_tree().get_edited_scene_root()
-
- return item_root
-
-
-static func get_or_create_multimesh(item: ProtonScatterItem, count: int) -> MultiMeshInstance3D:
- var item_root := get_or_create_item_root(item)
- var mmi: MultiMeshInstance3D = item_root.get_node_or_null("MultiMeshInstance3D")
-
- if not mmi:
- mmi = MultiMeshInstance3D.new()
- item_root.add_child(mmi)
- mmi.set_owner(item_root.owner)
- mmi.set_name("MultiMeshInstance3D")
-
- if not mmi.multimesh:
- mmi.multimesh = MultiMesh.new()
-
- mmi.position = Vector3.ZERO
- # item.update_shadows()
-
- var node = item.get_item()
- var mesh_instance: MeshInstance3D = get_merged_meshes_from(node)
- if not mesh_instance:
- return
-
- mmi.multimesh.instance_count = 0 # Set this to zero or you can't change the other values
- mmi.multimesh.mesh = mesh_instance.mesh
- mmi.multimesh.transform_format = 1
- mmi.multimesh.instance_count = count
-
- mesh_instance.queue_free()
-
- return mmi
-
-
-# Called from child nodes who affect the rebuild process (like ScatterShape)
-# Usually, it would be the Scatter node responsibility to listen to changes from
-# the children nodes, but keeping track of the children is annoying (they can
-# be moved around from a Scatter node to another, or put under a wrong node, or
-# other edge cases).
-# So instead, when a child changed, it notifies the parent Scatter node through
-# this method.
-static func request_parent_to_rebuild(node: Node, deferred := false) -> void:
- var parent = node.get_parent()
- if not parent or not parent.is_inside_tree():
- return
-
- if parent and parent is ProtonScatter:
- if deferred:
- parent.rebuild.call_deferred(true)
- else:
- parent.rebuild(true)
-
-
-### MESH UTILITY ###
-
-# Recursively search for all MeshInstances3D in the node's children and
-# returns them all in an array. If node is a MeshInstance, it will also be
-# added to the array
-static func get_all_mesh_instances_from(node: Node3D) -> Array[MeshInstance3D]:
- var res: Array[MeshInstance3D] = []
-
- if node is MeshInstance3D:
- res.push_back(node)
-
- for c in node.get_children():
- res.append_array(get_all_mesh_instances_from(c))
-
- return res
-
-
-# Find all the meshes below node and create a new single mesh with multiple
-# surfaces from all of them.
-static func get_merged_meshes_from(node: Node) -> MeshInstance3D:
- if not node:
- return null
-
- # Reset node transform for this step, overwise they'll stack
- var transform_backup: Transform3D
- if node.is_inside_tree():
- transform_backup = node.get_global_transform()
- node.global_transform = Transform3D()
- else:
- transform_backup = node.transform
- node.transform = Transform3D()
-
- var instances: Array[MeshInstance3D] = get_all_mesh_instances_from(node)
- if instances.is_empty():
- return null
-
- var total_surfaces = 0
- var array_mesh = ArrayMesh.new()
-
- for i in instances.size():
- var mi: MeshInstance3D = instances[i]
- var mesh: Mesh = mi.mesh
- var surface_count = mesh.get_surface_count()
- var material_override = mi.get_material_override()
- var inverse_transform: Transform3D
- if mi.is_inside_tree():
- inverse_transform = mi.global_transform.affine_inverse()
- else:
- inverse_transform = mi.transform.affine_inverse()
-
- for j in surface_count:
- var arrays = mesh.surface_get_arrays(j)
- var length = arrays[ArrayMesh.ARRAY_VERTEX].size()
-
- for k in length:
- var pos: Vector3 = arrays[ArrayMesh.ARRAY_VERTEX][k]
- pos = pos * inverse_transform
- arrays[ArrayMesh.ARRAY_VERTEX][k] = pos
-
- array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
-
- # Retrieve the material on the MeshInstance first, if none is defined,
- # use the one from the mesh resource.
- var material: Material
- if material_override:
- material = material_override
- else:
- material = mi.get_surface_override_material(j)
- if not material:
- material = mesh.surface_get_material(j)
- array_mesh.surface_set_material(total_surfaces, material)
-
- total_surfaces += 1
-
- # Restore node initial transform
- if node.is_inside_tree():
- node.global_transform = transform_backup
- else:
- node.transform = transform_backup
-
- # Return merged mesh
- var res := MeshInstance3D.new()
- res.mesh = array_mesh
- return res
-
-
-static func set_owner_recursive(node: Node, new_owner) -> void:
- node.set_owner(new_owner)
- for c in node.get_children():
- set_owner_recursive(c, new_owner)
diff --git a/addons/scatter-4/src/common/transform_list.gd b/addons/scatter-4/src/common/transform_list.gd
deleted file mode 100644
index 1d4b23a..0000000
--- a/addons/scatter-4/src/common/transform_list.gd
+++ /dev/null
@@ -1,66 +0,0 @@
-@tool
-extends RefCounted
-
-
-var list: Array[Transform3D] = []
-var max_count := -1
-
-
-func add(count: int) -> void:
- for i in count:
- var t := Transform3D()
- list.push_back(t)
-
-
-func append(array: Array[Transform3D]) -> void:
- list.append_array(array)
-
-
-func remove(count: int) -> void:
- count = int(max(count, 0)) # Prevent using a negative number
- var new_size = max(list.size() - count, 0)
- list.resize(new_size)
-
-
-func resize(count: int) -> void:
- if max_count >= 0:
- count = int(min(count, max_count))
-
- var size = list.size()
- if count > size:
- add(count - size)
- else:
- remove(size - count)
-
-
-# TODO: Faster algorithm probably exists for this, research an alternatives
-# if this ever becomes a performance bottleneck.
-func shuffle(random_seed := 0) -> void:
- var n = list.size()
- if n < 2:
- return
-
- var rng = RandomNumberGenerator.new()
- rng.set_seed(random_seed)
-
- var i = n - 1
- var j
- var tmp
- while i >= 1:
- j = rng.randi() % (i + 1)
- tmp = list[j]
- list[j] = list[i]
- list[i] = tmp
- i -= 1
-
-
-func clear() -> void:
- list = []
-
-
-func is_empty() -> bool:
- return list.is_empty()
-
-
-func size() -> int:
- return list.size()
diff --git a/addons/scatter-4/src/common/util.gd b/addons/scatter-4/src/common/util.gd
deleted file mode 100644
index 50242b0..0000000
--- a/addons/scatter-4/src/common/util.gd
+++ /dev/null
@@ -1,29 +0,0 @@
-@tool
-extends RefCounted
-
-
-static func get_position_and_normal_at(curve: Curve3D, offset: float) -> Array:
- if not curve:
- return []
-
- var pos: Vector3 = curve.sample_baked(offset)
- var normal := Vector3.ZERO
-
- var pos1
- if offset + curve.get_bake_interval() < curve.get_baked_length():
- pos1 = curve.sample_baked(offset + curve.get_bake_interval())
- normal = (pos1 - pos)
- else:
- pos1 = curve.sample_baked(offset - curve.get_bake_interval())
- normal = (pos - pos1)
-
- return [pos, normal]
-
-
-static func remove_line_breaks(text: String) -> String:
- # Remove tabs
- text = text.replace("\t", "")
- # Remove line breaks
- text = text.replace("\n", " ")
- # Remove occasional double space caused by the line above
- return text.replace(" ", " ")
diff --git a/addons/scatter-4/src/documentation/documentation.gd b/addons/scatter-4/src/documentation/documentation.gd
deleted file mode 100644
index b6091f8..0000000
--- a/addons/scatter-4/src/documentation/documentation.gd
+++ /dev/null
@@ -1,277 +0,0 @@
-@tool
-extends PopupPanel
-
-
-# Formats and displays the DocumentationData provided by other parts of the addon
-# TODO: Adjust title font size based on the editor font size / scaling
-
-
-const DocumentationInfo = preload("./documentation_info.gd")
-const SpecialPages = preload("./pages/special_pages.gd")
-
-var _pages := {}
-var _items := {}
-var _categories_roots := {}
-
-var _modifiers_root: TreeItem
-
-var _edited_text: String
-var _accent_color := Color.CORNFLOWER_BLUE
-var _editor_scale := 1.0
-var _header_size := 20
-var _sub_header_size := 16
-
-var _populated := false
-
-
-@onready var tree: Tree = $HSplitContainer/Tree
-@onready var label: RichTextLabel = $HSplitContainer/RichTextLabel
-
-
-func _ready() -> void:
- tree.create_item() # Create tree root
- tree.hide_root = true
- tree.item_selected.connect(_on_item_selected)
-
- add_page(SpecialPages.get_scatter_documentation(), tree.create_item())
- add_page(SpecialPages.get_item_documentation(), tree.create_item())
- add_page(SpecialPages.get_shape_documentation(), tree.create_item())
-
- _modifiers_root = tree.create_item()
- add_page(SpecialPages.get_modifiers_documentation(), _modifiers_root)
-
- _populate()
-
-
-# Fed from the StackPanel scene, before the ready function
-func set_editor_plugin(editor_plugin: EditorPlugin) -> void:
- if not editor_plugin:
- return
-
- var editor_interface := editor_plugin.get_editor_interface()
- var editor_settings := editor_interface.get_editor_settings()
-
- _accent_color = editor_settings.get("interface/theme/accent_color")
- _editor_scale = editor_interface.get_editor_scale()
-
-
-func show_page(page_name: String) -> void:
- if not page_name in _items:
- return
-
- var item: TreeItem = _items[page_name]
- item.select(0)
- popup_centered(Vector2i(900, 600))
-
-
-# Generate a formatted string from the DocumentationInfo input.
-# This string will be stored and later displayed in the RichTextLabel so we
-# we don't have to regenerate it everytime we look at another page.
-func add_page(info: DocumentationInfo, item: TreeItem = null) -> void:
- if not item:
- var root: TreeItem = _get_or_create_tree_root(info.get_category())
- item = tree.create_item(root)
-
- item.set_text(0, info.get_title())
-
- _begin_formatting()
-
- # Page title
- _format_title(info.get_title())
-
- # Paragraphs
- for p in info.get_paragraphs():
- _format_paragraph(p)
-
- # Parameters
- if not info.get_parameters().is_empty():
- _format_subtitle("Parameters")
-
- for p in info.get_parameters():
- _format_parameter(p)
-
- # Warnings
- if not info.get_warnings().is_empty():
- _format_subtitle("Warnings")
-
- for w in info.get_warnings():
- _format_warning(w)
-
- _pages[item] = _get_formatted_text()
- _items[info.get_title()] = item
-
-
-func _populate():
- if _populated: # Already generated the documentation pages
- return
-
- var path = _get_root_folder() + "/src/modifiers/"
- var result := {}
- _discover_modifiers_recursive(path, result)
-
- var names := result.keys()
- names.sort()
-
- for n in names:
- var info = result[n]
- add_page(info)
-
- _populated = true
-
-
-func _discover_modifiers_recursive(path, result) -> void:
- var dir = DirAccess.open(path)
- dir.list_dir_begin()
- var path_root = dir.get_current_dir() + "/"
-
- while true:
- var file = dir.get_next()
- if file == "":
- break
- if file == "base_modifier.gd":
- continue
- if dir.current_is_dir():
- _discover_modifiers_recursive(path_root + file, result)
- continue
- if not file.ends_with(".gd") and not file.ends_with(".gdc"):
- continue
-
- var full_path = path_root + file
- var script = load(full_path)
- if not script or not script.can_instantiate():
- print("Error: Failed to load script ", file)
- continue
-
- var modifier = script.new()
-
- var info: DocumentationInfo = modifier.documentation
- info.set_title(modifier.display_name)
- info.set_category(modifier.category)
- if modifier.use_edge_data:
- info.add_warning(
- "This modifier uses edge data (represented by the blue lines
- on the Scatter node). These edges are usually locked to the
- local XZ plane, (except for the Path shape when they are
- NOT closed). If you can't see these lines, make sure to have at
- least one Shape crossing the ProtonScatter local XZ plane.",
- 1)
-
- result[modifier.display_name] = info
-
- dir.list_dir_end()
-
-
-func _get_root_folder() -> String:
- var script: Script = get_script()
- var path: String = script.get_path().get_base_dir()
- var folders = path.right(-6) # Remove the res://
- var tokens = folders.split('/')
- return "res://" + tokens[0] + "/" + tokens[1]
-
-
-func _get_or_create_tree_root(root_name: String) -> TreeItem:
- if root_name in _categories_roots:
- return _categories_roots[root_name]
-
- var root = tree.create_item(_modifiers_root)
- root.set_text(0, root_name)
- root.set_selectable(0, false)
- _categories_roots[root_name] = root
- return root
-
-
-func _begin_formatting() -> void:
- _edited_text = ""
-
-
-func _get_formatted_text() -> String:
- return _edited_text
-
-
-func _format_title(text: String) -> void:
- _edited_text += "[font_size=" + var_to_str(_header_size * _editor_scale) + "]"
- _edited_text += "[color=" + _accent_color.to_html() + "]"
- _edited_text += "[center][b]"
- _edited_text += text
- _edited_text += "[/b][/center]"
- _edited_text += "[/color]"
- _edited_text += "[/font_size]"
- _format_line_break(2)
-
-
-func _format_subtitle(text: String) -> void:
- _edited_text += "[font_size=" + var_to_str(_header_size * _editor_scale) + "]"
- _edited_text += "[color=" + _accent_color.to_html() + "]"
- _edited_text += "[b]" + text + "[/b]"
- _edited_text += "[/color]"
- _edited_text += "[/font_size]"
- _format_line_break(2)
-
-
-func _format_line_break(count := 1) -> void:
- for i in count:
- _edited_text += "\n"
-
-
-func _format_paragraph(text: String) -> void:
- _edited_text += "[p]" + text + "[/p]"
- _format_line_break(2)
-
-
-func _format_parameter(p) -> void:
- var root_folder = _get_root_folder()
-
- _edited_text += "[indent]"
-
- if not p.type.is_empty():
- var file_name = p.type.to_lower() + ".svg"
- _edited_text += "[img]" + root_folder + "/icons/types/" + file_name + "[/img] "
-
- _edited_text += "[b]" + p.name + "[/b] "
-
- match p.cost:
- 1:
- _edited_text += "[img]" + root_folder + "/icons/arrow_log.svg[/img]"
- 2:
- _edited_text += "[img]" + root_folder + "/icons/arrow_linear.svg[/img]"
- 3:
- _edited_text += "[img]" + root_folder + "/icons/arrow_exp.svg[/img]"
-
- _format_line_break(2)
- _edited_text += "[indent]" + p.description + "[/indent]"
- _format_line_break(2)
-
- for warning in p.warnings:
- if not warning.text.is_empty():
- _format_warning(warning)
-
- _edited_text += "[/indent]"
-
-
-func _format_warning(w, indent := true) -> void:
- if indent:
- _edited_text += "[indent]"
-
- var color := "Darkgray"
- match w.importance:
- 1:
- color = "yellow"
- 2:
- color = "red"
-
- _edited_text += "[color=" + color + "][i]" + w.text + "[/i][/color]\n"
-
- if indent:
- _edited_text += "[/indent]"
-
- _format_line_break(1)
-
-
-func _on_item_selected() -> void:
- var selected: TreeItem = tree.get_selected()
-
- if _pages.has(selected):
- var text: String = _pages[selected]
- label.set_text(text)
- else:
- label.set_text("[center] Under construction [/center]")
diff --git a/addons/scatter-4/src/documentation/documentation.tscn b/addons/scatter-4/src/documentation/documentation.tscn
deleted file mode 100644
index 0bcaa8f..0000000
--- a/addons/scatter-4/src/documentation/documentation.tscn
+++ /dev/null
@@ -1,29 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://cfg8iqtuion8b"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/documentation/documentation.gd" id="1_5c4lw"]
-[ext_resource type="PackedScene" uid="uid://cojoo2c73fpsb" path="res://addons/proton_scatter/src/documentation/panel.tscn" id="2_vpfxu"]
-
-[node name="Documentation" type="PopupPanel"]
-title = "ProtonScatter documentation"
-exclusive = true
-unresizable = false
-borderless = false
-script = ExtResource("1_5c4lw")
-
-[node name="HSplitContainer" parent="." instance=ExtResource("2_vpfxu")]
-offset_left = 4.0
-offset_top = 4.0
-offset_right = -1824.0
-offset_bottom = -984.0
-
-[node name="Tree" parent="HSplitContainer" index="0"]
-offset_right = 80.0
-offset_bottom = 92.0
-hide_root = true
-
-[node name="RichTextLabel" parent="HSplitContainer" index="1"]
-offset_left = 92.0
-offset_right = 92.0
-offset_bottom = 92.0
-
-[editable path="HSplitContainer"]
diff --git a/addons/scatter-4/src/documentation/documentation_info.gd b/addons/scatter-4/src/documentation/documentation_info.gd
deleted file mode 100644
index b6eac91..0000000
--- a/addons/scatter-4/src/documentation/documentation_info.gd
+++ /dev/null
@@ -1,113 +0,0 @@
-@tool
-extends RefCounted
-
-
-# Stores raw documentation data.
-
-# The data is provided by any class that needs an entry in the documentation
-# panel. This was initially designed for all the modifiers, but might be expanded
-# to other parts of the addon as well.
-
-# Formatting is handled by the main Documentation class.
-
-const Util := preload("../common/util.gd")
-
-
-class Warning:
- var text: String
- var importance: int
-
-class Parameter:
- var name: String
- var cost: int
- var type: String
- var description: String
- var warnings: Array[Warning] = []
-
- func set_name(text: String) -> Parameter:
- name = Util.remove_line_breaks(text)
- return self
-
- func set_description(text: String) -> Parameter:
- description = Util.remove_line_breaks(text)
- return self
-
- func set_cost(val: int) -> Parameter:
- cost = val
- return self
-
- func set_type(val: String) -> Parameter:
- type = Util.remove_line_breaks(val)
- return self
-
- func add_warning(warning: String, warning_importance := -1) -> Parameter:
- var w = Warning.new()
- w.text = Util.remove_line_breaks(warning)
- w.importance = warning_importance
- warnings.push_back(w)
- return self
-
-
-var _category: String
-var _page_title: String
-var _paragraphs: Array[String] = []
-var _warnings: Array[Warning] = []
-var _parameters: Array[Parameter] = []
-
-
-func set_category(text: String) -> void:
- _category = text
-
-
-func set_title(text: String) -> void:
- _page_title = text
-
-
-func add_paragraph(text: String) -> void:
- _paragraphs.push_back(Util.remove_line_breaks(text))
-
-
-# Warning importance:
-# 0: Default (Grey)
-# 1: Mid (Yellow)
-# 2: Critical (Red)
-func add_warning(text: String, importance: int = 0) -> void:
- var w = Warning.new()
- w.text = Util.remove_line_breaks(text)
- w.importance = importance
-
- _warnings.push_back(w)
-
-
-# Add documentation for a user exposed parameter.
-# Cost:
-# 0: None
-# 1: Log
-# 2: Linear
-# 3: Exponential
-func add_parameter(name := "") -> Parameter:
- var p = Parameter.new()
- p.name = name
- p.cost = 0
- _parameters.push_back(p)
- return p
-
-
-func get_title() -> String:
- return _page_title
-
-
-func get_category() -> String:
- return _category
-
-
-func get_paragraphs() -> Array[String]:
- return _paragraphs
-
-
-func get_warnings() -> Array[Warning]:
- return _warnings
-
-
-func get_parameters() -> Array[Parameter]:
- return _parameters
diff --git a/addons/scatter-4/src/documentation/pages/special_pages.gd b/addons/scatter-4/src/documentation/pages/special_pages.gd
deleted file mode 100644
index 72f6bd9..0000000
--- a/addons/scatter-4/src/documentation/pages/special_pages.gd
+++ /dev/null
@@ -1,113 +0,0 @@
-@tool
-extends RefCounted
-
-const DocumentationInfo = preload("../documentation_info.gd")
-
-
-static func get_scatter_documentation() -> DocumentationInfo:
- var info := DocumentationInfo.new()
-
- info.set_title("ProtonScatter")
- info.add_paragraph(
- "ProtonScatter is a content positioning add-on. It is suited to place
- a large amount of objects in a procedural way.")
- info.add_paragraph(
- "This add-on is [color=red][b]IN BETA[/b][/color] which means breaking
- changes may happen. It is not recommended to use in production yet."
- )
- info.add_paragraph(
- "First, define [i]what[/i] you want to place using [b]ScatterItems[/b]
- nodes.")
- info.add_paragraph(
- "Then, define [i]where[/i] to place them using [b]ScatterShapes[/b]
- nodes.")
- info.add_paragraph(
- "Finaly, define [i]how[/i] the content should be placed using the
- [b]Modifier stack[/b] that's on the [b]ProtonScatter[/b] node.")
- info.add_paragraph(
- "Each of these components have their dedicated documenation page, but
- first, you should check out the example scenes in the demo folder.")
-
- var p := info.add_parameter("General / Global seed")
- p.set_type("int")
- p.set_description(
- "The random seed to use on this node. Modifiers using random components
- can access this value and use it accordingly. You can also specify
- a custom seed for specific modifiers as well.")
-
- p = info.add_parameter("General / Show output in tree")
- p.set_type("bool")
- p.set_description(
- "Show the generated items in the editor scene tree. By default this
- option is disabled as it creates quite a bit of clutter when instancing
- is disabled. It also increases the scene file size significantly.")
-
- p = info.add_parameter("Performance / Use instancing")
- p.set_type("bool")
- p.set_description(
- "When enabled, ProtonScatter will use MultiMeshInstance3D nodes
- instead of duplicating the source nodes. This allows the GPU to render
- thousands of meshes in a single draw call.")
- p.add_warning("Collisions and attached scripts are ignored when this
- option is enabled.", 1)
-
- return info
-
-
-static func get_item_documentation() -> DocumentationInfo:
- var info := DocumentationInfo.new()
-
- info.set_title("ScatterItems")
-
- info.add_paragraph("TODO: Write this page")
-
- return info
-
-
-static func get_shape_documentation() -> DocumentationInfo:
- var info := DocumentationInfo.new()
-
- info.set_title("ScatterShapes")
- info.add_paragraph("TODO: Write this page")
-
- return info
-
-
-static func get_modifiers_documentation() -> DocumentationInfo:
- var info := DocumentationInfo.new()
-
- info.set_title("Modifiers")
- info.add_paragraph(
- "A modifier takes in a Transform3D list, create, modify or delete
- transforms, then pass it down to the next modifier. Remember that
- [b] modifiers are processed from top to bottom [/b]. A modifier
- down the stack will recieve a list processed by the modifiers above.")
- info.add_paragraph(
- "The initial transform list is empty, so it's necessary to start the
- stack with a [b] Create [/b] modifier.")
- info.add_paragraph(
- "When clicking the [b] Expand button [/b] (the little arrow on the left)
- you get access to this modifier's parameters. This is where you can
- adjust its behavior according to your needs.")
- info.add_paragraph(
- "Three common options might be found on these modifiers. (They can
- be disabled if they are irrelevant). They are defined as follow:")
-
- var p := info.add_parameter("Use local seed")
- p.set_description(
- "The dice icon on the left allows you to force a specific seed for the
- modifier. If this option is not used then the Global seed from the
- ProtonScatter node will be used instead.")
-
- p = info.add_parameter("Restrict height")
- p.set_description(
- "When applicable, the modifier will remain within the local XZ plane
- instead of using the full volume described by the ScatterShape nodes.")
-
- p = info.add_parameter("Use local space")
- p.set_description(
- "When enabled, the modifier will operate in local space relative to
- the ProtonScatter node. If disabled, the modifier operates in
- global space.")
-
- return info
diff --git a/addons/scatter-4/src/documentation/panel.tscn b/addons/scatter-4/src/documentation/panel.tscn
deleted file mode 100644
index f5687e3..0000000
--- a/addons/scatter-4/src/documentation/panel.tscn
+++ /dev/null
@@ -1,22 +0,0 @@
-[gd_scene format=3 uid="uid://cojoo2c73fpsb"]
-
-[node name="HSplitContainer" type="HSplitContainer"]
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-split_offset = 250
-
-[node name="Tree" type="Tree" parent="."]
-layout_mode = 2
-offset_right = 250.0
-offset_bottom = 648.0
-
-[node name="RichTextLabel" type="RichTextLabel" parent="."]
-layout_mode = 2
-offset_left = 262.0
-offset_right = 1152.0
-offset_bottom = 648.0
-bbcode_enabled = true
-text = "[center] [b] [i] Documentation page [/i] [/b] [/center]"
diff --git a/addons/scatter-4/src/modifiers/array.gd b/addons/scatter-4/src/modifiers/array.gd
deleted file mode 100644
index 3d97271..0000000
--- a/addons/scatter-4/src/modifiers/array.gd
+++ /dev/null
@@ -1,167 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-# Takes existing objects and duplicates them recursively with given transforms
-
-
-@export var amount := 1
-@export var min_amount := -1
-@export var local_offset := false
-@export var offset := Vector3.ZERO
-@export var local_rotation := false
-@export var rotation := Vector3.ZERO
-@export var individual_rotation_pivots := true
-@export var rotation_pivot := Vector3.ZERO
-@export var local_scale := true
-@export var scale := Vector3.ONE
-@export var randomize_indices := true
-
-var _rng: RandomNumberGenerator
-
-
-func _init() -> void:
- display_name = "Array"
- category = "Create"
- can_override_seed = true
- can_restrict_height = false
- global_reference_frame_available = false
- local_reference_frame_available = false
- individual_instances_reference_frame_available = false
-
- documentation.add_paragraph(
- "Recursively creates copies of the existing transforms, with each copy
- being offset from the previous one in any of a number of possible ways.")
-
- var p := documentation.add_parameter("Amount")
- p.set_type("int")
- p.set_cost(2)
- p.set_description(
- "The iteration count. If set to 1, each existing transforms are copied
- once.")
- p.add_warning("If set to 0, no copies are created.")
-
- p = documentation.add_parameter("Minimum amount")
- p.set_type("int")
- p.set_description(
- "Creates a random amount of copies for each transforms, between this
- value and the amount value.")
- p.add_warning("Ignored if set to a negative value.")
-
- p = documentation.add_parameter("Offset")
- p.set_type("Vector3")
- p.set_description(
- "Adds a constant offset between each copies and the previous one.")
-
- p = documentation.add_parameter("Local offset")
- p.set_type("bool")
- p.set_description(
- "If enabled, offset is relative to the previous copy orientation.
- Otherwise, the offset is in global space.")
-
- p = documentation.add_parameter("Rotation")
- p.set_type("Vector3")
- p.set_description(
- "The rotation offset (on each axes) to add on each copy.")
-
- p = documentation.add_parameter("Local rotation")
- p.set_type("bool")
- p.set_description(
- "If enabled, the rotation is applied in local space relative to each
- individual transforms. Otherwise, the rotation is applied in global
- space.")
-
- p = documentation.add_parameter("Rotation Pivot")
- p.set_type("Vector3")
- p.set_description(
- "The point around which each copies are rotated. By default, each
- transforms are rotated around their individual centers.")
-
- p = documentation.add_parameter("Individual Rotation Pivots")
- p.set_type("bool")
- p.set_description(
- "If enabled, each copies will use their own pivot relative to the
- previous copy. Otherwise, a single pivot point (defined in global space)
- will be used for the rotation of [b]all[/b] the copies.")
-
- p = documentation.add_parameter("Scale")
- p.set_type("Vector3")
- p.set_description(
- "Scales the copies relative to the transforms they are from.")
-
- p = documentation.add_parameter("Local Scale")
- p.set_type("bool")
- p.set_description(
- "If enabled, scaling is applied in local space relative to each
- individual transforms. Otherwise, global axes are used, resulting
- in skewed transforms in most cases.")
-
- p = documentation.add_parameter("Randomize Indices")
- p.set_type("bool")
- p.set_description(
- "Randomize the transform list order. This is only useful to break up the
- repetitive patterns if you're using multiple ScatterItem nodes.")
-
-
-func _process_transforms(transforms, domain, seed: int) -> void:
- _rng = RandomNumberGenerator.new()
- _rng.set_seed(seed)
-
- var new_transforms: Array[Transform3D] = []
- var rotation_rad := Vector3.ZERO
-
- rotation_rad.x = deg_to_rad(rotation.x)
- rotation_rad.y = deg_to_rad(rotation.y)
- rotation_rad.z = deg_to_rad(rotation.z)
-
- for t in transforms.size():
- new_transforms.push_back(transforms.list[t])
-
- var steps = amount
- if min_amount >= 0:
- steps = _rng.randi_range(min_amount, amount)
-
- for a in steps:
- a += 1
-
- # use original object's transform as base transform
- var transform : Transform3D = transforms.list[t]
- var basis := transform.basis
-
- # first move to rotation point defined in rotation offset
- var rotation_pivot_offset = (float(individual_rotation_pivots) * (transform * rotation_pivot) + float(!individual_rotation_pivots) * (rotation_pivot))
- transform.origin -= rotation_pivot_offset
-
- # then rotate
- transform = transform.rotated(float(local_rotation) * basis.x + float(!local_rotation) * Vector3(1, 0, 0), rotation_rad.x * a)
- transform = transform.rotated(float(local_rotation) * basis.y + float(!local_rotation) * Vector3(0, 1, 0), rotation_rad.y * a)
- transform = transform.rotated(float(local_rotation) * basis.z + float(!local_rotation) * Vector3(0, 0, 1), rotation_rad.z * a)
-
- # scale
- # If the scale is different than 1, each transform gets bigger or
- # smaller for each iteration.
- var s = scale
- s.x = pow(s.x, a)
- s.y = pow(s.y, a)
- s.z = pow(s.z, a)
-
- if local_scale:
- transform.basis.x *= s.x
- transform.basis.y *= s.y
- transform.basis.z *= s.z
- else:
- transform.basis = transform.basis.scaled(s)
-
- # apply changes back to the transform and undo the rotation pivot offset
- transform.origin += rotation_pivot_offset
-
- # offset
- transform.origin += (float(!local_offset) * offset * a) + (float(local_offset) * (basis * offset) * a)
-
- # store the final result if the position is valid
- if not domain.is_point_excluded(transform.origin):
- new_transforms.push_back(transform)
-
- transforms.list = new_transforms
-
- if randomize_indices:
- transforms.shuffle(seed)
diff --git a/addons/scatter-4/src/modifiers/base_modifier.gd b/addons/scatter-4/src/modifiers/base_modifier.gd
deleted file mode 100644
index 9dd008b..0000000
--- a/addons/scatter-4/src/modifiers/base_modifier.gd
+++ /dev/null
@@ -1,106 +0,0 @@
-@tool
-extends Resource
-
-# Modifiers place transforms. They create, edit or remove transforms in a list,
-# before the next Modifier in the stack does the same.
-# All Modifiers must inherit from this class.
-# Transforms in the provided transforms list must be in global space.
-
-
-signal warning_changed
-
-const TransformList = preload("../common/transform_list.gd")
-const Domain = preload("../common/domain.gd")
-const DocumentationInfo = preload("../documentation/documentation_info.gd")
-
-@export var enabled := true
-@export var override_global_seed := false
-@export var custom_seed := 0
-@export var restrict_height := false # Tells the modifier whether to constrain transforms to the local XY plane or not
-@export var reference_frame := 0
-
-var display_name: String = "Base Modifier Name"
-var category: String = "None"
-var documentation := DocumentationInfo.new()
-var warning: String = ""
-var warning_ignore_no_transforms := false
-var warning_ignore_no_shape := false
-var expanded := false
-var can_override_seed := false
-var can_restrict_height := true
-var global_reference_frame_available := true
-var local_reference_frame_available := false
-var individual_instances_reference_frame_available := false
-var use_edge_data := false
-
-
-func get_warning() -> String:
- return warning
-
-
-func process_transforms(transforms: TransformList, domain: Domain, global_seed: int) -> void:
- _clear_warning()
-
- if not enabled:
- return
-
- if domain.is_empty() and not warning_ignore_no_shape:
- warning += """The Scatter node does not have a shape.
- Add at least one ScatterShape node as a child.\n"""
-
- if transforms.is_empty() and not warning_ignore_no_transforms:
- warning += """There's no transforms to act on.
- Make sure you have a Create modifier before this one.\n
- """
-
- var seed = global_seed
- if can_override_seed and override_global_seed:
- seed = custom_seed
-
- await _process_transforms(transforms, domain, seed)
-
- warning_changed.emit()
-
-
-func get_copy():
- var script = get_script()
- var copy = script.new()
- for p in get_property_list():
- var value = get(p.name)
- copy.set(p.name, value)
-
- return copy
-
-
-func is_using_global_space() -> bool:
- return reference_frame == 0
-
-
-func is_using_local_space() -> bool:
- return reference_frame == 1
-
-
-func is_using_individual_instances_space() -> bool:
- return reference_frame == 2
-
-
-func use_global_space_by_default() -> void:
- reference_frame = 0
-
-
-func use_local_space_by_default() -> void:
- reference_frame = 1
-
-
-func use_individual_instances_space_by_default() -> void:
- reference_frame = 2
-
-
-func _clear_warning() -> void:
- warning = ""
- warning_changed.emit()
-
-
-# Override in inherited class
-func _process_transforms(_transforms: TransformList, _domain: Domain, _seed: int) -> void:
- pass
diff --git a/addons/scatter-4/src/modifiers/clusterize.gd b/addons/scatter-4/src/modifiers/clusterize.gd
deleted file mode 100644
index 897b46d..0000000
--- a/addons/scatter-4/src/modifiers/clusterize.gd
+++ /dev/null
@@ -1,114 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export_file("Texture") var mask
-@export var mask_scale := Vector2.ONE
-@export var mask_offset := Vector2.ZERO
-@export var mask_rotation := 0.0
-@export_range(0.0, 1.0) var remove_below = 0.1
-
-
-func _init() -> void:
- display_name = "Clusterize"
- category = "Edit"
- global_reference_frame_available = true
- local_reference_frame_available = false # TODO, enable this and handle this case
- individual_instances_reference_frame_available = false
-
- documentation.add_paragraph(
- "Clump transforms together based on a mask.
- Sampling the mask returns values between 0 and 1. The transforms are
- scaled against these values which means, bright areas don't affect their
- scale while dark area scales them down. Transforms are then removed
- below a threshold, leaving clumps behind.")
-
- var p := documentation.add_parameter("Mask")
- p.set_type("Texture")
- p.set_description("The texture used as a mask.")
- p.add_warning(
- "The amount of texture fetch depends on the amount of transforms
- generated in the previous modifiers (4 reads for each transform).
- In theory, the texture size shouldn't affect performances in a
- noticeable way.")
-
- p = documentation.add_parameter("Mask scale")
- p.set_type("Vector2")
- p.set_description(
- "Depending on the mask resolution, the perceived scale will change.
- Use this parameter to increase or decrease the area covered by the mask.")
-
- p = documentation.add_parameter("Mask offset")
- p.set_type("Vector2")
- p.set_description("Moves the mask XZ position in 3D space")
-
- p = documentation.add_parameter("Mask rotation")
- p.set_type("Float")
- p.set_description("Rotates the mask around the Y axis. (Angle in degrees)")
-
- p = documentation.add_parameter("Remove below")
- p.set_type("Float")
- p.set_description("Threshold below which the transforms are removed.")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- if not ResourceLoader.exists(mask):
- warning += "The specified file " + mask + " could not be loaded."
- return
-
- var texture = load(mask)
-
- if not texture is Texture:
- warning += "The specified file is not a valid texture."
- return
-
- var image: Image = texture.get_data()
- var _err = image.decompress()
- image.lock()
-
- var width = image.get_width()
- var height = image.get_height()
- var i = 0
- var count = transforms.list.size()
- var angle = deg_to_rad(mask_rotation)
-
- while i < count:
- var t = transforms.list[i]
- var origin = t.origin.rotated(Vector3.UP, angle)
-
- var x = origin.x * mask_scale.x + mask_offset.x
- x = fposmod(x, width - 1)
- var y = origin.z * mask_scale.y + mask_offset.y
- y = fposmod(y, height - 1)
-
- var level = _get_pixel(image, x, y)
- if level < remove_below:
- transforms.list.remove(i)
- count -= 1
- continue
-
- origin = t.origin
- t.origin = Vector3.ZERO
- t = t.scaled(Vector3(level, level, level))
- t.origin = origin
-
- transforms.list[i] = t
- i += 1
-
- image.unlock()
-
-
-# x and y don't always match an exact pixel, so we sample the neighboring
-# pixels as well and return a weighted value based on the input coords.
-func _get_pixel(image: Image, x: float, y: float) -> float:
- var ix = int(x)
- var iy = int(y)
- x -= ix
- y -= iy
-
- var nw = image.get_pixel(ix, iy).v
- var ne = image.get_pixel(ix + 1, iy).v
- var sw = image.get_pixel(ix, iy + 1).v
- var se = image.get_pixel(ix + 1, iy + 1).v
-
- return nw * (1 - x) * (1 - y) + ne * x * (1 - y) + sw * (1 - x) * y + se * x * y
diff --git a/addons/scatter-4/src/modifiers/create_along_edge_continuous.gd b/addons/scatter-4/src/modifiers/create_along_edge_continuous.gd
deleted file mode 100644
index 66d6202..0000000
--- a/addons/scatter-4/src/modifiers/create_along_edge_continuous.gd
+++ /dev/null
@@ -1,97 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var item_length := 2.0
-@export var ignore_slopes := false
-
-var _current_offset = 0.0
-
-
-func _init() -> void:
- display_name = "Create Along Edge (Continuous)"
- category = "Create"
- warning_ignore_no_transforms = true
- warning_ignore_no_shape = false
- use_edge_data = true
- global_reference_frame_available = false
- local_reference_frame_available = false
- individual_instances_reference_frame_available = false
-
- var p
-
- documentation.add_paragraph(
- "Create new transforms along the edges of the Scatter shapes. These
- transforms are placed so they touch each other but don't overlap, even
- if the curve has sharp turns.")
-
- documentation.add_paragraph(
- "This is useful to place props suchs as fences, walls or anything that
- needs to look organized without leaving gaps.")
-
- documentation.add_warning(
- "The transforms are placed starting from the begining of each curves.
- If the curve is closed, there will be a gap at the end if the total
- curve length isn't a multiple of the item length.")
-
- p = documentation.add_parameter("Item length")
- p.set_type("float")
- p.set_description("How long is the item being placed")
- p.set_cost(2)
- p.add_warning(
- "The smaller this value, the more transforms will be created.
- Setting a slightly different length than the actual model length
- allow for gaps between each transforms.")
-
- p = documentation.add_parameter("Ignore slopes")
- p.set_type("bool")
- p.set_description(
- "If enabled, all the curves will be projected to the local XZ plane
- before creating the new transforms.")
-
-
-# TODO: Use dichotomic search instead of fixed step length?
-func _process_transforms(transforms, domain, _seed) -> void:
- var new_transforms: Array[Transform3D] = []
- var curves: Array[Curve3D] = domain.get_edges()
-
- for curve in curves:
- if not ignore_slopes:
- curve = curve.duplicate()
- else:
- curve = get_projected_curve(curve, domain.get_global_transform())
-
- var length_squared = pow(item_length, 2)
- var offset_max = curve.get_baked_length()
- var offset = 0.0
- var step = item_length / 20.0
-
- while offset < offset_max:
- var start := curve.sample_baked(offset)
- var end: Vector3
- var dist: float
- offset += item_length * 0.9 # Saves a few iterations, the target
- # point will never be closer than the item length, only further
-
- while offset < offset_max:
- offset += step
- end = curve.sample_baked(offset)
- dist = start.distance_squared_to(end)
-
- if dist >= length_squared:
- var t = Transform3D()
- t.origin = start + ((end - start) / 2.0)
- new_transforms.push_back(t.looking_at(end, Vector3.UP))
- break
-
- transforms.append(new_transforms)
-
-
-func get_projected_curve(curve: Curve3D, t: Transform3D) -> Curve3D:
- var points = curve.tessellate()
- var new_curve = Curve3D.new()
- for p in points:
- p.y = t.origin.y
- new_curve.add_point(p)
-
- return new_curve
diff --git a/addons/scatter-4/src/modifiers/create_along_edge_even.gd b/addons/scatter-4/src/modifiers/create_along_edge_even.gd
deleted file mode 100644
index 78cd97c..0000000
--- a/addons/scatter-4/src/modifiers/create_along_edge_even.gd
+++ /dev/null
@@ -1,106 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-const Util := preload("../common/util.gd")
-
-
-# TODO :
-# + change alignement parameters to something more usable and intuitive
-# + Use the curve up vector, default to local Y+ when not available
-@export var spacing := 1.0
-@export var offset := 0.0
-@export_enum("X", "Y", "Z") var up_axis := 1
-@export var align_x := false
-@export var align_y := false
-@export var align_z := false
-
-var _min_spacing := 0.05
-
-
-func _init() -> void:
- display_name = "Create Along Edge (Even)"
- category = "Create"
- warning_ignore_no_transforms = true
- warning_ignore_no_shape = false
- can_restrict_height = false
- global_reference_frame_available = false
- local_reference_frame_available = false
- individual_instances_reference_frame_available = false
- use_edge_data = true
-
- var p
- documentation.add_paragraph(
- "Evenly create transforms along the edges of the ScatterShapes")
-
- p = documentation.add_parameter("Spacing")
- p.set_type("float")
- p.set_description("How much space between the transforms origin")
- p.set_cost(3)
- p.add_warning("The smaller the value, the denser the resulting transforms list.", 1)
- p.add_warning(
- "A value of 0 would result in infinite transforms, so it's capped
- to 0.05 at least.")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- spacing = max(_min_spacing, spacing)
-
- var up := get_align_up_vector(up_axis)
- var inverse_basis: Basis = domain.get_global_transform().affine_inverse().basis
- var up_local := up * inverse_basis
-
- var new_transforms: Array[Transform3D] = []
- var curves: Array[Curve3D] = domain.get_edges()
-
- for curve in curves:
- var length: float = curve.get_baked_length()
- var count := int(round(length / spacing))
- var stepped_length: float = count * spacing
-
- for i in count:
- var curve_offset = i * spacing + abs(offset)
-
- while curve_offset > stepped_length: # Loop back to the curve start if offset is too large
- curve_offset -= stepped_length
-
- var data : Array = Util.get_position_and_normal_at(curve, curve_offset)
- var pos: Vector3 = data[0]
- var normal: Vector3 = data[1]
-
- if domain.is_point_excluded(pos):
- continue
-
- var t := Transform3D()
- t.origin = pos
- t = t.looking_at(normal + pos, up)
-
- var angles := t.basis.get_euler()
- angles.x *= int(align_x)
- angles.y *= int(align_y)
- angles.z *= int(align_y)
-
- t.basis = t.basis.from_euler(angles)
-
- new_transforms.push_back(t)
-
- transforms.append(new_transforms)
-
-
-static func get_align_up_vector(align : int) -> Vector3:
- var axis : Vector3
- match align:
- #x
- 0:
- axis = Vector3.RIGHT
- #y
- 1:
- axis = Vector3.UP
- #z
- 2:
- axis = Vector3.BACK
- _:
- #default return y axis
- axis = Vector3.UP
-
- return axis
diff --git a/addons/scatter-4/src/modifiers/create_along_edge_random.gd b/addons/scatter-4/src/modifiers/create_along_edge_random.gd
deleted file mode 100644
index 5a6cc41..0000000
--- a/addons/scatter-4/src/modifiers/create_along_edge_random.gd
+++ /dev/null
@@ -1,69 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var instance_count := 10
-@export var align_to_path := false
-@export var align_up_axis := Vector3.UP
-
-var _rng: RandomNumberGenerator
-
-
-func _init() -> void:
- display_name = "Create Along Edge (Random)"
- category = "Create"
- warning_ignore_no_transforms = true
- warning_ignore_no_shape = false
- use_edge_data = true
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = false
-
-
-func _process_transforms(transforms, domain, seed) -> void:
- _rng = RandomNumberGenerator.new()
- _rng.set_seed(seed)
-
- var st: Transform3D = domain.get_global_transform()
- var new_transforms: Array[Transform3D] = []
- var curves: Array[Curve3D] = domain.get_edges()
- var total_curve_length := 0.0
-
- for curve in curves:
- var length: float = curve.get_baked_length()
- total_curve_length += length
-
- for curve in curves:
- var length: float = curve.get_baked_length()
- var local_instance_count: int = round((length / total_curve_length) * instance_count)
-
- for i in local_instance_count:
- var data = get_pos_and_normal(curve, _rng.randf() * length)
- var pos: Vector3 = data[0]
- var normal: Vector3 = data[1]
- var t := Transform3D()
-
- t.origin = pos
- if align_to_path:
- t = t.looking_at(normal + pos, align_up_axis)
- elif is_using_local_space():
- t.basis = st.basis
-
- new_transforms.push_back(t)
-
- transforms.append(new_transforms)
-
-
-func get_pos_and_normal(curve: Curve3D, offset : float) -> Array:
- var pos: Vector3 = curve.sample_baked(offset)
- var normal := Vector3.ZERO
-
- var pos1
- if offset + curve.get_bake_interval() < curve.get_baked_length():
- pos1 = curve.sample_baked(offset + curve.get_bake_interval())
- normal = (pos1 - pos)
- else:
- pos1 = curve.sample_baked(offset - curve.get_bake_interval())
- normal = (pos - pos1)
-
- return [pos, normal]
diff --git a/addons/scatter-4/src/modifiers/create_inside_grid.gd b/addons/scatter-4/src/modifiers/create_inside_grid.gd
deleted file mode 100644
index bdbeb0f..0000000
--- a/addons/scatter-4/src/modifiers/create_inside_grid.gd
+++ /dev/null
@@ -1,100 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var spacing := Vector3(2.0, 2.0, 2.0)
-
-var _min_spacing := 0.05
-
-
-func _init() -> void:
- display_name = "Create Inside (Grid)"
- category = "Create"
- warning_ignore_no_transforms = true
- warning_ignore_no_shape = false
- can_restrict_height = true
- restrict_height = true
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = false
-
- documentation.add_paragraph(
- "Place transforms along the edges of the ScatterShapes")
-
- documentation.add_paragraph(
- "When [b]Local Space[/b] is enabled, the grid is aligned with the
- Scatter root node. Otherwise, the grid is aligned with the global
- axes."
- )
-
- var p = documentation.add_parameter("Spacing")
- p.set_type("vector3")
- p.set_description(
- "Defines the grid size along the 3 axes. A spacing of 1 means 1 unit
- of space between each transform on this axis.")
- p.set_cost(3)
- p.add_warning(
- "The smaller the value, the denser the resulting transforms list.
- Use with care as the performance impact will go up quickly.", 1)
- p.add_warning(
- "A value of 0 would result in infinite transforms, so it's capped to 0.05
- at least.")
-
-
-func _process_transforms(transforms, domain, seed) -> void:
- spacing.x = max(_min_spacing, spacing.x)
- spacing.y = max(_min_spacing, spacing.y)
- spacing.z = max(_min_spacing, spacing.z)
-
- var gt: Transform3D = domain.get_global_transform()
- var center: Vector3 = domain.bounds.center
- var size: Vector3 = domain.bounds_local.size
-
- if is_using_local_space():
- center = domain.bounds_local.center
-
- var half_size := size * 0.5
- var start_corner := center - half_size
- var baseline: float = 0.0
-
- var width := int(ceil(size.x / spacing.x))
- var height := int(ceil(size.y / spacing.y))
- var length := int(ceil(size.z / spacing.z))
-
- if restrict_height:
- height = 1
- baseline = domain.bounds_local.max.y
- else:
- height = max(1, height) # Make sure height never gets below 1 or else nothing happens
-
- var max_count: int = width * length * height
- var new_transforms: Array[Transform3D] = []
- new_transforms.resize(max_count)
-
- var t: Transform3D
- var pos: Vector3
- var t_index := 0
-
- for i in width * length:
- for j in height:
- t = Transform3D()
- pos = Vector3.ZERO
- pos.x = (i % width) * spacing.x
- pos.y = (j * spacing.y) + baseline
- pos.z = (i / width) * spacing.z
- pos += start_corner
-
- if is_using_local_space():
- pos = gt * pos
- t.basis = gt.basis
-
- if domain.is_point_inside(pos):
- t.origin = pos
- new_transforms[t_index] = t
- t_index += 1
-
- if t_index != new_transforms.size():
- new_transforms.resize(t_index)
-
- transforms.append(new_transforms)
- transforms.shuffle(seed)
diff --git a/addons/scatter-4/src/modifiers/create_inside_poisson.gd b/addons/scatter-4/src/modifiers/create_inside_poisson.gd
deleted file mode 100644
index 8b5896c..0000000
--- a/addons/scatter-4/src/modifiers/create_inside_poisson.gd
+++ /dev/null
@@ -1,225 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-# Poisson disc sampling based on Sebastian Lague implementation, modified to
-# support both 2D and 3D space.
-# Reference: https://www.youtube.com/watch?v=7WcmyxyFO7o
-
-# TODO: This doesn't work if the valid space isn't one solid space
-# (fails to fill the full domain if it's made of discrete, separate shapes)
-
-
-const Bounds := preload("../common/bounds.gd")
-
-@export var radius := 1.0
-@export var samples_before_rejection := 15
-
-
-var _rng: RandomNumberGenerator
-var _squared_radius: float
-var _domain
-var _bounds: Bounds
-
-var _gt: Transform3D
-var _points: Array[Transform3D] # Stores the generated points
-var _grid: Array[int] = [] # Flattened array
-var _grid_size := Vector3i.ZERO
-var _cell_size: float
-var _cell_x: int
-var _cell_y: int
-var _cell_z: int
-
-
-func _init() -> void:
- display_name = "Create Inside (Poisson)"
- category = "Create"
- warning_ignore_no_transforms = true
- warning_ignore_no_shape = false
- can_restrict_height = true
- can_override_seed = true
- restrict_height = true
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = false
- use_local_space_by_default()
-
- documentation.add_paragraph(
- "Place transforms without overlaps. Transforms are assumed to have a
- spherical shape.")
-
- var p := documentation.add_parameter("Radius")
- p.set_type("float")
- p.set_description("Transform size.")
- p.add_warning(
- "The larger the radius, the harder it will be to place the transform,
- resulting in a faster early exit.
- On the other hand, smaller radius means more room for more points,
- meaning more transforms to generate so it will take longer to complete.")
-
- p = documentation.add_parameter("Samples before rejection")
- p.set_type("int")
- p.set_description(
- "The algorithm tries a point at random until it finds a valid one. This
- parameter controls how many attempts before moving to the next
- iteration. Lower values are faster but gives poor coverage. Higher
- values generates better coverage but are slower.")
- p.set_cost(2)
-
- documentation.add_warning(
- "This modifier uses a poisson disk sampling algorithm which can be
- quite slow.")
-
-
-func _process_transforms(transforms, domain, seed) -> void:
- _rng = RandomNumberGenerator.new()
- _rng.set_seed(seed)
- _domain = domain
- _bounds = _domain.bounds
- _gt = domain.get_global_transform()
- _points = []
- _init_grid()
-
- # Stores the possible starting points from where we run the sampling.
- # This array will progressively be emptied as the algorithm progresses.
- var spawn_points: Array[Transform3D]
- spawn_points.push_back(_get_starting_point())
-
- # Sampler main loop
- while not spawn_points.is_empty():
-
- # Pick a starting point at random from the existing list
- var spawn_index: int = _rng.randi_range(0, spawn_points.size() - 1)
- var spawn_center := spawn_points[spawn_index]
-
- var tries := 0
- var candidate_accepted := false
-
- while tries < samples_before_rejection:
- tries += 1
-
- # Generate a random point in space, outside the radius of the spawn point
- var dir: Vector3 = _generate_random_vector()
- var candidate: Vector3 = spawn_center.origin + dir * _rng.randf_range(radius, radius * 2.0)
-
- if _is_valid(candidate):
- candidate_accepted = true
-
- # Add new points to the lists
- var t = Transform3D()
- t.origin = candidate
-
- if is_using_local_space():
- t.basis = _gt.basis
-
- _points.push_back(t)
- spawn_points.push_back(t)
- if restrict_height:
- _grid[_cell_x + _cell_z * _grid_size.z] = _points.size() - 1
- else:
- _grid[_cell_x + (_grid_size.y * _cell_y) + (_grid_size.x * _grid_size.y * _cell_z)] = _points.size() - 1
-
- break
-
- # Failed to find a point after too many tries. The space around this
- # spawn point is probably full, discard it.
- if not candidate_accepted:
- spawn_points.remove_at(spawn_index)
-
- transforms.append(_points)
- transforms.shuffle(seed)
-
-
-func _init_grid() -> void:
- _squared_radius = radius * radius
- _cell_size = radius / sqrt(2)
- _grid_size.x = ceil(_bounds.size.x / _cell_size)
- _grid_size.y = ceil(_bounds.size.y / _cell_size)
- _grid_size.z = ceil(_bounds.size.z / _cell_size)
-
- _grid_size = _grid_size.clamp(Vector3.ONE, _grid_size)
-
- _grid = []
- if restrict_height:
- _grid.resize(_grid_size.x * _grid_size.z)
- else:
- _grid.resize(_grid_size.x * _grid_size.y * _grid_size.z)
-
-
-# Starting point must be inside the domain, or we run the risk to never generate
-# any valid point later on
-# TODO: Domain may have islands, so we should use multiple starting points
-func _get_starting_point() -> Transform3D:
- var point: Vector3 = _bounds.center
-
- var tries := 0
- while not _domain.is_point_inside(point) or tries > 200:
- tries += 1
- point.x = _rng.randf_range(_bounds.min.x, _bounds.max.x)
- point.y = _rng.randf_range(_bounds.min.y, _bounds.max.y)
- point.z = _rng.randf_range(_bounds.min.z, _bounds.max.z)
-
- if restrict_height:
- point.y = _bounds.center.y
-
- var starting_point := Transform3D()
- starting_point.origin = point
- return starting_point
-
-
-func _is_valid(candidate: Vector3) -> bool:
- if not _domain.is_point_inside(candidate):
- return false
-
- # compute candidate current cell
- var t_candidate = candidate - _bounds.min
- _cell_x = floor(t_candidate.x / _cell_size)
- _cell_y = floor(t_candidate.y / _cell_size)
- _cell_z = floor(t_candidate.z / _cell_size)
-
- # Search the surrounding cells for other points
- var search_start_x: int = max(0, _cell_x - 2)
- var search_end_x: int = min(_cell_x + 2, _grid_size.x - 1)
- var search_start_y: int = max(0, _cell_y - 2)
- var search_end_y: int = min(_cell_y + 2, _grid_size.y - 1)
- var search_start_z: int = max(0, _cell_z - 2)
- var search_end_z: int = min(_cell_z + 2, _grid_size.z - 1)
-
- if restrict_height:
- for x in range(search_start_x, search_end_x + 1):
- for z in range(search_start_z, search_end_z + 1):
- var point_index = _grid[x + z * _grid_size.z]
- if _is_point_too_close(candidate, point_index):
- return false
- else:
- for x in range(search_start_x, search_end_x + 1):
- for y in range(search_start_y, search_end_y + 1):
- for z in range(search_start_z, search_end_z + 1):
- var point_index = _grid[x + (_grid_size.y * y) + (_grid_size.x * _grid_size.y * z)]
- if _is_point_too_close(candidate, point_index):
- return false
-
- return true
-
-
-func _is_point_too_close(candidate: Vector3, point_index) -> bool:
- if point_index == null:
- return false
-
- var other_point := _points[point_index]
- var squared_dist: float = candidate.distance_squared_to(other_point.origin)
- return squared_dist < _squared_radius
-
-
-func _generate_random_vector():
- var angle = _rng.randf_range(0.0, TAU)
- if restrict_height:
- return Vector3(sin(angle), 0.0, cos(angle))
-
- var costheta = _rng.randf_range(-1.0, 1.0)
- var theta = acos(costheta)
- var vector := Vector3.ZERO
- vector.x = sin(theta) * cos(angle)
- vector.y = sin(theta) * sin(angle)
- vector.z = cos(theta)
- return vector
diff --git a/addons/scatter-4/src/modifiers/create_inside_random.gd b/addons/scatter-4/src/modifiers/create_inside_random.gd
deleted file mode 100644
index 529916d..0000000
--- a/addons/scatter-4/src/modifiers/create_inside_random.gd
+++ /dev/null
@@ -1,85 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var amount := 10
-
-var _rng: RandomNumberGenerator
-
-
-func _init() -> void:
- display_name = "Create Inside (Random)"
- category = "Create"
- warning_ignore_no_transforms = true
- warning_ignore_no_shape = false
- can_override_seed = true
- global_reference_frame_available = true
- local_reference_frame_available = true
- use_local_space_by_default()
-
- documentation.add_paragraph(
- "Randomly place new transforms inside the area defined by
- the ScatterShape nodes.")
-
- var p := documentation.add_parameter("Amount")
- p.set_type("int")
- p.set_description("How many transforms will be created.")
- p.set_cost(2)
-
- documentation.add_warning(
- "In some cases, the amount of transforms created by this modifier
- might be lower than the requested amount (but never higher). This may
- happen if the provided ScatterShape has a huge bounding box but a tiny
- valid space, like a narrow path.")
-
-
-# TODO:
-# + Multithreading
-# + Spatial partionning to discard areas outside the domain earlier
-func _process_transforms(transforms, domain, seed) -> void:
- _rng = RandomNumberGenerator.new()
- _rng.set_seed(seed)
-
- var gt: Transform3D = domain.get_global_transform()
- var gt_inverse = gt.affine_inverse()
- var center: Vector3 = domain.bounds.center
- var half_size: Vector3 = domain.bounds.size / 2.0
- var height: float = domain.bounds.center.y
-
- # Generate a random point in the bounding box. Store if it's inside the
- # domain, or discard if invalid. Repeat until enough valid points are found.
- var t: Transform3D
- var pos: Vector3
- var new_transforms: Array[Transform3D] = []
- var max_retries = amount * 10
- var tries := 0
-
- while new_transforms.size() != amount:
- t = Transform3D()
- pos = _random_vec3() * half_size + center
-
- if restrict_height:
- pos.y = height
-
- if is_using_local_space():
- t.basis = gt.basis
-
- if domain.is_point_inside(pos):
- t.origin = pos
- new_transforms.push_back(t)
- continue
-
- # Prevents an infinite loop
- tries += 1
- if tries > max_retries:
- break
-
- transforms.append(new_transforms)
-
-
-func _random_vec3() -> Vector3:
- var vec3 = Vector3.ZERO
- vec3.x = _rng.randf_range(-1.0, 1.0)
- vec3.y = _rng.randf_range(-1.0, 1.0)
- vec3.z = _rng.randf_range(-1.0, 1.0)
- return vec3
diff --git a/addons/scatter-4/src/modifiers/look_at.gd b/addons/scatter-4/src/modifiers/look_at.gd
deleted file mode 100644
index 471d14c..0000000
--- a/addons/scatter-4/src/modifiers/look_at.gd
+++ /dev/null
@@ -1,42 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var target := Vector3.ZERO
-@export var up := Vector3.UP
-
-
-func _init() -> void:
- display_name = "Look At"
- category = "Edit"
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_local_space_by_default()
-
- documentation.add_paragraph("Rotates every transform such that the forward axis (-Z) points towards the target position.")
-
- documentation.add_parameter("Target").set_type("Vector3").set_description(
- "Target position (X, Y, Z)")
- documentation.add_parameter("Up").set_type("Vector3").set_description(
- "Up axes (X, Y, Z)")
-
-
-func _process_transforms(transforms, domain, _seed : int) -> void:
- var st: Transform3D = domain.get_global_transform()
-
- for t in transforms.size():
- var transform: Transform3D = transforms.list[t]
- var origin := transform.origin
- var original_scale := transform.basis.get_scale()
- var local_target := target - origin
-
- if is_using_local_space():
- local_target = st * local_target
- elif is_using_individual_instances_space():
- local_target = transform * local_target
-
- var lookat := Transform3D(Basis.looking_at(local_target, up), origin)
- lookat = lookat.scaled_local(original_scale)
- transforms.list[t] = lookat
diff --git a/addons/scatter-4/src/modifiers/offset_position.gd b/addons/scatter-4/src/modifiers/offset_position.gd
deleted file mode 100644
index 5a0ba43..0000000
--- a/addons/scatter-4/src/modifiers/offset_position.gd
+++ /dev/null
@@ -1,38 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var position := Vector3.ZERO
-
-
-func _init() -> void:
- display_name = "Offset Position"
- category = "Offset"
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_individual_instances_space_by_default()
-
- documentation.add_paragraph("Moves every transform the same way.")
-
- var p := documentation.add_parameter("Position")
- p.set_type("vector3")
- p.set_description("How far each transforms are moved.")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- var st: Transform3D = domain.get_global_transform()
- var t: Transform3D
-
- for i in transforms.list.size():
- t = transforms.list[i]
-
- if is_using_individual_instances_space():
- t.origin += t.basis * position
- elif is_using_local_space():
- t.origin += st.basis * position
- else:
- t.origin += position
-
- transforms.list[i] = t
diff --git a/addons/scatter-4/src/modifiers/offset_rotation.gd b/addons/scatter-4/src/modifiers/offset_rotation.gd
deleted file mode 100644
index d69a30a..0000000
--- a/addons/scatter-4/src/modifiers/offset_rotation.gd
+++ /dev/null
@@ -1,54 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var rotation := Vector3.ZERO
-
-
-func _init() -> void:
- display_name = "Offset Rotation"
- category = "Offset"
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_individual_instances_space_by_default()
-
- documentation.add_paragraph("Rotates every transform.")
-
- documentation.add_parameter("Rotation").set_type("Vector3").set_description(
- "Rotation angle (in degrees) along each axes (X, Y, Z)")
-
-
-func _process_transforms(transforms, domain, _seed : int) -> void:
- var rotation_rad := Vector3.ZERO
- rotation_rad.x = deg_to_rad(rotation.x)
- rotation_rad.y = deg_to_rad(rotation.y)
- rotation_rad.z = deg_to_rad(rotation.z)
-
- var t: Transform3D
- var basis: Basis
- var axis_x := Vector3.RIGHT
- var axis_y := Vector3.UP
- var axis_z := Vector3.FORWARD
-
- if is_using_local_space():
- var st: Transform3D = domain.get_global_transform()
- axis_x = st.basis.x
- axis_y = st.basis.y
- axis_z = st.basis.z
-
- for i in transforms.size():
- t = transforms.list[i]
- basis = t.basis
-
- if is_using_individual_instances_space():
- axis_x = basis.x
- axis_y = basis.y
- axis_z = basis.z
-
- basis = basis.rotated(axis_x, rotation_rad.x)
- basis = basis.rotated(axis_y, rotation_rad.y)
- basis = basis.rotated(axis_z, rotation_rad.z)
-
- transforms.list[i].basis = basis
diff --git a/addons/scatter-4/src/modifiers/offset_scale.gd b/addons/scatter-4/src/modifiers/offset_scale.gd
deleted file mode 100644
index 959e2fb..0000000
--- a/addons/scatter-4/src/modifiers/offset_scale.gd
+++ /dev/null
@@ -1,45 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var scale := Vector3(1, 1, 1)
-
-
-func _init() -> void:
- display_name = "Offset Scale"
- category = "Offset"
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_individual_instances_space_by_default()
-
- documentation.add_paragraph("Scales every transform.")
-
- var p := documentation.add_parameter("Scale")
- p.set_type("Vector3")
- p.set_description("How much to scale the transform along each axes (X, Y, Z)")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- var st: Transform3D = domain.get_global_transform()
- var basis: Basis
- var t: Transform3D
- var local_t: Transform3D
-
- for i in transforms.size():
- t = transforms.list[i]
- basis = t.basis
-
- if is_using_individual_instances_space():
- basis.x *= scale.x
- basis.y *= scale.y
- basis.z *= scale.z
- elif is_using_local_space():
- local_t = t * st
- local_t.basis = local_t.basis.scaled(scale)
- basis = (st * local_t).basis
- else:
- basis = basis.scaled(scale)
-
- transforms.list[i].basis = basis
diff --git a/addons/scatter-4/src/modifiers/offset_transform.gd b/addons/scatter-4/src/modifiers/offset_transform.gd
deleted file mode 100644
index cd3166d..0000000
--- a/addons/scatter-4/src/modifiers/offset_transform.gd
+++ /dev/null
@@ -1,81 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var position := Vector3.ZERO
-@export var rotation := Vector3(0.0, 0.0, 0.0)
-@export var scale := Vector3.ONE
-
-
-func _init() -> void:
- display_name = "Offset Transform"
- category = "Offset"
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_local_space_by_default()
-
- documentation.add_paragraph(
- "Offsets position, rotation and scale in a single modifier. Every
- transforms generated before will see the same transformation applied.")
-
- var p := documentation.add_parameter("Position")
- p.set_type("Vector3")
- p.set_description("How far each transforms are moved.")
-
- p = documentation.add_parameter("Rotation")
- p.set_type("Vector3")
- p.set_description("Rotation angle (in degrees) along each axes (X, Y, Z)")
-
- p = documentation.add_parameter("Scale")
- p.set_type("Vector3")
- p.set_description("How much to scale the transform along each axes (X, Y, Z)")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- var t: Transform3D
- var local_t: Transform3D
- var basis: Basis
- var axis_x := Vector3.RIGHT
- var axis_y := Vector3.UP
- var axis_z := Vector3.DOWN
- var final_scale := scale
- var final_position := position
- var st: Transform3D = domain.get_global_transform()
-
- if is_using_local_space():
- axis_x = st.basis.x
- axis_y = st.basis.y
- axis_z = st.basis.z
- final_scale = scale.rotated(Vector3.RIGHT, st.basis.get_euler().x)
- final_position = st.basis * position
-
- for i in transforms.size():
- t = transforms.list[i]
- basis = t.basis
-
- if is_using_individual_instances_space():
- axis_x = basis.x
- axis_y = basis.y
- axis_z = basis.z
- basis.x *= scale.x
- basis.y *= scale.y
- basis.z *= scale.z
- final_position = t.basis * position
-
- elif is_using_local_space():
- local_t = t * st
- local_t.basis = local_t.basis.scaled(final_scale)
- basis = (st * local_t).basis
-
- else:
- basis = basis.scaled(final_scale)
-
- basis = basis.rotated(axis_x, deg_to_rad(rotation.x))
- basis = basis.rotated(axis_y, deg_to_rad(rotation.y))
- basis = basis.rotated(axis_z, deg_to_rad(rotation.z))
- t.basis = basis
- t.origin += final_position
-
- transforms.list[i] = t
diff --git a/addons/scatter-4/src/modifiers/project_on_geometry.gd b/addons/scatter-4/src/modifiers/project_on_geometry.gd
deleted file mode 100644
index 4ad6209..0000000
--- a/addons/scatter-4/src/modifiers/project_on_geometry.gd
+++ /dev/null
@@ -1,179 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-signal projection_completed
-
-
-const ProtonScatterPhysicsHelper := preload("res://addons/proton_scatter/src/common/physics_helper.gd")
-
-
-@export var ray_direction := Vector3.DOWN
-@export var ray_length := 10.0
-@export var ray_offset := 1.0
-@export var remove_points_on_miss := true
-@export var align_with_collision_normal := false
-@export_range(0.0, 90.0) var max_slope = 90.0
-@export_flags_3d_physics var collision_mask = 1
-
-var _last_hit: Dictionary
-
-
-func _init() -> void:
- display_name = "Project On Colliders"
- category = "Edit"
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_global_space_by_default()
-
- documentation.add_paragraph(
- "Moves each transforms along the ray direction until they hit a collider.
- This is useful to avoid floating objects on uneven terrain for example.")
-
- documentation.add_warning(
- "This modifier only works when physics bodies are around. It will ignore
- simple MeshInstances nodes.")
-
- var p := documentation.add_parameter("Ray direction")
- p.set_type("Vector3")
- p.set_description(
- "In which direction we look for a collider. This default to the DOWN
- direction by default (look at the ground).")
- p.add_warning(
- "This is relative to the transform is local space is enabled, or aligned
- with the global axis if local space is disabled.")
-
- p = documentation.add_parameter("Ray length")
- p.set_type("float")
- p.set_description("How far we look for other physics objects.")
- p.set_cost(2)
-
- p = documentation.add_parameter("Ray offset")
- p.set_type("Vector3")
- p.set_description(
- "Moves back the raycast origin point along the ray direction. This is
- useful if the initial transform is slightly below the ground, which would
- make the raycast miss the collider (since it would start inside).")
-
- p = documentation.add_parameter("Remove points on miss")
- p.set_type("bool")
- p.set_description(
- "When enabled, if the raycast didn't collide with anything, or collided
- with a surface above the max slope setting, the transform is removed
- from the list.
- This is useful to avoid floating objects that are too far from the rest
- of the scene's geometry.")
-
- p = documentation.add_parameter("Align with collision normal")
- p.set_type("bool")
- p.set_description(
- "Rotate the transform to align it with the collision normal in case
- the ray cast hit a collider.")
-
- p = documentation.add_parameter("Max slope")
- p.set_type("float")
- p.set_description(
- "Angle (in degrees) after which the hit is considered invalid.
- When a ray cast hit, the normal of the ray is compared against the
- normal of the hit. If you set the slope to 0°, the ray and the hit
- normal would have to be perfectly aligned to be valid. On the other
- hand, setting the maximum slope to 90° treats every collisions as
- valid regardless of their normals.")
-
- p = documentation.add_parameter("Mask")
- p.set_description(
- "Only collide with colliders on these layers. Disabled layers will
- be ignored. It's useful to ignore players or npcs that might be on the
- scene when you're editing it.")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- if transforms.is_empty():
- return
-
- # Create all the physics ray queries
- var domain_basis: Basis = domain.get_root().get_global_transform().basis
- var queries: Array[PhysicsRayQueryParameters3D] = []
- for t in transforms.list:
- var start = t.origin
- var end = t.origin
- var dir = ray_direction.normalized()
-
- if is_using_individual_instances_space():
- dir = t.basis * dir
-
- elif is_using_local_space():
- dir = domain_basis * dir
-
- start -= ray_offset * dir
- end += ray_length * dir
-
- var ray_query := PhysicsRayQueryParameters3D.new()
- ray_query.from = start
- ray_query.to = end
- ray_query.collision_mask = collision_mask
-
- queries.push_back(ray_query)
-
- # Run the queries in the physics helper since we can't access the PhysicsServer
- # from outside the _physics_process while also being in a separate thread.
- var physics_helper: ProtonScatterPhysicsHelper = domain.get_root().get_physics_helper()
- var ray_hits = await physics_helper.execute(queries)
-
- if ray_hits.is_empty():
- return
-
- # Apply the results
-
- var index := 0
- var d: float
- var t: Transform3D
- var remapped_max_slope = remap(max_slope, 0.0, 90.0, 0.0, 1.0)
- var is_point_valid := false
-
- for hit in ray_hits:
- is_point_valid = true
-
- if hit.is_empty():
- is_point_valid = false
- else:
- d = abs(Vector3.UP.dot(hit.normal))
- is_point_valid = d >= (1.0 - remapped_max_slope)
-
- if is_point_valid:
- t = transforms.list[index]
-
- if align_with_collision_normal:
- t = _align_with(t, hit.normal)
-
- t.origin = hit.position
- transforms.list[index] = t
- index += 1
-
- elif remove_points_on_miss:
- transforms.list.remove_at(index)
-
- if transforms.is_empty():
- warning += """Every points have been removed. Possible reasons include: \n
- + No collider is close enough to the shapes.
- + Ray length is too short.
- + Ray direction is incorrect.
- + Collision mask is not set properly.
- + Max slope is too low.
- """
-
-
-func _align_with(t: Transform3D, normal: Vector3) -> Transform3D:
- var n1 = t.basis.y.normalized()
- var n2 = normal.normalized()
-
- var cosa = n1.dot(n2)
- var alpha = acos(cosa)
- var axis = n1.cross(n2)
-
- if axis == Vector3.ZERO:
- return t
-
- return t.rotated(axis.normalized(), alpha)
diff --git a/addons/scatter-4/src/modifiers/proxy.gd b/addons/scatter-4/src/modifiers/proxy.gd
deleted file mode 100644
index fd862ba..0000000
--- a/addons/scatter-4/src/modifiers/proxy.gd
+++ /dev/null
@@ -1,49 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-const Scatter = preload("../scatter.gd")
-
-
-@export_node_path var scatter_node
-#@export var auto_sync_changes := true # TODO: find a solution to this.
-
-var _reference_node
-
-
-func _init() -> void:
- display_name = "Proxy"
- category = "Misc"
- can_restrict_height = false
- can_override_seed = false
- global_reference_frame_available = false
- local_reference_frame_available = false
- individual_instances_reference_frame_available = false
- warning_ignore_no_transforms = true
-
- documentation.add_paragraph("Copy a modifier stack from another ProtonScatter node in the scene.")
- documentation.add_paragraph(
- "Useful when you need multiple Scatter nodes sharing the same rules, without having to
- replicate their modifiers and settings in each."
- )
- documentation.add_paragraph(
- "Unlike presets which are full independent copies, this method is more similar to a linked
- copy. Changes on the original modifier stack will be accounted for in here."
- )
-
- var p = documentation.add_parameter("Scatter node")
- p.set_type("NodePath")
- p.set_description("The Scatter node to use as a reference.")
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- var reference_node = domain.get_root().get_node_or_null(scatter_node)
-
- if not reference_node or not reference_node is Scatter:
- warning += "You need to select a valid ProtonScatter node."
- return
-
- if reference_node.modifier_stack:
- var stack = reference_node.modifier_stack.get_copy()
- var result = stack.update(domain.get_root(), domain)
- transforms.append(result.list)
diff --git a/addons/scatter-4/src/modifiers/randomize_rotation.gd b/addons/scatter-4/src/modifiers/randomize_rotation.gd
deleted file mode 100644
index aea5eae..0000000
--- a/addons/scatter-4/src/modifiers/randomize_rotation.gd
+++ /dev/null
@@ -1,88 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var rotation := Vector3(360.0, 360.0, 360.0)
-@export var snap_angle := Vector3.ZERO
-
-var _rng: RandomNumberGenerator
-
-
-func _init() -> void:
- display_name = "Randomize Rotation"
- category = "Edit"
- can_override_seed = true
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_individual_instances_space_by_default()
-
- documentation.add_paragraph("Randomly rotate every transforms individually.")
-
- var p := documentation.add_parameter("Rotation")
- p.set_type("Vector3")
- p.set_description("Rotation angle (in degrees) along each axes (X, Y, Z)")
-
- p = documentation.add_parameter("Snap angle")
- p.set_type("Vector3")
- p.set_description(
- "When set to any value above 0, the rotation will be done by increments
- of the snap angle.")
- p.add_warning(
- "Example: When Snap Angle is set to 90, the possible random rotation
- offsets around an axis will be among [0, 90, 180, 360].")
-
-
-func _process_transforms(transforms, domain, seed) -> void:
- _rng = RandomNumberGenerator.new()
- _rng.set_seed(seed)
-
- var t: Transform3D
- var b: Basis
-
- var gt: Transform3D = domain.get_global_transform()
- var gb: Basis = gt.basis
- var axis_x: Vector3 = Vector3.RIGHT
- var axis_y: Vector3 = Vector3.UP
- var axis_z: Vector3 = Vector3.FORWARD
-
- if is_using_local_space():
- axis_x = (Vector3.RIGHT * gb).normalized()
- axis_y = (Vector3.UP * gb).normalized()
- axis_z = (Vector3.FORWARD * gb).normalized()
-
- for i in transforms.list.size():
- t = transforms.list[i]
- b = t.basis
-
- if is_using_individual_instances_space():
- axis_x = t.basis.x.normalized()
- axis_y = t.basis.y.normalized()
- axis_z = t.basis.z.normalized()
-
- b = b.rotated(axis_x, _random_angle(rotation.x, snap_angle.x))
- b = b.rotated(axis_y, _random_angle(rotation.y, snap_angle.y))
- b = b.rotated(axis_z, _random_angle(rotation.z, snap_angle.z))
-
- t.basis = b
- transforms.list[i] = t
-
-
-func _random_vec3() -> Vector3:
- var vec3 = Vector3.ZERO
- vec3.x = _rng.randf_range(-1.0, 1.0)
- vec3.y = _rng.randf_range(-1.0, 1.0)
- vec3.z = _rng.randf_range(-1.0, 1.0)
- return vec3
-
-
-func _random_angle(rot: float, snap: float) -> float:
- return deg_to_rad(snapped(_rng.randf_range(-1.0, 1.0) * rot, snap))
-
-
-func _clamp_vector(vec3, vmin, vmax) -> Vector3:
- vec3.x = clamp(vec3.x, vmin.x, vmax.x)
- vec3.y = clamp(vec3.y, vmin.y, vmax.y)
- vec3.z = clamp(vec3.z, vmin.z, vmax.z)
- return vec3
diff --git a/addons/scatter-4/src/modifiers/randomize_transforms.gd b/addons/scatter-4/src/modifiers/randomize_transforms.gd
deleted file mode 100644
index eb675ad..0000000
--- a/addons/scatter-4/src/modifiers/randomize_transforms.gd
+++ /dev/null
@@ -1,94 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var position := Vector3.ONE
-@export var rotation := Vector3(360.0, 360.0, 360.0)
-@export var scale := Vector3.ONE
-
-var _rng: RandomNumberGenerator
-
-
-func _init() -> void:
- display_name = "Randomize Transforms"
- category = "Edit"
- can_override_seed = true
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = true
- use_individual_instances_space_by_default()
-
-
-func _process_transforms(transforms, domain, seed) -> void:
- _rng = RandomNumberGenerator.new()
- _rng.set_seed(seed)
-
- var t: Transform3D
- var local_t: Transform3D
- var basis: Basis
- var random_scale: Vector3
- var random_position: Vector3
- var st: Transform3D = domain.get_global_transform()
-
- # Global rotation axis
- var axis_x := Vector3.RIGHT
- var axis_y := Vector3.UP
- var axis_z := Vector3.DOWN
-
- if is_using_local_space():
- axis_x = st.basis.x
- axis_y = st.basis.y
- axis_z = st.basis.z
-
- for i in transforms.size():
- t = transforms.list[i]
- basis = t.basis
-
- random_scale = Vector3.ONE + (_rng.randf() * scale)
- random_position = _random_vec3() * position
-
- if is_using_individual_instances_space():
- axis_x = basis.x
- axis_y = basis.y
- axis_z = basis.z
- basis.x *= random_scale.x
- basis.y *= random_scale.y
- basis.z *= random_scale.z
- random_position = t.basis * random_position
-
- elif is_using_local_space():
- local_t = t * st
- local_t.basis = local_t.basis.scaled(random_scale)
- basis = (st * local_t).basis
-
- else:
- basis = basis.scaled(random_scale)
-
- basis = basis.rotated(axis_x, deg_to_rad(_random_float() * rotation.x))
- basis = basis.rotated(axis_y, deg_to_rad(_random_float() * rotation.y))
- basis = basis.rotated(axis_z, deg_to_rad(_random_float() * rotation.z))
-
- t.origin += random_position
- t.basis = basis
-
- transforms.list[i] = t
-
-
-func _random_vec3() -> Vector3:
- var vec3 = Vector3.ZERO
- vec3.x = _rng.randf_range(-1.0, 1.0)
- vec3.y = _rng.randf_range(-1.0, 1.0)
- vec3.z = _rng.randf_range(-1.0, 1.0)
- return vec3
-
-
-func _random_float() -> float:
- return _rng.randf_range(-1.0, 1.0)
-
-
-func _clamp_vector(vec3, vmin, vmax) -> Vector3:
- vec3.x = clamp(vec3.x, vmin.x, vmax.x)
- vec3.y = clamp(vec3.y, vmin.y, vmax.y)
- vec3.z = clamp(vec3.z, vmin.z, vmax.z)
- return vec3
diff --git a/addons/scatter-4/src/modifiers/relax.gd b/addons/scatter-4/src/modifiers/relax.gd
deleted file mode 100644
index 24b686d..0000000
--- a/addons/scatter-4/src/modifiers/relax.gd
+++ /dev/null
@@ -1,44 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var iterations : int = 3
-@export var offset_step : float = 0.01
-@export var consecutive_step_multiplier : float = 0.5
-
-
-func _init() -> void:
- display_name = "Relax Position"
- category = "Edit"
- global_reference_frame_available = false
- local_reference_frame_available = false
- individual_instances_reference_frame_available = false
- can_restrict_height = true
- restrict_height = true
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- # TODO this can benefit greatly from multithreading
- if transforms.size() < 2:
- return
-
- var offset = offset_step
-
- for iteration in iterations:
- for i in transforms.size():
- var min_vector = Vector3.ONE * 99999
- # Find the closest point
- for j in transforms.size():
- if i == j:
- continue
- var d = transforms.list[i].origin - transforms.list[j].origin
- if d.length() < min_vector.length():
- min_vector = d
-
- if restrict_height:
- min_vector.y = 0.0
-
- # move away from closest point
- transforms.list[i].origin += min_vector.normalized() * offset
-
- offset *= consecutive_step_multiplier
diff --git a/addons/scatter-4/src/modifiers/remove_outside_shapes.gd b/addons/scatter-4/src/modifiers/remove_outside_shapes.gd
deleted file mode 100644
index 50f20b9..0000000
--- a/addons/scatter-4/src/modifiers/remove_outside_shapes.gd
+++ /dev/null
@@ -1,44 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-
-@export var negative_shapes_only := false
-
-
-func _init() -> void:
- display_name = "Remove Outside"
- category = "Remove"
- can_restrict_height = false
- global_reference_frame_available = false
- local_reference_frame_available = false
- individual_instances_reference_frame_available = false
-
- documentation.add_paragraph(
- "Remove all transforms falling outside a ScatterShape node, or inside
- a shape set to 'Negative' mode.")
-
- var p := documentation.add_parameter("Negative Shapes Only")
- p.set_type("bool")
- p.set_description(
- "Only remove transforms falling inside the negative shapes (shown
- in red). Transforms outside any shapes will still remain.")
-
-
-func _process_transforms(transforms, domain, seed) -> void:
- var i := 0
- var point: Vector3
- var to_remove := false
-
- while i < transforms.size():
- point = transforms.list[i].origin
-
- if negative_shapes_only:
- to_remove = domain.is_point_excluded(point)
- else:
- to_remove = not domain.is_point_inside(point)
-
- if to_remove:
- transforms.list.remove_at(i)
- continue
-
- i += 1
diff --git a/addons/scatter-4/src/modifiers/single_item.gd b/addons/scatter-4/src/modifiers/single_item.gd
deleted file mode 100644
index fe1e120..0000000
--- a/addons/scatter-4/src/modifiers/single_item.gd
+++ /dev/null
@@ -1,36 +0,0 @@
-@tool
-extends "base_modifier.gd"
-
-# Adds a single object with the given transform
-
-@export var offset := Vector3.ZERO
-@export var rotation := Vector3.ZERO
-@export var scale := Vector3.ONE
-
-
-func _init() -> void:
- display_name = "Add Single Item"
- category = "Create"
- warning_ignore_no_shape = true
- warning_ignore_no_transforms = true
- can_restrict_height = false
- global_reference_frame_available = true
- local_reference_frame_available = true
- individual_instances_reference_frame_available = false
- use_local_space_by_default()
-
-
-func _process_transforms(transforms, domain, _seed) -> void:
- var basis := Basis()
- basis = basis.rotated(Vector3.RIGHT, deg_to_rad(rotation.x))
- basis = basis.rotated(Vector3.UP, deg_to_rad(rotation.y))
- basis = basis.rotated(Vector3.FORWARD, deg_to_rad(rotation.z))
- var transform := Transform3D(basis, offset)
-
- if is_using_local_space():
- var gt: Transform3D = domain.get_global_transform()
- transform = gt * transform.scaled_local(scale)
- else:
- transform = transform.scaled(scale)
-
- transforms.list.push_back(transform)
diff --git a/addons/scatter-4/src/presets/preset_entry.gd b/addons/scatter-4/src/presets/preset_entry.gd
deleted file mode 100644
index 8a9d11c..0000000
--- a/addons/scatter-4/src/presets/preset_entry.gd
+++ /dev/null
@@ -1,27 +0,0 @@
-@tool
-extends MarginContainer
-
-signal load_full
-signal load_stack_only
-signal delete
-
-
-func _ready() -> void:
- $%LoadStackOnly.pressed.connect(func (): load_stack_only.emit())
- $%LoadFullPreset.pressed.connect(func (): load_full.emit())
- $%DeleteButton.pressed.connect(func (): delete.emit())
-
-
-func set_preset_name(preset_name: String) -> void:
- $%Label.set_text(preset_name.capitalize())
-
-
-func show_save_controls() -> void:
- $%SaveButtons.visible = true
- $%LoadButtons.visible = false
-
-
-func show_load_controls() -> void:
- $%SaveButtons.visible = false
- $%LoadButtons.visible = true
-
diff --git a/addons/scatter-4/src/presets/preset_entry.tscn b/addons/scatter-4/src/presets/preset_entry.tscn
deleted file mode 100644
index 7482d15..0000000
--- a/addons/scatter-4/src/presets/preset_entry.tscn
+++ /dev/null
@@ -1,130 +0,0 @@
-[gd_scene load_steps=6 format=3 uid="uid://bosqtuvhckh3g"]
-
-[ext_resource type="Texture2D" uid="uid://ddjrq1h4mkn6a" path="res://addons/proton_scatter/icons/load.svg" id="1_0auay"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/presets/preset_entry.gd" id="1_bqha3"]
-[ext_resource type="Texture2D" uid="uid://bl1rkjjxvxu8f" path="res://addons/proton_scatter/icons/remove.svg" id="2_p04k2"]
-
-[sub_resource type="SystemFont" id="SystemFont_kgkwq"]
-font_style = 1
-
-[sub_resource type="LabelSettings" id="LabelSettings_poli7"]
-font = SubResource("SystemFont_kgkwq")
-
-[node name="PresetEntry" type="MarginContainer"]
-custom_minimum_size = Vector2(450, 0)
-anchors_preset = 14
-anchor_top = 0.5
-anchor_right = 1.0
-anchor_bottom = 0.5
-offset_top = -45.0
-offset_bottom = 45.0
-grow_horizontal = 2
-grow_vertical = 2
-size_flags_horizontal = 3
-script = ExtResource("1_bqha3")
-
-[node name="Panel" type="Panel" parent="."]
-layout_mode = 2
-offset_right = 1920.0
-offset_bottom = 90.0
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-layout_mode = 2
-offset_right = 1920.0
-offset_bottom = 90.0
-theme_override_constants/margin_left = 12
-theme_override_constants/margin_top = 12
-theme_override_constants/margin_right = 12
-theme_override_constants/margin_bottom = 12
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
-layout_mode = 2
-offset_left = 12.0
-offset_top = 12.0
-offset_right = 1908.0
-offset_bottom = 78.0
-
-[node name="Label" type="Label" parent="MarginContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 20.0
-offset_right = 1669.0
-offset_bottom = 46.0
-size_flags_horizontal = 3
-text = "Preset name"
-label_settings = SubResource("LabelSettings_poli7")
-
-[node name="VSeparator" type="VSeparator" parent="MarginContainer/HBoxContainer"]
-layout_mode = 2
-offset_left = 1673.0
-offset_right = 1677.0
-offset_bottom = 66.0
-
-[node name="LoadButtons" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 1681.0
-offset_right = 1860.0
-offset_bottom = 66.0
-alignment = 1
-
-[node name="LoadStackOnly" type="Button" parent="MarginContainer/HBoxContainer/LoadButtons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 179.0
-offset_bottom = 31.0
-text = "Load modifier stack"
-icon = ExtResource("1_0auay")
-alignment = 0
-
-[node name="LoadFullPreset" type="Button" parent="MarginContainer/HBoxContainer/LoadButtons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 35.0
-offset_right = 179.0
-offset_bottom = 66.0
-text = "Load full preset"
-icon = ExtResource("1_0auay")
-alignment = 0
-
-[node name="SaveButtons" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
-unique_name_in_owner = true
-visible = false
-layout_mode = 2
-offset_left = 1712.0
-offset_right = 1860.0
-offset_bottom = 96.0
-size_flags_stretch_ratio = 2.0
-alignment = 1
-
-[node name="OverrideButton" type="Button" parent="MarginContainer/HBoxContainer/SaveButtons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 32.0
-offset_right = 148.0
-offset_bottom = 63.0
-text = "Override preset"
-icon = ExtResource("1_0auay")
-alignment = 0
-
-[node name="VSeparator2" type="VSeparator" parent="MarginContainer/HBoxContainer"]
-layout_mode = 2
-offset_left = 1864.0
-offset_right = 1868.0
-offset_bottom = 66.0
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
-layout_mode = 2
-offset_left = 1872.0
-offset_right = 1896.0
-offset_bottom = 66.0
-alignment = 1
-
-[node name="DeleteButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 21.0
-offset_right = 24.0
-offset_bottom = 45.0
-theme_override_colors/icon_normal_color = Color(0.917647, 0.0784314, 0, 1)
-icon = ExtResource("2_p04k2")
diff --git a/addons/scatter-4/src/presets/presets.gd b/addons/scatter-4/src/presets/presets.gd
deleted file mode 100644
index c406ce8..0000000
--- a/addons/scatter-4/src/presets/presets.gd
+++ /dev/null
@@ -1,169 +0,0 @@
-@tool
-extends Popup
-
-
-const PRESETS_PATH = "res://addons/proton_scatter/presets"
-const PresetEntry := preload("./preset_entry.tscn")
-const ScatterUtil := preload('../common/scatter_util.gd')
-const ScatterItem := preload('../scatter_item.gd')
-const ScatterShape := preload('../scatter_shape.gd')
-
-var _scatter_node
-var _ideal_popup_size: Vector2i
-var _editor_file_system: EditorFileSystem
-
-
-func _ready():
- $%NewPresetButton.pressed.connect(_show_preset_dialog)
- $%NewPresetDialog.confirmed.connect(_on_new_preset_name_confirmed)
-
-
-func save_preset(scatter_node: Node3D) -> void:
- if not scatter_node:
- return
-
- _populate()
- _scatter_node = scatter_node
- $%NewPresetButton.visible = true
-
- for c in $%PresetsRoot.get_children():
- c.show_save_controls()
-
- popup_centered(_ideal_popup_size)
-
-
-func load_preset(scatter_node: Node3D) -> void:
- if not scatter_node:
- return
-
- _populate()
- _scatter_node = scatter_node
- $%NewPresetButton.visible = false
-
- for c in $%PresetsRoot.get_children():
- c.show_load_controls()
-
- popup_centered(_ideal_popup_size)
-
-
-func load_default(scatter_node: Node3D) -> void:
- pass # TODO
-
-
-func set_editor_plugin(editor_plugin: EditorPlugin) -> void:
- if not editor_plugin:
- return
-
- _editor_file_system = editor_plugin.get_editor_interface().get_resource_filesystem()
-
-
-func _clear():
- for c in $%PresetsRoot.get_children():
- c.queue_free()
-
-
-func _populate() -> void:
- _clear()
- var dir = DirAccess.open(PRESETS_PATH)
- if not dir:
- print_debug("ProtonScatter error: Could not open folder ", PRESETS_PATH)
- return
-
- dir.include_hidden = false
- dir.include_navigational = false
- dir.list_dir_begin()
-
- while true:
- var file := dir.get_next()
- if file == "":
- break
-
- if dir.current_is_dir():
- continue
-
- if not file.ends_with(".tscn") and not file.ends_with(".scn"):
- continue
-
- # Preset found, create an entry
- var full_path = PRESETS_PATH.path_join(file)
- var entry := PresetEntry.instantiate()
- entry.set_preset_name(file.get_basename())
- entry.load_full.connect(_on_load_full_preset.bind(full_path))
- entry.load_stack_only.connect(_on_load_stack_only.bind(full_path))
- entry.delete.connect(_on_delete_preset.bind(full_path, entry))
-
- $%PresetsRoot.add_child(entry)
-
- dir.list_dir_end()
- var full_height = $%PresetsRoot.get_child_count() * 120
- _ideal_popup_size = Vector2i(450, clamp(full_height, 120, 500))
-
-
-func _show_preset_dialog() -> void:
- $%NewPresetName.set_text("")
- $%NewPresetDialog.popup_centered()
-
-
-func _on_new_preset_name_confirmed() -> void:
- var file_name: String = $%NewPresetName.text.to_lower().strip_edges() + ".tscn"
- var full_path := PRESETS_PATH.path_join(file_name)
- _on_save_preset(full_path)
- hide()
-
-
-func _on_save_preset(path) -> void:
- var preset = _scatter_node.duplicate(7)
- preset.clear_output()
- ScatterUtil.set_owner_recursive(preset, preset)
- preset.global_transform.origin = Vector3.ZERO
-
- var packed_scene = PackedScene.new()
- if packed_scene.pack(preset) != OK:
- print_debug("ProtonScatter error: Failed to save preset")
- return
-
- var err = ResourceSaver.save(packed_scene, path)
- if err:
- print_debug("ProtonScatter error: Failed to save preset. Code: ", err)
-
-
-func _on_load_full_preset(path: String) -> void:
- var preset = load(path).instantiate()
- if preset:
- _scatter_node.modifier_stack = preset.modifier_stack.get_copy()
- preset.global_transform = _scatter_node.get_global_transform()
-
- for c in _scatter_node.get_children():
- if c is ScatterItem or c is ScatterShape:
- _scatter_node.remove_child(c)
- c.queue_free()
-
- for c in preset.get_children():
- if c is Marker3D or c.name == "ScatterOutput":
- continue
- preset.remove_child(c)
- _scatter_node.add_child(c, true)
-
- ScatterUtil.set_owner_recursive(_scatter_node, get_tree().get_edited_scene_root())
-
- _scatter_node.rebuild.call_deferred()
- preset.queue_free()
-
- hide()
-
-
-func _on_load_stack_only(path: String) -> void:
- var preset = load(path).instantiate()
- if preset:
- _scatter_node.modifier_stack = preset.modifier_stack.get_copy()
- _scatter_node.rebuild.call_deferred()
- preset.queue_free()
-
- hide()
-
-
-func _on_delete_preset(path: String, entry: Control) -> void:
- DirAccess.remove_absolute(path)
- $%PresetsRoot.remove_child(entry)
- entry.queue_free()
- _editor_file_system.scan() # Refresh the filesystem view
diff --git a/addons/scatter-4/src/presets/presets.tscn b/addons/scatter-4/src/presets/presets.tscn
deleted file mode 100644
index 86d9f92..0000000
--- a/addons/scatter-4/src/presets/presets.tscn
+++ /dev/null
@@ -1,93 +0,0 @@
-[gd_scene load_steps=4 format=3 uid="uid://bcsosdvstytoq"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/presets/presets.gd" id="1_ualle"]
-[ext_resource type="Texture2D" uid="uid://cun73k8jdmr4e" path="res://addons/proton_scatter/icons/add.svg" id="2_j26xt"]
-[ext_resource type="PackedScene" uid="uid://bosqtuvhckh3g" path="res://addons/proton_scatter/src/presets/preset_entry.tscn" id="2_orram"]
-
-[node name="Presets" type="PopupPanel"]
-title = "Manage presets"
-size = Vector2i(490, 200)
-unresizable = false
-borderless = false
-always_on_top = true
-extend_to_title = true
-min_size = Vector2i(400, 150)
-script = ExtResource("1_ualle")
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-offset_left = 4.0
-offset_top = 4.0
-offset_right = 486.0
-offset_bottom = 196.0
-theme_override_constants/margin_left = 12
-theme_override_constants/margin_top = 12
-theme_override_constants/margin_right = 12
-theme_override_constants/margin_bottom = 12
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
-layout_mode = 2
-offset_left = 12.0
-offset_top = 12.0
-offset_right = 470.0
-offset_bottom = 180.0
-
-[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
-layout_mode = 2
-offset_right = 458.0
-offset_bottom = 133.0
-size_flags_vertical = 3
-horizontal_scroll_mode = 0
-
-[node name="PresetsRoot" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 458.0
-offset_bottom = 133.0
-size_flags_horizontal = 3
-size_flags_vertical = 3
-alignment = 1
-
-[node name="PresetEntry" parent="MarginContainer/VBoxContainer/ScrollContainer/PresetsRoot" instance=ExtResource("2_orram")]
-layout_mode = 2
-anchors_preset = 0
-anchor_top = 0.0
-anchor_right = 0.0
-anchor_bottom = 0.0
-offset_top = 6.0
-offset_right = 458.0
-offset_bottom = 126.0
-grow_horizontal = 1
-grow_vertical = 1
-
-[node name="NewPresetButton" type="Button" parent="MarginContainer/VBoxContainer"]
-unique_name_in_owner = true
-custom_minimum_size = Vector2(200, 0)
-layout_mode = 2
-offset_left = 129.0
-offset_top = 137.0
-offset_right = 329.0
-offset_bottom = 168.0
-size_flags_horizontal = 4
-text = "Create new preset"
-icon = ExtResource("2_j26xt")
-
-[node name="NewPresetDialog" type="ConfirmationDialog" parent="."]
-unique_name_in_owner = true
-title = "Create new preset"
-
-[node name="MarginContainer" type="MarginContainer" parent="NewPresetDialog"]
-offset_left = 8.0
-offset_top = 8.0
-offset_right = 192.0
-offset_bottom = 51.0
-
-[node name="NewPresetName" type="LineEdit" parent="NewPresetDialog/MarginContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 6.0
-offset_right = 184.0
-offset_bottom = 37.0
-grow_horizontal = 2
-grow_vertical = 2
-size_flags_vertical = 4
-placeholder_text = "New preset name"
diff --git a/addons/scatter-4/src/scatter.gd b/addons/scatter-4/src/scatter.gd
deleted file mode 100644
index 12474a7..0000000
--- a/addons/scatter-4/src/scatter.gd
+++ /dev/null
@@ -1,397 +0,0 @@
-@tool
-extends Node3D
-
-
-signal shape_changed
-signal thread_completed
-signal build_completed
-
-
-# Includes
-const ProtonScatter := preload("./scatter.gd")
-const ProtonScatterDomain := preload("./common/domain.gd")
-const ProtonScatterItem := preload("./scatter_item.gd")
-const ProtonScatterModifierStack := preload("./stack/modifier_stack.gd")
-const ProtonScatterPhysicsHelper := preload("./common/physics_helper.gd")
-const ProtonScatterShape := preload("./scatter_shape.gd")
-const ProtonScatterTransformList := preload("./common/transform_list.gd")
-const ProtonScatterUtil := preload('./common/scatter_util.gd')
-
-
-@export_category("ProtonScatter")
-
-@export_group("General")
-@export var global_seed := 0:
- set(val):
- global_seed = val
- rebuild()
-@export var show_output_in_tree := false:
- set(val):
- show_output_in_tree = val
- if output_root:
- ProtonScatterUtil.enforce_output_root_owner(self)
-
-@export_group("Performance")
-@export var use_instancing := true:
- set(val):
- use_instancing = val
- full_rebuild(true)
-
-@export_group("Dependency")
-@export var scatter_parent: NodePath:
- set(val):
- if not is_inside_tree():
- scatter_parent = val
- return
-
- scatter_parent = NodePath()
- if is_instance_valid(_dependency_parent):
- _dependency_parent.build_completed.disconnect(rebuild)
- _dependency_parent = null
-
- var node = get_node_or_null(val)
- if not node:
- return
-
- var type = node.get_script()
- var scatter_type = get_script()
- if type != scatter_type:
- push_warning("ProtonScatter warning: Please select a ProtonScatter node as a parent dependency.")
- return
-
- # TODO: Check for cyclic dependency
-
- scatter_parent = val
- _dependency_parent = node
- _dependency_parent.build_completed.connect(rebuild, CONNECT_DEFERRED)
-
-
-@export_group("Debug", "dbg_")
-@export var dbg_disable_thread := false
-
-var undo_redo # EditorUndoRedoManager - Can't type this, class not available outside the editor
-var modifier_stack: ProtonScatterModifierStack:
- set(val):
- if modifier_stack:
- if modifier_stack.value_changed.is_connected(rebuild):
- modifier_stack.value_changed.disconnect(rebuild)
- if modifier_stack.stack_changed.is_connected(rebuild):
- modifier_stack.stack_changed.disconnect(rebuild)
- if modifier_stack.transforms_ready.is_connected(_on_transforms_ready):
- modifier_stack.transforms_ready.disconnect(_on_transforms_ready)
-
- modifier_stack = val.get_copy() # Enfore uniqueness
- modifier_stack.value_changed.connect(rebuild, CONNECT_DEFERRED)
- modifier_stack.stack_changed.connect(rebuild, CONNECT_DEFERRED)
- modifier_stack.transforms_ready.connect(_on_transforms_ready, CONNECT_DEFERRED)
-
-var domain: ProtonScatterDomain:
- set(val):
- domain = ProtonScatterDomain.new() # Enforce uniqueness
-
-var items: Array = []
-var total_item_proportion: int
-var output_root: Marker3D
-
-var editor_plugin # Holds a reference to the EditorPlugin. Used by other parts.
-
-var _thread: Thread
-var _rebuild_queued := false
-var _dependency_parent
-var _physics_helper: ProtonScatterPhysicsHelper
-var _thread_just_started := false
-
-
-func _exit_tree():
- if is_thread_running():
- _thread.wait_to_finish()
- _thread = null
-
-
-func _ready() -> void:
- _perform_sanity_check()
- set_notify_transform(true)
- child_exiting_tree.connect(_on_child_exiting_tree)
-
- # Check if the required nodes exists, if not, create them.
- _discover_items()
- domain.discover_shapes(self)
-
- if items.is_empty():
- var item = ProtonScatterItem.new()
- add_child(item, true)
- item.set_name("ScatterItem")
- item.set_owner(get_tree().get_edited_scene_root())
-
- if domain.is_empty() and not modifier_stack.does_not_require_shapes():
- var shape = ProtonScatterShape.new()
- add_child(shape, true)
- shape.set_owner(get_tree().get_edited_scene_root())
- shape.set_name("ScatterShape")
-
- if not is_instance_valid(_dependency_parent):
- print("in ", name, " calling full rebuild ")
- full_rebuild.call_deferred()
-
-
-func _get_property_list() -> Array:
- var list := []
- list.push_back({
- name = "modifier_stack",
- type = TYPE_OBJECT,
- hint_string = "ScatterModifierStack",
- })
- return list
-
-
-func _get_configuration_warnings() -> PackedStringArray:
- var warnings := PackedStringArray()
- if items.is_empty():
- warnings.push_back("At least one ScatterItem node is required.")
- if domain.is_empty():
- warnings.push_back("At least one ScatterShape node is required.")
- return warnings
-
-
-func _notification(what):
- match what:
- NOTIFICATION_TRANSFORM_CHANGED:
- domain.compute_bounds()
- rebuild()
-
-
-func _set(property, _value):
- if not Engine.is_editor_hint():
- return false
-
- # Workaround to detect when the node was duplicated from the editor.
- if property == "transform":
- _on_node_duplicated.call_deferred()
-
- return false
-
-
-func is_thread_running() -> bool:
- return _thread != null and _thread.is_started()
-
-
-# Used by some modifiers to retrieve a physics helper node
-func get_physics_helper() -> ProtonScatterPhysicsHelper:
- if not is_instance_valid(_physics_helper):
- _physics_helper = ProtonScatterPhysicsHelper.new()
- add_child(_physics_helper)
-
- return _physics_helper
-
-
-# Deletes what the Scatter node generated.
-func clear_output() -> void:
- if not output_root:
- output_root = get_node_or_null("ScatterOutput")
-
- if output_root:
- remove_child(output_root)
- output_root.queue_free()
- output_root = null
-
- ProtonScatterUtil.ensure_output_root_exists(self)
-
-
-func full_rebuild(delayed := false):
- if not is_inside_tree():
- return
-
- update_gizmos()
-
- if delayed:
- await get_tree().process_frame
-
- if is_thread_running():
- _thread.wait_to_finish()
- _thread = null
-
- clear_output()
- _rebuild(true)
-
-
-# A wrapper around the _rebuild function. Ensure it's not called more than once
-# per frame. (Happens when the Scatter node is moved, which triggers the
-# TRANSFORM_CHANGED notification in every children, which in turn notify the
-# parent Scatter node back about the changes.
-func rebuild(force_discover := false) -> void:
- update_gizmos()
-
- if not is_inside_tree():
- return
-
- if is_thread_running():
- _rebuild_queued = true
- return
-
- force_discover = true # TMP while we fix the other issues
- _rebuild(force_discover)
-
-
-# Re compute the desired output.
-# This is the main function, scattering the objects in the scene.
-# Scattered objects are stored under a Marker3D node called "ScatterOutput"
-# DON'T call this function directly outside of the 'rebuild()' function above.
-func _rebuild(force_discover) -> void:
- if force_discover:
- _discover_items()
- domain.discover_shapes(self)
-
- if items.is_empty() or domain.is_empty():
- clear_output()
- push_warning("ProtonScatter warning: No items or shapes, abort")
- return
-
- if not use_instancing:
- clear_output() # TMP, prevents raycasts in modifier to self intersect with previous output
-
- if dbg_disable_thread:
- modifier_stack.start_update(self, domain)
- return
-
- if _thread:
- await _thread.wait_to_finish()
-
- _thread = Thread.new()
- var update_function := modifier_stack.start_update.bind(self, domain.get_copy())
- _thread.start(update_function, Thread.PRIORITY_NORMAL)
-
-
-func _discover_items() -> void:
- items.clear()
- total_item_proportion = 0
-
- for c in get_children():
- if c is ProtonScatterItem:
- items.push_back(c)
- total_item_proportion += c.proportion
-
- if is_inside_tree():
- get_tree().node_configuration_warning_changed.emit(self)
-
-
-# Creates one MultimeshInstance3D for each ScatterItem node.
-func _update_multimeshes(transforms: ProtonScatterTransformList) -> void:
- var offset := 0
- var transforms_count: int = transforms.size()
- var inverse_transform := global_transform.affine_inverse()
-
- for item in items:
- var item_root = ProtonScatterUtil.get_or_create_item_root(item)
- var count = int(round(float(item.proportion) / total_item_proportion * transforms_count))
- var mmi = ProtonScatterUtil.get_or_create_multimesh(item, count)
- if not mmi:
- return
-
- var t: Transform3D
- for i in count:
- # Extra check because of how 'count' is calculated
- if (offset + i) >= transforms_count:
- mmi.multimesh.instance_count = i - 1
- return
-
- t = item.process_transform(transforms.list[offset + i])
- mmi.multimesh.set_instance_transform(i, inverse_transform * t)
-
- offset += count
-
-
-func _update_duplicates(transforms: ProtonScatterTransformList) -> void:
- var offset := 0
- var transforms_count: int = transforms.size()
- var inverse_transform := global_transform.affine_inverse()
-
- for item in items:
- var count = int(round(float(item.proportion) / total_item_proportion * transforms_count))
- var root = ProtonScatterUtil.get_or_create_item_root(item)
- var child_count = root.get_child_count()
-
- for i in count:
- if (offset + i) >= transforms_count:
- return
-
- var instance
- if i < child_count: # Grab an instance from the pool if there's one available
- instance = root.get_child(i)
- else:
- instance = _create_instance(item, root)
-
- if not instance:
- break
-
- var t: Transform3D = item.process_transform(transforms.list[offset + i])
- instance.transform = inverse_transform * t
-
- # Delete the unused instances left in the pool if any
- if count < child_count:
- for i in (child_count - count):
- root.get_child(-1).queue_free()
-
- offset += count
-
-
-func _create_instance(item: ProtonScatterItem, root: Node3D):
- if not item or not item.get_item():
- return null
-
- var instance = item.get_item().duplicate()
- instance.visible = true
- root.add_child.bind(instance, true).call_deferred()
-
- if show_output_in_tree:
- var defer_ownership := func(i, o):
- ProtonScatterUtil.set_owner_recursive(i, o)
- defer_ownership.bind(instance, get_tree().get_edited_scene_root()).call_deferred()
-
- return instance
-
-
-# Enforce the Scatter node has its required variables set.
-func _perform_sanity_check() -> void:
- if not modifier_stack:
- modifier_stack = ProtonScatterModifierStack.new()
-
- if not domain:
- domain = ProtonScatterDomain.new()
-
- scatter_parent = scatter_parent
-
-
-func _on_node_duplicated() -> void:
- clear_output() # Otherwise we get linked multimeshes or other unwanted side effects
- _perform_sanity_check()
-
-
-func _on_child_exiting_tree(node: Node) -> void:
- if node is ProtonScatterShape or node is ProtonScatterItem:
- rebuild.bind(true).call_deferred()
-
-
-# Called when the modifier stack is done generating the full transform list
-func _on_transforms_ready(transforms: ProtonScatterTransformList) -> void:
- if is_thread_running():
- _thread.wait_to_finish()
- _thread = null
-
- if _rebuild_queued:
- _rebuild_queued = false
- rebuild.call_deferred()
- return
-
- if not transforms or transforms.is_empty():
- clear_output()
- update_gizmos()
- return
-
- if use_instancing:
- _update_multimeshes(transforms)
- else:
- _update_duplicates(transforms)
-
- update_gizmos()
- await get_tree().process_frame
- build_completed.emit()
diff --git a/addons/scatter-4/src/scatter_gizmo_plugin.gd b/addons/scatter-4/src/scatter_gizmo_plugin.gd
deleted file mode 100644
index 55dbd53..0000000
--- a/addons/scatter-4/src/scatter_gizmo_plugin.gd
+++ /dev/null
@@ -1,76 +0,0 @@
-@tool
-extends EditorNode3DGizmoPlugin
-
-
-# Gizmo plugin for the ProtonScatter nodes.
-#
-# Displays a loading animation when the node is rebuilding its output
-# Also displays the domain edges if one of its modifiers is using this data.
-
-
-const ProtonScatter := preload("./scatter.gd")
-const LoadingAnimation := preload("../icons/loading/m_loading.tres")
-
-var _panel: Control
-var _loading_mesh: Mesh
-
-
-func _init():
- # TODO: Replace hardcoded colors by a setting fetch
- create_custom_material("line", Color(0.2, 0.4, 0.8))
- add_material("loading", LoadingAnimation)
-
- _loading_mesh = QuadMesh.new()
- _loading_mesh.set_size(Vector2.ONE * 0.15)
-
-
-func _get_gizmo_name() -> String:
- return "ProtonScatter"
-
-
-func _has_gizmo(node) -> bool:
- return node is ProtonScatter
-
-
-func _redraw(gizmo: EditorNode3DGizmo):
- gizmo.clear()
- var node = gizmo.get_node_3d()
-
- if not node.modifier_stack:
- return
-
- if node.is_thread_running():
- gizmo.add_mesh(_loading_mesh, get_material("loading"))
-
- if node.modifier_stack.is_using_edge_data():
- var curves: Array[Curve3D] = node.domain.get_edges()
- var inverse_transform := node.get_global_transform().affine_inverse()
-
- for curve in curves:
- var lines := PackedVector3Array()
- var points: PackedVector3Array = curve.tessellate(4, 8)
- var lines_count := points.size() - 1
-
- for i in lines_count:
- lines.append(inverse_transform * points[i])
- lines.append(inverse_transform * points[i + 1])
-
- gizmo.add_lines(lines, get_material("line"))
-
-
-func set_path_gizmo_panel(panel: Control) -> void:
- _panel = panel
-
-
-# WORKAROUND
-# Creates a standard material displayed on top of everything.
-# Only exists because 'create_material() on_top' parameter doesn't seem to work.
-func create_custom_material(name, color := Color.WHITE):
- var material := StandardMaterial3D.new()
- material.set_blend_mode(StandardMaterial3D.BLEND_MODE_ADD)
- material.set_shading_mode(StandardMaterial3D.SHADING_MODE_UNSHADED)
- material.set_flag(StandardMaterial3D.FLAG_DISABLE_DEPTH_TEST, true)
- material.set_albedo(color)
- material.render_priority = 100
-
- add_material(name, material)
diff --git a/addons/scatter-4/src/scatter_item.gd b/addons/scatter-4/src/scatter_item.gd
deleted file mode 100644
index d166f38..0000000
--- a/addons/scatter-4/src/scatter_item.gd
+++ /dev/null
@@ -1,121 +0,0 @@
-@tool
-extends Node3D
-
-
-const ScatterUtil := preload('./common/scatter_util.gd')
-
-
-@export_category("ScatterItem")
-@export var proportion := 100:
- set(val):
- proportion = val
- ScatterUtil.request_parent_to_rebuild(self)
-
-@export_enum("From current scene", "From disk") var source = 1:
- set(val):
- source = val
- property_list_changed.emit()
-
-@export_group("Source options", "source_")
-@export var source_scale_multiplier := 1.0:
- set(val):
- source_scale_multiplier = val
- ScatterUtil.request_parent_to_rebuild(self)
-
-@export var source_ignore_position := true:
- set(val):
- source_ignore_position = val
- ScatterUtil.request_parent_to_rebuild(self)
-
-@export var source_ignore_rotation := true:
- set(val):
- source_ignore_rotation = val
- ScatterUtil.request_parent_to_rebuild(self)
-
-@export var source_ignore_scale := true:
- set(val):
- source_ignore_scale = val
- ScatterUtil.request_parent_to_rebuild(self)
-
-var path: String:
- set(val):
- path = val
- _target_scene = load(path) if source != 0 else null
- ScatterUtil.request_parent_to_rebuild(self)
-
-var source_position: Vector3
-var source_rotation: Vector3
-var source_scale: Vector3
-
-var _target_scene: PackedScene
-
-
-func _get_property_list() -> Array:
- var list := []
-
- if source == 0:
- list.push_back({
- name = "path",
- type = TYPE_NODE_PATH,
- })
- else:
- list.push_back({
- name = "path",
- type = TYPE_STRING,
- hint = PROPERTY_HINT_FILE,
- })
-
- return list
-
-
-func get_item() -> Node3D:
- if path.is_empty():
- print("in get item, path empty")
- return null
-
- var node: Node3D
-
- if source == 0:
- node = get_node_or_null(path)
- else:
- node = _target_scene.instantiate()
-
- if node:
- _save_source_data(node)
- return node
-
- return null
-
-
-# Takes a transform in input, scale it based on the local scale multiplier
-# If the source transform is not ignored, also copy the source position, rotation and scale.
-# Returns the processed transform
-func process_transform(t: Transform3D) -> Transform3D:
- var origin = t.origin
- t.origin = Vector3.ZERO
-
- t = t.scaled(Vector3.ONE * source_scale_multiplier)
-
- if not source_ignore_scale:
- t = t.scaled(source_scale)
-
- if not source_ignore_rotation:
- t = t.rotated(t.basis.x.normalized(), source_rotation.x)
- t = t.rotated(t.basis.y.normalized(), source_rotation.y)
- t = t.rotated(t.basis.z.normalized(), source_rotation.z)
-
- t.origin = origin
-
- if not source_ignore_position:
- t.origin += source_position
-
- return t
-
-
-func _save_source_data(node: Node3D) -> void:
- if not node:
- return
-
- source_position = node.position
- source_rotation = node.rotation
- source_scale = node.scale
diff --git a/addons/scatter-4/src/scatter_shape.gd b/addons/scatter-4/src/scatter_shape.gd
deleted file mode 100644
index 2f622e3..0000000
--- a/addons/scatter-4/src/scatter_shape.gd
+++ /dev/null
@@ -1,56 +0,0 @@
-@tool
-extends Node3D
-
-
-const ScatterUtil := preload('./common/scatter_util.gd')
-
-
-@export_category("ScatterShape")
-@export var negative = false:
- set(val):
- negative = val
- update_gizmos()
- ScatterUtil.request_parent_to_rebuild(self)
-
-@export var shape: ProtonScatterBaseShape:
- set(val):
- # Disconnect the previous shape if any
- if shape and shape.changed.is_connected(_on_shape_changed):
- shape.changed.disconnect(_on_shape_changed)
-
- shape = val
- if shape:
- shape.changed.connect(_on_shape_changed)
-
- update_gizmos()
- ScatterUtil.request_parent_to_rebuild(self)
-
-
-func _ready() -> void:
- set_notify_transform(true)
-
-
-func _notification(what):
- match what:
- NOTIFICATION_TRANSFORM_CHANGED:
- ScatterUtil.request_parent_to_rebuild(self)
-
-
-func _set(property, _value):
- if not Engine.is_editor_hint():
- return false
-
- # Workaround to detect when the node was duplicated from the editor.
- if property == "transform":
- call_deferred("_on_node_duplicated")
-
- return false
-
-
-func _on_shape_changed() -> void:
- update_gizmos()
- ScatterUtil.request_parent_to_rebuild(self)
-
-
-func _on_node_duplicated() -> void:
- shape = shape.get_copy() # Enfore uniqueness on duplicate, could be an option
diff --git a/addons/scatter-4/src/shapes/base_shape.gd b/addons/scatter-4/src/shapes/base_shape.gd
deleted file mode 100644
index 775812c..0000000
--- a/addons/scatter-4/src/shapes/base_shape.gd
+++ /dev/null
@@ -1,33 +0,0 @@
-@tool
-class_name ProtonScatterBaseShape
-extends Resource
-
-
-func is_point_inside(point_global: Vector3, global_transform: Transform3D) -> bool:
- return false
-
-
-# Returns an array of Vector3. This should contain enough points to compute
-# a bounding box for the given shape.
-func get_corners_global(shape_global_transform: Transform3D) -> Array[Vector3]:
- return []
-
-
-# Returns the closed contour of the shape (closed, inner and outer if
-# applicable) as a 2D polygon.
-# Results in local space relative to the scatter node.
-func get_closed_edges(scatter_gt: Transform3D, shape_gt: Transform3D) -> Array[PackedVector2Array]:
- return []
-
-
-# Returns the open edges (in the case of a regular path, not closed)
-# in local space relative to the scatter node.
-func get_open_edges(scatter_gt: Transform3D, shape_gt: Transform3D) -> Array[Curve3D]:
- return []
-
-
-# Returns a copy of this shape.
-# TODO: check later when Godot4 enters beta if we can get rid of this and use
-# the built-in duplicate() method properly.
-func get_copy() -> Resource:
- return null
diff --git a/addons/scatter-4/src/shapes/box_shape.gd b/addons/scatter-4/src/shapes/box_shape.gd
deleted file mode 100644
index 1cc2ead..0000000
--- a/addons/scatter-4/src/shapes/box_shape.gd
+++ /dev/null
@@ -1,101 +0,0 @@
-@tool
-class_name ProtonScatterBoxShape
-extends ProtonScatterBaseShape
-
-
-@export var size := Vector3.ONE:
- set(val):
- size = val
- _half_size = size * 0.5
- emit_changed()
-
-var _half_size := Vector3.ONE
-
-
-func get_copy():
- var copy = get_script().new()
- copy.size = size
- return copy
-
-
-func is_point_inside(point: Vector3, global_transform: Transform3D) -> bool:
- var position = global_transform * -_half_size
- var local_point = global_transform.affine_inverse() * point
- return AABB(-_half_size, size).has_point(local_point)
-
-
-func get_corners_global(gt: Transform3D) -> Array:
- var res := []
- var corners := [
- 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),
- ]
-
- for c in corners:
- c *= size * 0.5
- res.push_back(gt * c)
-
- return res
-
-
-# Intersection between and box and a plane results in a polygon between 3 and 6
-# vertices.
-# Compute the intersection of each of the 12 edges to the plane, then recompute
-# the polygon from the positions found.
-func get_closed_edges(scatter_gt: Transform3D, shape_gt: Transform3D) -> Array[PackedVector2Array]:
- var polygon := PackedVector2Array()
-
- var a = scatter_gt.basis.x
- var b = scatter_gt.basis.z
- var c = a + b
- var o = scatter_gt.origin
- var plane = Plane(a + o, b + o, c + o)
-
- var box_edges := [
- # Bottom square
- [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)],
-
- # Top square
- [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)],
-
- # Vertical lines
- [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 intersection_points := PackedVector3Array()
- var point
- var gt_inverse := scatter_gt.affine_inverse()
- var shape_gt_inverse := shape_gt.affine_inverse()
-
- for edge in box_edges:
- var p1 = (edge[0] * _half_size) * shape_gt_inverse
- var p2 = (edge[1] * _half_size) * shape_gt_inverse
- point = plane.intersects_segment(p1, p2)
- if point:
- intersection_points.push_back(gt_inverse * point)
-
- if intersection_points.size() < 3:
- return []
-
- var points_unordered := PackedVector2Array()
- for p in intersection_points:
- points_unordered.push_back(Vector2(p.x, p.z))
-
- polygon = Geometry2D.convex_hull(points_unordered)
-
- return [polygon]
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/box_gizmo.gd b/addons/scatter-4/src/shapes/gizmos_plugin/box_gizmo.gd
deleted file mode 100644
index 504894f..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/box_gizmo.gd
+++ /dev/null
@@ -1,135 +0,0 @@
-@tool
-extends "gizmo_handler.gd"
-
-# 3D Gizmo for the Box shape.
-
-
-func get_handle_name(_gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool) -> String:
- return "Box Size"
-
-
-func get_handle_value(gizmo: EditorNode3DGizmo, handle_id: int, _secondary: bool) -> Variant:
- return gizmo.get_node_3d().shape.size
-
-
-func set_handle(gizmo: EditorNode3DGizmo, handle_id: int, _secondary: bool, camera: Camera3D, screen_pos: Vector2) -> void:
- if handle_id < 0 or handle_id > 2:
- return
-
- var axis := Vector3.ZERO
- axis[handle_id] = 1.0 # handle 0:x, 1:y, 2:z
-
- var shape_node = gizmo.get_node_3d()
- var gt := shape_node.get_global_transform()
- var gt_inverse := gt.affine_inverse()
-
- var origin := gt.origin
- var drag_axis := (axis * 4096) * gt_inverse
- var ray_from = camera.project_ray_origin(screen_pos)
- var ray_to = ray_from + camera.project_ray_normal(screen_pos) * 4096
-
- var points = Geometry3D.get_closest_points_between_segments(origin, drag_axis, ray_from, ray_to)
-
- var size = shape_node.shape.size
- size -= axis * size
- var dist = origin.distance_to(points[0]) * 2.0
- size += axis * dist
-
- shape_node.shape.size = size
-
-
-func commit_handle(gizmo: EditorNode3DGizmo, handle_id: int, _secondary: bool, restore: Variant, cancel: bool) -> void:
- var shape: ProtonScatterBoxShape = gizmo.get_node_3d().shape
- if cancel:
- shape.size = restore
- return
-
- _undo_redo.create_action("Set ScatterShape size")
- _undo_redo.add_undo_method(self, "_set_size", shape, restore)
- _undo_redo.add_do_method(self, "_set_size", shape, shape.size)
- _undo_redo.commit_action()
-
-
-func redraw(plugin: EditorNode3DGizmoPlugin, gizmo: EditorNode3DGizmo):
- gizmo.clear()
- var scatter_shape = gizmo.get_node_3d()
- var shape: ProtonScatterBoxShape = scatter_shape.shape
-
- ### Draw the Box lines
- var lines = PackedVector3Array()
- var lines_material := plugin.get_material("primary_top", gizmo)
- var half_size = shape.size * 0.5
-
- var corners := [
- [ # Bottom square
- Vector3(-1, -1, -1),
- Vector3(-1, -1, 1),
- Vector3(1, -1, 1),
- Vector3(1, -1, -1),
- Vector3(-1, -1, -1),
- ],
- [ # Top square
- Vector3(-1, 1, -1),
- Vector3(-1, 1, 1),
- Vector3(1, 1, 1),
- Vector3(1, 1, -1),
- Vector3(-1, 1, -1),
- ],
- [ # Vertical lines
- 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 block_count = corners.size()
- if not is_selected(gizmo):
- block_count = 1
-
- for i in block_count:
- var block = corners[i]
- for j in block.size() - 1:
- lines.push_back(block[j] * half_size)
- lines.push_back(block[j + 1] * half_size)
-
- gizmo.add_lines(lines, lines_material)
- gizmo.add_collision_segments(lines)
-
- ### Fills the box inside
- var mesh = BoxMesh.new()
- mesh.size = shape.size
-
- var mesh_material: StandardMaterial3D
- if scatter_shape.negative:
- mesh_material = plugin.get_material("exclusive", gizmo)
- else:
- mesh_material = plugin.get_material("inclusive", gizmo)
-
- gizmo.add_mesh(mesh, mesh_material)
-
- ### Draw the handles, one for each axis
- var handles := PackedVector3Array()
- var handles_ids := PackedInt32Array()
- var handles_material := plugin.get_material("default_handle", gizmo)
-
- handles.push_back(Vector3.RIGHT * shape.size.x * 0.5)
- handles.push_back(Vector3.UP * shape.size.y * 0.5)
- handles.push_back(Vector3.BACK * shape.size.z * 0.5)
-
- gizmo.add_handles(handles, handles_material, handles_ids)
-
-
-func _set_size(box: ProtonScatterBoxShape, size: Vector3) -> void:
- if box:
- box.size = size
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/components/curve_mode_button_group.tres b/addons/scatter-4/src/shapes/gizmos_plugin/components/curve_mode_button_group.tres
deleted file mode 100644
index f8a67d2..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/components/curve_mode_button_group.tres
+++ /dev/null
@@ -1,3 +0,0 @@
-[gd_resource type="ButtonGroup" format=3 uid="uid://1xy55037k3k5"]
-
-[resource]
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/components/path_advanced_options_panel.tscn b/addons/scatter-4/src/shapes/gizmos_plugin/components/path_advanced_options_panel.tscn
deleted file mode 100644
index c5a44f0..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/components/path_advanced_options_panel.tscn
+++ /dev/null
@@ -1,55 +0,0 @@
-[gd_scene format=3 uid="uid://qb8j7oasuqbc"]
-
-[node name="AdvancedOptionsPanel" type="MarginContainer"]
-offset_right = 221.0
-offset_bottom = 136.0
-grow_horizontal = 2
-size_flags_horizontal = 4
-size_flags_vertical = 4
-metadata/_edit_use_custom_anchors = true
-
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
-offset_right = 221.0
-offset_bottom = 136.0
-
-[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"]
-offset_right = 217.0
-offset_bottom = 136.0
-
-[node name="MirrorLength" type="CheckButton" parent="HBoxContainer/VBoxContainer"]
-offset_right = 217.0
-offset_bottom = 31.0
-focus_mode = 0
-text = "Mirror handles length"
-
-[node name="MirrorAngle" type="CheckButton" parent="HBoxContainer/VBoxContainer"]
-offset_top = 35.0
-offset_right = 217.0
-offset_bottom = 66.0
-focus_mode = 0
-text = "Mirror handles angle"
-
-[node name="LockToPlane" type="CheckButton" parent="HBoxContainer/VBoxContainer"]
-offset_top = 70.0
-offset_right = 217.0
-offset_bottom = 101.0
-focus_mode = 0
-text = "Lock to plane"
-
-[node name="MirrorAngle3" type="CheckButton" parent="HBoxContainer/VBoxContainer"]
-offset_top = 105.0
-offset_right = 217.0
-offset_bottom = 136.0
-focus_mode = 0
-text = "Snap to colliders"
-
-[node name="VSeparator" type="VSeparator" parent="HBoxContainer"]
-visible = false
-offset_left = 221.0
-offset_right = 225.0
-offset_bottom = 136.0
-
-[node name="VBoxContainer2" type="VBoxContainer" parent="HBoxContainer"]
-offset_left = 221.0
-offset_right = 221.0
-offset_bottom = 136.0
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/components/path_panel.gd b/addons/scatter-4/src/shapes/gizmos_plugin/components/path_panel.gd
deleted file mode 100644
index 9d0b735..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/components/path_panel.gd
+++ /dev/null
@@ -1,96 +0,0 @@
-@tool
-extends Control
-
-
-const ScatterShape = preload("../../../scatter_shape.gd")
-const PathShape = preload("../../path_shape.gd")
-
-var shape_node: ScatterShape
-
-@onready var _options_button: Button = $%Options
-@onready var _options_panel: Popup = $%OptionsPanel
-
-
-func _ready() -> void:
- _options_button.toggled.connect(_on_options_button_toggled)
- _options_panel.popup_hide.connect(_on_options_panel_hide)
- $%SnapToColliders.toggled.connect(_on_snap_to_colliders_toggled)
- $%ClosedPath.toggled.connect(_on_closed_path_toggled)
- $%MirrorAngle.toggled.connect(_on_mirror_angle_toggled)
-
- for button in [$%LockToPlane, $%SnapToColliders, $%ClosedPath]:
- button.pressed.connect(_on_button_pressed)
-
-
-# Called by the editor plugin when the node selection changes.
-# Hides the panel when the selected node is not a path shape.
-func selection_changed(selected: Array) -> void:
- if selected.is_empty():
- visible = false
- shape_node = null
- return
-
- var node = selected[0]
- visible = node is ScatterShape and node.shape is PathShape
- if visible:
- shape_node = node
- $%ClosedPath.button_pressed = node.shape.closed
-
-
-func is_select_mode_enabled() -> bool:
- return $%Select.button_pressed
-
-
-func is_create_mode_enabled() -> bool:
- return $%Create.button_pressed
-
-
-func is_delete_mode_enabled() -> bool:
- return $%Delete.button_pressed
-
-
-func is_lock_to_plane_enabled() -> bool:
- return $%LockToPlane.button_pressed and not is_snap_to_colliders_enabled()
-
-
-func is_snap_to_colliders_enabled() -> bool:
- return $%SnapToColliders.button_pressed
-
-
-func is_mirror_length_enabled() -> bool:
- return $%MirrorLength.button_pressed
-
-
-func is_mirror_angle_enabled() -> bool:
- return $%MirrorAngle.button_pressed
-
-
-func _on_options_button_toggled(enabled: bool) -> void:
- if enabled:
- var popup_position := Vector2i(get_global_transform().origin)
- popup_position.y += size.y + 12
- _options_panel.popup(Rect2i(popup_position, Vector2i.ZERO))
- else:
- _options_panel.hide()
-
-
-func _on_options_panel_hide() -> void:
- _options_button.button_pressed = false
-
-
-func _on_mirror_angle_toggled(enabled: bool) -> void:
- $%MirrorLength.disabled = not enabled
-
-
-func _on_snap_to_colliders_toggled(enabled: bool) -> void:
- $%LockToPlane.disabled = enabled
-
-
-func _on_closed_path_toggled(enabled: bool) -> void:
- if shape_node and shape_node.shape is PathShape:
- shape_node.shape.closed = enabled
-
-
-func _on_button_pressed() -> void:
- if shape_node:
- shape_node.update_gizmos()
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/components/path_panel.tscn b/addons/scatter-4/src/shapes/gizmos_plugin/components/path_panel.tscn
deleted file mode 100644
index 01898cc..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/components/path_panel.tscn
+++ /dev/null
@@ -1,163 +0,0 @@
-[gd_scene load_steps=7 format=3 uid="uid://vijpujrvtyin"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/shapes/gizmos_plugin/components/path_panel.gd" id="1_o7kkg"]
-[ext_resource type="Texture2D" uid="uid://b2b62ko7v686h" path="res://addons/proton_scatter/icons/curve_select.svg" id="2_d7o1n"]
-[ext_resource type="ButtonGroup" uid="uid://1xy55037k3k5" path="res://addons/proton_scatter/src/shapes/gizmos_plugin/components/curve_mode_button_group.tres" id="2_sl6yo"]
-[ext_resource type="Texture2D" uid="uid://c31odatai1367" path="res://addons/proton_scatter/icons/curve_create.svg" id="3_l70sn"]
-[ext_resource type="Texture2D" uid="uid://pog84tx0x6ka" path="res://addons/proton_scatter/icons/curve_delete.svg" id="4_b5yum"]
-[ext_resource type="Texture2D" uid="uid://n66mufjib4ds" path="res://addons/proton_scatter/icons/menu.svg" id="6_xiaj2"]
-
-[node name="PathPanel" type="MarginContainer"]
-offset_right = 108.0
-offset_bottom = 24.0
-size_flags_horizontal = 0
-size_flags_vertical = 4
-script = ExtResource("1_o7kkg")
-
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
-layout_mode = 2
-offset_right = 108.0
-offset_bottom = 24.0
-size_flags_horizontal = 4
-size_flags_vertical = 4
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"]
-layout_mode = 2
-offset_right = 80.0
-offset_bottom = 24.0
-
-[node name="Select" type="Button" parent="HBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 24.0
-focus_mode = 0
-toggle_mode = true
-button_pressed = true
-button_group = ExtResource("2_sl6yo")
-icon = ExtResource("2_d7o1n")
-flat = true
-icon_alignment = 1
-
-[node name="Create" type="Button" parent="HBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 28.0
-offset_right = 52.0
-offset_bottom = 24.0
-focus_mode = 0
-toggle_mode = true
-button_group = ExtResource("2_sl6yo")
-icon = ExtResource("3_l70sn")
-flat = true
-icon_alignment = 1
-
-[node name="Delete" type="Button" parent="HBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 56.0
-offset_right = 80.0
-offset_bottom = 24.0
-focus_mode = 0
-toggle_mode = true
-button_group = ExtResource("2_sl6yo")
-icon = ExtResource("4_b5yum")
-flat = true
-icon_alignment = 1
-
-[node name="Options" type="Button" parent="HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 84.0
-offset_right = 108.0
-offset_bottom = 24.0
-focus_mode = 0
-toggle_mode = true
-action_mode = 0
-icon = ExtResource("6_xiaj2")
-flat = true
-icon_alignment = 1
-
-[node name="OptionsPanel" type="PopupPanel" parent="."]
-unique_name_in_owner = true
-size = Vector2i(229, 179)
-
-[node name="AdvancedOptionsPanel" type="MarginContainer" parent="OptionsPanel"]
-offset_left = 4.0
-offset_top = 4.0
-offset_right = 225.0
-offset_bottom = 175.0
-grow_horizontal = 2
-size_flags_horizontal = 4
-size_flags_vertical = 4
-metadata/_edit_use_custom_anchors = true
-
-[node name="HBoxContainer" type="HBoxContainer" parent="OptionsPanel/AdvancedOptionsPanel"]
-layout_mode = 2
-offset_right = 221.0
-offset_bottom = 171.0
-
-[node name="VBoxContainer" type="VBoxContainer" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer"]
-layout_mode = 2
-offset_right = 217.0
-offset_bottom = 171.0
-
-[node name="MirrorAngle" type="CheckButton" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 217.0
-offset_bottom = 31.0
-focus_mode = 0
-button_pressed = true
-text = "Mirror handles angle"
-
-[node name="MirrorLength" type="CheckButton" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 35.0
-offset_right = 217.0
-offset_bottom = 66.0
-focus_mode = 0
-button_pressed = true
-text = "Mirror handles length"
-
-[node name="ClosedPath" type="CheckButton" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 70.0
-offset_right = 217.0
-offset_bottom = 101.0
-focus_mode = 0
-text = "Closed path"
-
-[node name="LockToPlane" type="CheckButton" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 105.0
-offset_right = 217.0
-offset_bottom = 136.0
-focus_mode = 0
-button_pressed = true
-text = "Lock to plane"
-
-[node name="SnapToColliders" type="CheckButton" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 140.0
-offset_right = 217.0
-offset_bottom = 171.0
-focus_mode = 0
-text = "Snap to colliders"
-
-[node name="VSeparator" type="VSeparator" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer"]
-visible = false
-layout_mode = 2
-offset_left = 221.0
-offset_right = 225.0
-offset_bottom = 136.0
-
-[node name="VBoxContainer2" type="VBoxContainer" parent="OptionsPanel/AdvancedOptionsPanel/HBoxContainer"]
-layout_mode = 2
-offset_left = 221.0
-offset_right = 221.0
-offset_bottom = 171.0
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/gizmo_handler.gd b/addons/scatter-4/src/shapes/gizmos_plugin/gizmo_handler.gd
deleted file mode 100644
index cfc8bfd..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/gizmo_handler.gd
+++ /dev/null
@@ -1,50 +0,0 @@
-@tool
-extends RefCounted
-
-# Abstract class.
-
-
-var _undo_redo: EditorUndoRedoManager
-var _plugin: EditorPlugin
-
-
-func set_undo_redo(ur: EditorUndoRedoManager) -> void:
- _undo_redo = ur
-
-
-func set_editor_plugin(plugin: EditorPlugin) -> void:
- _plugin = plugin
-
-
-func get_handle_name(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> String:
- return ""
-
-
-func get_handle_value(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> Variant:
- return null
-
-
-func set_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, camera: Camera3D, screen_pos: Vector2) -> void:
- pass
-
-
-func commit_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, restore: Variant, cancel: bool) -> void:
- pass
-
-
-func redraw(plugin: EditorNode3DGizmoPlugin, gizmo: EditorNode3DGizmo):
- pass
-
-
-func forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> bool:
- return false
-
-
-func is_selected(gizmo: EditorNode3DGizmo) -> bool:
- if not _plugin:
- return true
-
- var current_node = gizmo.get_node_3d()
- var selected_nodes := _plugin.get_editor_interface().get_selection().get_selected_nodes()
-
- return current_node in selected_nodes
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/icons/main_handle.svg b/addons/scatter-4/src/shapes/gizmos_plugin/icons/main_handle.svg
deleted file mode 100644
index d4bd434..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/icons/main_handle.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/icons/secondary_handle.svg b/addons/scatter-4/src/shapes/gizmos_plugin/icons/secondary_handle.svg
deleted file mode 100644
index 1bdf32d..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/icons/secondary_handle.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/path_gizmo.gd b/addons/scatter-4/src/shapes/gizmos_plugin/path_gizmo.gd
deleted file mode 100644
index 823311a..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/path_gizmo.gd
+++ /dev/null
@@ -1,356 +0,0 @@
-@tool
-extends "gizmo_handler.gd"
-
-
-const ProtonScatter := preload("res://addons/proton_scatter/src/scatter.gd")
-const ProtonScatterShape := preload("res://addons/proton_scatter/src/scatter_shape.gd")
-const ProtonScatterEventHelper := preload("res://addons/proton_scatter/src/common/event_helper.gd")
-const PathPanel := preload("./components/path_panel.gd")
-
-var _gizmo_panel: PathPanel
-var _event_helper: ProtonScatterEventHelper
-
-
-func get_handle_name(_gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool) -> String:
- return "Path point"
-
-
-func get_handle_value(gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool) -> Variant:
- var shape: ProtonScatterPathShape = gizmo.get_node_3d().shape
- return shape.get_copy()
-
-
-func set_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, camera: Camera3D, screen_pos: Vector2) -> void:
- if not _gizmo_panel.is_select_mode_enabled():
- return
-
- var shape_node: ProtonScatterShape = gizmo.get_node_3d()
- var curve: Curve3D = shape_node.shape.curve
- var point_count: int = curve.get_point_count()
- var curve_index := handle_id
- var previous_handle_position: Vector3
-
- if not secondary:
- previous_handle_position = curve.get_point_position(curve_index)
- else:
- curve_index = int(handle_id / 2)
- previous_handle_position = curve.get_point_position(curve_index)
- if handle_id % 2 == 0:
- previous_handle_position += curve.get_point_in(curve_index)
- else:
- previous_handle_position += curve.get_point_out(curve_index)
-
- var click_world_position := _intersect_with(shape_node, camera, screen_pos, previous_handle_position)
- var point_local_position: Vector3 = shape_node.get_global_transform().affine_inverse() * click_world_position
-
- if not secondary:
- # Main curve point moved
- curve.set_point_position(handle_id, point_local_position)
- else:
- # In out handle moved
- var mirror_angle := _gizmo_panel.is_mirror_angle_enabled()
- var mirror_length := _gizmo_panel.is_mirror_length_enabled()
-
- var point_origin = curve.get_point_position(curve_index)
- var in_out_position = point_local_position - point_origin
- var mirror_position = -in_out_position
-
- if handle_id % 2 == 0:
- curve.set_point_in(curve_index, in_out_position)
- if mirror_angle:
- if not mirror_length:
- mirror_position = curve.get_point_out(curve_index).length() * -in_out_position.normalized()
- curve.set_point_out(curve_index, mirror_position)
- else:
- curve.set_point_out(curve_index, in_out_position)
- if mirror_angle:
- if not mirror_length:
- mirror_position = curve.get_point_in(curve_index).length() * -in_out_position.normalized()
- curve.set_point_in(curve_index, mirror_position)
-
- shape_node.update_gizmos()
-
-
-func commit_handle(gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool, restore: Variant, cancel: bool) -> void:
- var shape_node: ProtonScatterShape = gizmo.get_node_3d()
-
- if cancel:
- _edit_path(shape_node, restore)
- else:
- _undo_redo.create_action("Edit ScatterShape Path")
- _undo_redo.add_undo_method(self, "_edit_path", shape_node, restore)
- _undo_redo.add_do_method(self, "_edit_path", shape_node, shape_node.shape.get_copy())
- _undo_redo.commit_action()
-
- shape_node.update_gizmos()
-
-
-func redraw(plugin: EditorNode3DGizmoPlugin, gizmo: EditorNode3DGizmo):
- gizmo.clear()
-
- # Force the path panel to appear when the scatter shape type is changed
- # from the inspector.
- if is_selected(gizmo):
- _gizmo_panel.selection_changed([gizmo.get_node_3d()])
-
- var shape_node: ProtonScatterShape = gizmo.get_node_3d()
- var shape: ProtonScatterPathShape = shape_node.shape
-
- if not shape:
- return
-
- var curve: Curve3D = shape.curve
- if not curve or curve.get_point_count() == 0:
- return
-
- # ------ Common stuff ------
- var points := curve.tessellate(4, 8)
- var points_2d := PackedVector2Array()
- for p in points:
- points_2d.push_back(Vector2(p.x, p.z))
-
- var line_material: StandardMaterial3D = plugin.get_material("primary_top", gizmo)
- var mesh_material: StandardMaterial3D = plugin.get_material("inclusive", gizmo)
- if shape_node.negative:
- mesh_material = plugin.get_material("exclusive", gizmo)
-
- # ------ Main line along the path curve ------
- var lines := PackedVector3Array()
- var lines_count := points.size() - 1
-
- for i in lines_count:
- lines.append(points[i])
- lines.append(points[i + 1])
-
- gizmo.add_lines(lines, line_material)
- gizmo.add_collision_segments(lines)
-
- # ------ Draw handles ------
- var main_handles := PackedVector3Array()
- var in_out_handles := PackedVector3Array()
- var handle_lines := PackedVector3Array()
- var ids := PackedInt32Array() # Stays empty on purpose
-
- for i in curve.get_point_count():
- var point_pos = curve.get_point_position(i)
- var point_in = curve.get_point_in(i) + point_pos
- var point_out = curve.get_point_out(i) + point_pos
-
- handle_lines.push_back(point_pos)
- handle_lines.push_back(point_in)
- handle_lines.push_back(point_pos)
- handle_lines.push_back(point_out)
-
- in_out_handles.push_back(point_in)
- in_out_handles.push_back(point_out)
- main_handles.push_back(point_pos)
-
- gizmo.add_handles(main_handles, plugin.get_material("primary_handle", gizmo), ids)
- gizmo.add_handles(in_out_handles, plugin.get_material("secondary_handle", gizmo), ids, false, true)
-
- if is_selected(gizmo):
- gizmo.add_lines(handle_lines, plugin.get_material("secondary_top", gizmo))
-
- # -------- Visual when lock to plane is enabled --------
- if _gizmo_panel.is_lock_to_plane_enabled() and is_selected(gizmo):
- var bounds = shape.get_bounds()
- var aabb = AABB(bounds.min, bounds.size).grow(shape.thickness / 2.0)
-
- var width: float = aabb.size.x
- var length: float = aabb.size.z
- var plane_center: Vector3 = bounds.center
- plane_center.y = 0.0
-
- var plane_mesh := PlaneMesh.new()
- plane_mesh.set_size(Vector2(width, length))
- plane_mesh.set_center_offset(plane_center)
-
- gizmo.add_mesh(plane_mesh, plugin.get_material("tertiary", gizmo))
-
- var plane_lines := PackedVector3Array()
- var corners = [
- Vector3(-width, 0, -length),
- Vector3(-width, 0, length),
- Vector3(width, 0, length),
- Vector3(width, 0, -length),
- Vector3(-width, 0, -length),
- ]
- for i in corners.size() - 1:
- plane_lines.push_back(corners[i] * 0.5 + plane_center)
- plane_lines.push_back(corners[i + 1] * 0.5 + plane_center)
-
- gizmo.add_lines(plane_lines, plugin.get_material("secondary_top", gizmo))
-
- # ----- Mesh representing the inside part of the path -----
- if shape.closed:
- var indices = Geometry2D.triangulate_polygon(points_2d)
- if indices.is_empty():
- indices = Geometry2D.triangulate_delaunay(points_2d)
-
- var st = SurfaceTool.new()
- st.begin(Mesh.PRIMITIVE_TRIANGLES)
- for index in indices:
- var p = points_2d[index]
- st.add_vertex(Vector3(p.x, 0.0, p.y))
-
- var mesh = st.commit()
- gizmo.add_mesh(mesh, mesh_material)
-
- # ------ Mesh representing path thickness ------
- if shape.thickness > 0 and points.size() > 1:
-
- # ____ TODO ____ : check if this whole section could be replaced by
- # Geometry2D.expand_polyline, or an extruded capsule along the path
-
- ## Main path mesh
- var st = SurfaceTool.new()
- st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
-
- for i in points.size() - 1:
- var p1: Vector3 = points[i]
- var p2: Vector3 = points[i + 1]
-
- var normal = (p2 - p1).cross(Vector3.UP).normalized()
- var offset = normal * shape.thickness * 0.5
-
- st.add_vertex(p1 - offset)
- st.add_vertex(p1 + offset)
-
- ## Add the last missing two triangles from the loop above
- var p1: Vector3 = points[-1]
- var p2: Vector3 = points[-2]
- var normal = (p1 - p2).cross(Vector3.UP).normalized()
- var offset = normal * shape.thickness * 0.5
-
- st.add_vertex(p1 - offset)
- st.add_vertex(p1 + offset)
-
- var mesh := st.commit()
- gizmo.add_mesh(mesh, mesh_material)
-
- ## Rounded cap (start)
- st.begin(Mesh.PRIMITIVE_TRIANGLES)
- var center = points[0]
- var next = points[1]
- normal = (center - next).cross(Vector3.UP).normalized()
-
- for i in 12:
- st.add_vertex(center)
- st.add_vertex(center + normal * shape.thickness * 0.5)
- normal = normal.rotated(Vector3.UP, PI / 12)
- st.add_vertex(center + normal * shape.thickness * 0.5)
-
- mesh = st.commit()
- gizmo.add_mesh(mesh, mesh_material)
-
- ## Rounded cap (end)
- st.begin(Mesh.PRIMITIVE_TRIANGLES)
- center = points[-1]
- next = points[-2]
- normal = (next - center).cross(Vector3.UP).normalized()
-
- for i in 12:
- st.add_vertex(center)
- st.add_vertex(center + normal * shape.thickness * 0.5)
- normal = normal.rotated(Vector3.UP, -PI / 12)
- st.add_vertex(center + normal * shape.thickness * 0.5)
-
- mesh = st.commit()
- gizmo.add_mesh(mesh, mesh_material)
-
-
-func forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> bool:
- if not _event_helper:
- _event_helper = ProtonScatterEventHelper.new()
-
- _event_helper.feed(event)
-
- if not event is InputEventMouseButton:
- return false
-
- if not _event_helper.is_key_just_pressed(MOUSE_BUTTON_LEFT): # Can't use just_released here
- return false
-
- var shape_node: ProtonScatterShape = _gizmo_panel.shape_node
- if not shape_node:
- return false
-
- if not shape_node.shape or not shape_node.shape is ProtonScatterPathShape:
- return false
-
- var shape: ProtonScatterPathShape = shape_node.shape
-
- # In select mode, the set_handle and commit_handle functions take over.
- if _gizmo_panel.is_select_mode_enabled():
- return false
-
- var click_world_position := _intersect_with(shape_node, viewport_camera, event.position)
- var point_local_position: Vector3 = shape_node.get_global_transform().affine_inverse() * click_world_position
-
- if _gizmo_panel.is_create_mode_enabled():
- shape.create_point(point_local_position) # TODO: add undo redo
- shape_node.update_gizmos()
- return true
-
- elif _gizmo_panel.is_delete_mode_enabled():
- var index = shape.get_closest_to(point_local_position)
- if index != -1:
- shape.remove_point(index) # TODO: add undo redo
- shape_node.update_gizmos()
- return true
-
- return false
-
-
-func set_gizmo_panel(panel: PathPanel) -> void:
- _gizmo_panel = panel
-
-
-func _edit_path(shape_node: ProtonScatterShape, restore: ProtonScatterPathShape) -> void:
- shape_node.shape.curve = restore.curve.duplicate()
- shape_node.shape.thickness = restore.thickness
- shape_node.update_gizmos()
-
-
-func _intersect_with(path: ProtonScatterShape, camera: Camera3D, screen_point: Vector2, handle_position_local = null) -> Vector3:
- # Get the ray data
- var from = camera.project_ray_origin(screen_point)
- var dir = camera.project_ray_normal(screen_point)
- var gt = path.get_global_transform()
-
- # Snap to collider enabled
- if _gizmo_panel.is_snap_to_colliders_enabled():
- var space_state: PhysicsDirectSpaceState3D = path.get_world_3d().get_direct_space_state()
- var parameters := PhysicsRayQueryParameters3D.new()
- parameters.from = from
- parameters.to = from + (dir * 2048)
- var hit := space_state.intersect_ray(parameters)
- if not hit.is_empty():
- return hit.position
-
- # Lock to plane enabled
- if _gizmo_panel.is_lock_to_plane_enabled():
- var t = Transform3D(gt)
- var a = t.basis.x
- var b = t.basis.z
- var c = a + b
- var o = t.origin
- var plane = Plane(a + o, b + o, c + o)
- var result = plane.intersects_ray(from, dir)
- if result != null:
- return result
-
- # Default case (similar to the built in Path3D node)
- var origin: Vector3
- if handle_position_local:
- origin = gt * handle_position_local
- else:
- origin = path.get_global_transform().origin
-
- var plane = Plane(dir, origin)
- var res = plane.intersects_ray(from, dir)
- if res != null:
- return res
-
- return origin
-
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/shape_gizmo_plugin.gd b/addons/scatter-4/src/shapes/gizmos_plugin/shape_gizmo_plugin.gd
deleted file mode 100644
index d2a704b..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/shape_gizmo_plugin.gd
+++ /dev/null
@@ -1,122 +0,0 @@
-@tool
-extends EditorNode3DGizmoPlugin
-
-
-# Actual logic split in the handler class to avoid cluttering this script as
-# we add extra shapes.
-#
-# Although we could make an actual gizmo per shape type and add the extra type
-# check in the 'has_gizmo' function, it causes more issues to the editor
-# than it's worth (2 fewer files), so it's done like this instead.
-
-
-const ScatterShape = preload("../../scatter_shape.gd")
-const GizmoHandler = preload("./gizmo_handler.gd")
-
-var _handlers: Dictionary
-
-
-func _init():
- var handle_icon = preload("./icons/main_handle.svg")
- var secondary_handle_icon = preload("./icons/secondary_handle.svg")
-
- # TODO: Replace hardcoded colors by a setting fetch
- create_material("primary", Color(1, 0.4, 0))
- create_material("secondary", Color(0.4, 0.7, 1.0))
- create_material("tertiary", Color(Color.STEEL_BLUE, 0.2))
- create_custom_material("primary_top", Color(1, 0.4, 0))
- create_custom_material("secondary_top", Color(0.4, 0.7, 1.0))
- create_custom_material("tertiary_top", Color(Color.STEEL_BLUE, 0.1))
-
- create_material("inclusive", Color(0.9, 0.7, 0.2, 0.15))
- create_material("exclusive", Color(0.9, 0.1, 0.2, 0.15))
-
- create_handle_material("default_handle")
- create_handle_material("primary_handle", false, handle_icon)
- create_handle_material("secondary_handle", false, secondary_handle_icon)
-
- _handlers[ProtonScatterSphereShape] = preload("./sphere_gizmo.gd").new()
- _handlers[ProtonScatterPathShape] = preload("./path_gizmo.gd").new()
- _handlers[ProtonScatterBoxShape] = preload("./box_gizmo.gd").new()
-
-
-func _get_gizmo_name() -> String:
- return "ScatterShape"
-
-
-func _has_gizmo(node) -> bool:
- return node is ScatterShape
-
-
-func _get_handle_name(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> String:
- return _get_handler(gizmo).get_handle_name(gizmo, handle_id, secondary)
-
-
-func _get_handle_value(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool) -> Variant:
- return _get_handler(gizmo).get_handle_value(gizmo, handle_id, secondary)
-
-
-func _set_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, camera: Camera3D, screen_pos: Vector2) -> void:
- _get_handler(gizmo).set_handle(gizmo, handle_id, secondary, camera, screen_pos)
-
-
-func _commit_handle(gizmo: EditorNode3DGizmo, handle_id: int, secondary: bool, restore: Variant, cancel: bool) -> void:
- _get_handler(gizmo).commit_handle(gizmo, handle_id, secondary, restore, cancel)
-
-
-func _redraw(gizmo: EditorNode3DGizmo):
- _get_handler(gizmo).redraw(self, gizmo)
-
-
-func forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> int:
- for handler in _handlers.values():
- if handler.forward_3d_gui_input(viewport_camera, event):
- return EditorPlugin.AFTER_GUI_INPUT_STOP
-
- return EditorPlugin.AFTER_GUI_INPUT_PASS
-
-
-func set_undo_redo(ur: EditorUndoRedoManager) -> void:
- for handler_type in _handlers:
- _handlers[handler_type].set_undo_redo(ur)
-
-
-func set_path_gizmo_panel(panel: Control) -> void:
- if ProtonScatterPathShape in _handlers:
- _handlers[ProtonScatterPathShape].set_gizmo_panel(panel)
-
-
-func set_editor_plugin(plugin: EditorPlugin) -> void:
- for handler_type in _handlers:
- _handlers[handler_type].set_editor_plugin(plugin)
-
-
-# Creates a standard material displayed on top of everything.
-# Only exists because 'create_material() on_top' parameter doesn't seem to work.
-func create_custom_material(name, color := Color.WHITE):
- var material := StandardMaterial3D.new()
- material.set_blend_mode(StandardMaterial3D.BLEND_MODE_ADD)
- material.set_shading_mode(StandardMaterial3D.SHADING_MODE_UNSHADED)
- material.set_flag(StandardMaterial3D.FLAG_DISABLE_DEPTH_TEST, true)
- material.set_albedo(color)
- material.render_priority = 100
-
- add_material(name, material)
-
-
-func _get_handler(gizmo) -> GizmoHandler:
- var null_handler = GizmoHandler.new() # Only so we don't have to check existence later
-
- var shape_node = gizmo.get_node_3d()
- if not shape_node or not shape_node is ScatterShape:
- return null_handler
-
- var shape_resource = shape_node.shape
- if not shape_resource:
- return null_handler
-
- var shape_type = shape_resource.get_script()
- if not shape_type in _handlers:
- return null_handler
-
- return _handlers[shape_type]
diff --git a/addons/scatter-4/src/shapes/gizmos_plugin/sphere_gizmo.gd b/addons/scatter-4/src/shapes/gizmos_plugin/sphere_gizmo.gd
deleted file mode 100644
index 1f7638d..0000000
--- a/addons/scatter-4/src/shapes/gizmos_plugin/sphere_gizmo.gd
+++ /dev/null
@@ -1,96 +0,0 @@
-@tool
-extends "gizmo_handler.gd"
-
-# 3D Gizmo for the Sphere shape. Draws three circle on each axis to represent
-# a sphere, displays one handle on the size to control the radius.
-#
-# (handle_id is ignored in every function since there's a single handle)
-
-const SphereShape = preload("../sphere_shape.gd")
-
-
-func get_handle_name(_gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool) -> String:
- return "Radius"
-
-
-func get_handle_value(gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool) -> Variant:
- return gizmo.get_node_3d().shape.radius
-
-
-func set_handle(gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool, camera: Camera3D, screen_pos: Vector2) -> void:
- var shape_node = gizmo.get_node_3d()
- var gt := shape_node.get_global_transform()
- var gt_inverse := gt.affine_inverse()
- var origin := gt.origin
-
- var ray_from = camera.project_ray_origin(screen_pos)
- var ray_to = ray_from + camera.project_ray_normal(screen_pos) * 4096
- var points = Geometry3D.get_closest_points_between_segments(origin, (Vector3.LEFT * 4096) * gt_inverse, ray_from, ray_to)
- shape_node.shape.radius = origin.distance_to(points[0])
-
-
-func commit_handle(gizmo: EditorNode3DGizmo, _handle_id: int, _secondary: bool, restore: Variant, cancel: bool) -> void:
- var shape: SphereShape = gizmo.get_node_3d().shape
- if cancel:
- shape.radius = restore
- return
-
- _undo_redo.create_action("Set ScatterShape Radius")
- _undo_redo.add_undo_method(self, "_set_radius", shape, restore)
- _undo_redo.add_do_method(self, "_set_radius", shape, shape.radius)
- _undo_redo.commit_action()
-
-
-func redraw(plugin: EditorNode3DGizmoPlugin, gizmo: EditorNode3DGizmo):
- gizmo.clear()
- var scatter_shape = gizmo.get_node_3d()
- var shape: SphereShape = scatter_shape.shape
-
- ### Draw the 3 circles on each axis to represent the sphere
- var lines = PackedVector3Array()
- var lines_material := plugin.get_material("primary_top", gizmo)
- var steps = 32 # TODO: Update based on sphere radius maybe ?
- var step_angle = 2 * PI / steps
- var radius = shape.radius
-
- for i in steps:
- lines.append(Vector3(cos(i * step_angle), 0.0, sin(i * step_angle)) * radius)
- lines.append(Vector3(cos((i + 1) * step_angle), 0.0, sin((i + 1) * step_angle)) * radius)
-
- if is_selected(gizmo):
- for i in steps:
- lines.append(Vector3(cos(i * step_angle), sin(i * step_angle), 0.0) * radius)
- lines.append(Vector3(cos((i + 1) * step_angle), sin((i + 1) * step_angle), 0.0) * radius)
-
- for i in steps:
- lines.append(Vector3(0.0, cos(i * step_angle), sin(i * step_angle)) * radius)
- lines.append(Vector3(0.0, cos((i + 1) * step_angle), sin((i + 1) * step_angle)) * radius)
-
- gizmo.add_lines(lines, lines_material)
- gizmo.add_collision_segments(lines)
-
- ### Draw the handle
- var handles := PackedVector3Array()
- var handles_ids := PackedInt32Array()
- var handles_material := plugin.get_material("default_handle", gizmo)
-
- var handle_position: Vector3 = Vector3.LEFT * radius
- handles.push_back(handle_position)
-
- gizmo.add_handles(handles, handles_material, handles_ids)
-
- ### Fills the sphere inside
- var mesh = SphereMesh.new()
- mesh.height = shape.radius * 2.0
- mesh.radius = shape.radius
- var mesh_material: StandardMaterial3D
- if scatter_shape.negative:
- mesh_material = plugin.get_material("exclusive", gizmo)
- else:
- mesh_material = plugin.get_material("inclusive", gizmo)
- gizmo.add_mesh(mesh, mesh_material)
-
-
-func _set_radius(sphere: SphereShape, radius: float) -> void:
- if sphere:
- sphere.radius = radius
diff --git a/addons/scatter-4/src/shapes/path_shape.gd b/addons/scatter-4/src/shapes/path_shape.gd
deleted file mode 100644
index bb4a5d5..0000000
--- a/addons/scatter-4/src/shapes/path_shape.gd
+++ /dev/null
@@ -1,242 +0,0 @@
-@tool
-class_name ProtonScatterPathShape
-extends ProtonScatterBaseShape
-
-
-const Bounds := preload("../common/bounds.gd")
-
-
-@export var closed := true:
- set(val):
- closed = val
- emit_changed()
-
-@export var thickness := 0.0:
- set(val):
- thickness = max(0, val) # Width cannot be negative
- _half_thickness_squared = pow(thickness * 0.5, 2)
- emit_changed()
-
-@export var curve: Curve3D:
- set(val):
- # Disconnect previous signal
- if curve and curve.changed.is_connected(_on_curve_changed):
- curve.changed.disconnect(_on_curve_changed)
-
- curve = val
- curve.changed.connect(_on_curve_changed)
- emit_changed()
-
-
-var _polygon: PolygonPathFinder
-var _half_thickness_squared: float
-var _bounds: Bounds
-
-
-func is_point_inside(point: Vector3, global_transform: Transform3D) -> bool:
- if not _polygon:
- _update_polygon_from_curve()
-
- if not _polygon:
- return false
-
- point = global_transform.affine_inverse() * point
-
- if thickness > 0:
- var closest_point_on_curve: Vector3 = curve.get_closest_point(point)
- var dist2 = closest_point_on_curve.distance_squared_to(point)
- if dist2 < _half_thickness_squared:
- return true
-
- if closed:
- return _polygon.is_point_inside(Vector2(point.x, point.z))
-
- return false
-
-
-func get_corners_global(gt: Transform3D) -> Array:
- var res := []
-
- if not curve:
- return res
-
- var half_thickness = thickness * 0.5
- var corners = [
- 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 points = curve.tessellate(3, 10)
- for p in points:
- res.push_back(gt * p)
-
- if thickness > 0:
- for offset in corners:
- res.push_back(gt * (p + offset * half_thickness))
-
- return res
-
-
-func get_bounds() -> Bounds:
- if not _bounds:
- _update_polygon_from_curve()
- return _bounds
-
-
-func get_copy():
- var copy = get_script().new()
-
- copy.thickness = thickness
- copy.closed = closed
- if curve:
- copy.curve = curve.duplicate()
-
- return copy
-
-
-func copy_from(source) -> void:
- thickness = source.thickness
- if source.curve:
- curve = source.curve.duplicate() # TODO, update signals
-
-
-# TODO: create points in the middle of the path
-func create_point(position: Vector3) -> void:
- if not curve:
- curve = Curve3D.new()
-
- curve.add_point(position)
-
-
-func remove_point(index):
- if index > curve.get_point_count() - 1:
- return
- curve.remove_point(index)
-
-
-func get_closest_to(position):
- if curve.get_point_count() == 0:
- return -1
-
- var closest = -1
- var dist_squared = -1
-
- for i in curve.get_point_count():
- var point_pos: Vector3 = curve.get_point_position(i)
- var point_dist: float = point_pos.distance_squared_to(position)
-
- if (closest == -1) or (dist_squared > point_dist):
- closest = i
- dist_squared = point_dist
-
- var threshold = 16 # Ignore if the closest point is farther than this
- if dist_squared >= threshold:
- return -1
-
- return closest
-
-
-func get_closed_edges(scatter_gt: Transform3D, shape_gt: Transform3D) -> Array[PackedVector2Array]:
- if not closed and thickness <= 0:
- return []
-
- if not curve:
- return []
-
- var edges: Array[PackedVector2Array] = []
- var polyline := PackedVector2Array()
- var shape_gt_inverse := shape_gt.affine_inverse()
- var scatter_gt_inverse := scatter_gt.affine_inverse()
- var points := curve.tessellate(5, 5) # TODO: find optimal values
-
- for p in points:
- p = p * shape_gt_inverse # Convert to global coords
- p = scatter_gt_inverse * p # convert to scatter local coords
- polyline.push_back(Vector2(p.x, p.z))
-
- # Prevents the polyline to be considered as a hole later.
- if Geometry2D.is_polygon_clockwise(polyline):
- polyline.reverse()
-
- # Expand the polyline to get the outer edge of the path.
- if thickness > 0:
- # WORKAROUND. We cant specify the round end caps resolution, but it's tied to the polyline
- # size. So we scale everything up before calling offset_polyline(), then scale the result
- # down so we get rounder caps.
- var scale = 5.0 * thickness
- var delta = (thickness / 2.0) * scale
-
- var t2 = Transform2D().scaled(Vector2.ONE * scale)
- var result := Geometry2D.offset_polyline(polyline * t2, delta, Geometry2D.JOIN_ROUND, Geometry2D.END_ROUND)
-
- t2 = Transform2D().scaled(Vector2.ONE * (1.0 / scale))
- for polygon in result:
- edges.push_back(polygon * t2)
-
- if closed:
- edges.push_back(polyline)
-
- return edges
-
-
-func get_open_edges(scatter_gt: Transform3D, shape_gt: Transform3D) -> Array[Curve3D]:
- if not curve or closed or thickness > 0:
- return []
-
- var res := Curve3D.new()
- var shape_gt_inverse := shape_gt.affine_inverse()
-
- for i in curve.get_point_count():
- var pos = curve.get_point_position(i)
- var pos_t = pos * shape_gt_inverse
- var p_in = (curve.get_point_in(i) + pos) * shape_gt_inverse - pos_t
- var p_out = (curve.get_point_out(i) + pos) * shape_gt_inverse - pos_t
- res.add_point(pos_t, p_in, p_out)
-
- return [res]
-
-
-func _update_polygon_from_curve() -> void:
- var connections = PackedInt32Array()
- var polygon_points = PackedVector2Array()
-
- if not _bounds:
- _bounds = Bounds.new()
-
- _bounds.clear()
- _polygon = PolygonPathFinder.new()
-
- if not curve:
- curve = Curve3D.new()
-
- if curve.get_point_count() == 0:
- return
-
- var baked_points = curve.tessellate(4, 6)
- var steps := baked_points.size()
-
- for i in baked_points.size():
- var point = baked_points[i]
- var projected_point = Vector2(point.x, point.z)
- _bounds.feed(point)
-
- polygon_points.push_back(projected_point)
- connections.append(i)
- if i == steps - 1:
- connections.append(0)
- else:
- connections.append(i + 1)
-
- _bounds.compute_bounds()
- _polygon.setup(polygon_points, connections)
-
-
-func _on_curve_changed() -> void:
- _update_polygon_from_curve()
- emit_changed()
diff --git a/addons/scatter-4/src/shapes/sphere_shape.gd b/addons/scatter-4/src/shapes/sphere_shape.gd
deleted file mode 100644
index dda0110..0000000
--- a/addons/scatter-4/src/shapes/sphere_shape.gd
+++ /dev/null
@@ -1,78 +0,0 @@
-@tool
-class_name ProtonScatterSphereShape
-extends ProtonScatterBaseShape
-
-
-@export var radius := 1.0:
- set(val):
- radius = val
- _radius_squared = val * val
- emit_changed()
-
-var _radius_squared := 0.0
-
-
-func get_copy():
- var copy = ProtonScatterSphereShape.new()
- copy.radius = radius
- return copy
-
-
-func is_point_inside(point: Vector3, global_transform: Transform3D) -> bool:
- var shape_center = global_transform * Vector3.ZERO
- return shape_center.distance_squared_to(point) < _radius_squared
-
-
-func get_corners_global(gt: Transform3D) -> Array:
- var res := []
-
- var corners := [
- 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),
- ]
-
- for c in corners:
- c *= radius
- res.push_back(gt * c)
-
- return res
-
-
-
-# Returns the circle matching the intersection between the transform's XZ plane
-# and the sphere. Returns an empty array if there's no intersection
-func get_closed_edges(scatter_gt: Transform3D, shape_gt: Transform3D) -> Array[PackedVector2Array]:
- var edge := PackedVector2Array()
-
- var a = scatter_gt.basis.x
- var b = scatter_gt.basis.z
- var c = a + b
- var o = scatter_gt.origin
- var plane = Plane(a + o, b + o, c + o)
-
- var sphere_center := shape_gt.origin
- var dist2plane = plane.distance_to(sphere_center)
- var radius_at_ground_level := sqrt(pow(radius, 2) - pow(dist2plane, 2))
-
- # No intersection with plane
- if radius_at_ground_level <= 0 or radius_at_ground_level > radius:
- return []
-
- var sphere_center_local = scatter_gt.affine_inverse() * sphere_center
-
- var origin := Vector2(sphere_center_local.x, sphere_center_local.z)
- var steps: int = max(16, radius_at_ground_level * 12.0)
- var angle: float = TAU / steps
-
- for i in steps + 1:
- var theta = angle * i
- var point := origin + Vector2(cos(theta), sin(theta)) * radius_at_ground_level
- edge.push_back(point)
-
- return [edge]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/editor_property.gd b/addons/scatter-4/src/stack/inspector_plugin/editor_property.gd
deleted file mode 100644
index 76c8cc3..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/editor_property.gd
+++ /dev/null
@@ -1,15 +0,0 @@
-@tool
-extends EditorProperty
-
-
-var _ui: Control
-
-
-func _init():
- _ui = preload("./ui/stack_panel.tscn").instantiate()
- add_child(_ui)
- set_bottom_editor(_ui)
-
-
-func set_node(object) -> void:
- _ui.set_node(object)
diff --git a/addons/scatter-4/src/stack/inspector_plugin/modifier_stack_plugin.gd b/addons/scatter-4/src/stack/inspector_plugin/modifier_stack_plugin.gd
deleted file mode 100644
index 1694bc5..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/modifier_stack_plugin.gd
+++ /dev/null
@@ -1,19 +0,0 @@
-@tool
-extends EditorInspectorPlugin
-
-
-const Editor = preload("./editor_property.gd")
-const Scatter = preload("../../scatter.gd")
-
-
-func _can_handle(object):
- return object is Scatter
-
-
-func _parse_property(object, type, name, hint_type, hint_string, usage_flags, wide):
- if name == "modifier_stack":
- var editor_property = Editor.new()
- editor_property.set_node(object)
- add_property_editor("modifier_stack", editor_property)
- return true
- return false
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/add_modifier_button.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/add_modifier_button.gd
deleted file mode 100644
index 2d2ecb0..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/add_modifier_button.gd
+++ /dev/null
@@ -1,19 +0,0 @@
-@tool
-extends Button
-
-
-@onready var _popup: PopupPanel = $ModifiersPopup
-
-
-func _ready() -> void:
- _popup.popup_hide.connect(_on_popup_closed)
-
-
-func _toggled(button_pressed):
- if button_pressed:
- _popup.position = global_position + Vector2(0.0, size.y)
- _popup.popup()
-
-
-func _on_popup_closed() -> void:
- button_pressed = false
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/base_parameter.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/base_parameter.gd
deleted file mode 100644
index cdf44ed..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/base_parameter.gd
+++ /dev/null
@@ -1,39 +0,0 @@
-@tool
-extends Control
-
-
-signal value_changed
-
-var _previous
-var _locked := false
-
-
-func set_parameter_name(_text: String) -> void:
- pass
-
-
-func set_hint_string(_hint: String) -> void:
- pass
-
-
-func set_value(val) -> void:
- _locked = true
- _set_value(val)
- _previous = get_value()
- _locked = false
-
-
-func get_value():
- pass
-
-
-func _set_value(_val):
- pass
-
-
-func _on_value_changed(_val) -> void:
- if not _locked:
- var value = get_value()
- if value != _previous:
- value_changed.emit(value, _previous)
- _previous = value
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/bitmask_button.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/bitmask_button.tscn
deleted file mode 100644
index 978b123..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/bitmask_button.tscn
+++ /dev/null
@@ -1,51 +0,0 @@
-[gd_scene load_steps=4 format=3 uid="uid://cf4lrr5tnlwnw"]
-
-[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_lylt6"]
-content_margin_left = 0.0
-content_margin_top = 0.0
-content_margin_right = 0.0
-content_margin_bottom = 0.0
-bg_color = Color(1, 1, 1, 0.54902)
-corner_radius_top_left = 2
-corner_radius_top_right = 2
-corner_radius_bottom_right = 2
-corner_radius_bottom_left = 2
-corner_detail = 6
-
-[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8hejw"]
-content_margin_left = 0.0
-content_margin_top = 0.0
-content_margin_right = 0.0
-content_margin_bottom = 0.0
-bg_color = Color(1, 1, 1, 0.784314)
-corner_radius_top_left = 2
-corner_radius_top_right = 2
-corner_radius_bottom_right = 2
-corner_radius_bottom_left = 2
-corner_detail = 6
-
-[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dmtgy"]
-content_margin_left = 0.0
-content_margin_top = 0.0
-content_margin_right = 0.0
-content_margin_bottom = 0.0
-bg_color = Color(1, 1, 1, 1)
-corner_radius_top_left = 2
-corner_radius_top_right = 2
-corner_radius_bottom_right = 2
-corner_radius_bottom_left = 2
-corner_detail = 6
-
-[node name="Button" type="Button"]
-custom_minimum_size = Vector2(20, 20)
-size_flags_horizontal = 3
-focus_mode = 0
-theme_override_colors/font_color = Color(0, 0, 0, 1)
-theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
-theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
-theme_override_font_sizes/font_size = 12
-theme_override_styles/normal = SubResource("StyleBoxFlat_lylt6")
-theme_override_styles/hover = SubResource("StyleBoxFlat_8hejw")
-theme_override_styles/pressed = SubResource("StyleBoxFlat_dmtgy")
-toggle_mode = true
-text = "00"
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/curve_panel.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/curve_panel.gd
deleted file mode 100644
index 9479118..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/curve_panel.gd
+++ /dev/null
@@ -1,332 +0,0 @@
-# warning-ignore-all:return_value_discarded
-
-@tool
-extends Control
-
-
-signal curve_updated
-
-
-@export var grid_color := Color(1, 1, 1, 0.2)
-@export var grid_color_sub := Color(1, 1, 1, 0.1)
-@export var curve_color := Color(1, 1, 1, 0.9)
-@export var point_color := Color.WHITE
-@export var selected_point_color := Color.ORANGE
-@export var point_radius := 4.0
-@export var text_color := Color(0.9, 0.9, 0.9)
-@export var columns := 4
-@export var rows := 2
-@export var dynamic_row_count := true
-
-var curve: Curve
-var gt: Transform2D
-
-var _hover_point := -1:
- set(val): set_hover(val)
-var _selected_point := -1:
- set(val): set_selected_point(val)
-var _selected_tangent := -1:
- set(val): set_selected_tangent(val)
-var _dragging := false
-var _hover_radius := 50.0 # Squared
-var _tangents_length := 30.0
-var _font: Font
-
-
-func _ready() -> void:
- #rect_min_size.y *= EditorUtil.get_editor_scale()
- var plugin := EditorPlugin.new()
- var theme := plugin.get_editor_interface().get_base_control().get_theme()
- _font = theme.get_font("Main", "EditorFonts")
- plugin.queue_free()
-
- queue_redraw()
- connect("resized", _on_resized)
-
-
-func set_curve(c: Curve) -> void:
- curve = c
- queue_redraw()
-
-
-func get_curve() -> Curve:
- return curve
-
-
-func _gui_input(event) -> void:
- if event is InputEventKey:
- if _selected_point != -1 and event.scancode == KEY_DELETE:
- remove_point(_selected_point)
-
- elif event is InputEventMouseButton:
- if event.doubleclick:
- add_point(_to_curve_space(event.position))
-
- elif event.pressed and event.button_index == MOUSE_BUTTON_MIDDLE:
- var i = get_point_at(event.position)
- if i != -1:
- remove_point(i)
-
- elif event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
- set_selected_tangent(get_tangent_at(event.position))
-
- if _selected_tangent == -1:
- set_selected_point(get_point_at(event.position))
- if _selected_point != -1:
- _dragging = true
-
- elif _dragging and not event.pressed:
- _dragging = false
- emit_signal("curve_updated")
-
- elif event is InputEventMouseMotion:
- if _dragging:
- var curve_amplitude: float = curve.get_max_value() - curve.get_min_value()
-
- # Snap to "round" coordinates when holding Ctrl.
- # Be more precise when holding Shift as well.
- var snap_threshold: float
- if event.control:
- snap_threshold = 0.025 if event.shift else 0.1
- else:
- snap_threshold = 0.0
-
- if _selected_tangent == -1: # Drag point
- var point_pos: Vector2 = _to_curve_space(event.position).snapped(Vector2(snap_threshold, snap_threshold * curve_amplitude))
-
- # The index may change if the point is dragged across another one
- var i: int = curve.set_point_offset(_selected_point, point_pos.x)
- set_hover(i)
- set_selected_point(i)
-
- # This is to prevent the user from losing a point out of view.
- if point_pos.y < curve.get_min_value():
- point_pos.y = curve.get_min_value()
- elif point_pos.y > curve.get_max_value():
- point_pos.y = curve.get_max_value()
-
- curve.set_point_value(_selected_point, point_pos.y)
-
- else: # Drag tangent
- var point_pos: Vector2 = curve.get_point_position(_selected_point)
- var control_pos: Vector2 = _to_curve_space(event.position).snapped(Vector2(snap_threshold, snap_threshold * curve_amplitude))
-
- var dir: Vector2 = (control_pos - point_pos).normalized()
-
- var tangent: float
- if not is_zero_approx(dir.x):
- tangent = dir.y / dir.x
- else:
- tangent = 1 if dir.y >= 0 else -1
- tangent *= 9999
-
- var link: bool = not Input.is_key_pressed(KEY_SHIFT)
-
- if _selected_tangent == 0:
- curve.set_point_left_tangent(_selected_point, tangent)
-
- # Note: if a tangent is set to linear, it shouldn't be linked to the other
- if link and _selected_point != (curve.get_point_count() - 1) and curve.get_point_right_mode(_selected_point) != Curve.TANGENT_LINEAR:
- curve.set_point_right_tangent(_selected_point, tangent)
-
- else:
- curve.set_point_right_tangent(_selected_point, tangent)
-
- if link and _selected_point != 0 and curve.get_point_left_mode(_selected_point) != Curve.TANGENT_LINEAR:
- curve.set_point_left_tangent(_selected_point, tangent)
- queue_redraw()
- else:
- set_hover(get_point_at(event.position))
-
-
-func add_point(pos: Vector2) -> void:
- if not curve:
- return
-
- pos.y = clamp(pos.y, 0.0, 1.0)
- curve.add_point(pos)
- queue_redraw()
- emit_signal("curve_updated")
-
-
-func remove_point(idx: int) -> void:
- if not curve:
- return
-
- if idx == _selected_point:
- set_selected_point(-1)
-
- if idx == _hover_point:
- set_hover(-1)
-
- curve.remove_point(idx)
- queue_redraw()
- emit_signal("curve_updated")
-
-
-func get_point_at(pos: Vector2) -> int:
- if not curve:
- return -1
-
- for i in curve.get_point_count():
- var p := _to_view_space(curve.get_point_position(i))
- if p.distance_squared_to(pos) <= _hover_radius:
- return i
-
- return -1
-
-
-func get_tangent_at(pos: Vector2) -> int:
- if not curve or _selected_point < 0:
- return -1
-
- if _selected_point != 0:
- var control_pos: Vector2 = _get_tangent_view_pos(_selected_point, 0)
- if control_pos.distance_squared_to(pos) < _hover_radius:
- return 0
-
- if _selected_point != curve.get_point_count() - 1:
- var control_pos = _get_tangent_view_pos(_selected_point, 1)
- if control_pos.distance_squared_to(pos) < _hover_radius:
- return 1
-
- return -1
-
-
-func _draw() -> void:
- if not curve:
- return
-
- var text_height = _font.get_height()
- var min_outer := Vector2(0, size.y)
- var max_outer := Vector2(size.x, 0)
- var min_inner := Vector2(text_height, size.y - text_height)
- var max_inner := Vector2(size.x - text_height, text_height)
-
- var width: float = max_inner.x - min_inner.x
- var height: float = max_inner.y - min_inner.y
-
- var curve_min: float = curve.get_min_value()
- var curve_max: float = curve.get_max_value()
-
-
- # Main area
- draw_line(Vector2(0, max_inner.y), Vector2(max_outer.x, max_inner.y), grid_color)
- draw_line(Vector2(0, min_inner.y), Vector2(max_outer.x, min_inner.y), grid_color)
- draw_line(Vector2(min_inner.x, max_outer.y), Vector2(min_inner.x, min_outer.y), grid_color)
- draw_line(Vector2(max_inner.x, max_outer.y), Vector2(max_inner.x, min_outer.y), grid_color)
-
- # Grid and scale
- ## Vertical lines
- var x_offset = 1.0 / columns
- var margin = 4
-
- for i in columns + 1:
- var x = width * (i * x_offset) + min_inner.x
- draw_line(Vector2(x, max_outer.y), Vector2(x, min_outer.y), grid_color_sub)
- draw_string(_font, Vector2(x + margin, min_outer.y - margin), str(snapped(i * x_offset, 0.01)), 0, -1, -1, text_color)
-
- ## Horizontal lines
- var y_offset = 1.0 / rows
-
- for i in rows + 1:
- var y = height * (i * y_offset) + min_inner.y
- draw_line(Vector2(min_outer.x, y), Vector2(max_outer.x, y), grid_color_sub)
- var y_value = i * ((curve_max - curve_min) / rows) + curve_min
- draw_string(_font, Vector2(min_inner.x + margin, y - margin), str(snapped(y_value, 0.01)), 0, -1, -1, text_color)
-
- # Plot curve
- var steps = 100
- var offset = 1.0 / steps
- x_offset = width / steps
-
- var a: float
- var a_y: float
- var b: float
- var b_y: float
-
- a = curve.sample_baked(0.0)
- a_y = remap(a, curve_min, curve_max, min_inner.y, max_inner.y)
-
- for i in steps - 1:
- b = curve.sample_baked((i + 1) * offset)
- b_y = remap(b, curve_min, curve_max, min_inner.y, max_inner.y)
- draw_line(Vector2(min_inner.x + x_offset * i, a_y), Vector2(min_inner.x + x_offset * (i + 1), b_y), curve_color)
- a_y = b_y
-
- # Draw points
- for i in curve.get_point_count():
- var pos: Vector2 = _to_view_space(curve.get_point_position(i))
- if _selected_point == i:
- draw_circle(pos, point_radius, selected_point_color)
- else:
- draw_circle(pos, point_radius, point_color);
-
- if _hover_point == i:
- draw_arc(pos, point_radius + 4.0, 0.0, 2 * PI, 12, point_color, 1.0, true)
-
- # Draw tangents
- if _selected_point >= 0:
- var i: int = _selected_point
- var pos: Vector2 = _to_view_space(curve.get_point_position(i))
-
- if i != 0:
- var control_pos: Vector2 = _get_tangent_view_pos(i, 0)
- draw_line(pos, control_pos, selected_point_color)
- draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), selected_point_color)
-
- if i != curve.get_point_count() - 1:
- var control_pos: Vector2 = _get_tangent_view_pos(i, 1)
- draw_line(pos, control_pos, selected_point_color)
- draw_rect(Rect2(control_pos, Vector2(1, 1)).grow(2), selected_point_color)
-
-
-func _to_view_space(pos: Vector2) -> Vector2:
- var h = _font.get_height()
- pos.x = remap(pos.x, 0.0, 1.0, h, size.x - h)
- pos.y = remap(pos.y, curve.get_min_value(), curve.get_max_value(), size.y - h, h)
- return pos
-
-
-func _to_curve_space(pos: Vector2) -> Vector2:
- var h = _font.get_height()
- pos.x = remap(pos.x, h, size.x - h, 0.0, 1.0)
- pos.y = remap(pos.y, size.y - h, h, curve.get_min_value(), curve.get_max_value())
- return pos
-
-
-func _get_tangent_view_pos(i: int, tangent: int) -> Vector2:
- var dir: Vector2
-
- if tangent == 0:
- dir = -Vector2(1.0, curve.get_point_left_tangent(i))
- else:
- dir = Vector2(1.0, curve.get_point_right_tangent(i))
-
- var point_pos = _to_view_space(curve.get_point_position(i))
- var control_pos = _to_view_space(curve.get_point_position(i) + dir)
-
- return point_pos + _tangents_length * (control_pos - point_pos).normalized()
-
-
-func set_hover(val: int) -> void:
- if val != _hover_point:
- _hover_point = val
- queue_redraw()
-
-
-func set_selected_point(val: int) -> void:
- if val != _selected_point:
- _selected_point = val
- queue_redraw()
-
-
-func set_selected_tangent(val: int) -> void:
- if val != _selected_tangent:
- _selected_tangent = val
- queue_redraw()
-
-
-func _on_resized() -> void:
- if dynamic_row_count:
- rows = (int(size.y / custom_minimum_size.y) + 1) * 2
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.gd
deleted file mode 100644
index fda5f0c..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.gd
+++ /dev/null
@@ -1,23 +0,0 @@
-@tool
-extends "../base_parameter.gd"
-
-
-var _button
-
-
-func _ready() -> void:
- _button = get_node("Button")
- _button.toggled.connect(_on_value_changed)
-
-
-func enable(enabled: bool) -> void:
- _button.disabled = not enabled
- _button.flat = not enabled
-
-
-func get_value() -> bool:
- return _button.button_pressed
-
-
-func _set_value(val: bool) -> void:
- _button.button_pressed = val
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.tscn
deleted file mode 100644
index 12dab18..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.tscn
+++ /dev/null
@@ -1,19 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://w6ycb4oveqhd"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.gd" id="1_f6puy"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/toggle_button.gd" id="2_167vc"]
-
-[node name="MarginContainer" type="MarginContainer"]
-offset_right = 40.0
-offset_bottom = 40.0
-size_flags_horizontal = 4
-size_flags_vertical = 4
-script = ExtResource( "1_f6puy" )
-
-[node name="Button" type="Button" parent="."]
-offset_right = 40.0
-offset_bottom = 40.0
-focus_mode = 0
-toggle_mode = true
-icon_alignment = 1
-script = ExtResource( "2_167vc" )
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.gd
deleted file mode 100644
index 688f19a..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.gd
+++ /dev/null
@@ -1,17 +0,0 @@
-@tool
-extends "../base_parameter.gd"
-
-
-@onready var _spinbox = $SpinBox
-
-
-func _ready() -> void:
- _spinbox.value_changed.connect(_on_value_changed)
-
-
-func get_value() -> int:
- return int(_spinbox.get_value())
-
-
-func _set_value(val: int) -> void:
- _spinbox.set_value(val)
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.tscn
deleted file mode 100644
index 76175e5..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.tscn
+++ /dev/null
@@ -1,17 +0,0 @@
-[gd_scene load_steps=2 format=3 uid="uid://c36gqn03pvlnr"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.gd" id="1_f0oq6"]
-
-[node name="ParameterSpinbox" type="MarginContainer"]
-offset_right = 83.0625
-offset_bottom = 31.0
-size_flags_horizontal = 4
-size_flags_vertical = 4
-script = ExtResource( "1_f0oq6" )
-
-[node name="SpinBox" type="SpinBox" parent="."]
-offset_right = 83.0
-offset_bottom = 31.0
-min_value = -100.0
-allow_greater = true
-allow_lesser = true
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.gd
deleted file mode 100644
index c32279a..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.gd
+++ /dev/null
@@ -1,131 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $Label
-@onready var _grid_1: Control = $%GridContainer1
-@onready var _grid_2: Control = $%GridContainer2
-@onready var _grid_3: Control = $%GridContainer3
-@onready var _grid_4: Control = $%GridContainer4
-@onready var _menu_button: MenuButton = $%MenuButton
-
-var _buttons: Array[Button]
-var _popup: PopupMenu
-var _layer_count := 32
-
-
-func _ready() -> void:
- _buttons = []
- var grids = [_grid_1, _grid_2, _grid_3, _grid_4]
-
- for g in grids:
- for c in g.get_children():
- if c is Button:
- var layer_number = c.text.to_int()
- if layer_number > _layer_count:
- c.visible = false
- continue
- _buttons.push_front(c)
- c.focus_mode = Control.FOCUS_NONE
- c.pressed.connect(_on_button_pressed)
-
- _popup = _menu_button.get_popup()
- _popup.clear()
-
- var layer_name := ""
- for i in _layer_count:
- if i != 0 and i % 4 == 0:
- _popup.add_separator("", 100 + i)
-
- layer_name = ProjectSettings.get_setting("layer_names/3d_physics/layer_" + str(i + 1))
- if layer_name.is_empty():
- layer_name = "Layer " + str(i + 1)
- _popup.add_check_item(layer_name, _layer_count - 1 - i)
-
- _sync_popup_state()
- _popup.id_pressed.connect(_on_id_pressed)
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func _set_value(val: int) -> void:
- var binary_string: String = _dec2bin(val)
- var length = binary_string.length()
-
- if length < _layer_count:
- binary_string = binary_string.pad_zeros(_layer_count)
- elif length > _layer_count:
- binary_string = binary_string.substr(length - _layer_count, length)
-
- for i in _layer_count:
- _buttons[i].button_pressed = binary_string[i] == "1"
-
- _sync_popup_state()
-
-
-func get_value() -> int:
- var binary_string = ""
- for b in _buttons:
- binary_string += "1" if b.button_pressed else "0"
-
- var val = _bin2dec(binary_string)
- return val
-
-
-func _dec2bin(value: int) -> String:
- if value == 0:
- return "0"
-
- var binary_string = ""
- while value != 0:
- var m = value % 2
- binary_string = str(m) + binary_string
- # warning-ignore:integer_division
- value = value / 2
-
- return binary_string
-
-
-func _bin2dec(binary_string: String) -> int:
- var decimal_value = 0
- var count = binary_string.length() - 1
-
- for i in binary_string.length():
- decimal_value += pow(2, count) * binary_string[i].to_int()
- count -= 1
-
- return decimal_value
-
-
-func _sync_popup_state() -> void:
- if not _popup:
- return
-
- for i in _layer_count:
- var idx = _popup.get_item_index(i)
- _popup.set_item_checked(idx, _buttons[i].button_pressed)
-
-
-func _on_button_pressed() -> void:
- _on_value_changed(null)
- _sync_popup_state()
-
-
-func _on_id_pressed(id: int) -> void:
- var idx = _popup.get_item_index(id)
- var checked = not _popup.is_item_checked(idx)
- _buttons[id].button_pressed = checked
- _popup.set_item_checked(idx, checked)
- _on_button_pressed()
-
-
-func _on_enable_all_pressed() -> void:
- _set_value(4294967295)
- _on_value_changed(null)
-
-
-func _on_clear_pressed() -> void:
- _set_value(0)
- _on_value_changed(null)
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.tscn
deleted file mode 100644
index 73e0361..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.tscn
+++ /dev/null
@@ -1,436 +0,0 @@
-[gd_scene load_steps=6 format=3 uid="uid://chondv2lhs4pl"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_bitmask.gd" id="1"]
-[ext_resource type="PackedScene" uid="uid://cf4lrr5tnlwnw" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/bitmask_button.tscn" id="2"]
-[ext_resource type="Texture2D" uid="uid://n66mufjib4ds" path="res://addons/proton_scatter/icons/menu.svg" id="3"]
-[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="4"]
-[ext_resource type="Texture2D" uid="uid://bb6107shgmiaw" path="res://addons/proton_scatter/icons/select_all.svg" id="4_h30jm"]
-
-[node name="parameter_bitmask" type="VBoxContainer"]
-anchor_right = 1.0
-offset_bottom = 178.0
-script = ExtResource("1")
-
-[node name="Label" type="Label" parent="."]
-offset_right = 1024.0
-offset_bottom = 26.0
-text = "Parameter name"
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-offset_top = 30.0
-offset_right = 1024.0
-offset_bottom = 130.0
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
-offset_right = 1024.0
-offset_bottom = 100.0
-alignment = 2
-
-[node name="MenuButton" type="MenuButton" parent="MarginContainer/HBoxContainer"]
-unique_name_in_owner = true
-offset_left = 772.0
-offset_right = 796.0
-offset_bottom = 100.0
-icon = ExtResource("3")
-item_count = 39
-popup/item_0/text = "Layer 1"
-popup/item_0/checkable = 1
-popup/item_0/id = 31
-popup/item_1/text = "Layer 2"
-popup/item_1/checkable = 1
-popup/item_1/id = 30
-popup/item_2/text = "Layer 3"
-popup/item_2/checkable = 1
-popup/item_2/id = 29
-popup/item_3/text = "Layer 4"
-popup/item_3/checkable = 1
-popup/item_3/id = 28
-popup/item_4/text = ""
-popup/item_4/id = 104
-popup/item_4/separator = true
-popup/item_5/text = "Layer 5"
-popup/item_5/checkable = 1
-popup/item_5/id = 27
-popup/item_6/text = "Layer 6"
-popup/item_6/checkable = 1
-popup/item_6/id = 26
-popup/item_7/text = "Layer 7"
-popup/item_7/checkable = 1
-popup/item_7/id = 25
-popup/item_8/text = "Layer 8"
-popup/item_8/checkable = 1
-popup/item_8/id = 24
-popup/item_9/text = ""
-popup/item_9/id = 108
-popup/item_9/separator = true
-popup/item_10/text = "Layer 9"
-popup/item_10/checkable = 1
-popup/item_10/id = 23
-popup/item_11/text = "Layer 10"
-popup/item_11/checkable = 1
-popup/item_11/id = 22
-popup/item_12/text = "Layer 11"
-popup/item_12/checkable = 1
-popup/item_12/id = 21
-popup/item_13/text = "Layer 12"
-popup/item_13/checkable = 1
-popup/item_13/id = 20
-popup/item_14/text = ""
-popup/item_14/id = 112
-popup/item_14/separator = true
-popup/item_15/text = "Layer 13"
-popup/item_15/checkable = 1
-popup/item_15/id = 19
-popup/item_16/text = "Layer 14"
-popup/item_16/checkable = 1
-popup/item_16/id = 18
-popup/item_17/text = "Layer 15"
-popup/item_17/checkable = 1
-popup/item_17/id = 17
-popup/item_18/text = "Layer 16"
-popup/item_18/checkable = 1
-popup/item_18/id = 16
-popup/item_19/text = ""
-popup/item_19/id = 116
-popup/item_19/separator = true
-popup/item_20/text = "Layer 17"
-popup/item_20/checkable = 1
-popup/item_20/id = 15
-popup/item_21/text = "Layer 18"
-popup/item_21/checkable = 1
-popup/item_21/id = 14
-popup/item_22/text = "Layer 19"
-popup/item_22/checkable = 1
-popup/item_22/id = 13
-popup/item_23/text = "Layer 20"
-popup/item_23/checkable = 1
-popup/item_23/id = 12
-popup/item_24/text = ""
-popup/item_24/id = 120
-popup/item_24/separator = true
-popup/item_25/text = "Layer 21"
-popup/item_25/checkable = 1
-popup/item_25/id = 11
-popup/item_26/text = "Layer 22"
-popup/item_26/checkable = 1
-popup/item_26/id = 10
-popup/item_27/text = "Layer 23"
-popup/item_27/checkable = 1
-popup/item_27/id = 9
-popup/item_28/text = "Layer 24"
-popup/item_28/checkable = 1
-popup/item_28/id = 8
-popup/item_29/text = ""
-popup/item_29/id = 124
-popup/item_29/separator = true
-popup/item_30/text = "Layer 25"
-popup/item_30/checkable = 1
-popup/item_30/id = 7
-popup/item_31/text = "Layer 26"
-popup/item_31/checkable = 1
-popup/item_31/id = 6
-popup/item_32/text = "Layer 27"
-popup/item_32/checkable = 1
-popup/item_32/id = 5
-popup/item_33/text = "Layer 28"
-popup/item_33/checkable = 1
-popup/item_33/id = 4
-popup/item_34/text = ""
-popup/item_34/id = 128
-popup/item_34/separator = true
-popup/item_35/text = "Layer 29"
-popup/item_35/checkable = 1
-popup/item_35/id = 3
-popup/item_36/text = "Layer 30"
-popup/item_36/checkable = 1
-popup/item_36/id = 2
-popup/item_37/text = "Layer 31"
-popup/item_37/checkable = 1
-popup/item_37/id = 1
-popup/item_38/text = "Layer 32"
-popup/item_38/checkable = 1
-popup/item_38/id = 0
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
-offset_left = 800.0
-offset_right = 996.0
-offset_bottom = 100.0
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
-offset_right = 196.0
-offset_bottom = 44.0
-
-[node name="GridContainer1" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-offset_right = 92.0
-offset_bottom = 44.0
-columns = 4
-
-[node name="Button1" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_right = 20.0
-offset_bottom = 20.0
-text = "1"
-
-[node name="Button2" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_left = 24.0
-offset_right = 44.0
-offset_bottom = 20.0
-text = "2"
-
-[node name="Button3" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_left = 48.0
-offset_right = 68.0
-offset_bottom = 20.0
-text = "3"
-
-[node name="Button4" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_left = 72.0
-offset_right = 92.0
-offset_bottom = 20.0
-text = "4"
-
-[node name="Button5" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_top = 24.0
-offset_right = 20.0
-offset_bottom = 44.0
-text = "5"
-
-[node name="Button6" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_left = 24.0
-offset_top = 24.0
-offset_right = 44.0
-offset_bottom = 44.0
-text = "6"
-
-[node name="Button7" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_left = 48.0
-offset_top = 24.0
-offset_right = 68.0
-offset_bottom = 44.0
-text = "7"
-
-[node name="Button8" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer1" instance=ExtResource("2")]
-offset_left = 72.0
-offset_top = 24.0
-offset_right = 92.0
-offset_bottom = 44.0
-text = "8"
-
-[node name="VSeparator" type="VSeparator" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer"]
-offset_left = 96.0
-offset_right = 100.0
-offset_bottom = 44.0
-
-[node name="GridContainer2" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-offset_left = 104.0
-offset_right = 196.0
-offset_bottom = 44.0
-columns = 4
-
-[node name="Button9" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_right = 20.0
-offset_bottom = 20.0
-text = "9"
-
-[node name="Button10" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_left = 24.0
-offset_right = 44.0
-offset_bottom = 20.0
-text = "10"
-
-[node name="Button11" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_left = 48.0
-offset_right = 68.0
-offset_bottom = 20.0
-text = "11"
-
-[node name="Button12" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_left = 72.0
-offset_right = 92.0
-offset_bottom = 20.0
-text = "12"
-
-[node name="Button13" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_top = 24.0
-offset_right = 20.0
-offset_bottom = 44.0
-text = "13"
-
-[node name="Button14" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_left = 24.0
-offset_top = 24.0
-offset_right = 44.0
-offset_bottom = 44.0
-text = "14"
-
-[node name="Button15" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_left = 48.0
-offset_top = 24.0
-offset_right = 68.0
-offset_bottom = 44.0
-text = "15"
-
-[node name="Button16" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer/GridContainer2" instance=ExtResource("2")]
-offset_left = 72.0
-offset_top = 24.0
-offset_right = 92.0
-offset_bottom = 44.0
-text = "16"
-
-[node name="HSeparator" type="HSeparator" parent="MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 48.0
-offset_right = 196.0
-offset_bottom = 52.0
-
-[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 56.0
-offset_right = 196.0
-offset_bottom = 100.0
-
-[node name="GridContainer3" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2"]
-unique_name_in_owner = true
-offset_right = 92.0
-offset_bottom = 44.0
-columns = 4
-
-[node name="Button17" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_right = 20.0
-offset_bottom = 20.0
-text = "17"
-
-[node name="Button18" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_left = 24.0
-offset_right = 44.0
-offset_bottom = 20.0
-text = "18"
-
-[node name="Button19" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_left = 48.0
-offset_right = 68.0
-offset_bottom = 20.0
-text = "19"
-
-[node name="Button20" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_left = 72.0
-offset_right = 92.0
-offset_bottom = 20.0
-text = "20"
-
-[node name="Button21" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_top = 24.0
-offset_right = 20.0
-offset_bottom = 44.0
-text = "21"
-
-[node name="Button22" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_left = 24.0
-offset_top = 24.0
-offset_right = 44.0
-offset_bottom = 44.0
-text = "22"
-
-[node name="Button23" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_left = 48.0
-offset_top = 24.0
-offset_right = 68.0
-offset_bottom = 44.0
-text = "23"
-
-[node name="Button24" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer3" instance=ExtResource("2")]
-offset_left = 72.0
-offset_top = 24.0
-offset_right = 92.0
-offset_bottom = 44.0
-text = "24"
-
-[node name="VSeparator2" type="VSeparator" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2"]
-offset_left = 96.0
-offset_right = 100.0
-offset_bottom = 44.0
-
-[node name="GridContainer4" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2"]
-unique_name_in_owner = true
-offset_left = 104.0
-offset_right = 196.0
-offset_bottom = 44.0
-columns = 4
-
-[node name="Button25" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_right = 20.0
-offset_bottom = 20.0
-text = "25"
-
-[node name="Button26" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_left = 24.0
-offset_right = 44.0
-offset_bottom = 20.0
-text = "26"
-
-[node name="Button27" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_left = 48.0
-offset_right = 68.0
-offset_bottom = 20.0
-text = "27"
-
-[node name="Button28" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_left = 72.0
-offset_right = 92.0
-offset_bottom = 20.0
-text = "28"
-
-[node name="Button29" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_top = 24.0
-offset_right = 20.0
-offset_bottom = 44.0
-text = "29"
-
-[node name="Button30" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_left = 24.0
-offset_top = 24.0
-offset_right = 44.0
-offset_bottom = 44.0
-text = "30"
-
-[node name="Button31" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_left = 48.0
-offset_top = 24.0
-offset_right = 68.0
-offset_bottom = 44.0
-text = "31"
-
-[node name="Button32" parent="MarginContainer/HBoxContainer/VBoxContainer/HBoxContainer2/GridContainer4" instance=ExtResource("2")]
-offset_left = 72.0
-offset_top = 24.0
-offset_right = 92.0
-offset_bottom = 44.0
-text = "32"
-
-[node name="VBoxContainer2" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
-offset_left = 1000.0
-offset_right = 1024.0
-offset_bottom = 100.0
-alignment = 1
-
-[node name="EnableAll" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer2"]
-offset_right = 24.0
-offset_bottom = 48.0
-size_flags_vertical = 3
-hint_tooltip = "Enable all layers"
-focus_mode = 0
-icon = ExtResource("4_h30jm")
-flat = true
-expand_icon = true
-
-[node name="ClearButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer2"]
-offset_top = 52.0
-offset_right = 24.0
-offset_bottom = 100.0
-size_flags_vertical = 3
-hint_tooltip = "Clear all layers"
-focus_mode = 0
-icon = ExtResource("4")
-flat = true
-
-[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer2/EnableAll" to="." method="_on_enable_all_pressed"]
-[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer2/ClearButton" to="." method="_on_clear_pressed"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.gd
deleted file mode 100644
index cdd2f2f..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.gd
+++ /dev/null
@@ -1,23 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $Label
-@onready var _check_box: CheckBox = $CheckBox
-
-
-func _ready() -> void:
- # warning-ignore:return_value_discarded
- _check_box.connect("toggled", _on_value_changed)
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func get_value() -> bool:
- return _check_box.button_pressed
-
-
-func _set_value(val: bool) -> void:
- _check_box.button_pressed = val
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.tscn
deleted file mode 100644
index 49aa944..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.tscn
+++ /dev/null
@@ -1,21 +0,0 @@
-[gd_scene load_steps=2 format=3 uid="uid://10wqs13p5i3d"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_bool.gd" id="1"]
-
-[node name="ParameterScalar" type="HBoxContainer"]
-anchor_right = 1.0
-script = ExtResource( "1" )
-
-[node name="Label" type="Label" parent="."]
-offset_top = 2.0
-offset_right = 996.0
-offset_bottom = 28.0
-size_flags_horizontal = 3
-text = "Parameter name"
-
-[node name="CheckBox" type="CheckBox" parent="."]
-offset_left = 1000.0
-offset_right = 1024.0
-offset_bottom = 31.0
-focus_mode = 0
-mouse_filter = 1
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.gd
deleted file mode 100644
index 763002e..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.gd
+++ /dev/null
@@ -1,26 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-const Util = preload("../../../../../common/util.gd")
-
-
-@onready var _label: Label = $Label
-@onready var _panel: Control = $MarginContainer/CurvePanel
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func get_value() -> String:
- return Util.curve_to_string(_panel.get_curve())
-
-
-func _set_value(val: String) -> void:
- var curve = Util.string_to_curve(val)
- _panel.set_curve(curve)
-
-
-func _on_curve_updated() -> void:
- _on_value_changed(get_value())
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.tscn
deleted file mode 100644
index c15dd91..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.tscn
+++ /dev/null
@@ -1,32 +0,0 @@
-[gd_scene load_steps=3 format=3]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_curve.gd" id="1"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/curve_panel.gd" id="2"]
-
-[node name="ParameterCurve" type="VBoxContainer"]
-anchor_right = 1.0
-anchor_bottom = 1.0
-script = ExtResource( 1 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="Label" type="Label" parent="."]
-margin_right = 1280.0
-margin_bottom = 14.0
-text = "Curve name"
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-margin_top = 18.0
-margin_right = 1280.0
-margin_bottom = 168.0
-
-[node name="CurvePanel" type="PanelContainer" parent="MarginContainer"]
-margin_right = 1280.0
-margin_bottom = 150.0
-rect_min_size = Vector2( 0, 150 )
-rect_clip_content = true
-script = ExtResource( 2 )
-selected_point_color = Color( 0.878431, 0.47451, 0, 1 )
-
-[connection signal="curve_updated" from="MarginContainer/CurvePanel" to="." method="_on_curve_updated"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_file.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_file.gd
deleted file mode 100644
index 7f5b9e3..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_file.gd
+++ /dev/null
@@ -1,54 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $%Label
-@onready var _select_button: Button = $%FileButton
-@onready var _dialog: FileDialog = $%FileDialog
-@onready var _texture: Button = $%TextureButton
-@onready var _preview_root: Control = $%PreviewRoot
-
-var _path := ""
-var _is_texture := false
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func set_hint_string(hint: String) -> void:
- _is_texture = hint == "Texture"
- _set_value(get_value())
-
-
-func _set_value(val: String) -> void:
- _path = val
- _select_button.text = val.get_file()
- _preview_root.visible = false
-
- if val.is_empty():
- _select_button.text = "Select a file"
-
- if _is_texture:
- var texture = load(get_value())
- if texture is Texture:
- _texture.icon = texture
- _preview_root.visible = true
-
-
-func get_value() -> String:
- return _path
-
-
-func _on_clear_button_pressed() -> void:
- _set_value("")
- _on_value_changed("")
-
-
-func _on_select_button_pressed() -> void:
- _dialog.popup_centered()
-
-
-func _on_file_selected(file: String) -> void:
- _set_value(file)
- _on_value_changed(file)
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_file.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_file.tscn
deleted file mode 100644
index 93ec9b2..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_file.tscn
+++ /dev/null
@@ -1,93 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://cvgj4rdc0mxxq"]
-
-[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="1"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_file.gd" id="2"]
-
-[node name="ParameterFile" type="VBoxContainer"]
-anchors_preset = 10
-anchor_right = 1.0
-offset_bottom = 31.0
-size_flags_horizontal = 0
-size_flags_vertical = 0
-theme_override_constants/separation = 0
-script = ExtResource("2")
-
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
-layout_mode = 2
-offset_right = 1152.0
-offset_bottom = 31.0
-
-[node name="Label" type="Label" parent="HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 2.0
-offset_right = 574.0
-offset_bottom = 28.0
-size_flags_horizontal = 3
-text = "Parameter name"
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"]
-layout_mode = 2
-offset_left = 578.0
-offset_right = 1152.0
-offset_bottom = 31.0
-size_flags_horizontal = 3
-
-[node name="FileButton" type="Button" parent="HBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 546.0
-offset_bottom = 31.0
-size_flags_horizontal = 3
-text = "Select a file"
-
-[node name="ClearButton" type="Button" parent="HBoxContainer/HBoxContainer"]
-layout_mode = 2
-offset_left = 550.0
-offset_right = 574.0
-offset_bottom = 31.0
-icon = ExtResource("1")
-
-[node name="PreviewRoot" type="HBoxContainer" parent="."]
-unique_name_in_owner = true
-visible = false
-layout_mode = 2
-offset_top = 31.0
-offset_right = 1024.0
-offset_bottom = 39.0
-size_flags_horizontal = 3
-
-[node name="Control" type="Control" parent="PreviewRoot"]
-layout_mode = 2
-anchors_preset = 0
-offset_right = 1012.0
-offset_bottom = 8.0
-size_flags_horizontal = 3
-
-[node name="TextureButton" type="Button" parent="PreviewRoot"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 1016.0
-offset_right = 1024.0
-offset_bottom = 8.0
-flat = true
-
-[node name="Control" type="Control" parent="."]
-layout_mode = 2
-anchors_preset = 0
-offset_top = 31.0
-offset_right = 1152.0
-offset_bottom = 31.0
-
-[node name="FileDialog" type="FileDialog" parent="Control"]
-unique_name_in_owner = true
-title = "Open a texture file"
-size = Vector2i(400, 600)
-ok_button_text = "Open"
-file_mode = 0
-filters = PackedStringArray("*.bmp", "*.dds", "*.exr", "*.hdr", "*.jpg", "*.jpeg", "*.png", "*.tga", "*.svg", "*.svgz", "*.webp")
-
-[connection signal="pressed" from="HBoxContainer/HBoxContainer/FileButton" to="." method="_on_select_button_pressed"]
-[connection signal="pressed" from="HBoxContainer/HBoxContainer/ClearButton" to="." method="_on_clear_button_pressed"]
-[connection signal="pressed" from="PreviewRoot/TextureButton" to="." method="_on_select_button_pressed"]
-[connection signal="file_selected" from="Control/FileDialog" to="." method="_on_file_selected"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.gd
deleted file mode 100644
index b9353e4..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.gd
+++ /dev/null
@@ -1,86 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $%Label
-@onready var _select_button: Button = $%SelectButton
-@onready var _clear_button: Button = $%ClearButton
-@onready var _popup: ConfirmationDialog = $%ConfirmationDialog
-@onready var _tree: Tree = $%Tree
-
-var _full_path: NodePath
-var _root: Node
-var _selected: Node
-
-
-func set_root(root) -> void:
- _root = root
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func _set_value(val) -> void:
- if val == null:
- return
-
- _full_path = val
-
- if val.is_empty():
- return
-
- _select_button.text = val.get_name(val.get_name_count() - 1)
-
- if _root and _root.has_node(val):
- _selected = _root.get_node(val)
-
- if val.is_empty():
- _select_button.text = "Select a node"
-
-
-func get_value() -> NodePath:
- #if _root and _selected:
- # _full_path = String(_root.get_path_to(_selected))
- return _full_path
-
-
-func _populate_tree() -> void:
- _tree.clear()
- var scene_root: Node = get_tree().get_edited_scene_root()
-
- var tmp = EditorPlugin.new() # TODO: check if this works in release builds
- var gui: Control = tmp.get_editor_interface().get_base_control()
- var editor_theme = gui.get_theme()
- tmp.queue_free()
-
- _create_items_recursive(scene_root, null, editor_theme)
-
-
-func _create_items_recursive(node, parent, theme) -> void:
- if parent and not node.owner:
- return # Hidden node.
-
- var node_item = _tree.create_item(parent)
- node_item.set_text(0, node.get_name())
- node_item.set_meta("node", node)
- node_item.set_icon(0, theme.get_icon(node.get_class(), "EditorIcons"))
-
- for child in node.get_children():
- _create_items_recursive(child, node_item, theme)
-
-
-func _on_select_button_pressed() -> void:
- _populate_tree()
- _popup.popup_centered(Vector2i(400, 600))
-
-
-func _on_clear_button_pressed() -> void:
- _select_button.text = "Select a node"
- _full_path = NodePath()
-
-
-func _on_node_selected():
- var node = _tree.get_selected().get_meta("node")
- _set_value(_root.get_path_to(node))
- _on_value_changed(get_value())
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.tscn
deleted file mode 100644
index baaff4b..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.tscn
+++ /dev/null
@@ -1,66 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://bku7i3ct7ftui"]
-
-[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="1"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_node_selector.gd" id="2"]
-
-[node name="NodeSelector" type="MarginContainer"]
-anchors_preset = 10
-anchor_right = 1.0
-script = ExtResource("2")
-
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
-layout_mode = 2
-offset_right = 1152.0
-offset_bottom = 31.0
-
-[node name="Label" type="Label" parent="HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_top = 2.0
-offset_right = 560.0
-offset_bottom = 28.0
-size_flags_horizontal = 3
-text = "Parameter name"
-
-[node name="SelectButton" type="Button" parent="HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 564.0
-offset_right = 1124.0
-offset_bottom = 31.0
-size_flags_horizontal = 3
-text = "Select Node"
-flat = true
-
-[node name="ClearButton" type="Button" parent="HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 1128.0
-offset_right = 1152.0
-offset_bottom = 31.0
-icon = ExtResource("1")
-
-[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
-unique_name_in_owner = true
-size = Vector2i(400, 500)
-
-[node name="ScrollContainer" type="ScrollContainer" parent="ConfirmationDialog"]
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-offset_left = 8.0
-offset_top = 8.0
-offset_right = -960.0
-offset_bottom = -597.0
-
-[node name="Tree" type="Tree" parent="ConfirmationDialog/ScrollContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 184.0
-offset_bottom = 43.0
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[connection signal="pressed" from="HBoxContainer/SelectButton" to="." method="_on_select_button_pressed"]
-[connection signal="pressed" from="HBoxContainer/ClearButton" to="." method="_on_clear_button_pressed"]
-[connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_node_selected"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.gd
deleted file mode 100644
index 19871da..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.gd
+++ /dev/null
@@ -1,114 +0,0 @@
-# warning-ignore-all:return_value_discarded
-
-@tool
-extends "base_parameter.gd"
-
-
-var _is_int := false
-var _is_enum := false
-
-@onready var _label: Label = $Label
-@onready var _spinbox: SpinBox = $%SpinBox
-@onready var _option: OptionButton = $%OptionButton
-
-
-func _ready() -> void:
- _spinbox.value_changed.connect(_on_value_changed)
- _option.item_selected.connect(_on_value_changed)
- mark_as_int(_is_int)
-
-
-func mark_as_int(val: bool) -> void:
- _is_int = val
- if _is_int and _spinbox:
- _spinbox.step = 1
-
-
-func mark_as_enum(val: bool) -> void:
- _is_enum = val
-
-
-func toggle_option_item(idx: int, value := false) -> void:
- _option.set_item_disabled(idx, not value)
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func set_hint_string(hint: String) -> void:
- # No hint provided, ignore.
- if hint.is_empty():
- return
-
- if hint == "float":
- _spinbox.step = 0.01
- return
-
- if hint == "int":
- _spinbox.step = 1
- return
-
- # One integer provided
- if hint.is_valid_int():
- _set_range(0, hint.to_int())
- return
-
- # Multiple items provided, check their types
- var tokens = hint.split(",")
- var all_int = true
- var all_float = true
-
- for t in tokens:
- if not t.is_valid_int():
- all_int = false
- if not t.is_valid_float():
- all_float = false
-
- # All items are integer
- if all_int and tokens.size() >= 2:
- _set_range(tokens[0].to_int(), tokens[1].to_int())
- return
-
- # All items are float
- if all_float:
- if tokens.size() >= 2:
- _set_range(tokens[0].to_float(), tokens[1].to_float())
- if tokens.size() >= 3:
- _spinbox.step = tokens[2].to_float()
- return
-
- # All items are strings, make it a dropdown
- _spinbox.visible = false
- _option.visible = true
- _is_enum = true
- _is_int = true
-
- for i in tokens.size():
- _option.add_item(tokens[i], i)
- set_value(int(_spinbox.get_value()))
-
-
-func get_value():
- if _is_enum:
- return _option.get_selected_id()
- if _is_int:
- return int(_spinbox.get_value())
- return _spinbox.get_value()
-
-
-func _set_value(val) -> void:
- if _is_int:
- val = int(val)
- if _is_enum:
- _option.select(val)
- else:
- _spinbox.set_value(val)
-
-
-func _set_range(start, end) -> void:
- if start < end:
- _spinbox.min_value = start
- _spinbox.max_value = end
- _spinbox.allow_greater = false
- _spinbox.allow_lesser = false
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.tscn
deleted file mode 100644
index dce7fe4..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.tscn
+++ /dev/null
@@ -1,55 +0,0 @@
-[gd_scene load_steps=2 format=3 uid="uid://bspbhkrpgak0e"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.gd" id="1"]
-
-[node name="ParameterScalar" type="HBoxContainer"]
-anchors_preset = 10
-anchor_right = 1.0
-script = ExtResource("1")
-
-[node name="Label" type="Label" parent="."]
-layout_mode = 2
-offset_top = 2.0
-offset_right = 1833.0
-offset_bottom = 28.0
-size_flags_horizontal = 3
-text = "Parameter name"
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-layout_mode = 2
-offset_left = 1837.0
-offset_right = 1920.0
-offset_bottom = 31.0
-mouse_filter = 2
-
-[node name="Panel" type="Panel" parent="MarginContainer"]
-visible = false
-layout_mode = 2
-offset_right = 83.0
-offset_bottom = 31.0
-mouse_filter = 2
-
-[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
-layout_mode = 2
-offset_right = 83.0
-offset_bottom = 31.0
-mouse_filter = 2
-
-[node name="SpinBox" type="SpinBox" parent="MarginContainer/MarginContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 83.0
-offset_bottom = 31.0
-mouse_filter = 1
-min_value = -100.0
-step = 0.001
-allow_greater = true
-allow_lesser = true
-
-[node name="OptionButton" type="OptionButton" parent="MarginContainer/MarginContainer"]
-unique_name_in_owner = true
-visible = false
-layout_mode = 2
-offset_right = 83.0
-offset_bottom = 31.0
-focus_mode = 0
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_string.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_string.gd
deleted file mode 100644
index 390d07d..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_string.gd
+++ /dev/null
@@ -1,27 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $Label
-@onready var _line_edit: LineEdit = $MarginContainer/MarginContainer/LineEdit
-
-
-func _ready() -> void:
- _line_edit.connect("text_entered", _on_value_changed)
- _line_edit.connect("focus_exited", _on_focus_exited)
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func _set_value(val: String) -> void:
- _line_edit.text = val
-
-
-func get_value() -> String:
- return _line_edit.get_text()
-
-
-func _on_focus_exited() -> void:
- _on_value_changed(get_value())
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_string.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_string.tscn
deleted file mode 100644
index 00fbce5..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_string.tscn
+++ /dev/null
@@ -1,56 +0,0 @@
-[gd_scene load_steps=4 format=3]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_string.gd" id="1"]
-
-[sub_resource type="StyleBoxFlat" id=1]
-bg_color = Color( 0, 0, 0, 0.392157 )
-
-[sub_resource type="StyleBoxFlat" id=2]
-bg_color = Color( 0.6, 0.6, 0.6, 0 )
-
-[node name="ParameterString" type="HBoxContainer"]
-anchor_right = 1.0
-script = ExtResource( 1 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="Label" type="Label" parent="."]
-margin_top = 2.0
-margin_right = 638.0
-margin_bottom = 16.0
-size_flags_horizontal = 3
-text = "Parameter name"
-valign = 1
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-margin_left = 642.0
-margin_right = 1280.0
-margin_bottom = 18.0
-mouse_filter = 2
-size_flags_horizontal = 3
-
-[node name="Panel" type="Panel" parent="MarginContainer"]
-margin_right = 638.0
-margin_bottom = 18.0
-mouse_filter = 2
-custom_styles/panel = SubResource( 1 )
-
-[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
-margin_right = 638.0
-margin_bottom = 18.0
-mouse_filter = 2
-custom_constants/margin_right = 4
-custom_constants/margin_top = 2
-custom_constants/margin_left = 4
-custom_constants/margin_bottom = 2
-
-[node name="LineEdit" type="LineEdit" parent="MarginContainer/MarginContainer"]
-margin_left = 4.0
-margin_top = 2.0
-margin_right = 634.0
-margin_bottom = 16.0
-mouse_filter = 1
-custom_styles/focus = SubResource( 2 )
-custom_styles/normal = SubResource( 2 )
-clear_button_enabled = true
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.gd
deleted file mode 100644
index 7a90a73..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.gd
+++ /dev/null
@@ -1,47 +0,0 @@
-# warning-ignore-all:return_value_discarded
-
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $Label
-@onready var _x: SpinBox = $%X
-@onready var _y: SpinBox = $%Y
-@onready var _link: Button = $%LinkButton
-
-
-func _ready() -> void:
- _x.value_changed.connect(_on_spinbox_value_changed)
- _y.value_changed.connect(_on_spinbox_value_changed)
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func get_value() -> Vector2:
- var vec2 = Vector2.ZERO
- vec2.x = _x.get_value()
- vec2.y = _y.get_value()
- return vec2
-
-
-func _set_value(val: Vector2) -> void:
- _x.set_value(val.x)
- _y.set_value(val.y)
-
-
-func _on_clear_pressed():
- var old = get_value()
- set_value(Vector2.ZERO)
- _previous = old
- _on_value_changed(Vector2.ZERO)
-
-
-func _on_spinbox_value_changed(value: float) -> void:
- if _link.button_pressed:
- var old = get_value()
- set_value(Vector2(value, value))
- _previous = old
-
- _on_value_changed(get_value())
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.tscn
deleted file mode 100644
index b7cf67d..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.tscn
+++ /dev/null
@@ -1,136 +0,0 @@
-[gd_scene load_steps=4 format=3 uid="uid://bjn8ydwp80y7q"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_vector2.gd" id="1"]
-[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="2"]
-[ext_resource type="Texture2D" uid="uid://dbn0ytoameq8f" path="res://addons/proton_scatter/icons/link.svg" id="3_u2lry"]
-
-[node name="ParameterVector2" type="HBoxContainer"]
-anchor_right = 1.0
-script = ExtResource("1")
-
-[node name="Label" type="Label" parent="."]
-offset_right = 884.0
-offset_bottom = 66.0
-size_flags_horizontal = 3
-size_flags_vertical = 5
-text = "Parameter name"
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-offset_left = 888.0
-offset_right = 1024.0
-offset_bottom = 66.0
-size_flags_horizontal = 0
-mouse_filter = 2
-
-[node name="Panel" type="Panel" parent="MarginContainer"]
-offset_right = 136.0
-offset_bottom = 66.0
-mouse_filter = 2
-
-[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
-offset_right = 136.0
-offset_bottom = 66.0
-size_flags_horizontal = 0
-size_flags_vertical = 4
-mouse_filter = 2
-theme_override_constants/margin_left = 6
-theme_override_constants/margin_right = 6
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer"]
-offset_left = 6.0
-offset_right = 130.0
-offset_bottom = 66.0
-
-[node name="GridContainer" type="GridContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
-offset_right = 96.0
-offset_bottom = 66.0
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
-offset_right = 96.0
-offset_bottom = 31.0
-
-[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
-modulate = Color(1, 0.447059, 0.368627, 1)
-offset_top = 2.0
-offset_right = 9.0
-offset_bottom = 28.0
-text = "x"
-
-[node name="X" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
-unique_name_in_owner = true
-offset_left = 13.0
-offset_right = 96.0
-offset_bottom = 31.0
-mouse_filter = 1
-min_value = -100.0
-step = 0.001
-allow_greater = true
-allow_lesser = true
-
-[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
-offset_top = 35.0
-offset_right = 96.0
-offset_bottom = 66.0
-
-[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
-modulate = Color(0.564706, 0.992157, 0.298039, 1)
-offset_top = 2.0
-offset_right = 9.0
-offset_bottom = 28.0
-text = "y"
-
-[node name="Y" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
-unique_name_in_owner = true
-offset_left = 13.0
-offset_right = 96.0
-offset_bottom = 31.0
-mouse_filter = 1
-min_value = -100.0
-step = 0.001
-allow_greater = true
-allow_lesser = true
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
-offset_left = 100.0
-offset_right = 124.0
-offset_bottom = 66.0
-
-[node name="Control" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_right = 24.0
-size_flags_vertical = 3
-
-[node name="ClearButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 4.0
-offset_right = 24.0
-offset_bottom = 28.0
-size_flags_vertical = 4
-focus_mode = 0
-mouse_filter = 1
-icon = ExtResource("2")
-flat = true
-
-[node name="Control2" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 32.0
-offset_right = 24.0
-offset_bottom = 33.0
-size_flags_vertical = 3
-
-[node name="LinkButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-offset_top = 37.0
-offset_right = 24.0
-offset_bottom = 61.0
-size_flags_vertical = 4
-focus_mode = 0
-mouse_filter = 1
-toggle_mode = true
-icon = ExtResource("3_u2lry")
-flat = true
-
-[node name="Control3" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 65.0
-offset_right = 24.0
-offset_bottom = 66.0
-size_flags_vertical = 3
-
-[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer/ClearButton" to="." method="_on_clear_pressed"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.gd
deleted file mode 100644
index 55678c9..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.gd
+++ /dev/null
@@ -1,49 +0,0 @@
-@tool
-extends "base_parameter.gd"
-
-
-@onready var _label: Label = $Label
-@onready var _x: SpinBox = $%X
-@onready var _y: SpinBox = $%Y
-@onready var _z: SpinBox = $%Z
-@onready var _link: Button = $%LinkButton
-
-
-func _ready() -> void:
- _x.value_changed.connect(_on_spinbox_value_changed)
- _y.value_changed.connect(_on_spinbox_value_changed)
- _z.value_changed.connect(_on_spinbox_value_changed)
-
-
-func set_parameter_name(text: String) -> void:
- _label.text = text
-
-
-func get_value() -> Vector3:
- var vec3 = Vector3.ZERO
- vec3.x = _x.get_value()
- vec3.y = _y.get_value()
- vec3.z = _z.get_value()
- return vec3
-
-
-func _set_value(val: Vector3) -> void:
- _x.set_value(val.x)
- _y.set_value(val.y)
- _z.set_value(val.z)
-
-
-func _on_clear_pressed():
- var old = get_value()
- set_value(Vector3.ZERO)
- _previous = old
- _on_value_changed(Vector3.ZERO)
-
-
-func _on_spinbox_value_changed(value: float) -> void:
- if _link.button_pressed:
- var old = get_value()
- set_value(Vector3(value, value, value))
- _previous = old
-
- _on_value_changed(get_value())
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.tscn
deleted file mode 100644
index 463f697..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.tscn
+++ /dev/null
@@ -1,162 +0,0 @@
-[gd_scene load_steps=4 format=3 uid="uid://cdpfgf0447ph4"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_vector3.gd" id="1"]
-[ext_resource type="Texture2D" uid="uid://bosx22dy64f11" path="res://addons/proton_scatter/icons/clear.svg" id="2"]
-[ext_resource type="Texture2D" uid="uid://dbn0ytoameq8f" path="res://addons/proton_scatter/icons/link.svg" id="3_gq2ti"]
-
-[node name="ParameterVector3" type="HBoxContainer"]
-anchor_right = 1.0
-script = ExtResource("1")
-
-[node name="Label" type="Label" parent="."]
-offset_right = 884.0
-offset_bottom = 101.0
-size_flags_horizontal = 3
-size_flags_vertical = 5
-text = "Parameter name"
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-offset_left = 888.0
-offset_right = 1024.0
-offset_bottom = 101.0
-size_flags_horizontal = 0
-mouse_filter = 2
-
-[node name="Panel" type="Panel" parent="MarginContainer"]
-offset_right = 136.0
-offset_bottom = 101.0
-mouse_filter = 2
-
-[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
-offset_right = 136.0
-offset_bottom = 101.0
-size_flags_horizontal = 0
-size_flags_vertical = 4
-mouse_filter = 2
-theme_override_constants/margin_left = 6
-theme_override_constants/margin_right = 6
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer"]
-offset_left = 6.0
-offset_right = 130.0
-offset_bottom = 101.0
-
-[node name="GridContainer" type="GridContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
-offset_right = 96.0
-offset_bottom = 101.0
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
-offset_right = 96.0
-offset_bottom = 31.0
-
-[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
-modulate = Color(1, 0.447059, 0.368627, 1)
-offset_top = 2.0
-offset_right = 9.0
-offset_bottom = 28.0
-text = "x"
-
-[node name="X" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer"]
-unique_name_in_owner = true
-offset_left = 13.0
-offset_right = 96.0
-offset_bottom = 31.0
-mouse_filter = 1
-min_value = -100.0
-step = 0.001
-allow_greater = true
-allow_lesser = true
-
-[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
-offset_top = 35.0
-offset_right = 96.0
-offset_bottom = 66.0
-
-[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
-modulate = Color(0.564706, 0.992157, 0.298039, 1)
-offset_top = 2.0
-offset_right = 9.0
-offset_bottom = 28.0
-text = "y"
-
-[node name="Y" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer2"]
-unique_name_in_owner = true
-offset_left = 13.0
-offset_right = 96.0
-offset_bottom = 31.0
-mouse_filter = 1
-min_value = -100.0
-step = 0.001
-allow_greater = true
-allow_lesser = true
-
-[node name="HBoxContainer3" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer"]
-offset_top = 70.0
-offset_right = 96.0
-offset_bottom = 101.0
-
-[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer3"]
-modulate = Color(0.14902, 0.8, 1, 1)
-offset_top = 2.0
-offset_right = 8.0
-offset_bottom = 28.0
-text = "z"
-
-[node name="Z" type="SpinBox" parent="MarginContainer/MarginContainer/HBoxContainer/GridContainer/HBoxContainer3"]
-unique_name_in_owner = true
-offset_left = 12.0
-offset_right = 95.0
-offset_bottom = 31.0
-mouse_filter = 1
-min_value = -100.0
-step = 0.001
-allow_greater = true
-allow_lesser = true
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
-offset_left = 100.0
-offset_right = 124.0
-offset_bottom = 101.0
-size_flags_vertical = 3
-alignment = 1
-
-[node name="Control3" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_right = 24.0
-offset_bottom = 12.0
-size_flags_vertical = 3
-
-[node name="ClearButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 16.0
-offset_right = 24.0
-offset_bottom = 40.0
-size_flags_vertical = 4
-focus_mode = 0
-mouse_filter = 1
-icon = ExtResource("2")
-flat = true
-
-[node name="Control" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 44.0
-offset_right = 24.0
-offset_bottom = 56.0
-size_flags_vertical = 3
-
-[node name="LinkButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-unique_name_in_owner = true
-offset_top = 60.0
-offset_right = 24.0
-offset_bottom = 84.0
-size_flags_vertical = 4
-focus_mode = 0
-mouse_filter = 1
-toggle_mode = true
-icon = ExtResource("3_gq2ti")
-flat = true
-
-[node name="Control2" type="Control" parent="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer"]
-offset_top = 88.0
-offset_right = 24.0
-offset_bottom = 101.0
-size_flags_vertical = 3
-
-[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/VBoxContainer/ClearButton" to="." method="_on_clear_pressed"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/drag_container.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/drag_container.gd
deleted file mode 100644
index 5b9957b..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/drag_container.gd
+++ /dev/null
@@ -1,93 +0,0 @@
-@tool
-extends Container
-
-# DragContainer
-# Custom containner similar to a VBoxContainer, but the user can rearrange the
-# children order via drag and drop. This is only used in the inspector plugin
-# for the modifier stack and won't work with arbitrary control nodes.
-
-
-signal child_moved(last_index: int, new_index: int)
-
-
-var _separation: int = 0
-var _drag_offset = null
-var _dragged_child = null
-var _old_index: int
-var _new_index: int
-var _map := [] # Stores the y top position of each child in the stack
-
-
-func _ready() -> void:
- _separation = get_theme_constant("separation", "VBoxContainer")
-
-
-func _notification(what):
- if what == NOTIFICATION_SORT_CHILDREN or what == NOTIFICATION_RESIZED:
- _update_layout()
-
-
-func _can_drop_data(at_position, data) -> bool:
- if data.get_parent() != self:
- return false
-
- # Drag just started
- if not _dragged_child:
- _dragged_child = data
- _drag_offset = at_position - data.position
- _old_index = data.get_index()
- _new_index = _old_index
-
- # Dragged control only follow the y mouse position
- data.position.y = at_position.y - _drag_offset.y
-
- # Check if the children order should be changed
- var computed_index = 0
- for pos_y in _map:
- if pos_y > data.position.y - 16:
- break
- computed_index += 1
-
- if computed_index != data.get_index():
- move_child(data, computed_index)
- _new_index = computed_index
-
- return true
-
-
-# Called once at the end of the drag
-func _drop_data(at_position, data) -> void:
- _drag_offset = null
- _dragged_child = null
- _update_layout()
-
- if _old_index != _new_index:
- child_moved.emit(_old_index, _new_index)
-
-
-# Detects if the user drops the children outside the container and treats it
-# as if the drop happened the moment the mouse left the container.
-func _unhandled_input(event):
- if not _dragged_child:
- return
-
- if event is InputEventMouseButton and not event.pressed:
- _drop_data(_dragged_child.position, _dragged_child)
-
-
-func _update_layout() -> void:
- _map.clear()
- var offset := Vector2.ZERO
-
- for c in get_children():
- if c is Control:
- _map.push_back(offset.y)
- var child_min_size = c.get_combined_minimum_size()
- var possible_space = Rect2(offset, Vector2(size.x, child_min_size.y))
-
- if c != _dragged_child:
- fit_child_in_rect(c, possible_space)
-
- offset.y += c.size.y + _separation
-
- custom_minimum_size.y = offset.y - _separation
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/modifier_panel.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/modifier_panel.gd
deleted file mode 100644
index 8291a19..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/modifier_panel.gd
+++ /dev/null
@@ -1,201 +0,0 @@
-@tool
-extends Control
-
-
-signal value_changed
-signal removed
-signal documentation_requested
-signal duplication_requested
-
-
-const ParameterBool := preload("./components/parameter_bool.tscn")
-const ParameterScalar := preload("./components/parameter_scalar.tscn")
-const ParameterNodeSelector = preload("./components/parameter_node_selector.tscn")
-const ParameterFile = preload("./components/parameter_file.tscn")
-const ParameterCurve = preload("./components/parameter_curve.tscn")
-const ParameterBitmask = preload("./components/parameter_bitmask.tscn")
-const ParameterString = preload("./components/parameter_string.tscn")
-const ParameterVector3 = preload("./components/parameter_vector3.tscn")
-const ParameterVector2 = preload("./components/parameter_vector2.tscn")
-const PARAMETER_IGNORE_LIST := [
- "enabled",
- "override_global_seed",
- "custom_seed",
- "restrict_height",
- "reference_frame",
- ]
-
-var _scatter
-var _modifier
-
-@onready var _parameters: Control = $%ParametersRoot
-@onready var _name: Label = $%ModifierName
-@onready var _expand: Button = $%Expand
-@onready var _enabled: Button = $%Enabled
-@onready var _remove: Button = $%Remove
-@onready var _warning: Button = $%Warning
-@onready var _warning_dialog: AcceptDialog = $WarningDialog
-@onready var _drag_control: Control = $%DragControl
-@onready var _override_ui = $%OverrideGlobalSeed
-@onready var _custom_seed_ui = $%CustomSeed
-@onready var _restrict_height_ui = $%RestrictHeight
-@onready var _transform_space_ui = $%TransformSpace
-
-
-func _ready() -> void:
- _name.text = name
- _enabled.toggled.connect(_on_enable_toggled)
- _remove.pressed.connect(_on_remove_pressed)
- _warning.pressed.connect(_on_warning_icon_pressed)
- _expand.toggled.connect(_on_expand_toggled)
- $%MenuButton.get_popup().id_pressed.connect(_on_menu_item_pressed)
-
-
-func _get_drag_data(at_position: Vector2):
- var drag_control_position = _drag_control.global_position - global_position
- var drag_rect := Rect2(drag_control_position, _drag_control.size)
- if drag_rect.has_point(at_position):
- return self
-
- return null
-
-
-func set_root(val) -> void:
- _scatter = val
-
-
-# Loops through all exposed parameters and create an UI component for each of
-# them. For special properties (listed in PARAMATER_IGNORE_LIST), a special
-# UI is created.
-func create_ui_for(modifier) -> void:
- _modifier = modifier
- _modifier.warning_changed.connect(_on_warning_changed)
- _on_warning_changed()
-
- _name.text = modifier.display_name
- _enabled.button_pressed = modifier.enabled
-
- # Enable or disable irrelevant controls for this modifier
- _override_ui.enable(modifier.can_override_seed)
- _restrict_height_ui.enable(modifier.can_restrict_height)
- _transform_space_ui.mark_as_enum(true)
- _transform_space_ui.toggle_option_item(0, modifier.global_reference_frame_available)
- _transform_space_ui.toggle_option_item(1, modifier.local_reference_frame_available)
- _transform_space_ui.toggle_option_item(2, modifier.individual_instances_reference_frame_available)
- if not modifier.global_reference_frame_available and \
- not modifier.local_reference_frame_available and \
- not modifier.individual_instances_reference_frame_available:
- _transform_space_ui.visible = false
-
- # Setup header connections
- _override_ui.value_changed.connect(_on_parameter_value_changed.bind("override_global_seed", _override_ui))
- _custom_seed_ui.value_changed.connect(_on_parameter_value_changed.bind("custom_seed", _custom_seed_ui))
- _restrict_height_ui.value_changed.connect(_on_parameter_value_changed.bind("restrict_height", _restrict_height_ui))
- _transform_space_ui.value_changed.connect(_on_parameter_value_changed.bind("reference_frame", _transform_space_ui))
-
- # Restore header values
- _override_ui.set_value(modifier.override_global_seed)
- _custom_seed_ui.set_value(modifier.custom_seed)
- _restrict_height_ui.set_value(modifier.restrict_height)
- _transform_space_ui.set_value(modifier.reference_frame)
-
- # Loop over the other properties and create a ui component for each of them
- for property in modifier.get_property_list():
- if property.usage != PROPERTY_USAGE_DEFAULT + PROPERTY_USAGE_SCRIPT_VARIABLE:
- continue
-
- if property.name in PARAMETER_IGNORE_LIST:
- continue
-
- var parameter_ui
- match property.type:
- TYPE_BOOL:
- parameter_ui = ParameterBool.instantiate()
- TYPE_FLOAT:
- parameter_ui = ParameterScalar.instantiate()
- TYPE_INT:
- if property.hint == PROPERTY_HINT_LAYERS_3D_PHYSICS:
- parameter_ui = ParameterBitmask.instantiate()
- else:
- parameter_ui = ParameterScalar.instantiate()
- parameter_ui.mark_as_int(true)
- TYPE_STRING:
- if property.hint_string == "File" or property.hint_string == "Texture":
- parameter_ui = ParameterFile.instantiate()
- elif property.hint_string == "Curve":
- parameter_ui = ParameterCurve.instantiate()
- else:
- parameter_ui = ParameterString.instantiate()
- TYPE_VECTOR3:
- parameter_ui = ParameterVector3.instantiate()
- TYPE_VECTOR2:
- parameter_ui = ParameterVector2.instantiate()
- TYPE_NODE_PATH:
- parameter_ui = ParameterNodeSelector.instantiate()
- parameter_ui.set_root(_scatter)
-
- if parameter_ui:
- _parameters.add_child(parameter_ui)
- parameter_ui.set_parameter_name(property.name.capitalize())
- parameter_ui.set_value(modifier.get(property.name))
- parameter_ui.set_hint_string(property.hint_string)
- parameter_ui.value_changed.connect(_on_parameter_value_changed.bind(property.name, parameter_ui))
-
- _expand.button_pressed = _modifier.expanded
-
-
-func _restore_value(name, val, ui) -> void:
- _modifier.set(name, val)
- ui.set_value(val)
- value_changed.emit()
-
-
-func _on_expand_toggled(toggled: bool) -> void:
- $%ParametersContainer.visible = toggled
- _modifier.expanded = toggled
-
-
-func _on_remove_pressed() -> void:
- removed.emit()
-
-
-func _on_parameter_value_changed(value, previous, parameter_name, ui) -> void:
- if _scatter.undo_redo:
- _scatter.undo_redo.create_action("Change value " + parameter_name.capitalize())
- _scatter.undo_redo.add_undo_method(self, "_restore_value", parameter_name, previous, ui)
- _scatter.undo_redo.add_do_method(self, "_restore_value", parameter_name, value, ui)
- _scatter.undo_redo.commit_action()
- else:
- _modifier.set(parameter_name, value)
- value_changed.emit()
-
-
-func _on_enable_toggled(pressed: bool):
- _modifier.enabled = pressed
- value_changed.emit()
-
-
-func _on_removed_pressed() -> void:
- removed.emit()
-
-
-func _on_warning_changed() -> void:
- var warning = _modifier.get_warning()
- _warning.visible = (warning != "")
- _warning_dialog.dialog_text = warning
-
-
-func _on_warning_icon_pressed() -> void:
- _warning_dialog.popup_centered()
-
-
-func _on_menu_item_pressed(id) -> void:
- match id:
- 0:
- documentation_requested.emit()
- 2:
- duplication_requested.emit()
- 3:
- _on_remove_pressed()
- _:
- pass
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/modifier_panel.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/modifier_panel.tscn
deleted file mode 100644
index f679c1d..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/modifier_panel.tscn
+++ /dev/null
@@ -1,352 +0,0 @@
-[gd_scene load_steps=21 format=3 uid="uid://blpobpd0eweog"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/modifier_panel.gd" id="1"]
-[ext_resource type="Texture2D" uid="uid://hshxh8s6k677" path="res://addons/proton_scatter/icons/arrow_right.svg" id="2_2djuo"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/toggle_button.gd" id="4"]
-[ext_resource type="Texture2D" uid="uid://85r2q2fd8gyh" path="res://addons/proton_scatter/icons/arrow_down.svg" id="4_7nlfc"]
-[ext_resource type="Texture2D" uid="uid://dahwdjl2er75o" path="res://addons/proton_scatter/icons/close.svg" id="5"]
-[ext_resource type="Texture2D" uid="uid://n66mufjib4ds" path="res://addons/proton_scatter/icons/menu.svg" id="6_lmo8k"]
-[ext_resource type="Texture2D" uid="uid://dpigmdo0x73yb" path="res://addons/proton_scatter/icons/duplicate.svg" id="7_f6nan"]
-[ext_resource type="Texture2D" uid="uid://cnwg3p2m4lbf3" path="res://addons/proton_scatter/icons/doc.svg" id="7_owhij"]
-[ext_resource type="Texture2D" uid="uid://dj0y6peid681t" path="res://addons/proton_scatter/icons/warning.svg" id="9"]
-[ext_resource type="Texture2D" uid="uid://lkb3k4nuipjn" path="res://addons/proton_scatter/icons/drag_area.svg" id="9_t6pse"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/override_seed_button.gd" id="10_ptukr"]
-[ext_resource type="Texture2D" uid="uid://dvlwsskebnfyf" path="res://addons/proton_scatter/icons/dice.svg" id="11_qwhro"]
-[ext_resource type="PackedScene" uid="uid://w6ycb4oveqhd" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_button.tscn" id="11_y7srw"]
-[ext_resource type="Texture2D" uid="uid://bxgtlg3v183xe" path="res://addons/proton_scatter/icons/restrict_volume.svg" id="12_lx60d"]
-[ext_resource type="Texture2D" uid="uid://d4hayrcip1oo0" path="res://addons/proton_scatter/icons/local.svg" id="13_txjs8"]
-[ext_resource type="PackedScene" uid="uid://c36gqn03pvlnr" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/header/parameter_spinbox.tscn" id="13_vhfch"]
-[ext_resource type="Texture2D" uid="uid://bjha4mlvffrca" path="res://addons/proton_scatter/icons/restrict_volume_lock.svg" id="15_0w0as"]
-[ext_resource type="Texture2D" uid="uid://bpygylbuxdu0f" path="res://addons/proton_scatter/icons/global.svg" id="16_ocvvf"]
-[ext_resource type="PackedScene" uid="uid://bspbhkrpgak0e" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/components/parameter_scalar.tscn" id="17_aoulv"]
-[ext_resource type="Texture2D" uid="uid://be7s3se8bo32r" path="res://addons/proton_scatter/icons/individual_instances.svg" id="19_ln8a3"]
-
-[node name="ModifierPanel" type="MarginContainer"]
-anchors_preset = 10
-anchor_right = 1.0
-grow_horizontal = 2
-theme_type_variation = &"fg"
-script = ExtResource("1")
-
-[node name="Panel" type="Panel" parent="."]
-layout_mode = 2
-offset_right = 1920.0
-offset_bottom = 34.0
-mouse_filter = 2
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-layout_mode = 2
-offset_right = 1920.0
-offset_bottom = 34.0
-grow_horizontal = 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
-metadata/_edit_layout_mode = 1
-metadata/_edit_use_custom_anchors = false
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
-layout_mode = 2
-offset_left = 4.0
-offset_top = 4.0
-offset_right = 1916.0
-offset_bottom = 30.0
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
-layout_mode = 2
-offset_right = 1912.0
-offset_bottom = 26.0
-size_flags_vertical = 0
-
-[node name="Expand" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 20.0
-offset_bottom = 26.0
-tooltip_text = "Toggle the parameters view"
-focus_mode = 0
-mouse_filter = 1
-toggle_mode = true
-icon = ExtResource("2_2djuo")
-flat = true
-icon_alignment = 1
-script = ExtResource("4")
-default_icon = ExtResource("2_2djuo")
-pressed_icon = ExtResource("4_7nlfc")
-
-[node name="ModifierName" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 24.0
-offset_right = 1808.0
-offset_bottom = 26.0
-size_flags_horizontal = 3
-text = "ModifierPanel"
-vertical_alignment = 1
-clip_text = true
-
-[node name="Buttons" type="HBoxContainer" parent="MarginContainer/VBoxContainer/HBoxContainer"]
-layout_mode = 2
-offset_left = 1812.0
-offset_right = 1912.0
-offset_bottom = 26.0
-theme_override_constants/separation = 2
-alignment = 1
-
-[node name="Warning" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
-unique_name_in_owner = true
-visible = false
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 31.0
-size_flags_horizontal = 0
-focus_mode = 0
-mouse_filter = 1
-icon = ExtResource("9")
-flat = true
-
-[node name="MenuButton" type="MenuButton" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 26.0
-tooltip_text = "Show options"
-icon = ExtResource("6_lmo8k")
-item_count = 4
-popup/item_0/text = "Show documentation"
-popup/item_0/icon = ExtResource("7_owhij")
-popup/item_0/id = 0
-popup/item_1/text = ""
-popup/item_1/id = 1
-popup/item_1/separator = true
-popup/item_2/text = "Duplicate"
-popup/item_2/icon = ExtResource("7_f6nan")
-popup/item_2/id = 2
-popup/item_3/text = "Delete"
-popup/item_3/icon = ExtResource("5")
-popup/item_3/id = 3
-
-[node name="Enabled" type="CheckBox" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 26.0
-offset_right = 50.0
-offset_bottom = 26.0
-tooltip_text = "Toggle the modifier.
-
-If the modifier is disabled, it will not contribute to the final result but will still remain in the stack.
-
-Use this feature to quickly see how the modifier affects the overall stack."
-focus_mode = 0
-mouse_filter = 1
-
-[node name="Remove" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 52.0
-offset_top = 1.0
-offset_right = 76.0
-offset_bottom = 25.0
-size_flags_vertical = 4
-tooltip_text = "Delete the modifier.
-This will remove it from the stack."
-focus_mode = 0
-mouse_filter = 1
-icon = ExtResource("5")
-flat = true
-icon_alignment = 1
-
-[node name="VSeparator" type="VSeparator" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
-modulate = Color(1, 1, 1, 0.54902)
-layout_mode = 2
-offset_left = 78.0
-offset_right = 82.0
-offset_bottom = 26.0
-
-[node name="DragControl" type="TextureRect" parent="MarginContainer/VBoxContainer/HBoxContainer/Buttons"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 84.0
-offset_right = 100.0
-offset_bottom = 26.0
-tooltip_text = "Drag and move this button to change the stack order.
-
-Modifiers are processed from top to bottom."
-mouse_default_cursor_shape = 6
-texture = ExtResource("9_t6pse")
-stretch_mode = 3
-
-[node name="ParametersContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer"]
-unique_name_in_owner = true
-visible = false
-layout_mode = 2
-offset_top = 30.0
-offset_right = 1912.0
-offset_bottom = 72.0
-theme_override_constants/margin_left = 3
-theme_override_constants/margin_top = 3
-theme_override_constants/margin_right = 3
-theme_override_constants/margin_bottom = 3
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer"]
-layout_mode = 2
-offset_left = 3.0
-offset_top = 3.0
-offset_right = 1909.0
-offset_bottom = 39.0
-
-[node name="ParametersRoot" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 1906.0
-
-[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer"]
-layout_mode = 2
-offset_top = 4.0
-offset_right = 1906.0
-offset_bottom = 8.0
-
-[node name="CommonHeader" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer"]
-layout_mode = 2
-offset_top = 12.0
-offset_right = 1906.0
-offset_bottom = 36.0
-size_flags_vertical = 4
-alignment = 1
-
-[node name="ExpandButton" type="MarginContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader"]
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 24.0
-script = ExtResource("10_ptukr")
-
-[node name="OverrideGlobalSeed" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton" instance=ExtResource("11_y7srw")]
-unique_name_in_owner = true
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 24.0
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="Button" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/OverrideGlobalSeed" index="0"]
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 24.0
-tooltip_text = "Random seed.
-
-Enable to force a custom seed on this modifier only. If this option is disabled, the Global Seed from the ProtonScatter node will be used instead."
-icon = ExtResource("11_qwhro")
-icon_alignment = 0
-default_icon = ExtResource("11_qwhro")
-pressed_icon = ExtResource("11_qwhro")
-
-[node name="SpinBoxRoot" type="HBoxContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton"]
-visible = false
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 24.0
-mouse_filter = 2
-
-[node name="VSeparator" type="VSeparator" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/SpinBoxRoot"]
-modulate = Color(1, 1, 1, 0)
-layout_mode = 2
-offset_right = 28.0
-offset_bottom = 31.0
-mouse_filter = 2
-theme_override_constants/separation = 28
-
-[node name="CustomSeed" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/SpinBoxRoot" instance=ExtResource("13_vhfch")]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 31.0
-offset_right = 114.062
-
-[node name="Control" type="Control" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader"]
-layout_mode = 2
-anchors_preset = 0
-offset_left = 28.0
-offset_right = 1831.0
-offset_bottom = 24.0
-size_flags_horizontal = 3
-
-[node name="RestrictHeight" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader" instance=ExtResource("11_y7srw")]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 1835.0
-offset_right = 1859.0
-offset_bottom = 24.0
-size_flags_horizontal = 1
-size_flags_vertical = 3
-
-[node name="Button" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/RestrictHeight" index="0"]
-layout_mode = 2
-offset_right = 24.0
-offset_bottom = 24.0
-tooltip_text = "Restrict height.
-
-If enabled, the modifier will try to remain in the local XZ plane instead of using the full volume defined by the ScatterShapes."
-icon = ExtResource("12_lx60d")
-default_icon = ExtResource("12_lx60d")
-pressed_icon = ExtResource("15_0w0as")
-
-[node name="TransformSpace" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader" instance=ExtResource("17_aoulv")]
-unique_name_in_owner = true
-layout_mode = 2
-anchors_preset = 0
-anchor_right = 0.0
-offset_left = 1863.0
-offset_right = 1906.0
-offset_bottom = 24.0
-grow_horizontal = 2
-
-[node name="Label" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace" index="0"]
-visible = false
-offset_top = 4.0
-offset_right = 1.0
-offset_bottom = 27.0
-text = ""
-
-[node name="MarginContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace" index="1"]
-offset_left = 0.0
-offset_right = 43.0
-offset_bottom = 24.0
-
-[node name="Panel" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace/MarginContainer" index="0"]
-offset_right = 43.0
-offset_bottom = 24.0
-
-[node name="MarginContainer" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace/MarginContainer" index="1"]
-offset_right = 43.0
-offset_bottom = 24.0
-
-[node name="SpinBox" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace/MarginContainer/MarginContainer" index="0"]
-visible = false
-offset_right = 43.0
-offset_bottom = 24.0
-
-[node name="OptionButton" parent="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace/MarginContainer/MarginContainer" index="1"]
-visible = true
-offset_right = 43.0
-offset_bottom = 24.0
-item_count = 3
-fit_to_longest_item = false
-popup/item_0/text = "Global"
-popup/item_0/icon = ExtResource("16_ocvvf")
-popup/item_0/id = 0
-popup/item_1/text = "Local"
-popup/item_1/icon = ExtResource("13_txjs8")
-popup/item_1/id = 1
-popup/item_2/text = "Individual"
-popup/item_2/icon = ExtResource("19_ln8a3")
-popup/item_2/id = 2
-
-[node name="WarningDialog" type="AcceptDialog" parent="."]
-title = "Warning"
-unresizable = true
-popup_window = true
-
-[editable path="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/ExpandButton/OverrideGlobalSeed"]
-[editable path="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/RestrictHeight"]
-[editable path="MarginContainer/VBoxContainer/ParametersContainer/VBoxContainer/CommonHeader/TransformSpace"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/override_seed_button.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/override_seed_button.gd
deleted file mode 100644
index 1d89240..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/override_seed_button.gd
+++ /dev/null
@@ -1,14 +0,0 @@
-@tool
-extends Control
-
-
-@onready var _button: Button = $OverrideGlobalSeed/Button
-@onready var _spinbox_root: Control = $SpinBoxRoot
-
-
-func _ready():
- _button.toggled.connect(_on_toggled)
-
-
-func _on_toggled(enabled: bool) -> void:
- _spinbox_root.visible = enabled
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/toggle_button.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/toggle_button.gd
deleted file mode 100644
index d02da8e..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier/toggle_button.gd
+++ /dev/null
@@ -1,18 +0,0 @@
-@tool
-extends Button
-
-
-@export var default_icon: Texture
-@export var pressed_icon: Texture
-
-
-func _ready() -> void:
- toggled.connect(_on_toggled)
- _on_toggled(button_pressed)
-
-
-func _on_toggled(pressed: bool) -> void:
- if pressed:
- icon = pressed_icon
- else:
- icon = default_icon
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/category.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/category.gd
deleted file mode 100644
index 2c73397..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/category.gd
+++ /dev/null
@@ -1,9 +0,0 @@
-@tool
-extends VBoxContainer
-
-
-@onready var label: Label = $Header/Label
-
-
-func set_category_name(text) -> void:
- label.text = text
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/category.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/category.tscn
deleted file mode 100644
index 690edec..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/category.tscn
+++ /dev/null
@@ -1,26 +0,0 @@
-[gd_scene load_steps=2 format=3]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier_list_popup/category.gd" id="1"]
-
-[node name="VBoxContainer" type="VBoxContainer"]
-margin_right = 40.0
-margin_bottom = 40.0
-rect_pivot_offset = Vector2( -591.851, -77.5574 )
-size_flags_horizontal = 3
-script = ExtResource( 1 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="Header" type="VBoxContainer" parent="."]
-margin_right = 40.0
-margin_bottom = 22.0
-
-[node name="Label" type="Label" parent="Header"]
-margin_right = 40.0
-margin_bottom = 14.0
-
-[node name="HSeparator" type="HSeparator" parent="Header"]
-margin_top = 18.0
-margin_right = 40.0
-margin_bottom = 22.0
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/popup.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/popup.gd
deleted file mode 100644
index 360c925..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/popup.gd
+++ /dev/null
@@ -1,113 +0,0 @@
-@tool
-extends PopupPanel
-
-
-signal add_modifier
-
-var _modifiers := []
-
-@onready var _category_root: Control = $MarginContainer/CategoryRoot
-
-
-func _ready() -> void:
- _rebuild_ui()
-
-
-func _rebuild_ui():
- for c in _category_root.get_children():
- c.queue_free()
-
- _discover_modifiers()
- for modifier in _modifiers:
- var instance = modifier.new()
- if instance.enabled:
- var category = _get_or_create_category(instance.category)
- var button = _create_button(instance.display_name)
- category.add_child(button, true)
- button.pressed.connect(_on_pressed.bind(modifier))
-
- for category in _category_root.get_children():
- var header = category.get_child(0)
- _sort_children_by_name(category)
- category.move_child(header, 0)
-
-
-func _create_button(display_name) -> Button:
- var button = Button.new()
- button.name = display_name
- button.text = display_name
- button.alignment = HORIZONTAL_ALIGNMENT_LEFT
- return button
-
-
-func _sort_children_by_name(node: Node) -> void:
- var dict := {}
- var names := []
-
- for child in node.get_children():
- names.push_back(child.name)
- dict[child.name] = child
-
- names.sort_custom(func(a, b): return String(a) < String(b))
-
- for i in names.size():
- var n = names[i]
- node.move_child(dict[n], i)
-
-
-func _get_or_create_category(text: String) -> Control:
- if _category_root.has_node(text):
- return _category_root.get_node(text) as Control
-
- var c = preload("category.tscn").instantiate()
- c.name = text
- _category_root.add_child(c, true)
- c.set_category_name(text)
- return c
-
-
-func _discover_modifiers() -> void:
- if _modifiers.is_empty():
- var path = _get_root_folder() + "/src/modifiers/"
- _discover_modifiers_recursive(path)
-
-
-func _discover_modifiers_recursive(path) -> void:
- var dir = DirAccess.open(path)
- dir.list_dir_begin()
- var path_root = dir.get_current_dir() + "/"
-
- while true:
- var file = dir.get_next()
- if file == "":
- break
- if file == "base_modifier.gd":
- continue
- if dir.current_is_dir():
- _discover_modifiers_recursive(path_root + file)
- continue
- if not file.ends_with(".gd") and not file.ends_with(".gdc"):
- continue
-
- var full_path = path_root + file
- var script = load(full_path)
- if not script or not script.can_instantiate():
- print("Error: Failed to load script ", file)
- continue
-
- _modifiers.push_back(script)
-
- dir.list_dir_end()
-
-
-func _get_root_folder() -> String:
- var script: Script = get_script()
- var path: String = script.get_path().get_base_dir()
- var folders = path.right(-6) # Remove the res://
- var tokens = folders.split('/')
- return "res://" + tokens[0] + "/" + tokens[1]
-
-
-func _on_pressed(modifier) -> void:
- add_modifier.emit(modifier.new())
- visible = false
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/popup.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/popup.tscn
deleted file mode 100644
index 644d361..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/modifier_list_popup/popup.tscn
+++ /dev/null
@@ -1,20 +0,0 @@
-[gd_scene load_steps=2 format=3 uid="uid://belutr5odecw2"]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier_list_popup/popup.gd" id="1"]
-
-[node name="ModifiersPopup" type="PopupPanel"]
-size = Vector2i(597, 322)
-visible = true
-script = ExtResource("1")
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-anchor_right = 1.0
-anchor_bottom = 1.0
-offset_left = 4.0
-offset_top = 4.0
-offset_right = -431.0
-offset_bottom = -282.0
-
-[node name="CategoryRoot" type="HBoxContainer" parent="MarginContainer"]
-offset_right = 589.0
-offset_bottom = 314.0
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/load_preset.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/presets/load_preset.gd
deleted file mode 100644
index 0c8306c..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/load_preset.gd
+++ /dev/null
@@ -1,87 +0,0 @@
-@tool
-extends Window
-
-
-signal load_preset
-signal delete_preset
-
-
-@onready var _no_presets: Label = $MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/NoPresets
-@onready var _root: VBoxContainer = $MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot
-@onready var _confirmation_dialog: ConfirmationDialog = $ConfirmationDialog
-
-var _selected: String
-var _selected_ui: Node
-
-
-func _ready():
- _rebuild_ui()
- about_to_popup.connect(_rebuild_ui)
-
-
-func _rebuild_ui():
- for c in _root.get_children():
- c.queue_free()
- _root.visible = false
-
- var presets = _find_all_presets()
- if presets.empty():
- _no_presets.visible = true
- return
-
- _no_presets.visible = false
- _root.visible = true
- for p in presets:
- var ui = preload("./preset.tscn").instantiate()
- _root.add_child(ui)
- ui.set_preset_name(p)
- ui.load_preset.connect(_on_load_preset.bind(p))
- ui.delete_preset.connect(_on_delete_preset.bind(p, ui))
-
-
-func _find_all_presets() -> Array:
- var root := _get_root_folder() + "/presets/"
- var res := []
- var dir = DirAccess.open(root)
- if not dir:
- return res
-
- dir.list_dir_begin()
- while true:
- var file = dir.get_next()
- if file == "":
- break
-
- if file.ends_with(".tscn"):
- res.push_back(file.get_basename())
-
- dir.list_dir_end()
- res.sort()
- return res
-
-
-func _get_root_folder() -> String:
- var path: String = get_script().get_path().get_base_dir()
- var folders = path.right(6) # Remove the res://
- var tokens = folders.split('/')
- return "res://" + tokens[0] + "/" + tokens[1]
-
-
-func _on_load_preset(preset_name) -> void:
- emit_signal("load_preset", preset_name)
- visible = false
-
-
-func _on_delete_preset(preset_name, ui) -> void:
- _selected = preset_name
- _selected_ui = ui
- _confirmation_dialog.popup_centered()
-
-
-func _on_delete_preset_confirmed():
- DirAccess.remove_absolute(_get_root_folder() + "/presets/" + _selected + ".tscn")
- _selected_ui.queue_free()
-
-
-func _on_cancel_pressed():
- visible = false
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/load_preset.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/presets/load_preset.tscn
deleted file mode 100644
index 7016386..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/load_preset.tscn
+++ /dev/null
@@ -1,101 +0,0 @@
-[gd_scene load_steps=2 format=3]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/presets/load_preset.gd" id="1"]
-
-[node name="LoadPresetPopup" type="WindowDialog"]
-visible = true
-anchor_left = 0.5
-anchor_top = 0.5
-anchor_right = 0.5
-anchor_bottom = 0.5
-margin_left = -147.5
-margin_top = -156.5
-margin_right = 147.5
-margin_bottom = 156.5
-size_flags_horizontal = 5
-size_flags_vertical = 5
-window_title = "Load Presets"
-resizable = true
-script = ExtResource( 1 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-anchor_right = 1.0
-anchor_bottom = 1.0
-custom_constants/margin_right = 8
-custom_constants/margin_top = 8
-custom_constants/margin_left = 8
-custom_constants/margin_bottom = 8
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
-margin_left = 8.0
-margin_top = 8.0
-margin_right = 287.0
-margin_bottom = 305.0
-custom_constants/separation = 6
-
-[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
-margin_right = 279.0
-margin_bottom = 271.0
-size_flags_vertical = 3
-scroll_horizontal_enabled = false
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
-margin_right = 279.0
-margin_bottom = 271.0
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="NoPresets" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
-visible = false
-margin_right = 247.0
-margin_bottom = 118.0
-size_flags_horizontal = 3
-size_flags_vertical = 3
-text = "No presets found.
-
-Create new presets by pressing the \"Save Preset\" button first."
-valign = 1
-autowrap = true
-
-[node name="PresetsRoot" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer"]
-margin_right = 279.0
-margin_bottom = 271.0
-size_flags_horizontal = 3
-size_flags_vertical = 3
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
-margin_left = 112.0
-margin_top = 277.0
-margin_right = 166.0
-margin_bottom = 297.0
-size_flags_horizontal = 4
-
-[node name="CancelButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
-margin_right = 54.0
-margin_bottom = 20.0
-text = "Cancel"
-
-[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
-visible = true
-margin_left = -320.0
-margin_top = 37.0
-margin_right = -120.0
-margin_bottom = 112.0
-dialog_text = "Delete preset?
-(This action can't be undone)"
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset" method="_on_pressed"]
-[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset2" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset2" method="_on_pressed"]
-[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset3" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset3" method="_on_pressed"]
-[connection signal="pressed" from="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset4" to="MarginContainer/VBoxContainer/ScrollContainer/VBoxContainer/PresetsRoot/Preset4" method="_on_pressed"]
-[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="_on_cancel_pressed"]
-[connection signal="confirmed" from="ConfirmationDialog" to="." method="_on_delete_preset_confirmed"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/preset.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/presets/preset.gd
deleted file mode 100644
index dba1842..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/preset.gd
+++ /dev/null
@@ -1,21 +0,0 @@
-@tool
-extends Button
-
-
-signal load_preset
-signal delete_preset
-
-
-@onready var _label: Label = $MarginContainer/HBoxContainer/Label
-
-
-func set_preset_name(text) -> void:
- _label.text = text
-
-
-func _on_pressed() -> void:
- emit_signal("load_preset")
-
-
-func _on_delete() -> void:
- emit_signal("delete_preset")
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/preset.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/presets/preset.tscn
deleted file mode 100644
index 967db9a..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/preset.tscn
+++ /dev/null
@@ -1,54 +0,0 @@
-[gd_scene load_steps=3 format=3]
-
-[ext_resource type="Texture" uid="uid://dahwdjl2er75o" path="res://addons/proton_scatter/icons/close.svg" id="1"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/presets/preset.gd" id="2"]
-
-[node name="Preset" type="Button"]
-anchor_top = 0.5
-anchor_right = 1.0
-anchor_bottom = 0.5
-margin_top = -10.0
-margin_bottom = 29.0
-rect_min_size = Vector2( 0, 40 )
-focus_mode = 0
-script = ExtResource( 2 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-anchor_right = 1.0
-anchor_bottom = 1.0
-mouse_filter = 2
-custom_constants/margin_right = 6
-custom_constants/margin_top = 6
-custom_constants/margin_left = 6
-custom_constants/margin_bottom = 6
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
-margin_left = 6.0
-margin_top = 6.0
-margin_right = 1274.0
-margin_bottom = 34.0
-
-[node name="Label" type="Label" parent="MarginContainer/HBoxContainer"]
-margin_top = 7.0
-margin_right = 1236.0
-margin_bottom = 21.0
-size_flags_horizontal = 3
-text = "Preset name"
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="Delete" type="Button" parent="MarginContainer/HBoxContainer"]
-margin_left = 1240.0
-margin_right = 1268.0
-margin_bottom = 28.0
-icon = ExtResource( 1 )
-
-[connection signal="pressed" from="." to="." method="_on_pressed"]
-[connection signal="pressed" from="MarginContainer/HBoxContainer/Delete" to="." method="_on_delete"]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/save_preset.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/presets/save_preset.gd
deleted file mode 100644
index 91cfed0..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/save_preset.gd
+++ /dev/null
@@ -1,78 +0,0 @@
-@tool
-extends Window
-
-
-signal save_preset
-
-
-@onready var _line_edit: LineEdit = $MarginContainer/VBoxContainer/LineEdit
-@onready var _cancel: Button = $MarginContainer/VBoxContainer/HBoxContainer/Cancel
-@onready var _save: Button = $MarginContainer/VBoxContainer/HBoxContainer/Save
-@onready var _warning: Label = $MarginContainer/VBoxContainer/Warning
-@onready var _confirm_overwrite = $ConfirmationDialog
-
-
-func _ready():
- _cancel.pressed.connect(_on_cancel_pressed)
- _save.pressed.connect(_on_save_pressed)
- _warning.text = ""
- _confirm_overwrite.confirmed.connect(_save_preset)
-
-
-func _on_cancel_pressed() -> void:
- visible = false
- _line_edit.text = ""
-
-
-func _on_save_pressed() -> void:
- var preset_name: String = _line_edit.text
- if preset_name.is_empty():
- _warning.text = "Preset name can't be empty"
- return
-
- if not preset_name.is_valid_filename():
- _warning.text = """Preset name must be a valid file name.
- It cannot contain the following characters:
- : / \\ ? * " | % < >"""
- return
-
- _warning.text = ""
- if _exists(preset_name):
- _confirm_overwrite.dialog_text = "Preset \"" + preset_name + "\" already exists. Overwrite?"
- _confirm_overwrite.popup_centered()
- else:
- _save_preset()
-
-
-func _save_preset() -> void:
- emit_signal("save_preset", _line_edit.text)
- visible = false
- _line_edit.text = ""
-
-
-func _exists(preset: String) -> bool:
- var dir = DirAccess.open(_get_root_folder() + "/presets/")
- if not dir:
- return false
-
- dir.list_dir_begin()
-
- while true:
- var file = dir.get_next()
- if file == "":
- break
-
- if file == preset + ".tscn":
- dir.list_dir_end()
- return true
-
- dir.list_dir_end()
- return false
-
-
-func _get_root_folder() -> String:
- var script: Script = get_script()
- var path: String = script.get_path().get_base_dir()
- var folders = path.right(6) # Remove the res://
- var tokens = folders.split('/')
- return "res://" + tokens[0] + "/" + tokens[1]
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/save_preset.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/presets/save_preset.tscn
deleted file mode 100644
index 65e7922..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/presets/save_preset.tscn
+++ /dev/null
@@ -1,96 +0,0 @@
-[gd_scene load_steps=2 format=3]
-
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/presets/save_preset.gd" id="1"]
-
-[node name="SavePresetPopup" type="WindowDialog"]
-visible = true
-anchor_left = 0.5
-anchor_top = 0.5
-anchor_right = 0.5
-anchor_bottom = 0.5
-margin_left = -180.0
-margin_top = -81.0
-margin_right = 180.0
-margin_bottom = 81.0
-size_flags_horizontal = 5
-size_flags_vertical = 5
-window_title = "Save Preset"
-resizable = true
-script = ExtResource( 1 )
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="MarginContainer" type="MarginContainer" parent="."]
-anchor_right = 1.0
-anchor_bottom = 1.0
-custom_constants/margin_right = 24
-custom_constants/margin_top = 4
-custom_constants/margin_left = 24
-custom_constants/margin_bottom = 24
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
-margin_left = 24.0
-margin_top = 4.0
-margin_right = 336.0
-margin_bottom = 120.0
-size_flags_vertical = 0
-custom_constants/separation = 12
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="Warning" type="Label" parent="MarginContainer/VBoxContainer"]
-modulate = Color( 1, 0.513726, 0.278431, 1 )
-margin_right = 312.0
-margin_bottom = 48.0
-text = "Preset name must be a valid file name.
-It cannot contain the following characters:
-: / \\\\ ? * \" | % < >"
-valign = 2
-autowrap = true
-
-[node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"]
-margin_top = 60.0
-margin_right = 312.0
-margin_bottom = 84.0
-placeholder_text = "Preset Name"
-caret_blink = true
-
-[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
-margin_top = 96.0
-margin_right = 312.0
-margin_bottom = 116.0
-custom_constants/separation = 24
-alignment = 1
-
-[node name="Cancel" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
-margin_left = 96.0
-margin_right = 150.0
-margin_bottom = 20.0
-text = "Cancel"
-
-[node name="Save" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"]
-margin_left = 174.0
-margin_right = 215.0
-margin_bottom = 20.0
-text = "Save"
-
-[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
-visible = true
-anchor_left = 0.5
-anchor_top = 0.5
-anchor_right = 0.5
-anchor_bottom = 0.5
-margin_left = -135.0
-margin_top = -44.0
-margin_right = 135.0
-margin_bottom = 44.0
-window_title = "Overwrite existing preset?"
-dialog_autowrap = true
-__meta__ = {
-"_edit_use_anchors_": false
-}
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/stack_panel.gd b/addons/scatter-4/src/stack/inspector_plugin/ui/stack_panel.gd
deleted file mode 100644
index 4089b2d..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/stack_panel.gd
+++ /dev/null
@@ -1,157 +0,0 @@
-@tool
-extends Control
-
-
-const ModifierPanel := preload("./modifier/modifier_panel.tscn")
-
-
-@onready var _modifiers_container: Control = $%ModifiersContainer
-@onready var _modifiers_popup: PopupPanel = $%ModifiersPopup
-
-var _scatter
-var _modifier_stack
-var _undo_redo
-var _is_ready := false
-
-
-func _ready():
- _modifiers_popup.add_modifier.connect(_on_modifier_added)
- _modifiers_container.child_moved.connect(_on_modifier_moved)
- $%Rebuild.pressed.connect(_on_rebuild_pressed)
- $%DocumentationButton.pressed.connect(_on_documentation_requested.bind("ProtonScatter"))
- $%LoadPreset.pressed.connect(_on_load_preset_pressed)
- $%SavePreset.pressed.connect(_on_save_preset_pressed)
-
- _is_ready = true
- rebuild_ui()
-
-
-func set_node(node) -> void:
- if not node:
- return
-
- _scatter = node
- _undo_redo = _scatter.undo_redo
- $%Documentation.set_editor_plugin(_scatter.editor_plugin)
- $%Presets.set_editor_plugin(_scatter.editor_plugin)
- rebuild_ui()
-
-
-func rebuild_ui() -> void:
- if not _is_ready:
- return
-
- _validate_stack_connections()
- _clear()
- for m in _modifier_stack.stack:
- var ui = ModifierPanel.instantiate()
- _modifiers_container.add_child(ui)
- ui.set_root(_scatter)
- ui.create_ui_for(m)
- ui.removed.connect(_on_modifier_removed.bind(m))
- ui.value_changed.connect(_on_value_changed)
- ui.documentation_requested.connect(_on_documentation_requested.bind(m.display_name))
- ui.duplication_requested.connect(_on_modifier_duplicated.bind(m))
-
-
-func _clear() -> void:
- for c in _modifiers_container.get_children():
- _modifiers_container.remove_child(c)
- c.queue_free()
-
-
-func _validate_stack_connections() -> void:
- if not _scatter:
- return
-
- if _modifier_stack:
- _modifier_stack.stack_changed.disconnect(_on_stack_changed)
-
- _modifier_stack = _scatter.modifier_stack
- _modifier_stack.stack_changed.connect(_on_stack_changed)
-
- if _modifier_stack.just_created:
- $%Presets.load_default(_scatter)
-
-
-func _set_children_owner(new_owner: Node, node: Node):
- for child in node.get_children():
- child.set_owner(new_owner)
- if child.get_children().size() > 0:
- _set_children_owner(new_owner, child)
-
-
-func _get_root_folder() -> String:
- var path: String = get_script().get_path().get_base_dir()
- var folders = path.right(6) # Remove the res://
- var tokens = folders.split('/')
- return "res://" + tokens[0] + "/" + tokens[1]
-
-
-func _on_modifier_added(modifier) -> void:
- if _undo_redo:
- _undo_redo.create_action("Create modifier " + modifier.display_name)
- _undo_redo.add_undo_method(_modifier_stack, "remove", modifier)
- _undo_redo.add_do_method(_modifier_stack, "add", modifier)
- _undo_redo.commit_action()
- else:
- _modifier_stack.add(modifier)
-
-
-func _on_modifier_moved(old_index: int, new_index: int) -> void:
- if _undo_redo:
- _undo_redo.create_action("Move modifier")
- _undo_redo.add_undo_method(_modifier_stack, "move", new_index, old_index)
- _undo_redo.add_do_method(_modifier_stack, "move", old_index, new_index)
- _undo_redo.commit_action()
- else:
- _modifier_stack.move(old_index, new_index)
-
-
-func _on_modifier_removed(modifier) -> void:
- if _undo_redo:
- _undo_redo.create_action("Remove modifier " + modifier.display_name)
- _undo_redo.add_undo_method(_modifier_stack, "add", modifier)
- _undo_redo.add_do_method(_modifier_stack, "remove", modifier)
- _undo_redo.commit_action()
- else:
- _modifier_stack.remove(modifier)
-
-
-func _on_modifier_duplicated(modifier) -> void:
- var index = _modifier_stack.get_index(modifier)
- if index == -1:
- return
-
- if _undo_redo:
- _undo_redo.create_action("Duplicate modifier " + modifier.display_name)
- _undo_redo.add_undo_method(_modifier_stack, "remove_at", index + 1)
- _undo_redo.add_do_method(_modifier_stack, "duplicate_modifier", modifier)
- _undo_redo.commit_action()
- else:
- _modifier_stack.duplicate_modifier(modifier)
-
-
-func _on_stack_changed() -> void:
- rebuild_ui()
-
-
-func _on_value_changed() -> void:
- _modifier_stack.value_changed.emit()
-
-
-func _on_rebuild_pressed() -> void:
- if _scatter:
- _scatter.rebuild(true)
-
-
-func _on_save_preset_pressed() -> void:
- $%Presets.save_preset(_scatter)
-
-
-func _on_load_preset_pressed() -> void:
- $%Presets.load_preset(_scatter)
-
-
-func _on_documentation_requested(page_name) -> void:
- $%Documentation.show_page(page_name)
diff --git a/addons/scatter-4/src/stack/inspector_plugin/ui/stack_panel.tscn b/addons/scatter-4/src/stack/inspector_plugin/ui/stack_panel.tscn
deleted file mode 100644
index 72b12b5..0000000
--- a/addons/scatter-4/src/stack/inspector_plugin/ui/stack_panel.tscn
+++ /dev/null
@@ -1,140 +0,0 @@
-[gd_scene load_steps=12 format=3 uid="uid://dllpt2dven8vw"]
-
-[ext_resource type="Texture2D" uid="uid://cun73k8jdmr4e" path="res://addons/proton_scatter/icons/add.svg" id="1_4vwtj"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/stack_panel.gd" id="1_ga4or"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier/drag_container.gd" id="1_hg5ys"]
-[ext_resource type="Texture2D" uid="uid://d2q0ecpon840i" path="res://addons/proton_scatter/icons/rebuild.svg" id="2_svid4"]
-[ext_resource type="Script" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/add_modifier_button.gd" id="3_01ldn"]
-[ext_resource type="PackedScene" uid="uid://belutr5odecw2" path="res://addons/proton_scatter/src/stack/inspector_plugin/ui/modifier_list_popup/popup.tscn" id="3_pkswu"]
-[ext_resource type="Texture2D" uid="uid://ddjrq1h4mkn6a" path="res://addons/proton_scatter/icons/load.svg" id="3_w72lv"]
-[ext_resource type="Texture2D" uid="uid://b2omj2e03x72e" path="res://addons/proton_scatter/icons/save.svg" id="4_5l2cx"]
-[ext_resource type="Texture2D" uid="uid://cnwg3p2m4lbf3" path="res://addons/proton_scatter/icons/doc.svg" id="8_fgqhd"]
-[ext_resource type="PackedScene" uid="uid://cfg8iqtuion8b" path="res://addons/proton_scatter/src/documentation/documentation.tscn" id="9_y57kc"]
-[ext_resource type="PackedScene" uid="uid://bcsosdvstytoq" path="res://addons/proton_scatter/src/presets/presets.tscn" id="11_2ut8s"]
-
-[node name="StackPanel" type="MarginContainer"]
-clip_children = 1
-clip_contents = true
-offset_right = 456.0
-offset_bottom = 144.0
-theme_override_constants/margin_left = 4
-theme_override_constants/margin_top = 4
-theme_override_constants/margin_right = 4
-theme_override_constants/margin_bottom = 4
-script = ExtResource("1_ga4or")
-
-[node name="VBoxContainer" type="VBoxContainer" parent="."]
-layout_mode = 2
-offset_left = 4.0
-offset_top = 4.0
-offset_right = 452.0
-offset_bottom = 140.0
-theme_override_constants/separation = 16
-
-[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
-layout_mode = 2
-offset_right = 448.0
-offset_bottom = 31.0
-
-[node name="Add" type="Button" parent="VBoxContainer/HBoxContainer"]
-layout_mode = 2
-offset_right = 131.0
-offset_bottom = 31.0
-size_flags_horizontal = 2
-focus_mode = 0
-toggle_mode = true
-text = " Add modifier"
-icon = ExtResource("1_4vwtj")
-script = ExtResource("3_01ldn")
-
-[node name="ModifiersPopup" parent="VBoxContainer/HBoxContainer/Add" instance=ExtResource("3_pkswu")]
-unique_name_in_owner = true
-size = Vector2i(755, 322)
-visible = false
-
-[node name="Rebuild" type="Button" parent="VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 324.0
-offset_right = 348.0
-offset_bottom = 31.0
-size_flags_horizontal = 4
-tooltip_text = "Force rebuild.
-
-If the placed items does not look as expected, you can press this button to force it to regenerate the result.
-
-Usually, you shouldn't have to use it so please report it on Github if you found a case where it's necessary to click this. "
-focus_mode = 0
-icon = ExtResource("2_svid4")
-icon_alignment = 1
-
-[node name="VSeparator" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
-modulate = Color(1, 1, 1, 0.54902)
-layout_mode = 2
-offset_left = 352.0
-offset_right = 356.0
-offset_bottom = 31.0
-
-[node name="LoadPreset" type="Button" parent="VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 360.0
-offset_right = 384.0
-offset_bottom = 31.0
-tooltip_text = "Load a preset."
-focus_mode = 0
-text = "
-"
-icon = ExtResource("3_w72lv")
-icon_alignment = 1
-
-[node name="SavePreset" type="Button" parent="VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 388.0
-offset_right = 412.0
-offset_bottom = 31.0
-tooltip_text = "Save a preset."
-focus_mode = 0
-text = "
-"
-icon = ExtResource("4_5l2cx")
-icon_alignment = 1
-
-[node name="VSeparator2" type="VSeparator" parent="VBoxContainer/HBoxContainer"]
-modulate = Color(1, 1, 1, 0.54902)
-layout_mode = 2
-offset_left = 416.0
-offset_right = 420.0
-offset_bottom = 31.0
-
-[node name="DocumentationButton" type="Button" parent="VBoxContainer/HBoxContainer"]
-unique_name_in_owner = true
-layout_mode = 2
-offset_left = 424.0
-offset_right = 448.0
-offset_bottom = 31.0
-tooltip_text = "Open documentation"
-focus_mode = 0
-text = "
-"
-icon = ExtResource("8_fgqhd")
-icon_alignment = 1
-
-[node name="ModifiersContainer" type="Container" parent="VBoxContainer"]
-unique_name_in_owner = true
-clip_children = 1
-custom_minimum_size = Vector2(0, -4)
-layout_mode = 2
-offset_top = 47.0
-offset_right = 448.0
-offset_bottom = 136.0
-size_flags_vertical = 3
-mouse_filter = 0
-script = ExtResource("1_hg5ys")
-
-[node name="Documentation" parent="." instance=ExtResource("9_y57kc")]
-unique_name_in_owner = true
-
-[node name="Presets" parent="." instance=ExtResource("11_2ut8s")]
-unique_name_in_owner = true
diff --git a/addons/scatter-4/src/stack/modifier_stack.gd b/addons/scatter-4/src/stack/modifier_stack.gd
deleted file mode 100644
index 018a34f..0000000
--- a/addons/scatter-4/src/stack/modifier_stack.gd
+++ /dev/null
@@ -1,85 +0,0 @@
-@tool
-extends Resource
-
-
-signal stack_changed
-signal value_changed
-signal transforms_ready
-
-
-const ProtonScatter := preload("../scatter.gd")
-const TransformList = preload("../common/transform_list.gd")
-
-
-@export var stack: Array[Resource] = []
-
-var just_created := false
-
-
-func start_update(scatter_node: ProtonScatter, domain) -> void:
- var transforms = TransformList.new()
-
- for modifier in stack:
- await modifier.process_transforms(transforms, domain, scatter_node.global_seed)
-
- transforms_ready.emit(transforms)
-
-
-func add(modifier) -> void:
- stack.push_back(modifier)
- stack_changed.emit()
-
-
-func move(old_index: int, new_index: int) -> void:
- var modifier = stack.pop_at(old_index)
- stack.insert(new_index, modifier)
- stack_changed.emit()
-
-
-func remove(modifier) -> void:
- if stack.has(modifier):
- stack.erase(modifier)
- stack_changed.emit()
-
-
-func remove_at(index: int) -> void:
- if stack.size() > index:
- stack.remove_at(index)
- stack_changed.emit()
-
-
-func duplicate_modifier(modifier) -> void:
- var index: int = stack.find(modifier)
- if index != -1:
- var duplicate = modifier.get_copy()
- add(duplicate)
- move(stack.size() - 1, index + 1)
-
-
-func get_copy():
- var copy = get_script().new()
- for modifier in stack:
- copy.stack.push_back(modifier.duplicate())
- return copy
-
-
-func get_index(modifier) -> int:
- return stack.find(modifier)
-
-
-func is_using_edge_data() -> bool:
- for modifier in stack:
- if modifier.use_edge_data:
- return true
-
- return false
-
-
-# Returns true if at least one modifier does not require shapes in order to work.
-# (This is the case for the "Add single item" modifier for example)
-func does_not_require_shapes() -> bool:
- for modifier in stack:
- if modifier.warning_ignore_no_shape:
- return true
-
- return false
diff --git a/project.godot b/project.godot
index 518a839..98547f3 100644
--- a/project.godot
+++ b/project.godot
@@ -29,7 +29,7 @@ window/stretch/mode="canvas_items"
[editor_plugins]
-enabled=PackedStringArray("res://addons/scatter-4/plugin.cfg", "res://addons/zylann.hterrain/plugin.cfg")
+enabled=PackedStringArray("res://addons/zylann.hterrain/plugin.cfg")
[input]
diff --git a/scenes/mainmenu.tscn b/scenes/mainmenu.tscn
index a89edc3..157265b 100644
--- a/scenes/mainmenu.tscn
+++ b/scenes/mainmenu.tscn
@@ -123,7 +123,7 @@ offset_left = 4.0
offset_top = 622.0
offset_right = 94.0
offset_bottom = 648.0
-text = "Version: 20230715"
+text = "Version: 20230910"
[node name="OurTimeIsNowSolo" type="Sprite2D" parent="."]
position = Vector2(123, 104)