Spatial gizmo plugins

Introduction

Spatial gizmo plugins are used by the editor and custom plugins to define the gizmos attached to any kind of Spatial node. This tutorial will show you the two main approaches to defining your own custom gizmos. The first option works well for simple gizmos and creates less clutter in your plugin structure, while the second one will let you store some per-gizmo data. Note Making plugins page.

The EditorSpatialGizmoPlugin

EditorSpatialGizmoPlugin. This will allow us to set a name for the new gizmo type and define other behaviors such as whether the gizmo can be hidden or not. This would be a basic setup:

  1. # MyCustomGizmoPlugin.gdextends EditorSpatialGizmoPluginfunc get_name(): return "CustomNode"
  1. # MyCustomEditorPlugin.gdtoolextends EditorPluginconst MyCustomGizmoPlugin = preload("res://addons/my-addon/MyCustomGizmoPlugin.gd")var gizmo_plugin = MyCustomGizmoPlugin.new()func _enter_tree(): add_spatial_gizmo_plugin(gizmo_plugin)func _exit_tree(): remove_spatial_gizmo_plugin(gizmo_plugin)

EditorSpatialGizmoPlugin is enough. If you want to store some per-gizmo data or you are porting a Godot 3.0 gizmo to 3.1+, you should go with the second approach.

Simple approach

has_gizmo() method so that it returns true when the spatial parameter is of our target type.

  1. # ...func has_gizmo(spatial): return spatial is MyCustomSpatial# ...

redraw() or all the handle related ones.

  1. # ...func _init(): create_material("main", Color(1, 0, 0)) create_handle_material("handles")func redraw(gizmo): gizmo.clear() var spatial = gizmo.get_spatial_node() var lines = PoolVector3Array() lines.push_back(Vector3(0, 1, 0)) lines.push_back(Vector3(0, spatial.my_custom_value, 0)) var handles = PoolVector3Array() handles.push_back(Vector3(0, 1, 0)) handles.push_back(Vector3(0, spatial.my_custom_value, 0)) gizmo.add_lines(lines, get_material("main", gizmo), false) gizmo.add_handles(handles, get_material("handles", gizmo))# ...

get_material(). This method retrieves one of the material’s variants depending on the state of the gizmo (selected and/or editable). So the final plugin would look somewhat like this:

  1. extends EditorSpatialGizmoPluginconst MyCustomSpatial = preload("res://addons/my-addon/MyCustomSpatial.gd")func _init(): create_material("main", Color(1,0,0)) create_handle_material("handles")func has_gizmo(spatial): return spatial is MyCustomSpatialfunc redraw(gizmo): gizmo.clear() var spatial = gizmo.get_spatial_node() var lines = PoolVector3Array() lines.push_back(Vector3(0, 1, 0)) lines.push_back(Vector3(0, spatial.my_custom_value, 0)) var handles = PoolVector3Array() handles.push_back(Vector3(0, 1, 0)) handles.push_back(Vector3(0, spatial.my_custom_value, 0)) gizmo.add_lines(lines, get_material("main", gizmo), false) gizmo.add_handles(handles, get_material("handles", gizmo))# You should implement the rest of handle-related callbacks# (get_handle_name(), get_handle_value(), commit_handle()...).

EditorSpatialGizmoPlugin to get properly working handles.

Alternative approach

EditorSpatialGizmo, maybe because we want to have some state stored in each gizmo or because we are porting an old gizmo plugin and we don’t want to go through the rewriting process. create_gizmo(), so it returns our custom gizmo implementation for the Spatial nodes we want to target.

  1. # MyCustomGizmoPlugin.gdextends EditorSpatialGizmoPluginconst MyCustomSpatial = preload("res://addons/my-addon/MyCustomSpatial.gd")const MyCustomGizmo = preload("res://addons/my-addon/MyCustomGizmo.gd")func _init(): create_material("main", Color(1, 0, 0)) create_handle_material("handles")func create_gizmo(spatial): if spatial is MyCustomSpatial: return MyCustomGizmo.new() else: return null

EditorSpatialGizmo, like so:

  1. # MyCustomGizmo.gdextends EditorSpatialGizmo# You can store data in the gizmo itself (more useful when working with handles).var gizmo_size = 3.0func redraw(): clear() var spatial = get_spatial_node() var lines = PoolVector3Array() lines.push_back(Vector3(0, 1, 0)) lines.push_back(Vector3(gizmo_size, spatial.my_custom_value, 0)) var handles = PoolVector3Array() handles.push_back(Vector3(0, 1, 0)) handles.push_back(Vector3(gizmo_size, spatial.my_custom_value, 0)) var material = get_plugin().get_material("main", self) add_lines(lines, material, false) var handles_material = get_plugin().get_material("handles", self) add_handles(handles, handles_material)# You should implement the rest of handle-related callbacks# (get_handle_name(), get_handle_value(), commit_handle()...).

EditorSpatialGizmo to get properly working handles.