Forráskód Böngészése

Merge branch 'master' into optimation_optimazations

Rémi Verschelde 6 éve
szülő
commit
a5e192c4ad

+ 9 - 9
tutorials/3d/vertex_animation/animating_thousands_of_fish.rst

@@ -80,13 +80,13 @@ We construct a rotation matrix like so:
   //angle is scaled by 0.1 so that the fish only pivots and doesn't rotate all the way around
   //pivot is a uniform float
   float pivot_angle = cos(time) * 0.1 * pivot;
-	mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle)));
+  mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle)));
 
 And then we apply it in the ``x`` and ``z`` axes by multiplying it by ``VERTEX.xz``.
 
 .. code-block:: glsl
 
-	VERTEX.xz = rotation_matrix * VERTEX.xz;
+  VERTEX.xz = rotation_matrix * VERTEX.xz;
 
 With only the pivot applied you should see something like this:
 
@@ -121,14 +121,14 @@ we first  construct a rotation matrix.
 
   //twist is a uniform float
   float twist_angle = cos(time + body) * 0.3 * twist;
-	mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle)));
+  mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle)));
 
 We apply the rotation in the ``xy`` axes so that the fish appears to roll around its spine. For 
 this to work, the fishes spine needs to be centered on the ``z`` axis.
 
 .. code-block:: glsl
 
-	VERTEX.xy = twist_matrix * VERTEX.xy;
+  VERTEX.xy = twist_matrix * VERTEX.xy;
 
 Here is the fish with twist applied:
 
@@ -219,10 +219,10 @@ to loop over all the instances and set their transform to a random position.
 
 ::
   
-	for i in range($School.multimesh.instance_count):
-		var position = Transform()
-		position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25))
-		$School.multimesh.set_instance_transform(i, position)
+  for i in range($School.multimesh.instance_count):
+    var position = Transform()
+    position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25))
+    $School.multimesh.set_instance_transform(i, position)
 
 Running this script will place the fish in random positions in a box around the position of the
 MultiMeshInstance.
@@ -244,7 +244,7 @@ We do that by adding the per-instance custom value ``INSTANCE_CUSTOM`` to ``time
 
 .. code-block:: glsl
 
- 	float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x);
+  float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x);
 
 Next, we need to pass a value into ``INSTANCE_CUSTOM``. We do that by adding one line into 
 the ``for`` loop from above. In the ``for`` loop we assign each instance a set of four 

+ 2 - 0
tutorials/content/index.rst

@@ -5,4 +5,6 @@ Creating content
    :maxdepth: 1
    :name: toc-tutorials-content
 
+   procedural_geometry
    making_trees
+

+ 150 - 0
tutorials/content/procedural_geometry.rst

@@ -0,0 +1,150 @@
+.. _doc_procedural_geometry:
+
+Procedural geometry generation
+===============================
+
+Users often ask how to generate geometry from easily code. This is not very complicated, but it's not obvious.
+Godot provides a few classes entirely dedicated to make it this easy. Still, the best tool for the job depends
+entirely on the use case.
+
+SurfaceTool
+-----------
+
+This is the most common helper. :ref:`SurfaceTool<class_SurfaceTool>` is a class you can instantiate to generate :ref:`Meshes<class_Mesh>`, specifically *Mesh Surfaces*. 
+
+It has a similar API to OpenGL 1.x, and it's meant for static content. This means, the mesh is generated once and then used.
+
+Here is a simple example of how to use it to add a single triangle.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var st = SurfaceTool.new()
+    
+    st.begin(Mesh.PRIMITIVE_TRIANGLE)
+
+    # Prepares attributes for add_vertex
+    st.add_normal( Vector3(0,0,1) )
+    st.add_uv( Vector2(0,0) )
+    # Call last for each vertex, adds the above attributes
+    st.add_vertex( Vector3(-1,-1,0) ) 
+
+    st.add_normal( Vector3(0,0,1) )
+    st.add_uv( Vector2(0,1) )
+    st.add_vertex( Vector3(-1,1,0) ) 
+
+    st.add_normal( Vector3(0,0,1) )
+    st.add_uv( Vector2(1,1) )
+    st.add_vertex( Vector3(1,1,0) ) 
+
+    # Create indices, indices are optional
+    st.index()
+
+    # Commit to a mesh
+
+    var mesh = st.commit()
+
+Just explore the APIs and the possibilities.
+
+ImmediateGeometry
+-----------------    
+
+Unlike *SurfaceTool*, :ref:`ImmediateGeometry<class_ImmediateGeometry>` is an actual node. It's similar in the "OpenGL 1.x" style API,
+but it's actually designed to create content on the fly and modify it every frame efficiently.
+
+Generating complex geometry (several thousand vertices) with this node is inefficient, even if it's done only once. Instead, *ImmediateGeometry* is designed to generate simple geometry that changes every frame.
+
+It's used similar to *SurfaceTool*.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends ImmediateGeometry
+
+
+    void _process(delta):
+
+        # Clean up before drawing
+
+        clear()
+
+        # Begin draw
+        begin(Mesh.PRIMITIVE_TRIANGLE)
+    
+        # Prepares attributes for add_vertex
+        set_normal( Vector3(0,0,1) )
+        set_uv( Vector2(0,0) )
+        # Call last for each vertex, adds the above attributes
+        add_vertex( Vector3(-1,-1,0) ) 
+    
+        set_normal( Vector3(0,0,1) )
+        set_uv( Vector2(0,1) )
+        add_vertex( Vector3(-1,1,0) ) 
+    
+        set_normal( Vector3(0,0,1) )
+        set_uv( Vector2(1,1) )
+        add_vertex( Vector3(1,1,0) ) 
+    
+        # End drawing
+        end()
+
+Arrays
+------
+
+Lastly, the final way to do this is to create arrays themselves. This is the most efficient way to create static geometry, and is only
+recommended when SurfaceTool is not fast enough.
+
+Similar code as before, but draw a square using indices:
+
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var arrays = []
+    arrays.resize(Mesh::ARRAY_MAX)
+
+    var normal_array = []
+    var uv_array = []
+    var vertex_array = []
+    var index_array = []
+
+    normal_array.resize(4)    
+    uv_array.resize(4)    
+    vertex_array.resize(4)    
+    index_array.resize(6)    
+
+    normal_array[0]=Vector3(0,0,1)
+    uv_array[0]=Vector2(0,0)
+    vertex_array[0]=Vector3(-1,-1)
+
+    normal_array[1]=Vector3(0,0,1)
+    uv_array[1]=Vector2(0,1)
+    vertex_array[1]=Vector3(-1, 1)
+
+    normal_array[2]=Vector3(0,0,1)
+    uv_array[2]=Vector2(1,1)
+    vertex_array[2]=Vector3( 1, 1)
+
+    normal_array[3]=Vector3(0,0,1)
+    uv_array[3]=Vector2(1,0)
+    vertex_array[3]=Vector3( 1, -1)
+
+    # indices are optional in Godot, but if they exist they are used
+    index_array[0]=0
+    index_array[1]=1
+    index_array[2]=2
+
+    index_array[3]=2
+    index_array[4]=3
+    index_array[5]=0
+
+    arrays[Mesh.ARRAY_VERTEX]=vertex_array
+    arrays[Mesh.ARRAY_NORMAL]=normal_array
+    arrays[Mesh.ARRAY_TEX_UV]=uv_array
+    arrays[Mesh.ARRAY_INDEX]=index_array
+
+    var mesh = ArrayMesh.new()
+
+    mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES,arrays)
+
+

+ 183 - 0
tutorials/math/beziers_and_curves.rst

@@ -0,0 +1,183 @@
+.. _doc_beziers_and_curves:
+
+Beziers, Curves and Paths
+==========================
+
+Introduction
+~~~~~~~~~~~~
+
+Bezier curves are a mathematical approximation to natural shapes. Their goal is to represent a curve with
+as little information as possible, and with a high level of flexibility.
+
+Unlike other more abstract mathematical concepts, Bezier curves were created for industrial design and are
+widely popular in the graphics software industry.
+
+The way they work is very simple, but to understand it, let's start from the most minimal example.
+
+If you are not fresh on interpolation, please read the :ref:`relevant page<doc_interpolation>`
+before proceeding.
+
+
+Quadratic Bezier
+----------------
+
+Take three points (the minimum required):
+
+.. image:: img/bezier_quadratic_points.png
+
+To draw the curve between them, just interpolate the two segments that form between the three points, individually (using values 0 to 1). This will result in two points.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _quadratic_bezier(p0 : Vector2,p1 : Vector2,p2 : Vector2 ,t : float):
+        var q0 = p0.linear_interpolate(p1,t)
+        var q1 = p1.linear_interpolate(p2,t)
+
+This will reduce the points from 3 to 2. Do the same process with *q0* and *q1* to obtain a single point *r*.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        var r = q0.linear_interpolate(q1,t)
+        return r
+
+Finally, this point fill follow the curve when t goes from 0 to 1. This type of curve is called *Quadratic Bezier*.
+
+.. image:: img/bezier_quadratic_points2.gif
+
+*(Image credit: Wikipedia)*
+
+Cubic Bezier
+----------------
+
+Let's add one more point and make it four. 
+
+.. image:: img/bezier_cubic_points.png
+
+Then let's modify the function to take four points as an input, *p0, p1, p2* and *p3*:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _cubic_bezier(p0 : Vector2,p1 : Vector2,p2 : Vector2,p3 : Vector2 ,t : float):
+
+Interpolate then into three points:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        var q0 = p0.linear_interpolate(p1,t)
+        var q1 = p1.linear_interpolate(p2,t)
+        var q2 = p2.linear_interpolate(p3,t)
+
+From there to two points:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        var r0 = q0.linear_interpolate(q1,t)
+        var r1 = q1.linear_interpolate(q2,t)
+
+And to one:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        var s = r0.linear_interpolate(r1,t)
+        return s
+
+The result will be a smooth curve interpolating between all four points:
+
+.. image:: img/bezier_cubic_points.gif
+
+*(Image credit: Wikipedia)*
+
+.. note:: For 3D, it's exactly the same, just change Vector2 into Vector3.
+
+Control point form
+-------------------
+
+Now, let's take these points and change the way we understand them. Instead of having p0, p1, p2 and p3, we will store them as:
+
+* **POINT0** = **P0**: Is the first point, the source
+* **CONTROL0** = **P1** - **P0**: Is a relative vector for the first control point
+* **CONTROL1** = **P3** - **P2**: Is a relative vector for the second control point
+* **POINT1** = **P3**: Is the second point, the destination
+
+This way, we have two points and two control points (which are relative vectors to the respective points). If visualized, they will look a lot more familiar:
+
+.. image:: img/bezier_cubic_handles.png
+
+This is actually how graphics software presents Bezier curves to the users, and how Godot supports them.
+
+Curve2D, Curve3D, Path and Path2D
+-------------------------------
+
+There are two objects that contain curves: :ref:`Curve3D <class_Curve3D>` and :ref:`Curve2D <class_Curve2D>` (for 3D and 2D respectively).
+
+They can contain several points, allowing for longer paths. It is also possible to set them to nodes: :ref:`Path <class_Path>` and :ref:`Path2D <class_Path2D>` (also for 3D and 2D respectively):
+
+.. image:: img/bezier_path_2d.png
+
+Using them, however, may not be completely obvious, so following is a description of the most common use cases for Bezier curves.
+
+Evaluating
+-----------
+
+Just evaluating them may be an option, but in most cases it's not very useful. The big drawback with Bezier curves is that if you traverse them at constant speed, from *t=0* to *t=1*, the actual interpolation will *not* move at constant speed. The speed is also an interpolation between the distances between points p0, p1, p2 and p3 and there is not a mathematically simple way to traverse the curve at constant speed.
+
+Let's do a simple example with the following pseudocode:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var t = 0.0
+    _process(delta):
+        t+=delta
+        position = _cubic_bezier(p0,p1,p2,p3,t)
+
+
+.. image:: img/bezier_interpolation_speed.gif
+
+As you can see, the speed (in pixels per second) of the circle varies, even though *t* is increased at constant speed. This makes beziers difficult to use for anything practical out of the box.
+
+Drawing
+-------
+
+Drawing beziers (or objects based on the curve) is a very common use case, but it's also not easy. For pretty much any case, Bezier curves need to be converted to some sort of segments. This is normally difficult, however, without creating a very high amount of them.
+
+The reason is that some sections of a curve (specifically, corners) may requiere considerable amounts of points, while other sections may not:
+
+.. image:: img/bezier_point_amount.png
+
+Additionally, if both control points were 0,0 (remember they are relative vectors), the Bezier curve would just be a straight line (so drawing a high amount of points would be wasteful).
+
+Before drawing Bezier curves, *tesselation* is required. This is often done with a recursive or divide and conquer function that splits the curve until the curvature amount becomes less than a certain threshold.
+
+The *Curve* classes provide this via the
+:ref:`Curve2D.tesselate()<class_Curve2D_method_tesselete>` function (which receives optional *stages* of recursion and angle *tolerance* arguments). This way, drawing something based on a curve is easier.
+
+Traversal
+---------
+
+The last common use case for the curves is to traverse them. Because of what was mentioned before regarding constant speed, this is also difficult. 
+
+To make this easier, the curves need to be *baked* into equidistant points. This way, they can be approximated with regular  interpolation (which can be improved further with a cubic option). To do this, just use the :ref:`Curve.interpolate_baked()<class_Curve_method_interpolate_baked>` method together with
+:ref:`Curve2D.get_baked_length()<class_Curve2D_method_get_baked_length>`. The first call to either of them will bake the curve internally.
+
+Traversal at constant speed, then, can be done with the following pseudo-code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var t = 0.0
+    _process(delta):
+        t+=delta	
+        position = curve.interpolate_baked( t * curve.get_baked_length(), true)
+
+And the output will, then, move at constant speed:
+
+.. image:: img/bezier_interpolation_baked.gif
+
+

BIN
tutorials/math/img/bezier_cubic_handles.png


BIN
tutorials/math/img/bezier_cubic_points.gif


BIN
tutorials/math/img/bezier_cubic_points.png


BIN
tutorials/math/img/bezier_interpolation_baked.gif


BIN
tutorials/math/img/bezier_interpolation_speed.gif


BIN
tutorials/math/img/bezier_path_2d.png


BIN
tutorials/math/img/bezier_point_amount.png


BIN
tutorials/math/img/bezier_quadratic_points.png


BIN
tutorials/math/img/bezier_quadratic_points2.gif


BIN
tutorials/math/img/interpolation_follow.gif


BIN
tutorials/math/img/interpolation_monkey.gif


BIN
tutorials/math/img/interpolation_positions.png


BIN
tutorials/math/img/interpolation_vector.gif


+ 2 - 0
tutorials/math/index.rst

@@ -8,3 +8,5 @@ Math
    vector_math
    vectors_advanced
    matrices_and_transforms
+   interpolation
+   beziers_and_curves

+ 107 - 0
tutorials/math/interpolation.rst

@@ -0,0 +1,107 @@
+.. _doc_interpolation:
+
+Interpolation
+=============
+
+Introduction
+~~~~~~~~~~~~
+
+Interpolation is a very basic operation in graphics programming. It's good to become familiar with it in order to expand your horizons as a graphics developer.
+
+The basic idea is that you want to transition from A to B. A value *t*, represents the states in-between. 
+
+As an example if *t* is 0, then the state is A. If *t* is 1, then the state is B. Anything in-between is an *interpolation*.
+
+Between two real (floating point) numbers, a simple interpolation is usually described as:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    interpolation = A * (t-1) + B * t
+
+And often simplified to:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    interpolation = A  + (B - A) * t
+
+which is exactly the same.
+
+The name of this type of interpolation, which transforms a value into another at *constant speed* is *"Linear"*. So, when you hear about *Linear Interpolation*, you know they are referring to this simple formula.
+
+There are other types of interpolations, which will not be covered here. A recommended read afterwards is the :ref:`Bezier <doc_beziers_and_curves>` page.
+
+Vector Interpolation
+--------------------
+
+Vector types (Vector2 and Vector3) can also be interpolated, they come with handy functions to do it
+:ref:`Vector2.linear_interpolate()<class_Vector2_method_linear_interpolate>` and :ref:`Vector3.linear_interpolate()<class_Vector3_method_linear_interpolate>`.
+
+For cubic interpolation, there are also :ref:`Vector2.cubic_interpolate()<class_Vector2_method_linear_interpolate>` and :ref:`Vector3.cubic_interpolate()<class_Vector3_method_linear_interpolate>`, which do a :ref:`Bezier <doc_beziers_and_curves>` style interpolation.
+
+Here is simple pseudo-code for going from point A to B using interpolation:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _physics_process(delta):
+	
+        t+=delta*0.4	
+        $Sprite.position = $A.position.linear_interpolate( $B.position,t )
+	
+It will produce the following motion:
+
+.. image:: img/interpolation_vector.gif
+
+Transform interpolation
+--------------------
+
+It is also possible to interpolate whole transforms (make sure they have either uniform scale or, at least, the same non-uniform scale).
+For this, the function :ref:`Transform.interpolate_with()<class_Transform_method_interpolate_with>` can be used.
+
+Here is an example of transforming a monkey from Position1 to Position2:
+
+.. image:: img/interpolation_positions.png
+
+Using the following pseudocode:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var t = 0.0
+
+    func _process(delta):
+
+        t+=delta
+			
+        $Monkey.transform = $Position1.transform.interpolate_with( $Position2.transform, t )
+		
+And again, it will produce the following motion:
+
+.. image:: img/interpolation_monkey.gif
+
+
+Smoothing Motion
+----------------
+
+Interpolation can be used to smooth movement, rotation, etc. Here is an example of a circle following the mouse using smoothed motion:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    const FOLLOW_SPEED = 4.0
+    
+    func _physics_process(delta):
+	
+        var mouse_pos = get_local_mouse_position()
+        
+        $Sprite.position = $Sprite.position.linear_interpolate( mouse_pos, delta * FOLLOW_SPEED )
+
+Here is how it looks:
+
+.. image:: img/interpolation_follow.gif
+
+This useful for smoothing camera movement, allies following you (ensuring they stay within a certain range), and many other common game patterns.
+
+

+ 25 - 20
tutorials/optimization/using_servers.rst

@@ -1,9 +1,9 @@
 .. _doc_using_servers:
 
-Optimization Using Servers
+Optimization using Servers
 ==========================
 
-Engines like Godot provide increased ease of use thanks to it's high level constructs and features.
+Engines like Godot provide increased ease of use thanks to their high level constructs and features.
 Most of them are accessed and used via the :ref:`Scene System<doc_scene_tree>`. Using nodes and
 resources simplifies project organization and asset management in complex games.
 
@@ -32,10 +32,10 @@ One of the most interesting design decisions for Godot, is the fact that the who
 At the core, Godot uses the concept of Servers. They are very low level APIs to control
 rendering, physics, sound, etc. The scene system is built on top of them and uses them directly. The most common servers are:
 
-* :ref:`Visual Server<class_VisualServer>`: handles everything related to graphics.
-* :ref:`PhysicsServer<class_PhysicsServer>`: handles everything related to 3D physics.
-* :ref:`Physics2DServer<class_Physics2DServer>`: handles everything related to 2D physics.
-* :ref:`AudioServer<class_AudioServer>`: handles everything related to audio.
+* :ref:`VisualServer <class_VisualServer>`: handles everything related to graphics.
+* :ref:`PhysicsServer <class_PhysicsServer>`: handles everything related to 3D physics.
+* :ref:`Physics2DServer <class_Physics2DServer>`: handles everything related to 2D physics.
+* :ref:`AudioServer <class_AudioServer>`: handles everything related to audio.
 
 Just explore their APIs and you will realize that the all functions provided are low-level
 implementations of everything Godot allows you to do.
@@ -43,7 +43,7 @@ implementations of everything Godot allows you to do.
 RIDs
 ----
 
-The key to using servers is understanding Resource ID (RID) objects. These are opaque
+The key to using servers is understanding Resource ID (:ref:`RID <class_RID>`) objects. These are opaque
 handles to the sever implementation. They are allocated and freed manually. Almost every
 function in the servers requires RIDs to access the actual resource.
 
@@ -56,31 +56,34 @@ the internal RID is erased too.
 
 For nodes, there are many functions available:
 
-* For CanvasItem, the :ref:`CanvasItem.get_canvas_item()<class_CanvasItem_method_get_canvas_item>`
+* For CanvasItem, the :ref:`CanvasItem.get_canvas_item() <class_CanvasItem_method_get_canvas_item>`
   method will return the canvas item RID in the server.
-* For CanvasLayer, the :ref:`CanvasLayer.get_canvas()<class_CanvasLayer_method_get_canvas>`
+* For CanvasLayer, the :ref:`CanvasLayer.get_canvas() <class_CanvasLayer_method_get_canvas>`
   method will return the canvas RID in the server.
-* For Viewport, the :ref:`Viewport.get_viewport_rid()<class_Viewport_method_get_viewport_rid>`
+* For Viewport, the :ref:`Viewport.get_viewport_rid() <class_Viewport_method_get_viewport_rid>`
   method will return the viewport RID in the server.
-* For 3D, the :ref:`World<class_World>` resource (obtainable in the *Viewport* and *Spatial* nodes)
+* For 3D, the :ref:`World <class_World>` resource (obtainable in the :ref:`Viewport <class_Viewport>`
+  and :ref:`Spatial <class_Spatial>` nodes)
   contains functions to get the *VisualServer Scenario*, and the *PhysicsServer Space*. This
   allows creating 3D objects directly with the server API and using them.
-* For 2D, the :ref:`World2D<class_World2D>` resource (obtainable in the *Viewport* and *CanvasItem*
+* For 2D, the :ref:`World2D <class_World2D>` resource (obtainable in the :ref:`Viewport <class_Viewport>`
+  and :ref:`CanvasItem <class_CanvasItem>` nodes)
   nodes) contains functions to get the *VisualServer Canvas*, and the *Physics2DServer Space*. This
   allows creating 2D objects directly with the server API and using them.
 * The :ref:`VisualInstance<class_VisualInstance>` class, allows getting the scenario *instance* and
-  *instance base* via the :ref:`VisualInstance.get_instance()<class_VisualInstance_method_get_instance>`
-  and :ref:`VisualInstance.get_base()<class_VisualInstance_method_get_base>` respectively.
+  *instance base* via the :ref:`VisualInstance.get_instance() <class_VisualInstance_method_get_instance>`
+  and :ref:`VisualInstance.get_base() <class_VisualInstance_method_get_base>` respectively.
 
 Just explore the nodes and resources you are familiar with and find the functions to obtain the server *RIDs*.
 
 It is not advised to control RIDs from objects that already have a node associated. Instead, server
 functions should always be used for creating and controlling new ones and interacting with the existing ones.
 
-Creating a Sprite
+Creating a sprite
 -----------------
 
-This is a simple example of how to create a sprite from code and move it using the low-level Canvas Item API.
+This is a simple example of how to create a sprite from code and move it using the low-level
+:ref:`CanvasItem <class_CanvasItem>` API.
 
 .. tabs::
  .. code-tab:: gdscript GDScript
@@ -138,10 +141,11 @@ The 3D APIs are different than the 2D ones, so the instantiation API must be use
         var xform = Transform(Basis(), Vector3(20, 100, 0))
         VisualServer.instance_set_transform(instance,xform)
 
-Creating a 2D RigidBody and moving a Sprite with it
+Creating a 2D RigidBody and moving a sprite with it
 ----------------------------------------------------
 
-This creates a *RigidBody* using the *Physics2DServer* API, and moves a *Canvas Item*  when the body moves.
+This creates a :ref:`RigidBody2D <class_RigidBody2D>` using the :ref:`Physics2DServer <Physics2DServer>` API,
+and moves a :ref:`CanvasItem <class_CanvasItem>` when the body moves.
 
 .. tabs::
  .. code-tab:: gdscript GDScript
@@ -168,12 +172,13 @@ This creates a *RigidBody* using the *Physics2DServer* API, and moves a *Canvas
         # if you have many bodies and a single callback.
         Physics2DServer.body_set_force_integration_callback(body, self, "_body_moved", 0)
 
-The 3D version should be very similar, as 2D and 3D physics servers are identical.
+The 3D version should be very similar, as 2D and 3D physics servers are identical (using
+:ref:`RigidBody <RigidBody>` and :ref:`PhysicsServer <PhysicsServer>` respectively).
 
 Getting data from the servers
 -----------------------------
 
-Try to **never** request any information from *VisualServer*, *PhysicsServer* or *Physics2DServer*
+Try to **never** request any information from ``VisualServer``, ``PhysicsServer`` or ``Physics2DServer``
 by calling functions unless you know what you are doing. These servers will often run asynchronously
 for performance and calling any function that returns a value will stall them and force them to process
 anything pending until the function is actually called. This will severely decrease performance if you

+ 1 - 0
tutorials/plugins/editor/index.rst

@@ -9,3 +9,4 @@ Editor plugins
    making_main_screen_plugins
    import_plugins
    spatial_gizmos
+   inspector_plugins

+ 83 - 0
tutorials/plugins/editor/inspector_plugins.rst

@@ -0,0 +1,83 @@
+.. _doc_inspector_plugins:
+
+Inspector Plugins
+=====================
+
+Introduction
+------------
+
+Godot 3.1 comes with a new inspector. Adding plugins to it is now possible.
+
+This tutorial will explain the process.
+
+
+Inspector Plugin
+----------------
+
+This short tutorial will explain how to make a simple value editor.
+Create an EditorInspectorPlugin first. This is needed to initialize your plugin.
+
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+    
+    # MyEditorPlugin.gd 
+    
+    extends EditorInspectorPlugin
+    
+    func can_handle(object):
+        # if only editing a specific type
+        # return object is TheTypeIWant
+        # if everything is supported
+        return true
+
+    func parse_property(object,type,path,hint,hint_text,usage):
+        if (type==TYPE_INT):
+             add_custom_property_editor(path,MyEditor.new())
+             return true # I want this one
+        else:
+             return false
+
+
+Editor
+------
+
+
+Here is an editor for editing integers
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+    
+    # MyEditor.gd 
+    class_name MyEditor
+
+    var updating = false
+
+    func _spin_changed(value):
+        if (updating):
+            return
+
+        emit_changed( get_property(), value )
+
+    func update_property():
+        var new_value = get_edited_object()[ get_edited_property() ]
+
+        updating=true
+        spin.set_value( new_value )
+        updating=false
+
+    var spin = EditorSpinSlider.new() # use the new spin slider
+    func _init():
+       # if you want to put the editor below the label
+       #   set_bottom_editor( spin )
+       # else use:
+       add_child( spin )
+       # to remember focus when selected back
+       add_focusable( spin ) 
+       spin.set_min(0)
+       spin.set_max(1000)
+       spin.connect("value_changed",self,"_spin_changed")
+     
+
+
+

+ 1 - 0
tutorials/threads/index.rst

@@ -5,4 +5,5 @@ Multi-threading
    :maxdepth: 1
    :name: toc-learn-features-threads
 
+   using_multiple_threads
    thread_safe_apis

+ 153 - 0
tutorials/threads/using_multiple_threads.rst

@@ -0,0 +1,153 @@
+.. _doc_using_multiple_threads:
+
+Using multiple threads
+======================
+
+Threads
+-------
+
+Threads allow simultaneous execution of code. It allows off-loading work from the main thread.
+
+Godot supports threads and provides many handy functions to use them. 
+
+.. note:: If using other languages (C#, C++), it may be easier to use the threading classes they support.
+
+Creating a Thread
+------------------
+
+Creating a thread is very simple, just use the following code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var thread = null
+
+    # The thread will start here
+    func _ready():
+
+        thread = Thread.new()
+        thread.start(self,"_thread_function","Wafflecopter")
+
+    # Run here and exit
+    func _thread_function(userdata):
+
+        print("I'm a thread! Userdata is: ",userdata)
+
+    # Thread must be disposed (or "Joined"), for portability
+    func _exit_tree():
+        thread.wait_to_finish()
+
+
+Your function will, then, run in a separate thread until it returns.
+Even if the function has returned already, the thread must collect it, so call :ref:`Thread.wait_to_finish()<class_Thread_method_wait_to_finish>`, which will wait until the thread is done (if not done yet), then properly dispose of it.
+
+Mutexes
+-------
+
+Accessing objects or data from multiple threads is not always supported (if you do it, it will cause unexpected behaviors or crashes). Read the :ref:`Thread Safe APIs<doc_thread_safe_apis>` to understand which engine APIs support multiple thread access.
+
+When processing your own data or calling your own functions, as a rule, try to avoid accessing the same data directly from different threads. You may run into synchronization problems, as the data is not allways updated between CPU cores when modified. Always use a :ref:`Mutex<class_Mutex>` when accessing a piece of data from different threads.
+
+Here is an example of using a mutex:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var counter = 0
+    var mutex = null
+    var thread = null
+
+    # The thread will start here
+    func _ready():
+        mutex = Mutex.new()
+        thread = Thread.new()
+        thread.start(self,"_thread_function")
+        
+        #increase value, protect it with mutex
+        mutex.lock()
+        counter+=1
+        mutex.unlock()
+
+    # Increment the value from the thread, too
+    func _thread_function(userdata):
+        mutex.lock()
+        counter+=1
+        mutex.unlock()
+
+    # Thread must be disposed (or "Joined"), for portability
+    func _exit_tree():
+        thread.wait_to_finish()
+        print("Counter is: ",counter) # Should be 2
+
+Semaphores
+-----------
+
+Sometimes you want your thread to work *"On Demand"*. In other words, tell it when to work and let it suspend when it isn't doing anything.
+For this *:ref:`Semaphores<class_Semaphore>`* are used. The function :ref:`Semaphore.wait()<class_Semaphore_method_wait>` is used in the thread to suspend it until some data arrives.
+
+The main thread, instead, uses :ref:`Semaphore.post()<class_Semaphore_method_post>` to signal that data is ready to be processed:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var counter = 0
+    var mutex = null
+    var semaphore = null
+    var thread = null
+    var exit_thread = false
+
+    # The thread will start here
+    func _ready():
+        mutex = Mutex.new()
+        semaphore = Semaphore.new()
+        exit_thread=false
+
+        thread = Thread.new()
+        thread.start(self,"_thread_function")
+        
+
+    func _thread_function(userdata):
+
+        while(true):
+            semaphore.wait() # wait until posted
+
+            mutex.lock()
+            var should_exit = exit_thread # protect with mutex
+            mutex.unlock()
+
+            if (should_exit):
+                break
+
+            mutex.lock()
+            counter+=1 # increment counter, protect with mutex
+            mutex.unlock()
+
+    func increment_counter():
+        semaphore.post() # Make the thread process 
+
+    func get_counter():
+        mutex.lock()
+        # copy counter, protect with mutex
+        var counter_value = counter 
+        mutex.unlock()
+        return counter_value
+
+
+    # Thread must be disposed (or "Joined"), for portability
+    func _exit_tree():
+        # Set exit condition to true       
+        mutex.lock()
+        exit_thread = true # protect with mutex
+        mutex.unlock()
+
+        # unblock by posting
+        semaphore.post()
+
+        # wait until it exits
+        thread.wait_to_finish()
+
+        # Print the counter
+        print("Counter is: ",counter)
+
+
+