Files
fps_project_1/addons/dreadpon.spatial_gardener/gardener/debug_viewer.gd

262 lines
8.5 KiB
GDScript

@tool
extends Node3D
#-------------------------------------------------------------------------------
# A previewer for octree structure
#-------------------------------------------------------------------------------
const FunLib = preload("../utility/fun_lib.gd")
const DponDebugDraw = preload("../utility/debug_draw.gd")
const MMIOctreeManager = preload("../arborist/mmi_octree/mmi_octree_manager.gd")
const MMIOctreeNode = preload("../arborist/mmi_octree/mmi_octree_node.gd")
# How many/which plants we want to preview
enum PlantViewModeFlags {
VIEW_NONE = 0,
VIEW_SELECTED_PLANT = 1,
VIEW_ALL_ACTIVE_PLANTS = 2,
VIEW_MAX = 3,
}
# What parts of an octree we want to preview
enum RenderModeFlags {
DRAW_OCTREE_NODES = 101,
DRAW_OCTREE_MEMBERS = 102,
}
var octree_MMIs:Array = []
var active_plant_view_mode:int = PlantViewModeFlags.VIEW_NONE
var active_render_modes:Array = [RenderModeFlags.DRAW_OCTREE_NODES]
var brush_active_plants:Array = []
var prop_edit_selected_plant: int = -1
#-------------------------------------------------------------------------------
# Debug view menu
#-------------------------------------------------------------------------------
# Create and initialize a debug view menu
static func make_debug_view_menu():
var debug_view_menu := MenuButton.new()
debug_view_menu.text = "Gardener Debug Viewer"
debug_view_menu.get_popup().hide_on_checkable_item_selection = false
debug_view_menu.get_popup().hide_on_item_selection = false
for i in range(0, PlantViewModeFlags.size() - 1):
debug_view_menu.get_popup().add_radio_check_item(PlantViewModeFlags.keys()[i].capitalize(), PlantViewModeFlags.values()[i])
debug_view_menu.get_popup().add_separator()
for i in range(0, RenderModeFlags.size()):
debug_view_menu.get_popup().add_check_item(RenderModeFlags.keys()[i].capitalize(), RenderModeFlags.values()[i])
return debug_view_menu
# Callback when flag is checked on a menu
func flag_checked(debug_view_menu:MenuButton, flag:int):
var flag_group = flag <= PlantViewModeFlags.VIEW_MAX
if flag_group:
active_plant_view_mode = flag
else:
if active_render_modes.has(flag):
active_render_modes.erase(flag)
else:
active_render_modes.append(flag)
up_to_date_debug_view_menu(debug_view_menu)
# Reset a menu to the current state of this DebugViewer
func up_to_date_debug_view_menu(debug_view_menu:MenuButton):
for i in range(0, debug_view_menu.get_popup().get_item_count()):
debug_view_menu.get_popup().set_item_checked(i, false)
update_debug_view_menu_to_flag(debug_view_menu, active_plant_view_mode)
for render_mode in active_render_modes:
update_debug_view_menu_to_flag(debug_view_menu, render_mode)
# Tick a flag in a menu
# TODO Decide if this should be simplified and moved to up_to_date_debug_view_menu
# Since flag checks happen in flag_checked anyways
func update_debug_view_menu_to_flag(debug_view_menu:MenuButton, flag:int):
var flag_group = flag <= PlantViewModeFlags.VIEW_MAX
for i in range(0, debug_view_menu.get_popup().get_item_count()):
var item_id = debug_view_menu.get_popup().get_item_id(i)
var id_group = item_id <= PlantViewModeFlags.VIEW_MAX
var opposite_state = !debug_view_menu.get_popup().is_item_checked(i)
if item_id == flag:
if flag_group:
debug_view_menu.get_popup().set_item_checked(i, true)
else:
debug_view_menu.get_popup().set_item_checked(i, opposite_state)
elif flag_group == id_group && flag_group:
debug_view_menu.get_popup().set_item_checked(i, false)
#-------------------------------------------------------------------------------
# Brush active plants
#-------------------------------------------------------------------------------
# Keep a local copy of selected for brush plant indexes
func set_brush_active_plant(is_brush_active, plant_index:int):
if is_brush_active:
if !brush_active_plants.has(plant_index):
brush_active_plants.append(plant_index)
else:
if brush_active_plants.has(plant_index):
brush_active_plants.erase(plant_index)
brush_active_plants.sort()
func reset_brush_active_plants():
brush_active_plants = []
#-------------------------------------------------------------------------------
# Selected for prop edit plants
#-------------------------------------------------------------------------------
func set_prop_edit_selected_plant(plant_index:int):
prop_edit_selected_plant = plant_index
func reset_prop_edit_selected_plant():
prop_edit_selected_plant = -1
#-------------------------------------------------------------------------------
# Debug redraw requests
#-------------------------------------------------------------------------------
func request_debug_redraw(octree_managers:Array):
debug_redraw(octree_managers)
#-------------------------------------------------------------------------------
# Drawing the structure
#-------------------------------------------------------------------------------
# Redraw every fitting octree
func debug_redraw(octree_managers:Array):
var used_octree_managers = []
match active_plant_view_mode:
# Don't draw anything
PlantViewModeFlags.VIEW_NONE:
ensure_MMIs(0)
# Draw only the plant selected for prop edit
PlantViewModeFlags.VIEW_SELECTED_PLANT:
if prop_edit_selected_plant >= 0:
ensure_MMIs(1)
used_octree_managers.append(octree_managers[prop_edit_selected_plant])
else:
ensure_MMIs(0)
# Draw all brush active plants
PlantViewModeFlags.VIEW_ALL_ACTIVE_PLANTS:
ensure_MMIs(brush_active_plants.size())
for plant_index in brush_active_plants:
used_octree_managers.append(octree_managers[plant_index])
for i in range(0, used_octree_managers.size()):
var MMI:MultiMeshInstance3D = octree_MMIs[i]
var octree_mamager:MMIOctreeManager = used_octree_managers[i]
debug_draw_node(octree_mamager.root_octree_node, MMI)
func erase_all():
ensure_MMIs(0)
# Make sure there is an MMI for every octree we're about to draw
# Passing 0 effectively erases any debug renders
func ensure_MMIs(amount:int):
if octree_MMIs.size() < amount:
for i in range(octree_MMIs.size(), amount):
var MMI = MultiMeshInstance3D.new()
add_child(MMI)
MMI.cast_shadow = false
MMI.multimesh = MultiMesh.new()
MMI.multimesh.transform_format = 1
MMI.multimesh.use_colors = true
MMI.multimesh.mesh = DponDebugDraw.generate_cube(Vector3.ONE, Color.WHITE)
octree_MMIs.append(MMI)
elif octree_MMIs.size() > amount:
var MMI = null
while octree_MMIs.size() > amount:
MMI = octree_MMIs.pop_back()
remove_child(MMI)
MMI.queue_free()
# Recursively draw an octree node
func debug_draw_node(octree_node:MMIOctreeNode, MMI:MultiMeshInstance3D):
var draw_node := active_render_modes.has(RenderModeFlags.DRAW_OCTREE_NODES)
var draw_members := active_render_modes.has(RenderModeFlags.DRAW_OCTREE_MEMBERS)
# Reset the instance counts if this node is a root
if !octree_node.parent:
MMI.multimesh.instance_count = 0
MMI.multimesh.visible_instance_count = 0
set_debug_redraw_instance_count(octree_node, MMI, draw_node, draw_members)
var extents:Vector3
var render_transform:Transform3D
var index:int
if draw_node:
extents = Vector3(octree_node.extent, octree_node.extent, octree_node.extent) * 0.999 * 2.0
render_transform = Transform3D(Basis.IDENTITY.scaled(extents), octree_node.center_pos)
index = MMI.multimesh.visible_instance_count
MMI.multimesh.visible_instance_count += 1
MMI.multimesh.set_instance_transform(index, render_transform)
MMI.multimesh.set_instance_color(index, octree_node.debug_get_color())
if draw_members && octree_node.is_leaf:
var member_extent = FunLib.get_setting_safe("dreadpons_spatial_gardener/debug/debug_viewer_octree_member_size", 0.0) * 0.5
extents = Vector3(member_extent, member_extent, member_extent)
var basis = Basis.IDENTITY.scaled(extents)
for placeform in octree_node.get_placeforms():
render_transform = Transform3D(basis, placeform[0])
index = MMI.multimesh.visible_instance_count
MMI.multimesh.visible_instance_count += 1
MMI.multimesh.set_instance_transform(index, render_transform)
MMI.multimesh.set_instance_color(index, Color.WHITE)
for child in octree_node.child_nodes:
debug_draw_node(child, MMI)
# Recursively set the appropriate instance count for an MMI
func set_debug_redraw_instance_count(octree_node:MMIOctreeNode, MMI:MultiMeshInstance3D, draw_node:bool, draw_members:bool):
if draw_node:
MMI.multimesh.instance_count += 1
if octree_node.is_leaf && draw_members:
MMI.multimesh.instance_count += octree_node.member_count()
for child in octree_node.child_nodes:
set_debug_redraw_instance_count(child, MMI, draw_node, draw_members)