614 lines
21 KiB
GDScript
614 lines
21 KiB
GDScript
@tool
|
|
extends Node3D
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Handles managing OctreeManager objects and changes applied when painting
|
|
# Instigates updates to OctreeManager MutliMeshInstance (MMI) objects
|
|
# To show the correct LOD variant when moving closer/further to plants
|
|
#-------------------------------------------------------------------------------
|
|
|
|
# It's worth considering to split this object into mutliple:
|
|
# Octree management
|
|
# Updating positions of individual plants (through painting)
|
|
# Threaded updates to LODs (if possible)
|
|
# However, these functions are very closely related, so maybe I'm overthinking this
|
|
|
|
|
|
const Logger = preload("../utility/logger.gd")
|
|
const Globals = preload("../utility/globals.gd")
|
|
const FunLib = preload("../utility/fun_lib.gd")
|
|
const Greenhouse_Plant = preload("../greenhouse/greenhouse_plant.gd")
|
|
const Toolshed_Brush = preload("../toolshed/toolshed_brush.gd")
|
|
const PaintingChanges = preload("painting_changes.gd")
|
|
const MMIOctreeManager = preload("mmi_octree/mmi_octree_manager.gd")
|
|
const UndoRedoInterface = preload("../utility/undo_redo_interface.gd")
|
|
|
|
const StrokeHandler = preload("stroke_handler/stroke_handler.gd")
|
|
const SH_Paint = preload("stroke_handler/sh_paint.gd")
|
|
const SH_Erase = preload("stroke_handler/sh_erase.gd")
|
|
const SH_Single = preload("stroke_handler/sh_single.gd")
|
|
const SH_Reapply = preload("stroke_handler/sh_reapply.gd")
|
|
const SH_Manual = preload("stroke_handler/sh_manual.gd")
|
|
|
|
var MMI_container:Node3D = null
|
|
var octree_managers:Array
|
|
|
|
var gardening_collision_mask:int = 0
|
|
|
|
# A manual override fot the camera (mainly used in Editor)
|
|
var active_camera_override:Camera3D = null
|
|
|
|
var active_stroke_handler:StrokeHandler = null
|
|
var active_painting_changes:PaintingChanges = null
|
|
|
|
# Threading LOD updates is not working for some reason. Gives error "Condition "!multimesh" is true." when closing a scene
|
|
# This might be related to https://github.com/godotengine/godot/pull/54650
|
|
# Possibly, there are some leftover references after closing a scene and idk how I'm supposed to clean them up
|
|
#var mutex_placement:Mutex = null
|
|
#var thread_instance_placement:Thread
|
|
#var semaphore_instance_placement:Semaphore
|
|
#var exit_instance_placement:bool
|
|
#var done_instance_placement:bool
|
|
|
|
var _undo_redo = null
|
|
|
|
var debug_redraw_requested_managers:Array = []
|
|
|
|
var logger = null
|
|
|
|
|
|
signal req_debug_redraw(octree_managers, requested_indexes)
|
|
signal member_count_updated(octree_index, new_count)
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Lifecycle and initialization
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
func _init():
|
|
set_meta("class", "Arborist")
|
|
|
|
|
|
|
|
func _ready():
|
|
# Workaround below fixes the problem of instanced nodes "sharing" exported arrays (and resources inside them)
|
|
# When instanced in the editor
|
|
# See https://github.com/godotengine/godot/issues/16478
|
|
# This fix is needed, so we can have multiple instances of same terrain with same plant placement
|
|
# But have LOD switch independently for each of these terrains
|
|
if octree_managers == null:
|
|
octree_managers = []
|
|
else:
|
|
var octree_managers_copy = octree_managers.duplicate()
|
|
octree_managers = []
|
|
for octree_manager in octree_managers_copy:
|
|
octree_managers.append(octree_manager.duplicate_tree())
|
|
|
|
logger = Logger.get_for(self, name)
|
|
|
|
owner = get_tree().get_edited_scene_root()
|
|
|
|
MMI_container = get_node_or_null("MMI_container")
|
|
if MMI_container && !is_instance_of(MMI_container, Node3D):
|
|
remove_child(MMI_container)
|
|
MMI_container.queue_free()
|
|
MMI_container = null
|
|
if !MMI_container:
|
|
FunLib.free_children(self)
|
|
MMI_container = Node3D.new()
|
|
MMI_container.name = "MMI_container"
|
|
add_child(MMI_container)
|
|
|
|
MMI_container.owner = owner
|
|
|
|
for octree_manager in octree_managers:
|
|
octree_manager.restore_after_load(MMI_container)
|
|
|
|
|
|
func _enter_tree():
|
|
pass
|
|
# thread_instance_placement = Thread.new()
|
|
# mutex_placement = Mutex.new()
|
|
# semaphore_instance_placement = Semaphore.new()
|
|
#
|
|
# exit_instance_placement = false
|
|
# done_instance_placement = true
|
|
#
|
|
# thread_instance_placement.start(Callable(self,"thread_update_LODs"))
|
|
|
|
|
|
func _notification(what):
|
|
match what:
|
|
NOTIFICATION_PREDELETE:
|
|
for octree_manager in octree_managers:
|
|
octree_manager.free_refs()
|
|
|
|
|
|
func _exit_tree():
|
|
pass
|
|
# This is... weird
|
|
# Apparently I need to free any Resources that are left after closing a scene
|
|
# I'm not exactly sure why
|
|
# And it *might* be destructive to do so in editor
|
|
#if Engine.is_editor_hint(): return
|
|
#for octree_manager in octree_managers:
|
|
#octree_manager.destroy()
|
|
# mutex_placement.lock()
|
|
# exit_instance_placement = true
|
|
# done_instance_placement = false
|
|
# mutex_placement.unlock()
|
|
#
|
|
# semaphore_instance_placement.post()
|
|
# thread_instance_placement.wait_to_finish()
|
|
#
|
|
# thread_instance_placement = null
|
|
# mutex_placement = null
|
|
# semaphore_instance_placement = null
|
|
|
|
|
|
# Expected to be called inside or after a parent's _ready()
|
|
func setup(plant_states):
|
|
verify_all_plants(plant_states)
|
|
|
|
|
|
# Restore all OctreeManager objects after load
|
|
# Create missing ones
|
|
func verify_all_plants(plant_states_to_verify:Array):
|
|
if !is_inside_tree(): return
|
|
debug_print_lifecycle("verifying for plant_states: " + str(plant_states_to_verify))
|
|
|
|
for plant_index in range(0, plant_states_to_verify.size()):
|
|
if octree_managers.size() - 1 >= plant_index:
|
|
octree_managers[plant_index].restore_after_load(MMI_container)
|
|
connect_octree_manager(octree_managers[plant_index])
|
|
else:
|
|
add_plant_octree_manager(plant_states_to_verify[plant_index], plant_index)
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Management of plant OctreeManager objects
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
# Instigate the OctreeManager adding process in response to an external signal
|
|
func on_plant_added(plant_state, plant_index:int):
|
|
debug_print_lifecycle("plant: %s added at plant_index %d" % [str(plant_state), plant_index])
|
|
add_plant_octree_manager(plant_state, plant_index)
|
|
request_debug_redraw_from_index(plant_index)
|
|
call_deferred("emit_member_count", plant_index)
|
|
|
|
|
|
# Instigate the OctreeManager removal process in response to an external signal
|
|
func on_plant_removed(plant_state, plant_index:int):
|
|
debug_print_lifecycle("plant: %s removed at plant_index %d" % [str(plant_state), plant_index])
|
|
remove_plant_octree_manager(plant_state, plant_index)
|
|
request_debug_redraw_from_index(plant_index)
|
|
|
|
|
|
# Up-to-date LOD variants of an OctreeManager
|
|
func on_LOD_variant_added(plant_index:int, mesh_index:int, LOD_variant):
|
|
debug_print_lifecycle("LOD Variant: %s added at plant_index %d and mesh_index %d" % [str(LOD_variant), plant_index, mesh_index])
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.insert_LOD_variant(LOD_variant, mesh_index)
|
|
octree_manager.set_LODs_to_active_index()
|
|
|
|
|
|
# Up-to-date LOD variants of an OctreeManager
|
|
func on_LOD_variant_removed(plant_index:int, mesh_index:int):
|
|
debug_print_lifecycle("LOD Variant: removed at plant_index %d and mesh_index %d" % [plant_index, mesh_index])
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.remove_LOD_variant(mesh_index)
|
|
octree_manager.set_LODs_to_active_index()
|
|
|
|
|
|
# Up-to-date LOD variants of an OctreeManager
|
|
func on_LOD_variant_set(plant_index:int, mesh_index:int, LOD_variant):
|
|
debug_print_lifecycle("LOD Variant: %s set at plant_index %d and mesh_index %d" % [str(LOD_variant), plant_index, mesh_index])
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.set_LOD_variant(LOD_variant, mesh_index)
|
|
octree_manager.set_LODs_to_active_index()
|
|
|
|
|
|
# Up-to-date LOD variants of an OctreeManager
|
|
func on_LOD_variant_prop_changed_spawned_spatial(plant_index:int, mesh_index:int, LOD_variant):
|
|
debug_print_lifecycle("LOD Variant: %s spawned spatial changed at plant_index %d and mesh_index %d" % [str(LOD_variant), plant_index, mesh_index])
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.set_LOD_variant_spawned_spatial(LOD_variant, mesh_index)
|
|
octree_manager.reset_member_spatials()
|
|
|
|
|
|
# Make sure LODs in OctreeNodes correspond to their active_LOD_index
|
|
# This is the preffered way to 'refresh' MMIs inside OctreeNodes
|
|
func set_LODs_to_active_index(plant_index:int):
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.set_LODs_to_active_index()
|
|
|
|
|
|
# Initialize an OctreeManager for a given plant
|
|
func add_plant_octree_manager(plant_state, plant_index:int):
|
|
var octree_manager:MMIOctreeManager = MMIOctreeManager.new()
|
|
octree_manager.init_octree(
|
|
plant_state.plant.mesh_LOD_max_capacity, plant_state.plant.mesh_LOD_min_size,
|
|
Vector3.ZERO, MMI_container, plant_state.plant.mesh_LOD_min_size)
|
|
octree_manager.LOD_max_distance = plant_state.plant.mesh_LOD_max_distance
|
|
octree_manager.LOD_kill_distance = plant_state.plant.mesh_LOD_kill_distance
|
|
octree_managers.insert(plant_index, octree_manager)
|
|
|
|
for mesh_index in range (0, plant_state.plant.mesh_LOD_variants.size()):
|
|
var LOD_variant = plant_state.plant.mesh_LOD_variants[mesh_index]
|
|
octree_manager.insert_LOD_variant(LOD_variant, mesh_index)
|
|
connect_octree_manager(octree_manager)
|
|
|
|
|
|
# Remove an OctreeManager for a given plant
|
|
func remove_plant_octree_manager(plant_state, plant_index:int):
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
disconnect_octree_manager(octree_manager)
|
|
octree_manager.prepare_for_removal()
|
|
octree_managers.remove_at(plant_index)
|
|
|
|
|
|
# A request to reconfigure an octree
|
|
func reconfigure_octree(plant_state, plant_index:int):
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.rebuild_octree(plant_state.plant.mesh_LOD_max_capacity, plant_state.plant.mesh_LOD_min_size)
|
|
|
|
|
|
# A request to recenter an octree
|
|
func recenter_octree(plant_state, plant_index:int):
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.recenter_octree()
|
|
|
|
|
|
# Connect all OctreeManager signals
|
|
func connect_octree_manager(octree_manager:MMIOctreeManager):
|
|
if !octree_manager.req_debug_redraw.is_connected(on_req_debug_redraw):
|
|
octree_manager.req_debug_redraw.connect(on_req_debug_redraw.bind(octree_manager))
|
|
|
|
|
|
# Disconnect all OctreeManager signals
|
|
func disconnect_octree_manager(octree_manager:MMIOctreeManager):
|
|
if octree_manager.req_debug_redraw.is_connected(on_req_debug_redraw):
|
|
octree_manager.req_debug_redraw.disconnect(on_req_debug_redraw)
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Setting/updating variables to outside signals
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
# To be called by a signal from Greenhouse_PlantState -> Gardener -> Arborist
|
|
func update_plant_LOD_max_distance(plant_index, val):
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.LOD_max_distance = val
|
|
|
|
|
|
# To be called by a signal from Greenhouse_PlantState -> Gardener -> Arborist
|
|
func update_plant_LOD_kill_distance(plant_index, val):
|
|
var octree_manager:MMIOctreeManager = octree_managers[plant_index]
|
|
octree_manager.LOD_kill_distance = val
|
|
|
|
|
|
# To be called by a signal from Gardener -> Arborist
|
|
func set_gardening_collision_mask(_gardening_collision_mask):
|
|
gardening_collision_mask = _gardening_collision_mask
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Application of brushes and transform generation
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
# Create PaintingChanges and a StrokeHandler for this specific brush stroke
|
|
func on_stroke_started(brush:Toolshed_Brush, plant_states:Array):
|
|
var space_state := get_world_3d().direct_space_state
|
|
var camera = get_camera_3d()
|
|
active_painting_changes = PaintingChanges.new()
|
|
match brush.behavior_brush_type:
|
|
brush.BrushType.PAINT:
|
|
active_stroke_handler = SH_Paint.new(brush, plant_states, octree_managers, space_state, camera, gardening_collision_mask)
|
|
brush.BrushType.ERASE:
|
|
active_stroke_handler = SH_Erase.new(brush, plant_states, octree_managers, space_state, camera, gardening_collision_mask)
|
|
brush.BrushType.SINGLE:
|
|
active_stroke_handler = SH_Single.new(brush, plant_states, octree_managers, space_state, camera, gardening_collision_mask)
|
|
brush.BrushType.REAPPLY:
|
|
active_stroke_handler = SH_Reapply.new(brush, plant_states, octree_managers, space_state, camera, gardening_collision_mask)
|
|
_:
|
|
active_stroke_handler = StrokeHandler.new(brush, plant_states, octree_managers, space_state, camera, gardening_collision_mask)
|
|
|
|
debug_print_lifecycle("Stroke %s started" % [active_stroke_handler.get_meta("class")])
|
|
|
|
|
|
# Draw instances at the new brush position
|
|
# And collect them all into one PaintingChanges object
|
|
func on_stroke_updated(brush_data:Dictionary):
|
|
assert(active_stroke_handler)
|
|
assert(active_painting_changes)
|
|
|
|
debug_print_lifecycle("Stroke %s updating..." % [active_stroke_handler.get_meta("class")])
|
|
var msec_start = FunLib.get_msec()
|
|
|
|
# mutex_placement.lock()
|
|
var changes = active_stroke_handler.get_stroke_update_changes(brush_data, global_transform)
|
|
apply_stroke_update_changes(changes)
|
|
# mutex_placement.unlock()
|
|
active_painting_changes.append_changes(changes)
|
|
|
|
var msec_end = FunLib.get_msec()
|
|
debug_print_lifecycle("Total stroke %s update took: %s" % [active_stroke_handler.get_meta("class"), FunLib.msec_to_time(msec_end - msec_start)])
|
|
|
|
|
|
# Use collected PaintingChanges to add UndoRedo actions
|
|
func on_stroke_finished():
|
|
assert(active_stroke_handler)
|
|
assert(active_painting_changes)
|
|
|
|
UndoRedoInterface.create_action(_undo_redo, "Apply Arborist MMI changes", 0, false, self)
|
|
UndoRedoInterface.add_do_method(_undo_redo, _action_apply_changes.bind(active_painting_changes))
|
|
UndoRedoInterface.add_undo_method(_undo_redo, _action_apply_changes.bind(active_painting_changes.pop_opposite()))
|
|
|
|
# We toggle this flag to avoid reapplying already commited changes all over again
|
|
UndoRedoInterface.commit_action(_undo_redo, false)
|
|
|
|
debug_print_lifecycle("Stroke %s finished, total changes made: %d" % [active_stroke_handler.get_meta("class"), active_painting_changes.changes.size()])
|
|
|
|
active_stroke_handler = null
|
|
active_painting_changes = null
|
|
|
|
|
|
# A wrapper for applying changes to avoid reaplying UndoRedo actions on commit_action()
|
|
func _action_apply_changes(changes):
|
|
# mutex_placement.lock()
|
|
apply_stroke_update_changes(changes)
|
|
# mutex_placement.unlock()
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Updating OctreeManager objects
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
# Replace LOD_Variants inside of a shared array owned by this OctreeManager
|
|
func refresh_octree_shared_LOD_variants(plant_index:int, LOD_variants:Array):
|
|
if octree_managers.size() > plant_index:
|
|
octree_managers[plant_index].set_LOD_variants(LOD_variants)
|
|
|
|
|
|
# Add changes to corresponding OctreeManager queues
|
|
# Then process them all at once
|
|
func apply_stroke_update_changes(changes:PaintingChanges):
|
|
debug_print_lifecycle(" Applying %d stroke changes" % [changes.changes.size()])
|
|
var msec_start = FunLib.get_msec()
|
|
|
|
var affected_octree_managers := []
|
|
|
|
for change in changes.changes:
|
|
var octree_manager:MMIOctreeManager = octree_managers[change.at_index]
|
|
|
|
match change.change_type:
|
|
0:
|
|
octree_manager.queue_placeforms_add(change.new_val)
|
|
1:
|
|
octree_manager.queue_placeforms_remove(change.new_val)
|
|
2:
|
|
octree_manager.queue_placeforms_set(change.new_val)
|
|
|
|
if !affected_octree_managers.has(change.at_index):
|
|
affected_octree_managers.append(change.at_index)
|
|
|
|
for index in affected_octree_managers:
|
|
var octree_manager = octree_managers[index]
|
|
octree_manager.process_queues()
|
|
emit_member_count(index)
|
|
|
|
var msec_end = FunLib.get_msec()
|
|
debug_print_lifecycle(" Applying stroke changes took: %s" % [FunLib.msec_to_time(msec_end - msec_start)])
|
|
|
|
|
|
func emit_member_count(octree_index:int):
|
|
member_count_updated.emit(octree_index, octree_managers[octree_index].root_octree_node.get_nested_member_count())
|
|
|
|
|
|
func _process(delta):
|
|
# try_update_LODs()
|
|
if visible:
|
|
update_LODs()
|
|
request_debug_redraw()
|
|
|
|
|
|
# Trigger a threaded LOD update
|
|
#func try_update_LODs():
|
|
# var should_post = false
|
|
#
|
|
# mutex_placement.lock()
|
|
# if done_instance_placement:
|
|
# done_instance_placement = false
|
|
# should_post = true
|
|
# mutex_placement.unlock()
|
|
#
|
|
# if should_post:
|
|
# semaphore_instance_placement.post()
|
|
|
|
|
|
# A function that carries out threaded LOD updates
|
|
#func thread_update_LODs(arg = null):
|
|
# while true:
|
|
# semaphore_instance_placement.wait()
|
|
#
|
|
# var should_exit = false
|
|
# mutex_placement.lock()
|
|
# if exit_instance_placement:
|
|
# should_exit = true
|
|
# mutex_placement.unlock()
|
|
# if should_exit: break
|
|
#
|
|
# mutex_placement.lock()
|
|
# update_LODs()
|
|
# done_instance_placement = true
|
|
# mutex_placement.unlock()
|
|
|
|
|
|
# Instigate LOD updates in OctreeManager objects
|
|
func update_LODs():
|
|
var camera_to_use:Camera3D = get_camera_3d()
|
|
if camera_to_use:
|
|
var camera_pos := camera_to_use.global_transform.origin
|
|
for octree_manager in octree_managers:
|
|
octree_manager.update_LODs(camera_pos, global_transform)
|
|
# This exists to properly render instances in editor even if there is no forwarded_input()
|
|
else:
|
|
for octree_manager in octree_managers:
|
|
octree_manager.update_LODs_no_camera()
|
|
|
|
|
|
# Add instances as a batch (mostly, as a result of importing Greenhouse data)
|
|
func batch_add_instances(placeforms: Array, plant_idx: int):
|
|
active_painting_changes = PaintingChanges.new()
|
|
active_stroke_handler = SH_Manual.new()
|
|
|
|
for placeform in placeforms:
|
|
active_stroke_handler.add_instance_placeform(placeform, plant_idx, active_painting_changes)
|
|
|
|
apply_stroke_update_changes(active_painting_changes)
|
|
on_stroke_finished()
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Input
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
func _unhandled_input(event):
|
|
if is_instance_of(event, InputEventKey) && !event.pressed:
|
|
if event.keycode == debug_get_dump_tree_key():
|
|
for octree_manager in octree_managers:
|
|
logger.info(octree_manager.root_octree_node.debug_dump_tree())
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Utility
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
# A hack to get editor camera
|
|
# active_camera_override should be set by a Gardener
|
|
# In-game just gets an active viewport's camera
|
|
func get_camera_3d():
|
|
if is_instance_valid(active_camera_override):
|
|
return active_camera_override
|
|
else:
|
|
active_camera_override = null
|
|
return get_viewport().get_camera_3d()
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Property export
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
func _get(property):
|
|
match property:
|
|
"octree_managers":
|
|
return octree_managers
|
|
return null
|
|
|
|
|
|
func _set(property, val):
|
|
var return_val = true
|
|
|
|
match property:
|
|
"octree_managers":
|
|
octree_managers = val
|
|
_:
|
|
return_val = false
|
|
|
|
return return_val
|
|
|
|
|
|
func _get_property_list():
|
|
var props := [
|
|
{
|
|
"name": "octree_managers",
|
|
"type": TYPE_ARRAY,
|
|
"usage": PROPERTY_USAGE_DEFAULT,
|
|
"hint": PROPERTY_HINT_NONE
|
|
},
|
|
]
|
|
return props
|
|
|
|
|
|
func _get_configuration_warnings():
|
|
var MMI_container_check = get_node("MMI_container")
|
|
if MMI_container_check && is_instance_of(MMI_container_check, Node3D):
|
|
return ""
|
|
else:
|
|
return "Arborist is missing a valid MMI_container child\nSince it should be created automatically, try reloading a scene or recreating a Gardener"
|
|
|
|
|
|
func add_child(node:Node, legible_unique_name:bool = false, internal:InternalMode=0) -> void:
|
|
super.add_child(node, legible_unique_name)
|
|
update_configuration_warnings()
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
# Debug
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
|
# A wrapper to request debug redraw for a specific OctreeManager
|
|
func request_debug_redraw_from_index(plant_index):
|
|
for index in range(plant_index, octree_managers.size()):
|
|
on_req_debug_redraw(octree_managers[index])
|
|
|
|
|
|
# Add an OctreeManager to the debug redraw waiting list
|
|
func on_req_debug_redraw(octree_manager:MMIOctreeManager):
|
|
if debug_redraw_requested_managers.has(octree_manager): return
|
|
debug_redraw_requested_managers.append(octree_manager)
|
|
|
|
|
|
# Request a debug redraw for all OctreeManager objects in a waiting list using a signal
|
|
# We manually get all indexes here instead of when an OctreeManager is added to the waiting list
|
|
# Because we expect the order of managers might change and indexes will become inaccurate
|
|
# Typically called from _process()
|
|
func request_debug_redraw():
|
|
if debug_redraw_requested_managers.is_empty(): return
|
|
|
|
var requested_indexes := []
|
|
for octree_manager in debug_redraw_requested_managers:
|
|
requested_indexes.append(octree_managers.find(octree_manager))
|
|
|
|
if !requested_indexes.is_empty():
|
|
req_debug_redraw.emit(octree_managers)
|
|
debug_redraw_requested_managers = []
|
|
|
|
|
|
func debug_get_dump_tree_key():
|
|
var key = FunLib.get_setting_safe("dreadpons_spatial_gardener/debug/dump_all_octrees_key", 0)
|
|
return Globals.index_to_enum(key, Globals.KeyboardKey)
|
|
|
|
|
|
func debug_print_lifecycle(string:String):
|
|
if !FunLib.get_setting_safe("dreadpons_spatial_gardener/debug/arborist_log_lifecycle", false): return
|
|
logger.info(string)
|