فهرست منبع

Merge pull request #7614 from Calinou/update-tscn-file-format

Max Hilbrunner 2 سال پیش
والد
کامیت
a14cf32e08
1فایلهای تغییر یافته به همراه313 افزوده شده و 244 حذف شده
  1. 313 244
      contributing/development/file_formats/tscn.rst

+ 313 - 244
contributing/development/file_formats/tscn.rst

@@ -1,5 +1,3 @@
-:article_outdated: True
-
 .. _doc_tscn_file_format:
 
 TSCN file format
@@ -17,41 +15,70 @@ SCN files stored inside the ``.godot/imported/`` folder.
 This reduces the data size and speeds up loading, as binary formats are faster
 to load compared to text-based formats.
 
+To make files more compact, properties equal to the default value are not stored
+in scene/resource files. It is possible to write them manually, but they will be
+discarded when saving the file.
+
 For those looking for a complete description, the parsing is handled in the file
 `resource_format_text.cpp <https://github.com/godotengine/godot/blob/master/scene/resources/resource_format_text.cpp>`_
 in the ``ResourceFormatLoaderText`` class.
 
+.. note::
+
+    The scene and resource file formats have changed significantly in Godot 4,
+    with the introduction of string-based UIDs to replace incremental integer
+    IDs.
+
+    Mesh, skeleton and animation data is also stored differently compared to Godot 3.
+    You can read about some of the changes in this article:
+    `Animation data rework for 4.0 <https://godotengine.org/article/animation-data-redesign-40/>`__
+
+    Scenes and resources saved with Godot 4.x contain ``format=3`` in their
+    header, whereas Godot 3.x uses ``format=2`` instead.
+
 File structure
 --------------
 
 There are five main sections inside the TSCN file:
 
-0. File Descriptor
+0. File descriptor
 1. External resources
 2. Internal resources
 3. Nodes
 4. Connections
 
-The file descriptor looks like ``[gd_scene load_steps=3 format=2]`` and should
-be the first entry in the file. The ``load_steps`` parameter is equal to the
+The file descriptor looks like ``[gd_scene load_steps=4 format=3 uid="uid://cecaux1sm7mo0"]``
+and should be the first entry in the file. The ``load_steps`` parameter is equal to the
 total amount of resources (internal and external) plus one (for the file itself).
 If the file has no resources, ``load_steps`` is omitted. The engine will
 still load the file correctly if ``load_steps`` is incorrect, but this will affect
 loading bars and any other piece of code relying on that value.
 
+``uid`` is an unique string-based identifier representing the scene. This is
+used by the engine to track files that are moved around, even while the editor
+is closed. Scripts can also load UID-based resources using the ``uid://`` path
+prefix to avoid relying on filesystem paths. This makes it possible to move
+around a file in the project, but still be able to load it in scripts without
+having to modify the script. Godot does not use external files to keep track of
+IDs, which means no central metadata storage location is required within the
+project. See `this pull request <https://github.com/godotengine/godot/pull/50786>`__
+for detailed information.
+
 These sections should appear in order, but it can be hard to distinguish them.
 The only difference between them is the first element in the heading for all of
 the items in the section. For example, the heading of all external resources
-should start with ``[ext_resource .....]``.
+should start with ``[ext_resource ...]``.
 
 A TSCN file may contain single-line comments starting with a semicolon (``;``).
 However, comments will be discarded when saving the file using the Godot editor.
+Whitespace within a TSCN file is not significant (except within strings), but
+extraneous whitespace will be discarded when saving the file.
 
 Entries inside the file
 ~~~~~~~~~~~~~~~~~~~~~~~
 
 A heading looks like
-``[<resource_type> key=value key=value key=value ...]``
+``[<resource_type> key1=value1 key2=value2 key3=value3 ...]``
 where resource_type is one of:
 
 - ``ext_resource``
@@ -65,27 +92,26 @@ so on. For example, a Node3D looks like:
 
 ::
 
-    [node name="Cube" type="Node3D" parent="."]
-    transform=Transform( 1.0, 0.0, 0.0 ,0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 )
-
+    [node name="Cube" type="Node3D"]
+    transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 2, 3)
 
 The scene tree
 --------------
 
 The scene tree is made up of… nodes! The heading of each node consists of
-its name, parent and (most of the time) a type. For example
-``[node type="Camera" name="PlayerCamera" parent="Player/Head"]``
+its name, parent and (most of the time) a type. For example:
+``[node name="PlayerCamera" type="Camera" parent="Player/Head"]``
 
 Other valid keywords include:
 
  - ``instance``
  - ``instance_placeholder``
  - ``owner``
- - ``index`` (sets the order of appearance in the tree. If absent, inherited nodes will take precedence over plain ones)
+ - ``index`` (sets the order of appearance in the tree; if absent, inherited nodes will take precedence over plain ones)
  - ``groups``
 
-The first node in the file, which is also the scene root, must not have a
-``parent=Path/To/Node`` entry in its heading. All scene files should have
+The first node in the file, which is also the scene root, must **not** have a
+``parent="Path/To/Node"`` entry in its heading. All scene files should have
 exactly *one* scene root. If it doesn't, Godot will fail to import the file.
 The parent path of other nodes should be absolute, but shouldn't contain
 the scene root's name. If the node is a direct child of the scene root,
@@ -94,172 +120,140 @@ the path should be ``"."``. Here is an example scene tree
 
 ::
 
-    [node name="Player" type="Node3D"]             ; The scene root
-    [node name="Arm" parent="." type="Node3D"]     ; Parented to the scene root
-    [node name="Hand" parent="Arm" type="Node3D"]
-    [node name="Finger" parent="Arm/Hand" type="Node3D"]
+    [node name="Player" type="Node3D"]                    ; The scene root
+    [node name="Arm" type="Node3D" parent="."]            ; Parented to the scene root
+    [node name="Hand" type="Node3D" parent="Arm"]         ; Child of "Arm"
+    [node name="Finger" type="Node3D" parent="Arm/Hand"]  ; Child of "Hand"
 
+.. tip::
 
-Similar to the internal resource, the document for each node is currently
-incomplete. Fortunately, it is easy to find out because you can simply
-save a file with that node in it. Some example nodes are:
+    To make the file structure easier to grasp, you can saving a file with any
+    given node or resource then inspect it yourself in an external editor. You
+    can also make incremental changes in the Godot editor, and keep an external
+    text editor open on the ``.tscn`` or ``.tres`` file with auto-reload enabled
+    to see what changes.
 
-::
-
-    [node type="CollisionShape" name="SphereCollision" parent="SpherePhysics"]
-
-    shape = SubResource(8)
-    transform = Transform( 1.0 , 0.0 , -0.0 , 0.0 , -4.371138828673793e-08 , 1.0 , -0.0 , -1.0 , -4.371138828673793e-08 ,0.0 ,0.0 ,-0.0  )
+Here is an example of a scene containing a RigidBody3D-based ball with
+collision, visuals (mesh + light) and a camera parented to the RigidBody3D:
 
+::
 
-    [node type="MeshInstance3D" name="Sphere" parent="SpherePhysics"]
+    [gd_scene load_steps=4 format=3 uid="uid://cecaux1sm7mo0"]
 
-    mesh = SubResource(9)
-    transform = Transform( 1.0 , 0.0 , -0.0 , 0.0 , 1.0 , -0.0 , -0.0 , -0.0 , 1.0 ,0.0 ,0.0 ,-0.0  )
+    [sub_resource type="SphereShape3D" id="SphereShape3D_tj6p1"]
 
+    [sub_resource type="SphereMesh" id="SphereMesh_4w3ye"]
 
-    [node type="OmniLight" name="Lamp" parent="."]
+    [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_k54se"]
+    albedo_color = Color(1, 0.639216, 0.309804, 1)
 
-    light_energy = 1.0
-    light_specular = 1.0
-    transform = Transform( -0.29086464643478394 , -0.7711008191108704 , 0.5663931369781494 , -0.05518905818462372 , 0.6045246720314026 , 0.7946722507476807 , -0.9551711678504944 , 0.199883371591568 , -0.21839118003845215 ,4.076245307922363 ,7.3235554695129395 ,-1.0054539442062378  )
-    omni_range = 30
-    shadow_enabled = true
-    light_negative = false
-    light_color = Color( 1.0, 1.0, 1.0, 1.0 )
+    [node name="Ball" type="RigidBody3D"]
 
+    [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
+    shape = SubResource("SphereShape3D_tj6p1")
 
-    [node type="Camera" name="Camera" parent="."]
+    [node name="MeshInstance3D" type="MeshInstance3D" parent="."]
+    mesh = SubResource("SphereMesh_4w3ye")
+    surface_material_override/0 = SubResource("StandardMaterial3D_k54se")
 
-    projection = 0
-    near = 0.10000000149011612
-    fov = 50
-    transform = Transform( 0.6859206557273865 , -0.32401350140571594 , 0.6515582203865051 , 0.0 , 0.8953956365585327 , 0.44527143239974976 , -0.7276763319969177 , -0.3054208755493164 , 0.6141703724861145 ,14.430776596069336 ,10.093015670776367 ,13.058500289916992  )
-    far = 100.0
+    [node name="OmniLight3D" type="OmniLight3D" parent="."]
+    light_color = Color(1, 0.698039, 0.321569, 1)
+    omni_range = 10.0
 
+    [node name="Camera3D" type="Camera3D" parent="."]
+    transform = Transform3D(1, 0, 0, 0, 0.939693, 0.34202, 0, -0.34202, 0.939693, 0, 1, 3)
 
 NodePath
 ~~~~~~~~
 
 A tree structure is not enough to represent the whole scene. Godot uses a
 ``NodePath(Path/To/Node)`` structure to refer to another node or attribute of
-the node anywhere in the scene tree. For instance, MeshInstance3D uses
-``NodePath()`` to point to its skeleton. Likewise, Animation tracks use
-``NodePath()`` to point to node properties to animate.
+the node anywhere in the scene tree. Paths are relative to the current node,
+with ``NodePath(".")`` pointing to the current node and ``NodePath("")``
+pointing to no node at all.
 
-::
-
-    [node name="mesh" type="MeshInstance3D" parent="Armature001"]
+For instance, MeshInstance3D uses ``NodePath()`` to point to its skeleton.
+Likewise, Animation tracks use ``NodePath()`` to point to node properties to
+animate.
 
-    mesh = SubResource(1)
-    skeleton = NodePath("..:")
+NodePath can also point to a property using a ``:property_name`` suffix, and
+even point to a specific component for vector, transform and color types. This
+is used by Animation resources to point to specific properties to animate. For
+example, ``NodePath("MeshInstance3D:scale.x")`` points to the ``x`` component of
+the ``scale`` Vector3 property in MeshInstance3D.
 
+For example, the ``skeleton`` property in the MeshInstance3D node called
+``mesh`` points to its parent, ``Armature01``:
 
 ::
 
-    [sub_resource id=3 type="Animation"]
+    [node name="mesh" type="MeshInstance3D" parent="Armature01"]
+    skeleton = NodePath("..")
 
-    ...
-    tracks/0/type = "transform"
-    tracks/0/path = NodePath("Cube:")
-    ...
+Skeleton3D
+~~~~~~~~~~
 
+The :ref:`class_Skeleton3D` node inherits the Node3D node, but may alsohave a
+list of bones described in key-value pairs in the format
+``bones/<id>/<attribute> = value``. The bone attributes consist of:
 
-Skeleton
-~~~~~~~~
+- ``position``: Vector3
+- ``rotation``: Quaternion
+- ``scale``: Vector3
 
-The Skeleton node inherits the Node3D node, but also may have a list of bones
-described in key-value pairs in the format ``bones/Id/Attribute=Value``. The
-bone attributes consist of:
-
-- ``name``
-- ``parent``
-- ``rest``
-- ``pose``
-- ``enabled``
-- ``bound_children``
-
-1. ``name`` must be the first attribute of each bone.
-2. ``parent`` is the index of parent bone in the bone list, with parent index,
-   the bone list is built to a bone tree.
-3. ``rest`` is the transform matrix of bone in its "resting" position.
-4. ``pose`` is the pose matrix; use ``rest`` as the basis.
-5. ``bound_children`` is a list of ``NodePath()`` which point to
-   BoneAttachments belonging to this bone.
+These attributes are all optional. For instance, a bone may only define
+``position`` or ``rotation`` without defining the other properties.
 
 Here's an example of a skeleton node with two bones:
 
 ::
 
-    [node name="Skeleton" type="Skeleton" parent="Armature001" index="0"]
+    [node name="Skeleton3D" type="Skeleton3D" parent="PlayerModel/Robot_Skeleton" index="0"]
+    bones/1/position = Vector3(0.114471, 2.19771, -0.197845)
+    bones/1/rotation = Quaternion(0.191422, -0.0471201, -0.00831942, 0.980341)
+    bones/2/position = Vector3(-2.59096e-05, 0.236002, 0.000347473)
+    bones/2/rotation = Quaternion(-0.0580488, 0.0310587, -0.0085914, 0.997794)
+    bones/2/scale = Vector3(0.9276, 0.9276, 0.9276)
 
-    bones/0/name = "Bone.001"
-    bones/0/parent = -1
-    bones/0/rest = Transform( 1, 0, 0, 0, 0, -1, 0, 1, 0, 0.038694, 0.252999, 0.0877164 )
-    bones/0/pose = Transform( 1.0, 0.0, -0.0, 0.0, 1.0, -0.0, -0.0, -0.0, 1.0, 0.0, 0.0, -0.0 )
-    bones/0/enabled = true
-    bones/0/bound_children = [  ]
-    bones/1/name = "Bone.002"
-    bones/1/parent = 0
-    bones/1/rest = Transform( 0.0349042, 0.99939, 0.000512929, -0.721447, 0.0248417, 0.692024, 0.691589, -0.0245245, 0.721874, 0, 5.96046e-08, -1.22688 )
-    bones/1/pose = Transform( 1.0, 0.0, -0.0, 0.0, 1.0, -0.0, -0.0, -0.0, 1.0, 0.0, 0.0, -0.0 )
-    bones/1/enabled = true
-    bones/1/bound_children = [  ]
+BoneAttachment3D
+~~~~~~~~~~~~~~~~
 
+The :ref:`class_BoneAttachment3D` node is an intermediate node to describe some
+node being parented to a single bone in a Skeleton node. The BoneAttachment has
+a ``bone_name = "name of bone"`` property, as well as a property for the matching
+bone index.
 
-BoneAttachment
-~~~~~~~~~~~~~~
-
-BoneAttachment node is an intermediate node to describe some node being parented
-to a single bone in a Skeleton node. The BoneAttachment has a
-``bone_name=NameOfBone`` attribute, and the corresponding bone being the parent has the
-BoneAttachment node in its ``bound_children`` list.
-
-An example of one MeshInstance3D parented to a bone in Skeleton:
+An example of a :ref:`class_Marker3D` node parented to a bone in Skeleton:
 
 ::
 
-    [node name="Armature" type="Skeleton" parent="."]
-
-    transform = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -0.0219986, 0.0125825, 0.0343127)
-    bones/0/name = "Bone"
-    bones/0/parent = -1
-    bones/0/rest = Transform(1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0)
-    bones/0/pose = Transform(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
-    bones/0/enabled = true
-    bones/0/bound_children = [NodePath("BoneAttachment:")]
-
-    [node name="BoneAttachment" type="BoneAttachment" parent="Armature"]
-
-    bone_name = "Bone"
-
-    [node name="Cylinder" type="MeshInstance3D" parent="Armature/BoneAttachment"]
-
-    mesh = SubResource(1)
-    transform = Transform(1.0, 0.0, 0.0, 0.0, 1.86265e-09, 1.0, 0.0, -1.0, 0.0, 0.0219986, -0.0343127, 2.25595)
+    [node name="GunBone" type="BoneAttachment3D" parent="PlayerModel/Robot_Skeleton/Skeleton3D" index="5"]
+    transform = Transform3D(0.333531, 0.128981, -0.933896, 0.567174, 0.763886, 0.308015, 0.753209, -0.632331, 0.181604, -0.323915, 1.07098, 0.0497144)
+    bone_name = "hand.R"
+    bone_idx = 55
 
+    [node name="ShootFrom" type="Marker3D" parent="PlayerModel/Robot_Skeleton/Skeleton3D/GunBone"]
+    transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0)
 
 AnimationPlayer
 ~~~~~~~~~~~~~~~
 
-AnimationPlayer works as an animation library. It stores animations listed in
-the format ``anim/Name=SubResource(ResourceId)``; each line refers to an
-Animation resource. All the animation resources use the root node of
-AnimationPlayer. The root node is stored as
-``root_node=NodePath(Path/To/Node)``.
+The :ref:`class_AnimationPlayer` node works with one or more animation libraries
+stored in :ref:`class_AnimationLibrary` resources. An animation library is a
+collection of individual :ref:`class_Animation` resources, whose structure is
+documented :ref:`here <doc_tscn_animation>`.
 
-::
-
-    [node name="AnimationPlayer" type="AnimationPlayer" parent="." index="1"]
-
-    root_node = NodePath("..")
-    autoplay = ""
-    playback_process_mode = 1
-    playback_default_blend_time = 0.0
-    playback_speed = 1.0
-    anims/default = SubResource( 2 )
-    blend_times = [  ]
+This split between animations themselves and animation libraries was done in
+Godot 4, so that animations can be imported separately from 3D meshes, which is
+a common workflow in 3D animation software. See the `original pull request
+<https://github.com/godotengine/godot/pull/59980>`__ for details.
 
+If the library name is empty, then it acts acts the unique source of animations
+for this AnimationPlayer. This allows using ``<animation_name>`` directly to
+play animations from script. If you name the library, then you must play it as
+``<library_name>/<animation_name>``. This ensures backwards compatibility and
+keeps the existing workflow if you don't want to use multiple animation
+libraries.
 
 Resources
 ---------
@@ -268,20 +262,26 @@ Resources are components that make up the nodes. For example, a MeshInstance3D
 node will have an accompanying ArrayMesh resource. The ArrayMesh resource
 may be either internal or external to the TSCN file.
 
-References to the resources are handled by ``id`` numbers in the resource's
-heading. External resources and internal resources are referred to with
-``ExtResource(id)`` and ``SubResource(id)``, respectively. Because there
+References to the resources are handled by unique string-based IDs in the
+resource's heading. This is different from the ``uid`` property, which each
+external resource also has (but subresources don't).
+
+External resources and internal resources are referred to with
+``ExtResource("id")`` and ``SubResource("id")``, respectively. Because there
 have different methods to refer to internal and external resources, you can have
 the same ID for both an internal and external resource.
 
-For example, to refer to the resource ``[ext_resource id=3 type="PackedScene"
-path=....]``, you would use ``ExtResource(3)``.
+For example, to refer to the resource
+``[ext_resource type="Material" uid="uid://c4cp0al3ljsjv" path="res://material.tres" id="1_7bt6s"]``,
+you would use ``ExtResource("1_7bt6s")``.
 
 External resources
 ~~~~~~~~~~~~~~~~~~
 
 External resources are links to resources not contained within the TSCN file
-itself. An external resource consists of a path, a type and an ID.
+itself. An external resource consists of a path, a type, an UID (used to map its
+filesystem location to an unique identifier) and an ID (used to refer to the
+resource in the scene file).
 
 Godot always generates absolute paths relative to the resource directory and
 thus prefixed with ``res://``, but paths relative to the TSCN file's location
@@ -291,13 +291,14 @@ Some example external resources are:
 
 ::
 
-    [ext_resource path="res://characters/player.dae" type="PackedScene" id=1]
-    [ext_resource path="metal.tres" type="Material" id=2]
-
+    [ext_resource type="Texture2D" uid="uid://ccbm14ebjmpy1" path="res://gradient.tres" id="2_eorut"]
+    [ext_resource type="Material" uid="uid://c4cp0al3ljsjv" path="material.tres" id="1_7bt6s"]
 
 Like TSCN files, a TRES file may contain single-line comments starting with a
 semicolon (``;``). However, comments will be discarded when saving the resource
 using the Godot editor.
+Whitespace within a TRES file is not significant (except within strings), but
+extraneous whitespace will be discarded when saving the file.
 
 Internal resources
 ~~~~~~~~~~~~~~~~~~
@@ -310,140 +311,208 @@ heading. For example, a capsule collision shape looks like:
 
 ::
 
-    [sub_resource type="CapsuleShape" id=2]
-
-    radius = 0.5
+    [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_fdxgg"]
+    radius = 1.0
     height = 3.0
 
-
 Some internal resources contain links to other internal resources (such as a
 mesh having a material). In this case, the referring resource must appear
 *before* the reference to it. This means that order matters in the file's
 internal resources section.
 
-Unfortunately, documentation on the formats for these subresources isn't
-complete. Some examples can be found by inspecting saved resource files, but
-others can only be found by looking through Godot's source.
-
 ArrayMesh
 ~~~~~~~~~
 
-ArrayMesh consists of several surfaces, each in the format ``surface\Index={}``.
-Each surface is a set of vertices and a material.
-
-TSCN files support two surface formats:
+An ArrayMesh consists of several surfaces contained in the ``_surfaces`` array
+(notice the leading underscore). Each surface's data is stored in a dictionary
+with the following keys:
+
+- ``aabb``: The computed axis-aligned bounding box for visibility.
+  ``Mesh.PrimitiveType`` Godot enum. ``0`` = points, ``1`` = lines, ``2`` = line
+  strip, ``3`` = triangles (most common), ``4`` = triangle strip.
+- ``attribute_data``: Vertex attribute data, such as normals, tangents, vertex
+  colors, UV1, UV2 and custom vertex data.
+- ``bone_aabbs``: The axis-aligned bounding box of each bone for visibility.
+- ``format``: The surface's buffer format.
+- ``index_count``: The number of indices in the surface. This must match
+  ``index_data``'s size.
+- ``index_data``: The index data, which determines which vertices from
+  ``vertex_data`` are drawn.
+- ``lods``: Level of detail variations, stored as an array. Each LOD level
+  represents two values in the array. The first value is the percentage of
+  screen space the LOD level is most suited for (edge length); the second value
+  is the list of indices that should be drawn for the given LOD level.
+- ``material``: The material used when drawing the surface.
+- ``name``: The surface's name. This can be used in scripts and is imported from
+  3D DCCs.
+- ``primitive``: The surface's primitive type, matching the
+- ``skin_data``: Bone weight data.
+- ``vertex_count``: Number of vertices in the surface. This must match ``vertex_data``'s size.
+- ``vertex_data``: The vertex position data.
+
+Here's an example of an ArrayMesh saved to its own ``.tres`` file. Some fields were shortened with ``...`` for brevity:
 
-1. For the old format, each surface has three essential keys:
-
-- ``primitive``
-- ``arrays``
-- ``morph_arrays``
+::
 
-    i. ``primitive`` is an enumerate variable, ``primitive=4`` which is
-       ``PRIMITIVE_TRIANGLES`` is frequently used.
+    [gd_resource type="ArrayMesh" load_steps=2 format=3 uid="uid://dww8o7hsqrhx5"]
+
+    [ext_resource type="Material" path="res://player/model/playerobot.tres" id="1_r3bjq"]
+
+    [resource]
+    resource_name = "player_Sphere_016"
+    _surfaces = [{
+    "aabb": AABB(-0.207928, 1.21409, -0.14545, 0.415856, 0.226569, 0.223374),
+    "attribute_data": PackedByteArray(63, 121, ..., 117, 63),
+    "bone_aabbs": [AABB(0, 0, 0, -1, -1, -1), ..., AABB(-0.207928, 1.21409, -0.14545, 0.134291, 0.226569, 0.223374)],
+    "format": 7191,
+    "index_count": 1224,
+    "index_data": PackedByteArray(30, 0, ..., 150, 4),
+    "lods": [0.0382013, PackedByteArray(33, 1, ..., 150, 4)],
+    "material": ExtResource("1_r3bjq"),
+    "name": "playerobot",
+    "primitive": 3,
+    "skin_data": PackedByteArray(15, 0, ..., 0, 0),
+    "vertex_count": 1250,
+    "vertex_data": PackedByteArray(196, 169, ..., 11, 38)
+    }]
+    blend_shape_mode = 0
+
+.. _doc_tscn_animation:
 
-    ii. ``arrays`` is a two-dimensional array, it contains:
+Animation
+~~~~~~~~~
 
-        1. Vertex positions array
-        2. Normals array
-        3. Tangents array
-        4. Vertex colors array
-        5. UV array 1
-        6. UV array 2
-        7. Bone indexes array
-        8. Bone weights array
-        9. Vertex indexes array
+Each animation has the following properties:
 
-    iii. ``morph_arrays`` is an array of morphs. Each morph is exactly an
-         ``arrays`` without the vertex indexes array.
+- ``length``: The animation's length in seconds. Note that keyframes may be
+  placed outside the ``[0; length]`` interval, but they may have no effect
+  depending on the interpolation mode chosen.
+- ``loop_mode``: ``0`` = no looping, ``1`` = wrap-around looping, ``2`` =
+  clamped looping.
+- ``step``: The step size to use when editing this animation in the editor.
+  This is only used in the editor; it doesn't affect animation playback in any way.
 
-An example of ArrayMesh:
+Each track is described by a list of key-value pairs in the format
+``tracks/<id>/<attribute>``. Each track includes:
+
+- ``type``: The track's type. This defines what kind of properties may be
+  animated by this track, and how it'll be exposed to the user in the editor.
+  Valid types are ``value`` (generic property track), ``position_3d``,
+  ``rotation_3d``, ``scale_3d``, ``blend_shape`` (optimized 3D animation
+  tracks), ``method`` (method call tracks), ``bezier`` (Bezier curve tracks),
+  ``audio`` (audio playback tracks), ``animation`` (tracks that play other
+  animations).
+- ``imported``: ``true`` if the track was created from an imported 3D scene,
+  ``false`` if it was manually created by the user in the Godot editor or using
+  a script.
+- ``enabled``: ``true`` if the track is effective, ``false`` if it was disabled
+  in the editor.
+- ``path``: Path to the node property that will be affected by the track. The
+  property is written after the node path with a ``:`` separator.
+- ``interp``: The interpolation mode to use. ``0`` = nearest, ``1`` = linear,
+  ``2`` = cubic, ``3`` = linear angle, ``4`` = cubic angle.
+- ``loop_wrap``: ``true`` if the track is designed to wrap around when the
+  animation is looping, ``false`` if the track clamps to the first/last
+  keyframes.
+- ``keys``: The animation track's values. This attribute's structure depends on the ``type``.
+
+Here is a scene containing an AnimationPlayer that scales down a cube over time
+using a generic property track. The AnimationLibrary workflow was not used, so
+the animation library has an empty name (but the animation is still given a
+``scale_down`` name). Note that the ``RESET`` track was not created in this
+AnimationPlayer for brevity:
 
 ::
 
-    [sub_resource id=1 type="ArrayMesh"]
-
-    surfaces/0 = {
-        "primitive":4,
-        "arrays":[
-            Vector3Array(0.0, 1.0, -1.0, 0.866025, -1.0, -0.5, 0.0, -1.0, -1.0, 0.866025, 1.0, -0.5, 0.866025, -1.0, 0.5, 0.866025, 1.0, 0.5, -8.74228e-08, -1.0, 1.0, -8.74228e-08, 1.0, 1.0, -0.866025, -1.0, 0.5, -0.866025, 1.0, 0.5, -0.866025, -1.0, -0.5, -0.866025, 1.0, -0.5),
-            Vector3Array(0.0, 0.609973, -0.792383, 0.686239, -0.609973, -0.396191, 0.0, -0.609973, -0.792383, 0.686239, 0.609973, -0.396191, 0.686239, -0.609973, 0.396191, 0.686239, 0.609973, 0.396191, 0.0, -0.609973, 0.792383, 0.0, 0.609973, 0.792383, -0.686239, -0.609973, 0.396191, -0.686239, 0.609973, 0.396191, -0.686239, -0.609973, -0.396191, -0.686239, 0.609973, -0.396191),
-            null, ; No Tangents,
-            null, ; no Vertex Colors,
-            null, ; No UV1,
-            null, ; No UV2,
-            null, ; No Bones,
-            null, ; No Weights,
-            IntArray(0, 2, 1, 3, 1, 4, 5, 4, 6, 7, 6, 8, 0, 5, 9, 9, 8, 10, 11, 10, 2, 1, 10, 8, 0, 1, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 5, 0, 3, 0, 9, 11, 9, 5, 7, 9, 10, 11, 11, 2, 0, 10, 1, 2, 1, 6, 4, 6, 1, 8)
-        ],
-        "morph_arrays":[]
-    }
+    [gd_scene load_steps=4 format=3 uid="uid://cdyt3nktp6y6"]
 
+    [sub_resource type="Animation" id="Animation_r2qdp"]
+    resource_name = "scale_down"
+    length = 1.5
+    loop_mode = 2
+    step = 0.05
+    tracks/0/type = "value"
+    tracks/0/imported = false
+    tracks/0/enabled = true
+    tracks/0/path = NodePath("Box:scale")
+    tracks/0/interp = 1
+    tracks/0/loop_wrap = true
+    tracks/0/keys = {
+    "times": PackedFloat32Array(0, 1),
+    "transitions": PackedFloat32Array(1, 1),
+    "update": 0,
+    "values": [Vector3(1, 1, 1), Vector3(0, 0, 0)]
+    }
 
-Animation
-~~~~~~~~~
+    [sub_resource type="AnimationLibrary" id="AnimationLibrary_4qx36"]
+    _data = {
+    "scale_down": SubResource("Animation_r2qdp")
+    }
 
-An animation resource consists of tracks. Besides, it has ``length``, ``loop``
-and ``step`` applied to all the tracks.
+    [sub_resource type="BoxMesh" id="BoxMesh_u688r"]
 
-1. ``length`` and ``step`` are both durations in seconds.
+    [node name="Node3D" type="Node3D"]
 
-Each track is described by a list of key-value pairs in the format
-``tracks/Id/Attribute``. Each track includes:
+    [node name="AnimationPlayer" type="AnimationPlayer" parent="."]
+    autoplay = "scale_down"
+    libraries = {
+    "": SubResource("AnimationLibrary_4qx36")
+    }
 
-- ``type``
-- ``path``
-- ``interp``
-- ``keys``
-- ``loop_wrap``
-- ``imported``
-- ``enabled``
+    [node name="Box" type="MeshInstance3D" parent="."]
+    mesh = SubResource("BoxMesh_u688r")
 
-1. The ``type`` must be the first attribute of each track.
-   The value of ``type`` can be:
+For generic property ``value`` tracks, ``keys`` is a dictionary containing 3
+arrays with positions in ``times`` (PackedFloat32Array), easing values in
+``transitions`` (PackedFloat32Array) and values in ``values`` (Array). There is
+an additional ``update`` property, which is an integer with the values ``0`` =
+continuous, ``1`` = discrete, ``2`` = capture.
 
-    - ``transform``
-    - ``value``
-    - ``method``
+Here is a second Animation resource that makes use of the 3D Position and 3D
+Rotation tracks. These tracks (in addition to the 3D Scale track) replace
+Transform tracks from Godot 3. They are optimized for fast playback and can
+optionally be compressed.
 
-2. The ``path`` has the format ``NodePath(Path/To/Node:attribute)``.
-   It's the path to the animated node or attribute, relative to the root node
-   defined in the AnimationPlayer.
+The downside of these optimized track types is that they can't use custom easing
+values. Instead, all keyframes use linear interpolation. That said, you can
+still opt for using nearest or cubic interpolation for all keyframes in a given
+track by changing the track's interpolation mode.
 
-3. The ``interp`` is the method to interpolate frames from the keyframes.
-   It is an enum variable with one of the following values:
+::
 
-    - ``0`` (constant)
-    - ``1`` (linear)
-    - ``2`` (cubic)
+    [sub_resource type="Animation" id="Animation_r2qdp"]
+    resource_name = "move_and_rotate"
+    length = 1.5
+    loop_mode = 2
+    step = 0.05
+    tracks/0/type = "position_3d"
+    tracks/0/imported = false
+    tracks/0/enabled = true
+    tracks/0/path = NodePath("Box")
+    tracks/0/interp = 1
+    tracks/0/loop_wrap = true
+    tracks/0/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1.5, 1, 1.5, 1, 0)
+    tracks/1/type = "rotation_3d"
+    tracks/1/imported = false
+    tracks/1/enabled = true
+    tracks/1/path = NodePath("Box")
+    tracks/1/interp = 1
+    tracks/1/loop_wrap = true
+    tracks/1/keys = PackedFloat32Array(0, 1, 0.211, -0.047, 0.211, 0.953, 1.5, 1, 0.005, 0.976, -0.216, 0.022)
 
-4. The ``keys`` correspond to the keyframes. It appears as a ``PackedFloat32Array()``,
-   but may have a different structure for tracks with different types.
+For 3D position, rotation and scale tracks, ``keys`` is a PackedFloat32Array
+with all values stored in a sequence.
 
-    - A Transform track uses every 12 real numbers in the ``keys`` to describe
-      a keyframe. The first number is the timestamp. The second number is the
-      transition followed by a 3-number translation vector, followed by a
-      4-number rotation quaternion (X, Y, Z, W) and finally a 3-number
-      scale vector. The default transition in a Transform track is 1.0.
+In the visual guide below, ``T`` is the keyframe's time in seconds since the
+start of the animation, ``E`` is the keyframe's transition (currently always
+``1``). For 3D position and scale tracks, ``X``, ``Y``, ``Z`` are the Vector3's
+coordinates. For 3D rotation tracks, ``X``, ``Y``, ``Z`` and ``W`` are the
+Quaternion's coordinates.
 
 ::
 
-    [sub_resource type="Animation" id=2]
+    # For 3D position and scale, which use Vector3:
+    tracks/<id>/keys = PackedFloat32Array(T, E,   X, Y, Z,      T, E,   X, Y, Z, ...)
 
-    length = 4.95833
-    loop = false
-    step = 0.1
-    tracks/0/type = "transform"
-    tracks/0/path = NodePath("Armature001")
-    tracks/0/interp = 1
-    tracks/0/loop_wrap = true
-    tracks/0/imported = true
-    tracks/0/enabled = true
-    tracks/0/keys = PackedFloat32Array( 0, 1, -0.0358698, -0.829927, 0.444204, 0, 0, 0, 1, 0.815074, 0.815074, 0.815074, 4.95833, 1, -0.0358698, -0.829927, 0.444204, 0, 0, 0, 1, 0.815074, 0.815074, 0.815074 )
-    tracks/1/type = "transform"
-    tracks/1/path = NodePath("Armature001/Skeleton:Bone.001")
-    tracks/1/interp = 1
-    tracks/1/loop_wrap = true
-    tracks/1/imported = true
-    tracks/1/enabled = false
-    tracks/1/keys = PackedFloat32Array( 0, 1, 0, 5.96046e-08, 0, 0, 0, 0, 1, 1, 1, 1, 4.95833, 1, 0, 5.96046e-08, 0, 0, 0, 0, 1, 1, 1, 1 )
+    # For 3D rotation, which use Quaternion:
+    tracks/<id>/keys = PackedFloat32Array(T, E,   X, Y, Z, W,      T, E,   X, Y, Z, W, ...)