262 lines
8.5 KiB
GDScript
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)
|