working_with_3d_skeletons.rst 11 KB

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