working_with_3d_skeletons.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. .. _doc_working_with_3d_skeletons:
  2. Working with 3D skeletons
  3. =========================
  4. Godot 3D skeleton support is currently quite rudimentary. The
  5. :ref:`class_Skeleton` node and class were designed mainly to support importing
  6. skeletal animations as a set of transformation matrices.
  7. Skeleton node
  8. -------------
  9. The Skeleton node can be directly added anywhere you want on a scene. Usually
  10. the target mesh is a child of Skeleton, as it easier to manipulate this way, since
  11. Transforms within a skeleton are relative to where the Skeleton is. But you
  12. can specify a Skeleton node in every MeshInstance.
  13. Naturally, Skeleton is intended to deform meshes and consists of
  14. structures called "bones". Each "bone" is represented as a Transform, which is
  15. applied to a group of vertices within a mesh. You can directly control a group
  16. of vertices from Godot. For that please reference the :ref:`class_MeshDataTool`
  17. class and its method :ref:`set_vertex_bones <class_MeshDataTool_set_vertex_bones>`.
  18. The "bones" are organized hierarchically. Every bone, except for root
  19. bone(s) have a parent. Every bone also has an associated name you can use to
  20. refer to it (e.g. "root" or "hand.L", etc.). All bones are numbered, and
  21. these numbers are bone IDs. Bone parents are referred by their numbered
  22. IDs.
  23. For the rest of the article we consider the following scene:
  24. ::
  25. main (Spatial) - script is always here
  26. == skel (Skeleton)
  27. ==== mesh (MeshInstance)
  28. This scene is imported from Blender. It contains an arm mesh with 2 bones,
  29. upperarm and lowerarm, with the lowerarm bone parented to the upperarm.
  30. Skeleton class
  31. --------------
  32. You can view Godots internal help for descriptions of all functions.
  33. Basically, all operations on bones are done using their numeric ID. You
  34. can convert from a name to a numeric ID and vice versa.
  35. **To find the number of bones in a skeleton we use the get_bone_count()
  36. function:**
  37. ::
  38. extends Spatial
  39. var skel
  40. func _ready():
  41. skel = get_node("skel")
  42. var count = skel.get_bone_count()
  43. print("bone count:", count)
  44. **To find the ID of a bone, use the find_bone() function:**
  45. ::
  46. extends Spatial
  47. var skel
  48. func _ready():
  49. skel = get_node("skel")
  50. var id = skel.find_bone("upperarm")
  51. print("bone id:", id)
  52. Now, we want to do something interesting with the ID, not just printing it.
  53. Also, we might need additional information, finding bone parents to
  54. complete chains, etc. This is done with the get/set_bone\_\* functions.
  55. **To find the parent of a bone we use the get_bone_parent(id) function:**
  56. ::
  57. extends Spatial
  58. var skel
  59. func _ready():
  60. skel = get_node("skel")
  61. var id = skel.find_bone("upperarm")
  62. print("bone id:", id)
  63. var parent = skel.get_bone_parent(id)
  64. print("bone parent id:", id)
  65. The bone transforms are the things of our interest here. There are 3 kind of
  66. transforms: local, global, custom.
  67. **To find the local Transform of a bone we use get_bone_pose(id) function:**
  68. ::
  69. extends Spatial
  70. var skel
  71. func _ready():
  72. skel = get_node("skel")
  73. var id = skel.find_bone("upperarm")
  74. print("bone id:", id)
  75. var parent = skel.get_bone_parent(id)
  76. print("bone parent id:", id)
  77. var t = skel.get_bone_pose(id)
  78. print("bone transform: ", t)
  79. So we get a 3x4 matrix there, with the first column filled with 1s. What can we do
  80. with this matrix? It is a Transform, so we can do everything we can do with
  81. Transforms (basically translate, rotate and scale). We could also multiply
  82. transforms to have more complex transforms. Remember, "bones" in Godot are
  83. just Transforms over a group of vertices. We could also copy Transforms of
  84. other objects there. So let's rotate our "upperarm" bone:
  85. ::
  86. extends Spatial
  87. var skel
  88. var id
  89. func _ready():
  90. skel = get_node("skel")
  91. id = skel.find_bone("upperarm")
  92. print("bone id:", id)
  93. var parent = skel.get_bone_parent(id)
  94. print("bone parent id:", id)
  95. var t = skel.get_bone_pose(id)
  96. print("bone transform: ", t)
  97. set_process(true)
  98. func _process(delta):
  99. var t = skel.get_bone_pose(id)
  100. t = t.rotated(Vector3(0.0, 1.0, 0.0), 0.1 * delta)
  101. skel.set_bone_pose(id, t)
  102. Now we can rotate individual bones. The same happens for scale and
  103. translate. Try these on your own and check the results.
  104. What we used here was the local pose. By default all bones are not modified.
  105. But this Transform tells us nothing about the relationship between bones.
  106. This information is needed for quite a number of tasks. How can we get
  107. it? Here the global transform comes into play:
  108. **To find the bone global Transform we use get_bone_global_pose(id)
  109. function:**
  110. Let's find the global Transform for the lowerarm bone:
  111. ::
  112. extends Spatial
  113. var skel
  114. func _ready():
  115. skel = get_node("skel")
  116. var id = skel.find_bone("lowerarm")
  117. print("bone id:", id)
  118. var parent = skel.get_bone_parent(id)
  119. print("bone parent id:", id)
  120. var t = skel.get_bone_global_pose(id)
  121. print("bone transform: ", t)
  122. As you can see, this transform is not zeroed. While being called global, it
  123. is actually relative to the Skeleton origin. For a root bone, the origin is always
  124. at 0 if not modified. Let's print the origin for our lowerarm bone:
  125. ::
  126. extends Spatial
  127. var skel
  128. func _ready():
  129. skel = get_node("skel")
  130. var id = skel.find_bone("lowerarm")
  131. print("bone id:", id)
  132. var parent = skel.get_bone_parent(id)
  133. print("bone parent id:", id)
  134. var t = skel.get_bone_global_pose(id)
  135. print("bone origin: ", t.origin)
  136. You will see a number. What does this number mean? It is a rotation
  137. point of the Transform. So it is base part of the bone. In Blender you can
  138. go to Pose mode and try there to rotate bones. They will rotate around
  139. their origin.
  140. But what about the bone tip? We can't know things like the bone length,
  141. which we need for many things, without knowing the tip location. For all
  142. bones in a chain, except for the last one, we can calculate the tip location. It is
  143. simply a child bone's origin. There are situations when this is not
  144. true, such as for non-connected bones, but that is OK for us for now, as it is
  145. not important regarding Transforms.
  146. Notice that the leaf bone tip is nowhere to be found. A leaf bone is a bone
  147. without children, so you don't have any information about its tip.
  148. But this is not a showstopper. You can overcome this by either adding an extra
  149. bone to the chain or just calculating the length of the leaf bone in Blender
  150. and storing the value in your script.
  151. Using 3D "bones" for mesh control
  152. ---------------------------------
  153. Now as you know the basics we can apply these to make full FK-control of our
  154. arm (FK is forward-kinematics).
  155. To fully control our arm we need the following parameters:
  156. - Upperarm angle x, y, z
  157. - Lowerarm angle x, y, z
  158. All of these parameters can be set, incremented, and decremented.
  159. Create the following node tree:
  160. ::
  161. main (Spatial) <- script is here
  162. +-arm (arm scene)
  163. + DirectionLight (DirectionLight)
  164. + Camera
  165. Set up the Camera so that the arm is properly visible. Rotate DirectionLight
  166. so that the arm is properly lit while in scene play mode.
  167. Now we need to create a new script under main:
  168. First we define the setup parameters:
  169. ::
  170. var lowerarm_angle = Vector3()
  171. var upperarm_angle = Vector3()
  172. Now we need to setup a way to change them. Let us use keys for that.
  173. Please create 7 actions under project settings -> Input Map:
  174. - **selext_x** - bind to X key
  175. - **selext_y** - bind to Y key
  176. - **selext_z** - bind to Z key
  177. - **select_upperarm** - bind to key 1
  178. - **select_lowerarm** - bind to key 2
  179. - **increment** - bind to key numpad +
  180. - **decrement** - bind to key numpad -
  181. So now we want to adjust the above parameters. Therefore we create code
  182. which does that:
  183. ::
  184. func _ready():
  185. set_process(true)
  186. var bone = "upperarm"
  187. var coordinate = 0
  188. func _process(delta):
  189. if Input.is_action_pressed("select_x"):
  190. coordinate = 0
  191. elif Input.is_action_pressed("select_y"):
  192. coordinate = 1
  193. elif Input.is_action_pressed("select_z"):
  194. coordinate = 2
  195. elif Input.is_action_pressed("select_upperarm"):
  196. bone = "upperarm"
  197. elif Input.is_action_pressed("select_lowerarm"):
  198. bone = "lowerarm"
  199. elif Input.is_action_pressed("increment"):
  200. if bone == "lowerarm":
  201. lowerarm_angle[coordinate] += 1
  202. elif bone == "upperarm":
  203. upperarm_angle[coordinate] += 1
  204. The full code for arm control is this:
  205. ::
  206. extends Spatial
  207. # member variables here, example:
  208. # var a=2
  209. # var b="textvar"
  210. var upperarm_angle = Vector3()
  211. var lowerarm_angle = Vector3()
  212. var skel
  213. func _ready():
  214. skel = get_node("arm/Armature/Skeleton")
  215. set_process(true)
  216. var bone = "upperarm"
  217. var coordinate = 0
  218. func set_bone_rot(bone, ang):
  219. var b = skel.find_bone(bone)
  220. var rest = skel.get_bone_rest(b)
  221. var newpose = rest.rotated(Vector3(1.0, 0.0, 0.0), ang.x)
  222. var newpose = newpose.rotated(Vector3(0.0, 1.0, 0.0), ang.y)
  223. var newpose = newpose.rotated(Vector3(0.0, 0.0, 1.0), ang.z)
  224. skel.set_bone_pose(b, newpose)
  225. func _process(delta):
  226. if Input.is_action_pressed("select_x"):
  227. coordinate = 0
  228. elif Input.is_action_pressed("select_y"):
  229. coordinate = 1
  230. elif Input.is_action_pressed("select_z"):
  231. coordinate = 2
  232. elif Input.is_action_pressed("select_upperarm"):
  233. bone = "upperarm"
  234. elif Input.is_action_pressed("select_lowerarm"):
  235. bone = "lowerarm"
  236. elif Input.is_action_pressed("increment"):
  237. if bone == "lowerarm":
  238. lowerarm_angle[coordinate] += 1
  239. elif bone == "upperarm":
  240. upperarm_angle[coordinate] += 1
  241. elif Input.is_action_pressed("decrement"):
  242. if bone == "lowerarm":
  243. lowerarm_angle[coordinate] -= 1
  244. elif bone == "upperarm":
  245. upperarm_angle[coordinate] -= 1
  246. set_bone_rot("lowerarm", lowerarm_angle)
  247. set_bone_rot("upperarm", upperarm_angle)
  248. Pressing keys 1/2 selects upperarm/lowerarm, select the axis by pressing x,
  249. y, z, rotate using numpad "+"/"-"
  250. This way you fully control your arm in FK mode using 2 bones. You can
  251. add additional bones and/or improve the "feel" of the interface by using
  252. coefficients for the change. I recommend you play with this example a
  253. lot before going to next part.
  254. You can clone the demo code for this chapter using
  255. ::
  256. git clone [email protected]:slapin/godot-skel3d.git
  257. cd demo1
  258. Or you can browse it using the web-interface:
  259. https://github.com/slapin/godot-skel3d
  260. Using 3D "bones" to implement Inverse Kinematics
  261. ------------------------------------------------
  262. See :ref:`doc_inverse_kinematics`.
  263. Using 3D "bones" to implement ragdoll-like physics
  264. --------------------------------------------------
  265. TODO.