Gizmo not updated when changing data in Dictionary in inspector

Godot Version

4.2.2 Stable

Question

I made a plugin to make a gizmo for a custom asset I’m developing. Thing is, that asset relies on a Dictionary to set the properties of the gizmo, and when I update the dictionary from the inspector, it does nothing and the changes only apply when I change something else in the scene.

Relevant code for the plugin and gizmo plugin:

#faking2d.gd
@tool
extends EditorPlugin

const GIZMO = preload("res://addons/faking2d/linegizmo.gd")

var gizmo_plugin = GIZMO.new()

func _enter_tree():
	add_node_3d_gizmo_plugin(gizmo_plugin)

func _exit_tree():
	remove_node_3d_gizmo_plugin(gizmo_plugin)
#linegizmo.gd
extends EditorNode3DGizmoPlugin

func _get_gizmo_name():
	return "Lines for 3D mapped platforms"

func _has_gizmo(node):
	return node is MyClass #custom class

func _redraw(gizmo : EditorNode3DGizmo):
	gizmo.clear()

	var materials : Dictionary = {}
	
	var meshes = []
	var transforms = []
	var colors = []

	#all arrays are filled in here with data from the dictionary in the editor

	for i in range(meshes.size()):
		gizmo.add_mesh(meshes[i], materials[colors[i]], transforms[i])

Is this intended or did I find a bug?

1 Like

I have a similar problem where I have a custom node type that has properties that I want reflected in my 3D gizmo. When I change these properties in the inspector, _redraw is not called on my 3D gizmo, so it becomes out of date until I switch back and forth between nodes in my scene panel to force a refresh of the gizmo.

My only solution right now is very hacky, so I’d like to hear how others approach this problem.

My hack is to have an autoload tool script that calls _redraw on the last selected gizmo for every _process:

@tool
extends Node3D
var gizmo: EditorNode3DGizmo;
var gizmo_plugin: EditorNode3DGizmoPlugin;

func _process(_delta: float) -> void:
	if gizmo_plugin != null \
		&& !gizmo_plugin.is_queued_for_deletion() \
		&& !gizmo.is_queued_for_deletion() \
		&& gizmo.get_node_3d() != null \
		&& !gizmo.get_node_3d().is_queued_for_deletion():
			gizmo_plugin._redraw(gizmo)

This hack requires me to add this into my EditorNode3DGizmoPlugin class:

func _redraw(gizmo: EditorNode3DGizmo) -> void:
	GizmoUpdater.gizmo = gizmo
	GizmoUpdater.gizmo_plugin = self

This also requires a bunch of cleanup code to be added, so my full gizmo script looks something more like this:

class_name MyGizmo extends EditorNode3DGizmoPlugin

var mat: StandardMaterial3D

func _init() -> void:
	cleanup()
	create_material("main", Color(0.5,1,0.5))
	mat = get_material("main") # saving mat to work around Godot issue #96481

func _get_gizmo_name() -> String:
	return "My Gizmo"

func _has_gizmo(node: Node3D) -> bool:
	cleanup()
	return node is MyNodeType

func _redraw(gizmo: EditorNode3DGizmo) -> void:
	gizmo.clear()
	GizmoUpdater.gizmo = gizmo
	GizmoUpdater.gizmo_plugin = self

	var node3d = gizmo.get_node_3d()

	var lines = PackedVector3Array()

	lines.push_back(Vector3(0, 1, 0))
	lines.push_back(Vector3(0, node3d.my_custom_value, 0))

	gizmo.add_lines(lines, mat, false)  # should be get_material("main", gizmo), but this works around Godot issue #96481

func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		cleanup()

func cleanup() -> void:
	GizmoUpdater.gizmo = null
	GizmoUpdater.gizmo_plugin = null