From 8c2470bf3bf7aeabc38b41ae16f17d7c6f7e92fc Mon Sep 17 00:00:00 2001 From: JFonS Date: Sun, 27 Jan 2019 12:34:16 +0100 Subject: [PATCH] Add tutorial for custom spatial gizmos plugin. --- tutorials/plugins/editor/index.rst | 1 + tutorials/plugins/editor/spatial_gizmos.rst | 219 ++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 tutorials/plugins/editor/spatial_gizmos.rst diff --git a/tutorials/plugins/editor/index.rst b/tutorials/plugins/editor/index.rst index 86b845cee..de0cd12d7 100644 --- a/tutorials/plugins/editor/index.rst +++ b/tutorials/plugins/editor/index.rst @@ -7,3 +7,4 @@ Editor plugins making_plugins import_plugins + spatial_gizmos \ No newline at end of file diff --git a/tutorials/plugins/editor/spatial_gizmos.rst b/tutorials/plugins/editor/spatial_gizmos.rst new file mode 100644 index 000000000..623fb9d43 --- /dev/null +++ b/tutorials/plugins/editor/spatial_gizmos.rst @@ -0,0 +1,219 @@ +.. _doc_spatial_gizmo_plugins: + +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:: This tutorial assumes you already know how to make generic plugins. If + in doubt, refer to the :ref:`doc_making_plugins` page. + +The EditorSpatialGizmoPlugin +---------------------------- + +Regardless of the approach we choose, we will need to create a new +:ref:`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: + +:: + + # MyCustomGizmoPlugin.gd + + extends EditorSpatialGizmoPlugin + + func get_name(): + return "CustomNode" + + +:: + + # MyCustomEditorPlugin.gd + + tool + extends EditorPlugin + + const 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) + + +For simple gizmos, just inheriting :ref:`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 +--------------- + +The first step is to, in our custom gizmo plugin, override the :ref:`has_gizmo()` +method so that it returns ``true`` when the spatial parameter is of our target type. + +:: + + # ... + + func has_gizmo(spatial): + return spatial is MyCustomSpatial + # ... + +Then we can override methods like :ref:`redraw()` +or all the handle related ones. + +:: + + # ... + + func _init(): + create_material("main", Color(1, 0, 0)) + create_handles_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)) + + # ... + +Note that we created a material in the `_init` method, and retrieved it in the `redraw` +method using :ref:`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: + +:: + + extends EditorSpatialGizmoPlugin + + const MyCustomSpatial = preload("res://addons/my-addon/MyCustomSpatial.gd") + + func _init(): + create_material("main", Color(1,0,0)) + create_handles_material("handles") + + func has_gizmo(spatial): + return spatial is MyCustomSpatial + + 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)) + + # you should implement the rest of handle-related callbacks + # (get_handle_name(), get_handle_value(), commit_handle()...) + +Note that we just added some handles in the redraw method, but we still need to implement +the rest of handle-related callbacks in :ref:`EditorSpatialGizmoPlugin ` +to get properly working handles. + +Alternative approach +-------------------- + +In some cases we want to provide our own implementation of :ref:`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. + +In these cases all we need to do is, in our new gizmo plugin, override +:ref:`create_gizmo()`, so it returns our custom gizmo implementation +for the Spatial nodes we want to target. + +:: + + # MyCustomGizmoPlugin.gd + extends EditorSpatialGizmoPlugin + + const 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_handles_material("handles") + + func create_gizmo(spatial): + if spatial is MyCustomSpatial: + return MyCustomGizmo.new() + else: + return null + +This way all the gizmo logic and drawing methods can be implemented in a new clas extending +:ref:`EditorSpatialGizmo`, like so: + +:: + + # MyCustomGizmo.gd + + extends EditorSpatialGizmo + + # You can store data in the gizmo itself (more useful when working with handles) + var gizmo_size = 3.0 + + func 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()...) + +Note that we just added some handles in the redraw method, but we still need to implement +the rest of handle-related callbacks in :ref:`EditorSpatialGizmo` + to get properly working handles. \ No newline at end of file