Custom resource Librarys (just like MeshLibrarys?)

Godot Version

4.2

Question

Hi everyone, pls excuse my bad english

in Godot there are MeshLibrary, i talked a lot with ChatGPT, cause for my project every Mesh needs a few more data, so i tried to make a plugin with a custom Library (the following code is a shortened example)

CustomLibrary.gd

@tool
extends Resource
class_name CustomLibrary

@export var custom : Dictionary = Dictionary.new()

Empty the Library

func clear():
custom = Dictionary()

Create a new Item with given ID

func create_item( _id : int ):
custom[_id] = {
“entry1”: null }

Remove Item with given ID

func remove_item( _id : int ): custom.erase(_id)

should work like the same Method at the MeshLibrary

func get_item_list() → PackedInt32Array:
var item_list : PackedInt32Array
for i in custom.keys(): item_list.append(i)
return item_list

just like find_item_by_name in a MeshLibrary

func find_item_by_entry1( _entry : Object ) → int:
for _id in custom.keys(): if custom[_id][“entry1”] == _entry: return _id
return -1 # as i know, -1 is the last element, but i cant return null as a int :confused:

Object will replaced with the one i need in my project

func get_item_enty1( _id : int ) → Object: return custom[_id][“entry1”}

func set_item_entry1( _id : int, _entry : Object ): custom[_id][“entry1”] = _entry

when you chose a MeshLibrary, you got one dropdown menu where you can add and remove items or export from szene (with and without transform),
also you can manipulate the items in the isnpector, but sadly there is no where a gdscript inspectorplugin which shows how the meshlibrary manipulation is working.

i thought something like use parse_property, make a loop for every item and add for every item a group with controls in the inspector to manipulate the data, just like the MeshLibrary has, but i dont know how i can make these groups (in a class i would use export_group) and i dont know which control to add.

The perfect help, i think, would be a EditorInspectorPlugin script which exactly adds the same things like the MeshLibrary has

and for the dropdown Menu over the szene window i have no idea how to implement it :\

If you want to do it as the MeshLibrary does it then you’ll have to implement the functions Object._get(), Object._set(), and Object._get_property_list() and make the script a @tool script.

And use the format <main_group>/<sub_group>/<property_name> for the properties you will _get(), _set(), and export in the _get_property_list() functions.

Example:

@tool
class_name AdvancedResource extends Resource


var _data:Array[Dictionary] = []


func _init() -> void:
	_data.push_back({"name": "", "mesh": null, "transform": Transform3D()})
	_data.push_back({"name": "", "mesh": null, "transform": Transform3D()})
	_data.push_back({"name": "", "mesh": null, "transform": Transform3D()})


func _get(property: StringName) -> Variant:
	# split the property by /
	var splits = property.split("/")
	if splits.size() == 3 and splits[0] == "items":
		# if the size is 3 and the first split is "items"
		# then we have a property with name items/<index>/<key>
		var idx = int(splits[1])
		var key = splits[2]
		# return the value
		return _data[idx][key]

	return null


func _set(property: StringName, value: Variant) -> bool:
# split the property by /
	var splits = property.split("/")
	if splits.size() == 3 and splits[0] == "items":
		# if the size is 3 and the first split is "items"
		# then we have a property with name items/<index>/<key>
		var idx = int(splits[1])
		var key = splits[2]
		# set the value and return true
		_data[idx][key] = value
		return true

	return false


func _get_property_list() -> Array[Dictionary]:
	var result:Array[Dictionary] = []

	# Save the _data array to the disk but don't show it in the editor
	result.push_back({
			"name": "_data",
			"type": TYPE_ARRAY,
			"usage": PROPERTY_USAGE_NO_EDITOR
		})

	# for each entry in the _data array create the corresponding items/<index>/<prop_name>
	# with the correct configuration
	for i in _data.size():
		var obj = _data[i]
		result.push_back({
			"name": "items/%d/name" % i,
			"type": TYPE_STRING,
			"usage": PROPERTY_USAGE_EDITOR,
		})
		result.push_back({
			"name": "items/%d/mesh" % i,
			"type": TYPE_OBJECT,
			"hint": PROPERTY_HINT_RESOURCE_TYPE,
			"hint_string": "Mesh",
			"usage": PROPERTY_USAGE_EDITOR,
		})
		result.push_back({
			"name": "items/%d/transform" % i,
			"type": TYPE_TRANSFORM3D,
			"usage": PROPERTY_USAGE_EDITOR,
		})

	return result

But, honestly, just create a custom Resource that holds the information you want each entry to hold and export an array of those resources. For example:

Your main resource script:

class_name AdvancedResource extends Resource

@export var data:Array[CustomData]

and the data you want to hold in each entry:

class_name CustomData extends Resource

@export var name:String = ""
@export var mesh:Mesh = null
@export var transform:Transform3D = Transform3D()

And you will have pretty much the same functionality with way less code and more strongly typed.

1 Like

Thanks for your explanation.

Do you also know how to implement a menu like the MeshLibrary shows:
image

You’ll need to create a small plugin and do it from there.

For example:

@tool
extends EditorPlugin

enum MenuItem {
	ENTRY_1, ENTRY_2
}

var menu_button:MenuButton

func _enter_tree() -> void:
	var inspector = EditorInterface.get_inspector()
	inspector.edited_object_changed.connect(_on_edited_object_changed)


func _exit_tree() -> void:
	# Clean up nodes
	remove_menu_button()
	var inspector = EditorInterface.get_inspector()
	inspector.edited_object_changed.disconnect(_on_edited_object_changed)


func _on_edited_object_changed() -> void:
	var inspector = EditorInterface.get_inspector()
	if inspector.get_edited_object() is MeshInstance3D:
		add_menu_button()
	else:
		remove_menu_button()

func add_menu_button() -> void:
	menu_button = MenuButton.new()
	menu_button.text = "Menu"
	var popup = menu_button.get_popup()
	popup.add_item("Entry 1", MenuItem.ENTRY_1)
	popup.add_item("Entry 2", MenuItem.ENTRY_2)

	popup.id_pressed.connect(func(id:int):
		print('ID %s pressed' % id)
	)

	add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, menu_button)


func remove_menu_button() -> void:
	if is_instance_valid(menu_button):
		remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, menu_button)
		menu_button.queue_free()

Check the EditorInterface and EditorPlugin pages for more info

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.