Revamping of the docs organisation for a more coherent TOC

Only the pages were moved so far and some empty ones created,
the up-to-date toctrees come in the next commit.

(cherry picked from commit b408bdb918)
This commit is contained in:
Julian Murgia
2017-03-29 13:59:20 +02:00
committed by Rémi Verschelde
parent 6a730cb057
commit 5e05011eae
148 changed files with 0 additions and 376 deletions

View File

@@ -0,0 +1,165 @@
.. _doc_inverse_kinematics:
Inverse kinematics
==================
This tutorial is a follow-up of :ref:`doc_working_with_3d_skeletons`.
Before continuing on, I'd recommend reading some theory, the simplest
article I find is this:
http://freespace.virgin.net/hugo.elias/models/m_ik2.htm
Initial problem
~~~~~~~~~~~~~~~
Talking in Godot terminology, the task we want to solve here is position
our 2 angles we talked about above so, that the tip of lowerarm bone is
as close to target point, which is set by target Vector3() as possible
using only rotations. This task is very calculation-intensive and never
resolved by analytical equation solve. So, it is an underconstrained
problem, which means there is unlimited number of solutions to the
equation.
.. image:: /img/inverse_kinematics.png
For easy calculation, for this chapter we consider target is also
child of Skeleton. If it is not the case for your setup you can always
reparent it in your script, as you will save on calculations if you
do.
In the picture you see angles alpha and beta. In this case we don't
use poles and constraints, so we need to add our own. On the picture
the angles are 2D angles living in plane which is defined by bone
base, bone tip and target.
The rotation axis is easily calculated using cross-product of bone
vector and target vector. The rotation in this case will be always in
positive direction. If t is the Transform which we get from
get_bone_global_pose() function, the bone vector is
::
t.basis[2]
So we have all information here to execute our algorithm.
In game dev it is common to resolve this problem by iteratively closing
to the desired location, adding/subtracting small numbers to the angles
until the distance change achieved is less than some small error value.
Sounds easy enough, but there are Godot problems we need to resolve
there to achieve our goal.
- **How to find coordinates of the tip of the bone?**
- **How to find vector from bone base to target?**
For our goal (tip of the bone moved within area of target), we need to know
where the tip of our IK bone is. As we don't use a leaf bone as IK bone, we
know the coordinate of the bone base is the tip of parent bone. All these
calculations are quite dependant on the skeleton's structure. You can use
pre-calculated constants as well. You can add an extra bone for the tip of
IK and calculate using that.
Implementation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We will just use exported variable for bone length to be easy.
::
export var IK_bone="lowerarm"
export var IK_bone_length=1.0
export var IK_error = 0.1
Now, we need to apply our transformations from IK bone to the base of
chain. So we apply rotation to IK bone then move from our IK bone up to
its parent, then apply rotation again, then move to the parent of
current bone again, etc. So we need to limit our chain somewhat.
::
export var IK_limit = 2
For ``_ready()`` function:
::
var skel
func _ready():
skel = get_node("arm/Armature/Skeleton")
set_process(true)
Now we can write our chain-passing function:
::
func pass_chain():
var b = skel.find_bone(IK_bone)
var l = IK_limit
while b >= 0 and l > 0:
print( "name":", skel.get_bone_name(b))
print( "local transform":", skel.get_bone_pose(b))
print( "global transform":", skel.get_bone_global_pose(b))
b = skel.get_bone_parent(b)
l = l - 1
And for the ``_process()`` function:
::
func _process(dt):
pass_chain(dt)
Executing this script will just pass through bone chain printing bone
transforms.
::
extends Spatial
export var IK_bone="lowerarm"
export var IK_bone_length=1.0
export var IK_error = 0.1
export var IK_limit = 2
var skel
func _ready():
skel = get_node("arm/Armature/Skeleton")
set_process(true)
func pass_chain(dt):
var b = skel.find_bone(IK_bone)
var l = IK_limit
while b >= 0 and l > 0:
print("name: ", skel.get_bone_name(b))
print("local transform: ", skel.get_bone_pose(b))
print( "global transform:", skel.get_bone_global_pose(b))
b = skel.get_bone_parent(b)
l = l - 1
func _process(dt):
pass_chain(dt)
Now we need to actually work with target. The target should be placed
somewhere accessible. Since "arm" is imported scene, we better place
target node within our top level scene. But for us to work with target
easily its Transform should be on the same level as Skeleton.
To cope with this problem we create "target" node under our scene root
node and at script run we will reparent it copying global transform,
which will achieve wanted effect.
Create new Spatial node under root node and rename it to "target".
Then modify ``_ready()`` function to look like this:
::
var skel
var target
func _ready():
skel = get_node("arm/Armature/Skeleton")
target = get_node("target")
var ttrans = target.get_global_transform()
remove_child(target)
skel.add_child(target)
target.set_global_transform(ttrans)
set_process(true)

View File

@@ -0,0 +1,252 @@
.. _doc_mesh_generation_with_heightmap_and_shaders:
Mesh generation with heightmap and shaders
==========================================
Introduction
------------
This tutorial will help you to use Godot shaders to deform a plane
mesh so it appears like a basic terrain. Remember that this solution
has pros and cons.
Pros:
- Pretty easy to do.
- This approach allows computation of LOD terrains.
- The heightmap can be used in Godot to create a normal map.
Cons:
- The Vertex Shader can't re-compute normals of the faces. Thus, if
your mesh is not static, this method will **not** work with shaded
materials.
- This tutorial uses a plane mesh imported from Blender to Godot
Engine. Godot is able to create meshes as well.
See this tutorial as an introduction, not a method that you should
employ in your games, except if you intend to do LOD. Otherwise, this is
probably not the best way.
However, let's first create a heightmap,or a 2D representation of the terrain.
To do this, I'll use GIMP, but you can use any image editor you like.
The heightmap
-------------
We will use a few functions of GIMP image editor to produce a simple
heightmap. Start GIMP and create a square image of 512x512 pixels.
.. image:: /img/1_GIMP_createImage512.png
You are now in front of a new, blank, square image.
.. image:: /img/2_GIMP.png
Then, use a filter to render some clouds on this new image.
.. image:: /img/3_GIMP_FilterRenderClouds.png
Parameter this filter to whatever you want. A white pixel corresponds
to the highest point of the heightmap, a black pixel corresponds to
the lowest one. So, darker regions are valleys and brighter are
mountains. If you want, you can check "tileable" to render a heightmap
that can be cloned and tiled close together with another one. X and Y
size don't matter a lot as long as they are big enough to provide a
decent ground. A value of 4.0 or 5.0 for both is nice. Click on the
"New Seed" button to roll a dice and GIMP will create a new random
heightmap. Once you are happy with the result, click "OK".
.. image:: /img/4_GIMP_Clouds.png
You can continue to edit your image if you wish. For our example,
let's keep the heightmap as is, and let's export it to a PNG file, say
"heightmap.png". Save it in your Godot project folder.
The plane mesh
--------------
Now, we will need a plane mesh to import in Godot. Let's run Blender.
.. image:: /img/5_Blender.png
Remove the start cube mesh, then add a new plane to the scene.
.. image:: /img/6_Blender_CreatePlane.png
Zoom a bit, then switch to Edit mode (Tab key) and in the Tools
buttongroup at the left, hit "Subdivide" 5 or 6 times.
.. image:: /img/7_Blender_subdivided.png
Your mesh is now subdivided, which means we added vertices to the
plane mesh that we will later be able to move. Job's not finished yet:
in order to texture this mesh a proper UV map is necessary. Currently,
the default UV map contains only the 4 corner vertices we had at the
beginning. However, we now have more, and we want to be able to
texture over the whole mesh correctly.
If all the vertices of your mesh are not selected, select them all
(hit "A"). They must appear orange, not black. Then, in the
Shading/UVs button group to the left, click the "Unwrap" button (or
simply hit "U") and select "Smart UV Project". Keep the default
options and hit "Ok".
.. image:: /img/8_Blender_UVSmart.png
Now, we need to switch our view to "UV/Image editor".
.. image:: /img/9_Blender_UV_editor.png
Select all the vertices again ("A") then in the UV menu, select
"Export UV Layout".
.. image:: /img/10_Blender_exportUV.png
Export the layout as a PNG file. Name it "plane.png" and save it in
your Godot project folder. Now, let's export our mesh as an OBJ file.
Top of the screen, click "File/Export/Wavefront (obj)". Save your
object as "plane.obj" in your Godot project folder.
Shader magic
------------
Let's now open Godot Editor.
Create a new project in the folder you previously created and name it
what you want.
.. image:: /img/11_Godot.png
In our default scene (3D), create a root node "Spatial". Next, import
the mesh OBJ file. Click "Import", choose "3D Mesh" and select your
plane.obj file, set the target path as "/" (or wherever you want in
your project folder).
.. image:: /img/12_Godot_ImportMesh.png
I like to check "Normals" in the import pop-up so the import will also
consider faces normals, which can be useful (even if we don't use them
in this tutorial). Your mesh is now displayed in the FileSystem in
"res://".
.. image:: /img/13_Godot_ImportPopup.png
Create a MeshInstance node. In the Inspector, load the mesh we just
imported. Select "plane.msh" and hit ok.
.. image:: /img/14_Godot_LoadMesh.png
Great! Our plane is now rendered in the 3D view.
.. image:: /img/15_Godot_MeshPlaneRendered.png
It is time to add some shader stuff. In the Inspector, in the
"Material Override" line, add a "New ShaderMaterial". Edit it by
clicking the ">" button just right to it.
.. image:: /img/16_Godot_ShaderMaterial.png
You have two ways to create a shader: by code (MaterialShader), or
using a shader graph (MaterialShaderGraph). The second one is a bit
more visual, but we will not cover it for now. Create a "New
MaterialShader".
.. image:: /img/17_Godot_newMaterialShader.png
Edit it by clicking the ">" button just right to it. The Shaders
editor opens.
.. image:: /img/18_Godot_ShaderEditorOpened.png
The Vertex tab is for the Vertex shader, and the Fragment tab is for
the Fragment shader. No need to explain what both of them do, right?
If so, head to the :ref:`doc_shading_language` page. Else, let's start with the
Fragment shader. This one is used to texture the plane using an image.
For this example, we will texture it with the heightmap image itself,
so we'll actually see mountains as brighter regions and canyons as
darker regions. Use this code:
::
uniform texture source;
uniform color col;
DIFFUSE = col.rgb * tex(source,UV).rgb;
This shader is very simple (it actually comes from the :ref:`doc_shading_language` page).
What it basically does is take 2 parameters that we have to provide from
outside the shader ("uniform"):
- the texture file
- a color
Then, we multiply every pixel of the image given by
``tex(source, UV).rgb`` by the color defined ``col`` and we set it to
DIFFUSE variable, which is the rendered color. Remember that the
``UV`` variable is a shader variable that returns the 2D position of
the pixel in the texture image, according to the vertex we are
currently dealing with. That is the use of the UV Layout we made
before. The color ``col`` is actually not necessary to display the
texture, but it is interesting to play and see how it does, right?
However, the plane is displayed black! This is because we didn't set
the texture file and the color to use.
.. image:: /img/19_Godot_BlackPlane.png
In the Inspector, click the "Previous" button to get back to the
ShaderMaterial. This is where you want to set the texture and the
color. In "Source", click "Load" and select the texture file
"heightmap.png". But the mesh is still black! This is because our
Fragment shader multiplies each pixel value of the texture by the
``col`` parameter. However, this color is currently set to black
(0,0,0), and as you know, 0\*x = 0 ;) . Just change the ``col``
parameter to another color to see your texture appear:
.. image:: /img/20_Godot_TexturedPlane.png
Good. Now, the Vertex Shader.
The Vertex Shader is the first shader to be executed by the pipeline. It
deals with vertices.
Click the "Vertex" tab to switch, and paste this code:
::
uniform texture source;
uniform float height_range;
vec2 xz = SRC_VERTEX.xz;
float h = tex(source, UV).g * height_range;
VERTEX = vec3(xz.x, h, xz.y);
VERTEX = MODELVIEW_MATRIX * VERTEX;
This shader uses two "uniform" parameters. The ``source`` parameter is
already set for the fragment shader. Thus, the same image will be used
in this shader as the heightmap. The ``height_range`` parameter is a
parameter that we will use to increase the height effect.
At line 3, we save the x and z position of the SRC_VERTEX, because we
do not want them to change : the plane must remain square. Remember
that Y axis corresponds to the "altitude", which is the only one we
want to change with the heightmap.
At line 4, we compute an ``h`` variable by multiplying the pixel value
at the UV position and the ``height_range``. As the heightmap is a
greyscale image, all r, g and b channels contain the same value. I
used ``g``, but any of r, g and b have the same effect.
At line 5, we set the current vertex' position at (xz.x, h, xz.y)
position. Concerning xz.y remember that its type is "vec2". Thus, its
components are x and y. The y component simply contains the z position
we set at line 3.
Finally, at line 6, we multiply the vertex by the model/view matrix in
order to set its position according to camera position. If you try to
comment this line, you'll see that the mesh behaves weird as you move
and rotate the camera.
That's all good, but our plane remains flat. This is because the
``height_range`` value is 0. Increase this value to observe the mesh
distort and take to form of the terrain we set before:
.. image:: /img/21_Godot_Fini.png

View File

@@ -0,0 +1,350 @@
.. _doc_working_with_3d_skeletons:
Working with 3D skeletons
=========================
Godot 3D skeleton support is currently quite rudimentary. The
:ref:`class_Skeleton` node and class were designed mainly to support importing
skeletal animations as a set of transformation matrices.
Skeleton node
-------------
Skeleton node can be directly added anywhere you want on scene. Usually
mesh is a child of Skeleton, as it easier to manipulate this way, as
Transforms within skeleton are relative to where Skeleton is. But you
can specify Skeleton node in every MeshInstance.
Being obvious, Skeleton is intended to deform meshes, and consists of
structures called "bones". Each "bone" is represented as Transform, which is
applied to a group of vertices within a mesh. You can directly control a group
of vertices from Godot. For that please reference :ref:`class_MeshDataTool`
class, method :ref:`set_vertex_bones <class_MeshDataTool_set_vertex_bones>`.
This class is very powerful.
The "bones" are organized in hierarchy, every bone, except for root
bone(s) have parent. Every bone have associated name you can use to
refer to it (e.g. "root" or "hand.L", etc.). Also bones are all numbered,
these numbers are bone IDs. Bone parents are referred by their numbered
IDs.
For the rest of the article we consider the following scene:
::
main (Spatial) - script is always here
== skel (Skeleton)
==== mesh (MeshInstance)
This scene is imported from Blender. It contains arm mesh with 2 bones -
upperarm and lowerarm, with lowerarm parented to upperarm.
Skeleton class
--------------
You can view Godot internal help for descriptions of every function.
Basically all operations on bones are done using their numeric ID. You
can convert from name to numeric ID and vise versa.
**To find number of bones in skeleton we use get_bone_count()
function**
::
extends Spatial
var skel
func _ready():
skel = get_node("skel")
var id = skel.find_bone("upperarm")
print("bone id:", id)
var parent = skel.get_bone_parent(id)
print("bone parent id:", id)
**to find ID for the bone, use find_bone() function**
::
extends Spatial
var skel
func _ready():
skel = get_node("skel")
var id = skel.find_bone("upperarm")
print("bone id:", id)
Now, we want to do something interesting with ID except for printing it.
Also, we might need additional information - to find bone parents to
complete chain, etc. This all is done with get/set_bone\_\* functions.
**To find bone parent we use get_bone_parent(id) function**
::
extends Spatial
var skel
func _ready():
skel = get_node("skel")
var id = skel.find_bone("upperarm")
print("bone id:", id)
var parent = skel.get_bone_parent(id)
print("bone parent id:", id)
Bone transforms is the thing why we're here at all. There are 3 kind of
transforms - local, global, custom.
**To find bone local Transform we use get_bone_pose(id) function**
::
extends Spatial
var skel
func _ready():
skel = get_node("skel")
var id = skel.find_bone("upperarm")
print("bone id:", id)
var parent = skel.get_bone_parent(id)
print("bone parent id:", id)
var t = skel.get_bone_pose(id)
print("bone transform: ", t)
So we see 3x4 matrix there, with first column of 1s. What can we do
about that? It is a Transform, so we can do everything we can do with
Transform, basically translate, rotate and scale. Also we can multiply
transforms to have complex transforms. Remember, "bones" in Godot are
just Transforms over a group of vertices. Also we can copy Transforms of
other objects there. So lets rotate our "upperarm" bone:
::
extends Spatial
var skel
var id
func _ready():
skel = get_node("skel")
id = skel.find_bone("upperarm")
print("bone id:", id)
var parent = skel.get_bone_parent(id)
print("bone parent id:", id)
var t = skel.get_bone_pose(id)
print("bone transform: ", t)
set_process(true)
func _process(dt):
var t = skel.get_bone_pose(id)
t = t.rotated(Vector3(0.0, 1.0, 0.0), 0.1 * dt)
skel.set_bone_pose(id, t)
Now we can rotate individual bones. The same happens for scale and
translate - try these on your own and see results.
What we used now was local pose. By default all bones are not modified.
But this Transform tells us nothing about relationship between bones.
This information is needed for quite a number of tasks. How can we get
it? Here comes global transform:
**To find bone global Transform we use get_bone_global_pose(id)
function**
We will find global Transform for lowerarm bone:
::
extends Spatial
var skel
func _ready():
skel = get_node("skel")
var id = skel.find_bone("lowerarm")
print("bone id:", id)
var parent = skel.get_bone_parent(id)
print("bone parent id:", id)
var t = skel.get_bone_global_pose(id)
print("bone transform: ", t)
As you see, this transform is not zeroed. While being called global, it
is actually relative to Skeleton origin. For root bone, origin is always
at 0 if not modified. Lets print origin for our lowerarm bone:
::
extends Spatial
var skel
func _ready():
skel = get_node("skel")
var id = skel.find_bone("lowerarm")
print("bone id:", id)
var parent = skel.get_bone_parent(id)
print("bone parent id:", id)
var t = skel.get_bone_global_pose(id)
print("bone origin: ", t.origin)
You will see a number. What does this number mean? It is a rotation
point of Transform. So it is base part of the bone. In Blender you can
go to Pose mode and try there to rotate bones - they will rotate around
their origin. But what about tip? We can't know things like bone length,
which we need for many things, without knowing tip location. For all
bones in chain except for last one we can calculate tip location - it is
simply a child bone origin. Yes, there are situations when this is not
true, for non-connected bones. But that is OK for us for now, as it is
not important regarding Transforms. But the leaf bone tip is nowhere to
be found. Leaf bone is a bone without children. So you don't have any
information about its tip. But this is not a showstopper. You can
overcome this by either adding extra bone to the chain or just
calculating leaf bone length in Blender and store the value in your
script.
Using 3D "bones" for mesh control
---------------------------------
Now as you know basics we can apply these to make full FK-control of our
arm (FK is forward-kinematics)
To fully control our arm we need the following parameters:
- Upperarm angle x, y, z
- Lowerarm angle x, y, z
All of these parameters can be set, incremented and decremented.
Create the following node tree:
::
main (Spatial) <- script is here
+-arm (arm scene)
+ DirectionLight (DirectionLight)
+ Camera
Set up Camera so that arm is properly visible. Rotate DirectionLight
so that arm is properly lit while in scene play mode.
Now we need to create new script under main:
First we setup parameters:
::
var lowerarm_angle = Vector3()
var upperarm_angle = Vector3()
Now we need to setup a way to change them. Let us use keys for that.
Please create 7 actions under project settings:
- **selext_x** - bind to X key
- **selext_y** - bind to Y key
- **selext_z** - bind to Z key
- **select_upperarm** - bind to key 1
- **select_lowerarm** - bind to key 2
- **increment** - bind to key numpad +
- **decrement** - bind to key numpad -
So now we want to adjust the above parameters. Therefore we create code
which does that:
::
func _ready():
set_process(true)
var bone = "upperarm"
var coordinate = 0
func _process(dt):
if Input.is_action_pressed("select_x"):
coordinate = 0
elif Input.is_action_pressed("select_y"):
coordinate = 1
elif Input.is_action_pressed("select_z"):
coordinate = 2
elif Input.is_action_pressed("select_upperarm"):
bone = "upperarm"
elif Input.is_action_pressed("select_lowerarm"):
bone = "lowerarm"
elif Input.is_action_pressed("increment"):
if bone == "lowerarm":
lowerarm_angle[coordinate] += 1
elif bone == "upperarm":
upperarm_angle[coordinate] += 1
The full code for arm control is this:
::
extends Spatial
# member variables here, example:
# var a=2
# var b="textvar"
var upperarm_angle = Vector3()
var lowerarm_angle = Vector3()
var skel
func _ready():
skel = get_node("arm/Armature/Skeleton")
set_process(true)
var bone = "upperarm"
var coordinate = 0
func set_bone_rot(bone, ang):
var b = skel.find_bone(bone)
var rest = skel.get_bone_rest(b)
var newpose = rest.rotated(Vector3(1.0, 0.0, 0.0), ang.x)
var newpose = newpose.rotated(Vector3(0.0, 1.0, 0.0), ang.y)
var newpose = newpose.rotated(Vector3(0.0, 0.0, 1.0), ang.z)
skel.set_bone_pose(b, newpose)
func _process(dt):
if Input.is_action_pressed("select_x"):
coordinate = 0
elif Input.is_action_pressed("select_y"):
coordinate = 1
elif Input.is_action_pressed("select_z"):
coordinate = 2
elif Input.is_action_pressed("select_upperarm"):
bone = "upperarm"
elif Input.is_action_pressed("select_lowerarm"):
bone = "lowerarm"
elif Input.is_action_pressed("increment"):
if bone == "lowerarm":
lowerarm_angle[coordinate] += 1
elif bone == "upperarm":
upperarm_angle[coordinate] += 1
elif Input.is_action_pressed("decrement"):
if bone == "lowerarm":
lowerarm_angle[coordinate] -= 1
elif bone == "upperarm":
upperarm_angle[coordinate] -= 1
set_bone_rot("lowerarm", lowerarm_angle)
set_bone_rot("upperarm", upperarm_angle)
Pressing keys 1/2 select upperarm/lowerarm, select axis by pressing x,
y, z, rotate using numpad "+"/"-"
This way you fully control your arm in FK mode using 2 bones. You can
add additional bones and/or improve "feel" of the interface by using
coefficients for the change. I recommend you play with this example a
lot before going to next part.
You can clone the demo code for this chapter using
::
git clone git@github.com:slapin/godot-skel3d.git
cd demo1
Or you can browse it using web-interface:
https://github.com/slapin/godot-skel3d
Using 3D "bones" to implement Inverse Kinematics
------------------------------------------------
See :ref:`doc_inverse_kinematics`.
Using 3D "bones" to implement ragdoll-like physics
--------------------------------------------------
TODO.