Adds missing data

This commit is contained in:
Michel 2025-02-03 19:17:20 +01:00
parent e6391d9fdd
commit 53cdcc3433
620 changed files with 47293 additions and 151 deletions

View file

@ -0,0 +1,124 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends Window
class_name CreateMaterialDialog
signal create_material(params:Dictionary)
var texture_list:Array[Texture2D]
var parent_dir_path:String
var plugin:CyclopsLevelBuilder:
get:
return plugin
set(value):
if value == plugin:
return
plugin = value
#print("CreateMaterialDialog setting plugin")
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func _on_bn_okay_pressed():
var mat_type:String = "standard" if %radio_stdMat.is_pressed() else "shader"
var tgt_param:String = "albedo_texture"
create_material.emit({
"name": %line_material_name.text,
"material_type" : mat_type,
"shader_res_path" : %line_shader_path.text,
"texture_parameter" : %target_slot.get_item_text(%target_slot.selected),
"uv_parameter" : %uv_slot.get_item_text(%uv_slot.selected),
"uv_type" : "1x1" if %radio_uv_1x1.is_pressed() else "pix_per_game_unit",
"pix_per_game_unit" : %line_pix_per_game_unit.text.to_int(),
"parent_dir" : parent_dir_path,
"textures" : texture_list
#material_type: ""
})
hide()
func _on_bn_cancel_pressed():
hide()
func _on_bn_browse_shader_pressed():
%FileDialog.popup_centered()
func _on_about_to_popup():
#print("CreateMaterialDialog about to popup")
var ed_iface:EditorInterface = plugin.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
var root_dir:EditorFileSystemDirectory = efs.get_filesystem()
if !texture_list.is_empty():
%line_material_name.text = texture_list[0].resource_path.get_file().get_basename()
func _on_file_dialog_file_selected(path:String):
var shader:Shader = ResourceLoader.load(path, "Shader")
if !shader:
return
%line_shader_path.text = path
update_shader_slot_list()
func update_shader_slot_list():
var path:String = %line_shader_path.text
var shader:Shader = ResourceLoader.load(path, "Shader")
%target_slot.clear()
%uv_slot.clear()
#TYPE_VECTOR2
if shader:
#Array of dictionaries
var params:Array = shader.get_shader_uniform_list()
for p in params:
#print("shader param ", str(p))
if p["hint_string"] == "Texture2D":
%target_slot.add_item(p["name"])
if p["type"] == TYPE_VECTOR2 || p["type"] == TYPE_VECTOR3:
%uv_slot.add_item(p["name"])
func _on_line_shader_path_text_changed(new_text):
update_shader_slot_list()

View file

@ -0,0 +1,182 @@
[gd_scene load_steps=6 format=3 uid="uid://b510d4yme5xtx"]
[ext_resource type="Script" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/create_material_dialog.gd" id="1_ysdvw"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7cu3j"]
bg_color = Color(0.309804, 0.309804, 0.309804, 0)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
[sub_resource type="Theme" id="Theme_u2063"]
PanelContainer/styles/panel = SubResource("StyleBoxFlat_7cu3j")
[sub_resource type="ButtonGroup" id="ButtonGroup_hlttb"]
[sub_resource type="ButtonGroup" id="ButtonGroup_rde8s"]
[node name="CreateMaterialDialog" type="Window"]
title = "Create Material"
position = Vector2i(0, 36)
size = Vector2i(600, 400)
script = ExtResource("1_ysdvw")
[node name="PanelContainer" type="PanelContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"]
layout_mode = 2
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
text = "Material Name:"
[node name="line_material_name" type="LineEdit" parent="PanelContainer/VBoxContainer/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="PanelContainer2" type="PanelContainer" parent="PanelContainer/VBoxContainer"]
layout_mode = 2
theme = SubResource("Theme_u2063")
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer2"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer"]
layout_mode = 2
text = "Material Type"
[node name="radio_stdMat" type="CheckBox" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
button_pressed = true
button_group = SubResource("ButtonGroup_hlttb")
text = "StandardMaterial3D"
[node name="radio_shaderMat" type="CheckBox" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
button_group = SubResource("ButtonGroup_hlttb")
text = "ShaderMaterial"
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer"]
layout_mode = 2
theme_override_constants/margin_left = 64
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Shader:"
[node name="line_shader_path" type="LineEdit" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="bn_browse_shader" type="Button" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
tooltip_text = "Pick shader to use for material"
text = "..."
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
text = "Texture Parameter:"
[node name="target_slot" type="OptionButton" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
[node name="HBoxContainer3" type="HBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer3"]
layout_mode = 2
text = "UV Parameter:"
[node name="uv_slot" type="OptionButton" parent="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer3"]
unique_name_in_owner = true
layout_mode = 2
[node name="PanelContainer" type="PanelContainer" parent="PanelContainer/VBoxContainer"]
layout_mode = 2
theme = SubResource("Theme_u2063")
[node name="VBoxContainer2" type="VBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2"]
layout_mode = 2
text = "UVs"
[node name="radio_uv_1x1" type="CheckBox" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
button_group = SubResource("ButtonGroup_rde8s")
text = "1 x 1"
[node name="radio_uv_scale_to_pix" type="CheckBox" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
button_pressed = true
button_group = SubResource("ButtonGroup_rde8s")
text = "Scale to pixel size"
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2"]
layout_mode = 2
theme_override_constants/margin_left = 64
[node name="HBoxContainer2" type="HBoxContainer" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2/MarginContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2/MarginContainer/HBoxContainer2"]
layout_mode = 2
text = "Pixels per game unit"
[node name="line_pix_per_game_unit" type="LineEdit" parent="PanelContainer/VBoxContainer/PanelContainer/VBoxContainer2/MarginContainer/HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
text = "32"
[node name="HBoxContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="bn_okay" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Okay"
[node name="bn_cancel" type="Button" parent="PanelContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Cancel
"
[node name="FileDialog" type="FileDialog" parent="."]
unique_name_in_owner = true
title = "Open a File"
size = Vector2i(600, 400)
ok_button_text = "Open"
file_mode = 0
[connection signal="about_to_popup" from="." to="." method="_on_about_to_popup"]
[connection signal="text_changed" from="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer/line_shader_path" to="." method="_on_line_shader_path_text_changed"]
[connection signal="pressed" from="PanelContainer/VBoxContainer/PanelContainer2/VBoxContainer/MarginContainer/VBoxContainer/HBoxContainer/bn_browse_shader" to="." method="_on_bn_browse_shader_pressed"]
[connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer/bn_okay" to="." method="_on_bn_okay_pressed"]
[connection signal="pressed" from="PanelContainer/VBoxContainer/HBoxContainer/bn_cancel" to="." method="_on_bn_cancel_pressed"]
[connection signal="file_selected" from="FileDialog" to="." method="_on_file_dialog_file_selected"]

View file

@ -0,0 +1,69 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends PopupPanel
class_name LineInput
@export var text:String:
get:
return text
set(value):
text = value
title = value
#%Label.text = text
@export var edit_text:String:
get:
return edit_text
set(value):
edit_text = value
%LineEdit.text = text
signal text_chosen(text:String)
# Called when the node enters the scene tree for the first time.
func _ready():
#%Label.text = text
#%LineEdit.text = edit_text
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func _on_line_edit_text_submitted(new_text):
text_chosen.emit(%LineEdit.text)
hide()
func _on_bn_accept_pressed():
text_chosen.emit(%LineEdit.text)
hide()
func _on_bn_cancel_pressed():
hide()

View file

@ -0,0 +1,40 @@
[gd_scene load_steps=2 format=3 uid="uid://tkp4i7e1fs5"]
[ext_resource type="Script" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/line_input.gd" id="1_5x46h"]
[node name="LineInput" type="PopupPanel"]
title = "Input"
size = Vector2i(200, 74)
visible = true
unresizable = false
borderless = false
always_on_top = true
script = ExtResource("1_5x46h")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
offset_left = 4.0
offset_top = 4.0
offset_right = 196.0
offset_bottom = 70.0
[node name="LineEdit" type="LineEdit" parent="VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="CenterContainer" type="CenterContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/CenterContainer"]
layout_mode = 2
[node name="bn_accept" type="Button" parent="VBoxContainer/CenterContainer/HBoxContainer"]
layout_mode = 2
text = "Okay"
[node name="bn_cancel" type="Button" parent="VBoxContainer/CenterContainer/HBoxContainer"]
layout_mode = 2
text = "Cancel"
[connection signal="text_submitted" from="VBoxContainer/LineEdit" to="." method="_on_line_edit_text_submitted"]
[connection signal="pressed" from="VBoxContainer/CenterContainer/HBoxContainer/bn_accept" to="." method="_on_bn_accept_pressed"]
[connection signal="pressed" from="VBoxContainer/CenterContainer/HBoxContainer/bn_cancel" to="." method="_on_bn_cancel_pressed"]

View file

@ -0,0 +1,16 @@
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://eajwlh2rlu3a"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_amf1g"]
bg_color = Color(0, 0, 0, 1)
border_width_left = 4
border_width_top = 4
border_width_right = 4
border_width_bottom = 4
border_color = Color(1, 0.890196, 0.729412, 1)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[resource]
PanelContainer/styles/panel = SubResource("StyleBoxFlat_amf1g")

View file

@ -0,0 +1,17 @@
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://di7ydnvl4n54r"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_amf1g"]
bg_color = Color(0, 0, 0, 1)
border_width_left = 4
border_width_top = 4
border_width_right = 4
border_width_bottom = 4
border_color = Color(0, 0, 0, 1)
border_blend = true
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[resource]
PanelContainer/styles/panel = SubResource("StyleBoxFlat_amf1g")

View file

@ -0,0 +1,16 @@
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://8ufqa1nourhn"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_amf1g"]
bg_color = Color(0, 0, 0, 1)
border_width_left = 4
border_width_top = 4
border_width_right = 4
border_width_bottom = 4
border_color = Color(1, 0.533333, 0, 1)
corner_radius_top_left = 4
corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[resource]
PanelContainer/styles/panel = SubResource("StyleBoxFlat_amf1g")

View file

@ -0,0 +1,154 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends PanelContainer
class_name MaterialButton
signal apply_material(mat_bn:MaterialButton)
signal select_material(mat_bn:MaterialButton, selection_type:SelectionList.Type)
@export var selected:bool = false:
get:
return selected
set(value):
if selected == value:
return
selected = value
update_border()
@export var active:bool = false:
get:
return active
set(value):
if active == value:
return
active = value
update_border()
@export_file("*.tres") var material_path:String:
get:
return material_path
set(value):
if material_path == value:
return
material_path = value
dirty = true
@export var group:RadioButtonGroup:
get:
return group
set(value):
if group == value:
return
if group != null:
group.remove_button(self)
group = value
if group != null:
group.add_button(self)
@export var theme_normal:Theme = preload("res://addons/cyclops_level_builder/docks/material_palette/material_viewer/mat_bn_normal_theme.tres")
@export var theme_selected:Theme = preload("res://addons/cyclops_level_builder/docks/material_palette/material_viewer/mat_bn_selected_theme.tres")
@export var theme_active:Theme = preload("res://addons/cyclops_level_builder/docks/material_palette/material_viewer/mat_bn_active_theme.tres")
var plugin:CyclopsLevelBuilder:
get:
return plugin
set(value):
if value == plugin:
return
plugin = value
dirty = true
var dirty:bool = true
var material_local:Material
func rebuild_thumbnail():
if !plugin:
return
var rp:EditorResourcePreview = plugin.get_editor_interface().get_resource_previewer()
rp.queue_resource_preview(material_path, self, "resource_preview_callback", null)
material_local = ResourceLoader.load(material_path, "Material")
# material_local = load(material_path)
%MaterialName.text = GeneralUtil.calc_resource_name(material_local)
tooltip_text = material_path
func resource_preview_callback(path:String, preview:Texture2D, thumbnail_preview:Texture2D, userdata:Variant):
#print("Set bn tex ", path)
%TextureRect.texture = preview
func _gui_input(event:InputEvent):
if event is InputEventMouseButton:
var e:InputEventMouseButton = event
if e.button_index == MOUSE_BUTTON_LEFT:
if e.pressed:
if e.double_click:
#apply_material_to_selected()
apply_material.emit(self)
else:
#if group:
#group.select_thumbnail(self)
#else:
#selected = true
# builder.tool_material_path = material_path
select_material.emit(self, SelectionList.choose_type(e.shift_pressed, e.ctrl_pressed))
get_viewport().set_input_as_handled()
func update_border():
if active:
theme = theme_active
elif selected:
theme = theme_selected
else:
theme = theme_normal
# Called when the node enters the scene tree for the first time.
func _ready():
update_border()
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
if dirty:
rebuild_thumbnail()
dirty = false
pass

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,58 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends RefCounted
class_name MaterialGroup
class Tier:
var name:String
var children:Array[MaterialGroup]
func _init(name:String = ""):
self.name = name
func create_child_with_name(name:String)->Tier:
var child:Tier = Tier.new(name)
children.append(child)
return child
func get_child_with_name(name:String):
for child in children:
if child.name == name:
return child
return null
func get_child_index_with_name(name:String)->int:
for i in children.size():
if children[i].name == name:
return i
return -1
func remove_child_with_name(name:String):
var idx:int = get_child_index_with_name(name)
if idx > -1:
children.remove_at(idx)
var root:Tier = Tier.new("Any")

View file

@ -0,0 +1,321 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends Tree
class_name MaterialGroupsTree
signal visiblity_changed
enum ButtonType { VISIBLE }
const bn_vis_off = preload("res://addons/cyclops_level_builder/art/icons/eye_closed.svg")
const bn_vis_on = preload("res://addons/cyclops_level_builder/art/icons/eye_open.svg")
@export var show_unused_dirs:bool = true:
get:
return show_unused_dirs
set(value):
if value == show_unused_dirs:
return
show_unused_dirs = value
reload_materials()
var plugin:CyclopsLevelBuilder:
get:
return plugin
set(value):
if value == plugin:
return
if plugin:
var ed_iface:EditorInterface = plugin.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
efs.filesystem_changed.disconnect(on_filesystem_changed)
efs.resources_reimported.disconnect(on_resources_reimported)
efs.resources_reload.disconnect(on_resources_reload)
plugin = value
%CreateMaterialDialog.plugin = plugin
if plugin:
var ed_iface:EditorInterface = plugin.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
efs.filesystem_changed.connect(on_filesystem_changed)
efs.resources_reimported.connect(on_resources_reimported)
efs.resources_reload.connect(on_resources_reload)
reload_materials()
var tree_item_to_path_map:Dictionary
var path_to_tree_item_map:Dictionary
func reload_materials():
#print("reload_materials")
clear()
tree_item_to_path_map.clear()
path_to_tree_item_map.clear()
if !plugin:
return
var ed_iface:EditorInterface = plugin.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
var root_dir:EditorFileSystemDirectory = efs.get_filesystem()
var root_tree_item:TreeItem = create_item()
root_tree_item.set_text(0, root_dir.get_name())
root_tree_item.set_checked(1, true)
root_tree_item.set_editable(1, true)
tree_item_to_path_map[root_tree_item] = root_dir.get_path()
path_to_tree_item_map[root_dir.get_path()] = root_tree_item
build_tree_recursive(root_dir, root_tree_item)
collapse_unused_dirs()
func build_tree_recursive(parent_dir:EditorFileSystemDirectory, tree_item_parent:TreeItem):
#print("par_dir count ", parent_dir.get_path(), parent_dir.get_subdir_count())
for i in parent_dir.get_subdir_count():
var child_dir:EditorFileSystemDirectory = parent_dir.get_subdir(i)
#print("add child ", child_dir.get_path())
if !show_unused_dirs && !dir_has_materials_recursive(child_dir):
continue
var item:TreeItem = create_item(tree_item_parent)
item.set_text(0, child_dir.get_name())
item.set_checked(1, true)
#item.set_editable(1, true)
item.add_button(1, bn_vis_on, ButtonType.VISIBLE, false, "Visible")
tree_item_to_path_map[item] = child_dir.get_path()
path_to_tree_item_map[child_dir.get_path()] = item
#print("path ", child_dir.get_path())
build_tree_recursive(child_dir, item)
func on_filesystem_changed():
reload_materials()
pass
func on_resources_reimported(resources: PackedStringArray):
pass
func on_resources_reload(resources: PackedStringArray):
pass
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func create_new_group():
pass
func delete_selected_group():
pass
func rename_selected_group():
pass
func _on_item_selected():
var item:TreeItem = get_selected()
item.get_index()
pass # Replace with function body.
func _on_item_edited():
var item:TreeItem = get_edited()
pass # Replace with function body.
func _on_popup_menu_id_pressed(id:int):
match id:
0:
create_new_group()
1:
delete_selected_group()
2:
rename_selected_group()
func _on_button_clicked(item:TreeItem, column:int, id:int, mouse_button_index:int):
var checked:bool = !item.is_checked(1)
item.set_checked(1, checked)
item.set_button(1, ButtonType.VISIBLE, bn_vis_on if checked else bn_vis_off)
visiblity_changed.emit()
func is_path_visible(path:String)->bool:
if !path_to_tree_item_map.has(path):
return false
var item:TreeItem = path_to_tree_item_map[path]
return item.is_checked(1)
func get_hidden_directories()->Array[String]:
var ret_paths:Array[String]
for path in path_to_tree_item_map.keys():
var item:TreeItem = path_to_tree_item_map[path]
if !item.is_checked(1):
ret_paths.append(path)
return ret_paths
func dir_has_materials(dir:EditorFileSystemDirectory)->bool:
for i in dir.get_file_count():
var file_type:StringName = dir.get_file_type(i)
if file_type == "StandardMaterial3D" || file_type == "ORMMaterial3D" || file_type == "ShaderMaterial":
return true
return false
func dir_has_materials_recursive(dir:EditorFileSystemDirectory)->bool:
if dir_has_materials(dir):
return true
for i in dir.get_subdir_count():
var child_dir:EditorFileSystemDirectory = dir.get_subdir(i)
if dir_has_materials_recursive(child_dir):
return true
return false
func collapse_unused_dirs():
if !plugin:
return
var ed_iface:EditorInterface = plugin.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
var root_dir:EditorFileSystemDirectory = efs.get_filesystem()
collapse_unused_dirs_recursive(root_dir)
func collapse_unused_dirs_recursive(dir:EditorFileSystemDirectory)->bool:
#print("path ", dir.get_path())
if !path_to_tree_item_map.has(dir.get_path()):
return false
var item:TreeItem = path_to_tree_item_map[dir.get_path()]
#print("item ", item.get_text(0))
var expanded:bool = dir_has_materials(dir)
# item.collapsed = !dir_has_materials(dir)
#
for i in dir.get_subdir_count():
var child_dir:EditorFileSystemDirectory = dir.get_subdir(i)
var result:bool = collapse_unused_dirs_recursive(child_dir)
if result:
expanded = true
item.collapsed = !expanded
return expanded
func _can_drop_data(at_position:Vector2, data:Variant):
# print("_can_drop_data %s" % data)
return typeof(data) == TYPE_DICTIONARY and data.has("type") and data["type"] == "files"
func _drop_data(at_position:Vector2, data:Variant):
var item:TreeItem = get_item_at_position(at_position)
if !item:
return
var files = data["files"]
#print("--drop")
var texture_list:Array[Texture2D]
for f in files:
# print("Dropping %s" % f)
var res:Resource = load(f)
if res is Texture2D:
#print("Dropping %s" % res.resource_path)
texture_list.append(res)
if texture_list.is_empty():
return
var parent_dir_path:String = tree_item_to_path_map[item]
%CreateMaterialDialog.parent_dir_path = parent_dir_path
%CreateMaterialDialog.texture_list = texture_list
%CreateMaterialDialog.popup_centered()
#%CreateMaterialDialog.popup_on_parent()
func _on_create_material_dialog_create_material(params:Dictionary):
#Prepare texture
var target_texture:Texture2D
var tex_list:Array = params["textures"]
if tex_list.size() == 1:
target_texture = tex_list[0]
elif tex_list.size() > 1:
var anim_tex:AnimatedTexture = AnimatedTexture.new()
anim_tex.frames = tex_list.size()
for i in tex_list.size():
anim_tex.set_frame_texture(i, tex_list[i])
target_texture = anim_tex
#Create material
if params["material_type"] == "standard":
var new_mat:StandardMaterial3D = StandardMaterial3D.new()
new_mat.albedo_texture = target_texture
if params["uv_type"] == "pix_per_game_unit":
var ppgu:int = params["pix_per_game_unit"]
new_mat.uv1_scale = Vector3(tex_list[0].get_width() / ppgu, tex_list[0].get_height() / ppgu, 1)
ResourceSaver.save(new_mat, params["parent_dir"] + "/" + params["name"] + ".tres")
elif params["material_type"] == "shader":
var new_mat:ShaderMaterial = ShaderMaterial.new()
new_mat.shader = ResourceLoader.load(params["shader_res_path"], "Shader")
#print("tex param ", params["texture_parameter"])
new_mat.set_shader_parameter(params["texture_parameter"], target_texture)
if params["uv_type"] == "pix_per_game_unit":
var ppgu:float = params["pix_per_game_unit"]
new_mat.set_shader_parameter(params["uv_parameter"], Vector3(tex_list[0].get_width() / ppgu, tex_list[0].get_height() / ppgu, 1))
ResourceSaver.save(new_mat, params["parent_dir"] + "/" + params["name"] + ".tres")
pass # Replace with function body.

View file

@ -0,0 +1,29 @@
[gd_scene load_steps=3 format=3 uid="uid://cchlfqbh0djdn"]
[ext_resource type="Script" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/material_groups_tree.gd" id="1_u4dlj"]
[ext_resource type="PackedScene" uid="uid://b510d4yme5xtx" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/create_material_dialog.tscn" id="2_adfk6"]
[node name="MatGroupTree" type="Tree"]
unique_name_in_owner = true
columns = 2
script = ExtResource("1_u4dlj")
[node name="PopupMenu" type="PopupMenu" parent="."]
unique_name_in_owner = true
item_count = 3
item_0/text = "New Group"
item_0/id = 0
item_1/text = "Delete Group"
item_1/id = 1
item_2/text = "Rename"
item_2/id = 2
[node name="CreateMaterialDialog" parent="." instance=ExtResource("2_adfk6")]
unique_name_in_owner = true
visible = false
[connection signal="button_clicked" from="." to="." method="_on_button_clicked"]
[connection signal="item_edited" from="." to="." method="_on_item_edited"]
[connection signal="item_selected" from="." to="." method="_on_item_selected"]
[connection signal="id_pressed" from="PopupMenu" to="." method="_on_popup_menu_id_pressed"]
[connection signal="create_material" from="CreateMaterialDialog" to="." method="_on_create_material_dialog_create_material"]

View file

@ -0,0 +1,240 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends PanelContainer
class_name MaterialViewer
#var button_group:RadioButtonGroup = RadioButtonGroup.new()
var builder:CyclopsLevelBuilder:
get:
return builder
set(value):
if value == builder:
return
if builder:
var ed_iface:EditorInterface = builder.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
efs.filesystem_changed.disconnect(on_filesystem_changed)
efs.resources_reimported.disconnect(on_resources_reimported)
efs.resources_reload.disconnect(on_resources_reload)
builder = value
%MatGroupTree.plugin = builder
if builder:
var ed_iface:EditorInterface = builder.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
efs.filesystem_changed.connect(on_filesystem_changed)
efs.resources_reimported.connect(on_resources_reimported)
efs.resources_reload.connect(on_resources_reload)
reload_materials()
var material_groups:MaterialGroup
var selected_material_paths:Array[String]
var material_viewer_state:MaterialViewerState = preload("res://addons/cyclops_level_builder/docks/material_palette/material_viewer/material_viewer_state_res.tres")
func on_filesystem_changed():
#print("on_filesystem_changed")
reload_materials()
pass
func on_resources_reimported(resources:PackedStringArray):
#print("on_resources_reimported ", resources)
pass
func on_resources_reload(resources:PackedStringArray):
#print("on_resources_reload ", resources)
pass
func reload_materials():
#return
for child in %ButtonArea.get_children():
%ButtonArea.remove_child(child)
child.queue_free()
if !builder:
return
var ed_iface:EditorInterface = builder.get_editor_interface()
var efs:EditorFileSystem = ed_iface.get_resource_filesystem()
var efsd:EditorFileSystemDirectory = efs.get_filesystem()
reload_materials_recursive(efsd)
pass
func reload_materials_recursive(dir:EditorFileSystemDirectory):
var mat_name_filter:String = %lineEd_filter.text
if !%MatGroupTree.is_path_visible(dir.get_path()):
return
#var vis = %MatGroupTree.is_path_visible(dir.get_path())
#print("reload check path ", dir.get_path(), " vis ", vis)
#get_hidden_directories()
var ed_iface:EditorInterface = builder.get_editor_interface()
var res_prev:EditorResourcePreview = ed_iface.get_resource_previewer()
for i in dir.get_file_count():
# dir.get_file(i)
var type:String = dir.get_file_type(i)
#"StandardMaterial3D"
if type == "StandardMaterial3D" || type == "ShaderMaterial" || type == "ORMMaterial3D":
var path:String = dir.get_file_path(i)
if !mat_name_filter.is_empty() && !path.contains(mat_name_filter):
continue
#print("path %s type %s" % [path, type])
#res_prev.queue_resource_preview(path, self, "resource_preview_callback", null)
var bn:MaterialButton = preload("res://addons/cyclops_level_builder/docks/material_palette/material_viewer/material_button.tscn").instantiate()
bn.material_path = path
bn.plugin = builder
bn.selected = selected_material_paths.has(path)
bn.active = !selected_material_paths.is_empty() && path == selected_material_paths[-1]
#button_group.add_button(bn)
bn.apply_material.connect(func(mat_bn:MaterialButton): apply_material(mat_bn))
bn.select_material.connect(func(mat_bn:MaterialButton, type:SelectionList.Type): select_material(mat_bn, type))
%ButtonArea.add_child(bn)
pass
for i in dir.get_subdir_count():
reload_materials_recursive(dir.get_subdir(i))
func apply_material(mat_bn:MaterialButton):
var cmd:CommandSetMaterial = CommandSetMaterial.new()
cmd.builder = builder
cmd.material_path = mat_bn.material_path
var is_obj_mode:bool = builder.mode == CyclopsLevelBuilder.Mode.OBJECT
var sel_blocks:Array[CyclopsBlock] = builder.get_selected_blocks()
for block in sel_blocks:
if is_obj_mode:
cmd.add_target(block.get_path(), block.control_mesh.get_face_indices())
else:
var face_indices:PackedInt32Array = block.control_mesh.get_face_indices(true)
if !face_indices.is_empty():
cmd.add_target(block.get_path(), face_indices)
if cmd.will_change_anything():
var undo:EditorUndoRedoManager = builder.get_undo_redo()
cmd.add_to_undo_manager(undo)
func is_active_material(path:String):
return !selected_material_paths.is_empty() && path == selected_material_paths[-1]
func select_material(mat_bn:MaterialButton, sel_type:SelectionList.Type):
match sel_type:
SelectionList.Type.REPLACE:
selected_material_paths = [mat_bn.material_path]
SelectionList.Type.TOGGLE:
var idx:int = selected_material_paths.find(mat_bn.material_path)
if idx >= 0:
selected_material_paths.remove_at(idx)
else:
selected_material_paths.append(mat_bn.material_path)
SelectionList.Type.RANGE:
var bn_list = %ButtonArea.get_children()
var range_from_idx:int = -1
var range_to_idx:int = -1
for i in bn_list.size():
if bn_list[i] == mat_bn:
range_to_idx = i
if is_active_material(bn_list[i].material_path):
range_from_idx = i
for i in range(range_from_idx, range_to_idx + (1 if range_from_idx < range_to_idx else -1), 1 if range_from_idx < range_to_idx else -1):
var path = bn_list[i].material_path
if selected_material_paths.has(path):
selected_material_paths.erase(path)
selected_material_paths.append(path)
material_viewer_state.active_material_path = \
"" if selected_material_paths.is_empty() else selected_material_paths[-1]
#print("set sel mat: ", material_viewer_state.active_material_path)
#print("sel mat list: ", selected_material_paths)
for bn in %ButtonArea.get_children():
var mat_idx:int = selected_material_paths.find(bn.material_path)
if mat_idx >= 0:
if mat_idx == selected_material_paths.size() - 1:
bn.active = true
else:
bn.active = false
bn.selected = true
else:
bn.active = false
bn.selected = false
#func resource_preview_callback(path:String, preview:Texture2D, userdata:Variant):
#pass
# Called when the node enters the scene tree for the first time.
func _ready():
#material_groups = MaterialGroup.new("All")
#
#reload_materials()
#var root:TreeItem = %Tree.create_item()
#var child1:TreeItem = %Tree.create_item(root)
#var child2:TreeItem = %Tree.create_item(root)
#var subchild1:TreeItem = %Tree.create_item(child1)
#subchild1.set_text(0, "Subchild1")
pass
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func _on_line_ed_filter_text_changed(new_text):
reload_materials()
func _on_mat_group_tree_visiblity_changed():
reload_materials()
func _on_bn_show_unused_dirs_toggled(toggled_on):
%MatGroupTree.show_unused_dirs = toggled_on

View file

@ -0,0 +1,66 @@
[gd_scene load_steps=3 format=3 uid="uid://denc7grw42qsu"]
[ext_resource type="Script" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/material_viewer.gd" id="1_nrjye"]
[ext_resource type="PackedScene" uid="uid://cchlfqbh0djdn" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/material_groups_tree.tscn" id="2_8hnut"]
[node name="MaterialViewer" type="PanelContainer"]
offset_right = 523.0
offset_bottom = 350.0
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1_nrjye")
[node name="HSplitContainer" type="HSplitContainer" parent="."]
layout_mode = 2
split_offset = 240
[node name="VBoxContainer2" type="VBoxContainer" parent="HSplitContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/VBoxContainer2"]
layout_mode = 2
[node name="bn_show_unused_dirs" type="Button" parent="HSplitContainer/VBoxContainer2/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Show unused directories"
toggle_mode = true
button_pressed = true
text = "Show unused"
[node name="MatGroupTree" parent="HSplitContainer/VBoxContainer2" instance=ExtResource("2_8hnut")]
layout_mode = 2
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="HSplitContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
text = "Filter:"
[node name="lineEd_filter" type="LineEdit" parent="HSplitContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "Filter materials"
[node name="PanelContainer" type="PanelContainer" parent="HSplitContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="ScrollContainer" type="ScrollContainer" parent="HSplitContainer/VBoxContainer/PanelContainer"]
layout_mode = 2
[node name="ButtonArea" type="HFlowContainer" parent="HSplitContainer/VBoxContainer/PanelContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[connection signal="toggled" from="HSplitContainer/VBoxContainer2/HBoxContainer/bn_show_unused_dirs" to="." method="_on_bn_show_unused_dirs_toggled"]
[connection signal="visiblity_changed" from="HSplitContainer/VBoxContainer2/MatGroupTree" to="." method="_on_mat_group_tree_visiblity_changed"]
[connection signal="text_changed" from="HSplitContainer/VBoxContainer/HBoxContainer/lineEd_filter" to="." method="_on_line_ed_filter_text_changed"]

View file

@ -0,0 +1,32 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends Resource
class_name MaterialViewerState
@export var active_material_path:String:
set(value):
if active_material_path != value:
active_material_path = value
emit_changed()

View file

@ -0,0 +1,7 @@
[gd_resource type="Resource" script_class="MaterialViewerState" load_steps=2 format=3 uid="uid://cwq6b2p7f631n"]
[ext_resource type="Script" path="res://addons/cyclops_level_builder/docks/material_palette/material_viewer/material_viewer_state.gd" id="1_3sifp"]
[resource]
script = ExtResource("1_3sifp")
active_material_path = "res://content/cardboard_material.tres"

View file

@ -0,0 +1,40 @@
# MIT License
#
# Copyright (c) 2023 Mark McKay
# https://github.com/blackears/cyclopsLevelBuilder
#
# 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.
@tool
extends Resource
class_name RadioButtonGroup
var buttons:Array
func select_button(button):
for t in buttons:
t.selected = t == button
func add_button(button):
buttons.append(button)
func remove_button(button):
buttons.remove_at(buttons.find(button))