Added footsteps, new tree, various other tweaks
This commit is contained in:
45
addons/proton_scatter/src/cache/inspector_plugin/cache_panel.gd
vendored
Normal file
45
addons/proton_scatter/src/cache/inspector_plugin/cache_panel.gd
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
|
||||
const ScatterCache := preload("res://addons/proton_scatter/src/cache/scatter_cache.gd")
|
||||
|
||||
|
||||
@onready var _rebuild_button: Button = %RebuildButton
|
||||
@onready var _restore_button: Button = %RestoreButton
|
||||
@onready var _clear_button: Button = %ClearButton
|
||||
@onready var _enable_for_all_button: Button = %EnableForAllButton
|
||||
|
||||
var _cache: ScatterCache
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_rebuild_button.pressed.connect(_on_rebuild_pressed)
|
||||
_restore_button.pressed.connect(_on_restore_pressed)
|
||||
_clear_button.pressed.connect(_on_clear_pressed)
|
||||
_enable_for_all_button.pressed.connect(_on_enable_for_all_pressed)
|
||||
custom_minimum_size.y = size.y * 1.25
|
||||
|
||||
|
||||
func set_object(cache: ScatterCache) -> void:
|
||||
_cache = cache
|
||||
|
||||
|
||||
func _on_rebuild_pressed() -> void:
|
||||
if is_instance_valid(_cache):
|
||||
_cache.update_cache()
|
||||
|
||||
|
||||
func _on_restore_pressed() -> void:
|
||||
if is_instance_valid(_cache):
|
||||
_cache.restore_cache()
|
||||
|
||||
|
||||
func _on_clear_pressed() -> void:
|
||||
if is_instance_valid(_cache):
|
||||
_cache.clear_cache()
|
||||
|
||||
|
||||
func _on_enable_for_all_pressed() -> void:
|
||||
if is_instance_valid(_cache):
|
||||
_cache.enable_for_all_nodes()
|
||||
49
addons/proton_scatter/src/cache/inspector_plugin/cache_panel.tscn
vendored
Normal file
49
addons/proton_scatter/src/cache/inspector_plugin/cache_panel.tscn
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dilbceex72g24"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/proton_scatter/src/cache/inspector_plugin/cache_panel.gd" id="1_h1g4a"]
|
||||
[ext_resource type="Texture2D" uid="uid://yqlpvcmb7mfi" path="res://addons/proton_scatter/icons/rebuild.svg" id="2_0ml76"]
|
||||
[ext_resource type="Texture2D" uid="uid://ddjrq1h4mkn6a" path="res://addons/proton_scatter/icons/load.svg" id="3_i6mdl"]
|
||||
[ext_resource type="Texture2D" uid="uid://btb6rqhhi27mx" path="res://addons/proton_scatter/icons/remove.svg" id="4_bfbdy"]
|
||||
|
||||
[node name="CachePanel" type="PanelContainer"]
|
||||
custom_minimum_size = Vector2(0, 82.5)
|
||||
offset_right = 161.0
|
||||
offset_bottom = 66.0
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1_h1g4a")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="RebuildButton" type="Button" parent="MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
text = "Update Cache"
|
||||
icon = ExtResource("2_0ml76")
|
||||
|
||||
[node name="RestoreButton" type="Button" parent="MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
text = "Restore Transforms"
|
||||
icon = ExtResource("3_i6mdl")
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ClearButton" type="Button" parent="MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
text = "Clear Cache"
|
||||
icon = ExtResource("4_bfbdy")
|
||||
|
||||
[node name="EnableForAllButton" type="Button" parent="MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Enable Cache For All"
|
||||
17
addons/proton_scatter/src/cache/inspector_plugin/scatter_cache_plugin.gd
vendored
Normal file
17
addons/proton_scatter/src/cache/inspector_plugin/scatter_cache_plugin.gd
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
@tool
|
||||
extends EditorInspectorPlugin
|
||||
|
||||
|
||||
const CachePanel = preload("./cache_panel.tscn")
|
||||
const ScatterCache = preload("../../cache/scatter_cache.gd")
|
||||
|
||||
|
||||
func _can_handle(object):
|
||||
return is_instance_of(object, ScatterCache)
|
||||
|
||||
|
||||
func _parse_category(object, category: String):
|
||||
if category == "ScatterCache" or category == "scatter_cache.gd":
|
||||
var ui = CachePanel.instantiate()
|
||||
ui.set_object(object)
|
||||
add_custom_control(ui)
|
||||
216
addons/proton_scatter/src/cache/scatter_cache.gd
vendored
Normal file
216
addons/proton_scatter/src/cache/scatter_cache.gd
vendored
Normal file
@@ -0,0 +1,216 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
# ProtonScatterCacheNode
|
||||
#
|
||||
# Saves the transforms created by ProtonScatter nodes in an external resource
|
||||
# and restore them when loading the scene.
|
||||
#
|
||||
# Use this node when you don't want to wait for scatter nodes to fully rebuild
|
||||
# at start.
|
||||
# You can also enable "Show output in tree" to get the same effect, but the
|
||||
# cache makes it much more VCS friendly, and doesn't clutter your scene tree.
|
||||
|
||||
const DEFAULT_CACHE_FOLDER := "res://addons/proton_scatter/cache/"
|
||||
|
||||
const ProtonScatter := preload("res://addons/proton_scatter/src/scatter.gd")
|
||||
const ProtonScatterTransformList := preload("../common/transform_list.gd")
|
||||
|
||||
|
||||
signal cache_restored
|
||||
|
||||
|
||||
@export_file("*.res", "*.tres") var cache_file := "":
|
||||
set(val):
|
||||
cache_file = val
|
||||
update_configuration_warnings()
|
||||
@export var auto_rebuild_cache_when_saving := true
|
||||
|
||||
@export_group("Debug", "dbg_")
|
||||
@export var dbg_disable_thread := false
|
||||
|
||||
# The resource where transforms are actually stored
|
||||
var _local_cache: ProtonScatterCacheResource
|
||||
var _scene_root: Node
|
||||
var _scatter_nodes: Dictionary #Key: ProtonScatter, Value: cached version
|
||||
var _local_cache_changed := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
_scene_root = _get_local_scene_root(self)
|
||||
|
||||
# Check if cache_file is empty, indicating the default case
|
||||
if cache_file.is_empty():
|
||||
if Engine.is_editor_hint():
|
||||
# Ensure the cache folder exists
|
||||
_ensure_cache_folder_exists()
|
||||
else:
|
||||
printerr("ProtonScatter error: You load a ScatterCache node with an empty cache file attribute. Outside of the editor, the addon can't set a default value. Please open the scene in the editor and set a default value.")
|
||||
return
|
||||
|
||||
# Retrieve the scene name to create a unique recognizable name
|
||||
var scene_path: String = _scene_root.get_scene_file_path()
|
||||
var scene_name: String
|
||||
|
||||
# If the scene path is not available, set a random name
|
||||
if scene_path.is_empty():
|
||||
scene_name = str(randi())
|
||||
else:
|
||||
# Use the base name of the scene file and append a hash to avoid collisions
|
||||
scene_name = scene_path.get_file().get_basename()
|
||||
scene_name += "_" + str(scene_path.hash())
|
||||
|
||||
# Set the cache path to the cache folder, incorporating the scene name
|
||||
cache_file = DEFAULT_CACHE_FOLDER.get_basename().path_join(scene_name + "_scatter_cache.res")
|
||||
return
|
||||
|
||||
restore_cache.call_deferred()
|
||||
|
||||
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
var warnings = PackedStringArray()
|
||||
if cache_file.is_empty():
|
||||
warnings.push_back("No path set for the cache file. Select where to store the cache in the inspector.")
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_EDITOR_PRE_SAVE and auto_rebuild_cache_when_saving:
|
||||
update_cache()
|
||||
|
||||
|
||||
func clear_cache() -> void:
|
||||
_scatter_nodes.clear()
|
||||
_local_cache = null
|
||||
|
||||
|
||||
func update_cache() -> void:
|
||||
if cache_file.is_empty():
|
||||
printerr("Cache file path is empty.")
|
||||
return
|
||||
|
||||
_purge_outdated_nodes()
|
||||
_discover_scatter_nodes(_scene_root)
|
||||
|
||||
if not _local_cache:
|
||||
_local_cache = ProtonScatterCacheResource.new()
|
||||
for s in _scatter_nodes:
|
||||
# Ignore this node if its cache is already up to date
|
||||
var cached_version: int = _scatter_nodes[s]
|
||||
if s.build_version == cached_version:
|
||||
continue
|
||||
|
||||
# If transforms are not available, try to rebuild once.
|
||||
if not s.transforms:
|
||||
s.rebuild.call_deferred()
|
||||
await s.build_completed
|
||||
|
||||
if not s.transforms:
|
||||
continue # Move on to the next if still no results.
|
||||
|
||||
# Store the transforms in the cache.
|
||||
_local_cache.store(_scene_root.get_path_to(s), s.transforms.list)
|
||||
_scatter_nodes[s] = s.build_version
|
||||
_local_cache_changed = true
|
||||
|
||||
# Only save the cache on disk if there's something new to save
|
||||
if not _local_cache_changed:
|
||||
return
|
||||
|
||||
# TODO: Save large files on a thread
|
||||
var err = ResourceSaver.save(_local_cache, cache_file)
|
||||
_local_cache_changed = false
|
||||
|
||||
if err != OK:
|
||||
printerr("ProtonScatter error: Failed to save the cache file. Code: ", err)
|
||||
|
||||
|
||||
func restore_cache() -> void:
|
||||
# Load the cache file if it exists
|
||||
if not ResourceLoader.exists(cache_file):
|
||||
printerr("Could not find cache file ", cache_file)
|
||||
return
|
||||
|
||||
# Cache files are large, load on a separate thread
|
||||
ResourceLoader.load_threaded_request(cache_file)
|
||||
while true:
|
||||
match ResourceLoader.load_threaded_get_status(cache_file):
|
||||
ResourceLoader.ThreadLoadStatus.THREAD_LOAD_INVALID_RESOURCE:
|
||||
return
|
||||
ResourceLoader.ThreadLoadStatus.THREAD_LOAD_IN_PROGRESS:
|
||||
await get_tree().process_frame
|
||||
ResourceLoader.ThreadLoadStatus.THREAD_LOAD_FAILED:
|
||||
return
|
||||
ResourceLoader.ThreadLoadStatus.THREAD_LOAD_LOADED:
|
||||
break
|
||||
|
||||
_local_cache = ResourceLoader.load_threaded_get(cache_file)
|
||||
if not _local_cache:
|
||||
printerr("Could not load cache: ", cache_file)
|
||||
return
|
||||
|
||||
_scatter_nodes.clear()
|
||||
_discover_scatter_nodes(_scene_root)
|
||||
|
||||
for s in _scatter_nodes:
|
||||
if s.force_rebuild_on_load:
|
||||
continue # Ignore the cache if the scatter node is about to rebuild anyway.
|
||||
|
||||
# Send the cached transforms to the scatter node.
|
||||
var transforms = ProtonScatterTransformList.new()
|
||||
transforms.list = _local_cache.get_transforms(_scene_root.get_path_to(s))
|
||||
s._perform_sanity_check()
|
||||
s._on_transforms_ready(transforms)
|
||||
s.build_version = 0
|
||||
_scatter_nodes[s] = 0
|
||||
|
||||
cache_restored.emit()
|
||||
|
||||
|
||||
func enable_for_all_nodes() -> void:
|
||||
_purge_outdated_nodes()
|
||||
_discover_scatter_nodes(_scene_root)
|
||||
for s in _scatter_nodes:
|
||||
s.force_rebuild_on_load = false
|
||||
|
||||
|
||||
# If the node comes from an instantiated scene, returns the root of that
|
||||
# instance. Returns the tree root node otherwise.
|
||||
func _get_local_scene_root(node: Node) -> Node:
|
||||
if not node.scene_file_path.is_empty():
|
||||
return node
|
||||
|
||||
var parent: Node = node.get_parent()
|
||||
if not parent:
|
||||
return node
|
||||
|
||||
return _get_local_scene_root(parent)
|
||||
|
||||
|
||||
func _discover_scatter_nodes(node: Node) -> void:
|
||||
if node is ProtonScatter and not _scatter_nodes.has(node):
|
||||
_scatter_nodes[node] = node.build_version
|
||||
|
||||
for c in node.get_children():
|
||||
_discover_scatter_nodes(c)
|
||||
|
||||
|
||||
func _purge_outdated_nodes() -> void:
|
||||
var nodes_to_remove: Array[ProtonScatter] = []
|
||||
for node in _scatter_nodes:
|
||||
if not is_instance_valid(node):
|
||||
nodes_to_remove.push_back(node)
|
||||
_local_cache.erase(_scene_root.get_path_to(node))
|
||||
_local_cache_changed = true
|
||||
|
||||
for node in nodes_to_remove:
|
||||
_scatter_nodes.erase(node)
|
||||
|
||||
|
||||
func _ensure_cache_folder_exists() -> void:
|
||||
if not DirAccess.dir_exists_absolute(DEFAULT_CACHE_FOLDER):
|
||||
DirAccess.make_dir_recursive_absolute(DEFAULT_CACHE_FOLDER)
|
||||
Reference in New Issue
Block a user