3d_gizmos.rst 6.6 KB

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