spatial_gizmos.rst 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. .. _doc_spatial_gizmo_plugins:
  2. Spatial gizmo plugins
  3. =====================
  4. Introduction
  5. ------------
  6. Spatial gizmo plugins are used by the editor and custom plugins to define the
  7. gizmos attached to any kind of Spatial node.
  8. This tutorial will show you the two main approaches to defining your own custom
  9. gizmos. The first option works well for simple gizmos and creates less clutter in
  10. your plugin structure, while the second one will let you store some per-gizmo data.
  11. .. note:: This tutorial assumes you already know how to make generic plugins. If
  12. in doubt, refer to the :ref:`doc_making_plugins` page.
  13. The EditorSpatialGizmoPlugin
  14. ----------------------------
  15. Regardless of the approach we choose, we will need to create a new
  16. :ref:`EditorSpatialGizmoPlugin <class_EditorSpatialGizmoPlugin>`. This will allow
  17. us to set a name for the new gizmo type and define other behaviors such as whether
  18. the gizmo can be hidden or not.
  19. This would be a basic setup:
  20. ::
  21. # MyCustomGizmoPlugin.gd
  22. extends EditorSpatialGizmoPlugin
  23. func get_name():
  24. return "CustomNode"
  25. ::
  26. # MyCustomEditorPlugin.gd
  27. tool
  28. extends EditorPlugin
  29. const MyCustomGizmoPlugin = preload("res://addons/my-addon/MyCustomGizmoPlugin.gd")
  30. var gizmo_plugin = MyCustomGizmoPlugin.new()
  31. func _enter_tree():
  32. add_spatial_gizmo_plugin(gizmo_plugin)
  33. func _exit_tree():
  34. remove_spatial_gizmo_plugin(gizmo_plugin)
  35. For simple gizmos, just inheriting :ref:`EditorSpatialGizmoPlugin <class_EditorSpatialGizmoPlugin>`
  36. is enough. If you want to store some per-gizmo data or you are porting a Godot 3.0 gizmo
  37. to 3.1+, you should go with the second approach.
  38. Simple approach
  39. ---------------
  40. The first step is to, in our custom gizmo plugin, override the :ref:`has_gizmo()<class_EditorSpatialGizmoPlugin_method_has_gizmo>`
  41. method so that it returns ``true`` when the spatial parameter is of our target type.
  42. ::
  43. # ...
  44. func has_gizmo(spatial):
  45. return spatial is MyCustomSpatial
  46. # ...
  47. Then we can override methods like :ref:`redraw()<class_EditorSpatialGizmoPlugin_method_redraw>`
  48. or all the handle related ones.
  49. ::
  50. # ...
  51. func _init():
  52. create_material("main", Color(1, 0, 0))
  53. create_handle_material("handles")
  54. func redraw(gizmo):
  55. gizmo.clear()
  56. var spatial = gizmo.get_spatial_node()
  57. var lines = PoolVector3Array()
  58. lines.push_back(Vector3(0, 1, 0))
  59. lines.push_back(Vector3(0, spatial.my_custom_value, 0))
  60. var handles = PoolVector3Array()
  61. handles.push_back(Vector3(0, 1, 0))
  62. handles.push_back(Vector3(0, spatial.my_custom_value, 0))
  63. gizmo.add_lines(lines, get_material("main", gizmo), false)
  64. gizmo.add_handles(handles, get_material("handles", gizmo))
  65. # ...
  66. Note that we created a material in the `_init` method, and retrieved it in the `redraw`
  67. method using :ref:`get_material()<class_EditorSpatialGizmoPlugin_method_get_material>`. This
  68. method retrieves one of the material's variants depending on the state of the gizmo
  69. (selected and/or editable).
  70. So the final plugin would look somewhat like this:
  71. ::
  72. extends EditorSpatialGizmoPlugin
  73. const MyCustomSpatial = preload("res://addons/my-addon/MyCustomSpatial.gd")
  74. func _init():
  75. create_material("main", Color(1,0,0))
  76. create_handle_material("handles")
  77. func has_gizmo(spatial):
  78. return spatial is MyCustomSpatial
  79. func redraw(gizmo):
  80. gizmo.clear()
  81. var spatial = gizmo.get_spatial_node()
  82. var lines = PoolVector3Array()
  83. lines.push_back(Vector3(0, 1, 0))
  84. lines.push_back(Vector3(0, spatial.my_custom_value, 0))
  85. var handles = PoolVector3Array()
  86. handles.push_back(Vector3(0, 1, 0))
  87. handles.push_back(Vector3(0, spatial.my_custom_value, 0))
  88. gizmo.add_lines(lines, get_material("main", gizmo), false)
  89. gizmo.add_handles(handles, get_material("handles", gizmo))
  90. # You should implement the rest of handle-related callbacks
  91. # (get_handle_name(), get_handle_value(), commit_handle()...).
  92. Note that we just added some handles in the redraw method, but we still need to implement
  93. the rest of handle-related callbacks in :ref:`EditorSpatialGizmoPlugin <class_EditorSpatialGizmoPlugin>`
  94. to get properly working handles.
  95. Alternative approach
  96. --------------------
  97. In some cases we want to provide our own implementation of :ref:`EditorSpatialGizmo<class_EditorSpatialGizmo>`,
  98. maybe because we want to have some state stored in each gizmo or because we are porting
  99. an old gizmo plugin and we don't want to go through the rewriting process.
  100. In these cases all we need to do is, in our new gizmo plugin, override
  101. :ref:`create_gizmo()<class_EditorSpatialGizmoPlugin_method_create_gizmo>`, so it returns our custom gizmo implementation
  102. for the Spatial nodes we want to target.
  103. ::
  104. # MyCustomGizmoPlugin.gd
  105. extends EditorSpatialGizmoPlugin
  106. const MyCustomSpatial = preload("res://addons/my-addon/MyCustomSpatial.gd")
  107. const MyCustomGizmo = preload("res://addons/my-addon/MyCustomGizmo.gd")
  108. func _init():
  109. create_material("main", Color(1, 0, 0))
  110. create_handle_material("handles")
  111. func create_gizmo(spatial):
  112. if spatial is MyCustomSpatial:
  113. return MyCustomGizmo.new()
  114. else:
  115. return null
  116. This way all the gizmo logic and drawing methods can be implemented in a new class extending
  117. :ref:`EditorSpatialGizmo<class_EditorSpatialGizmo>`, like so:
  118. ::
  119. # MyCustomGizmo.gd
  120. extends EditorSpatialGizmo
  121. # You can store data in the gizmo itself (more useful when working with handles).
  122. var gizmo_size = 3.0
  123. func redraw():
  124. clear()
  125. var spatial = get_spatial_node()
  126. var lines = PoolVector3Array()
  127. lines.push_back(Vector3(0, 1, 0))
  128. lines.push_back(Vector3(gizmo_size, spatial.my_custom_value, 0))
  129. var handles = PoolVector3Array()
  130. handles.push_back(Vector3(0, 1, 0))
  131. handles.push_back(Vector3(gizmo_size, spatial.my_custom_value, 0))
  132. var material = get_plugin().get_material("main", self)
  133. add_lines(lines, material, false)
  134. var handles_material = get_plugin().get_material("handles", self)
  135. add_handles(handles, handles_material)
  136. # You should implement the rest of handle-related callbacks
  137. # (get_handle_name(), get_handle_value(), commit_handle()...).
  138. Note that we just added some handles in the redraw method, but we still need to implement
  139. the rest of handle-related callbacks in :ref:`EditorSpatialGizmo<class_EditorSpatialGizmo>`
  140. to get properly working handles.