Formato de archivo TSCN

El formato de archivo TSCN (escena de texto) representa un solo árbol de escena dentro de Godot. A diferencia de los archivos binarios SCN, los archivos TSCN tienen la ventaja de ser principalmente legibles por humanos y fáciles de gestionar en sistemas de control de versiones.

El formato de archivo ESCN (escena exportada) es idéntico al formato de archivo TSCN, pero se utiliza para indicar a Godot que el archivo ha sido exportado desde otro programa y no debe ser editado por el usuario desde dentro de Godot. A diferencia de los archivos SCN y TSCN, durante la importación, los archivos ESCN se compilan a archivos SCN binarios que se almacenan dentro de la carpeta .import/. Esto reduce el tamaño de los datos y acelera la carga, ya que los formatos binarios se cargan más rápido en comparación con los formatos basados en texto.

Para los que buscan una descripción completa, el análisis sintáctico se maneja en el archivo resource_format_text.cpp en la clase ResourceFormatLoaderText.

Estructura de los archivos

Hay cinco secciones principales dentro de los archivos TSCN:

  1. Descriptor de Archivos

  2. Recursos externos

  3. Recursos internos

  4. Nodos

  5. Conexiones

La descripción del archivo tiene el siguiente aspecto: [gd_scene load_steps=3 format=2] y debe ser la primera entrada en el archivo. El parámetro load_steps es igual al número total de recursos (internos y externos) más uno (por el propio archivo). Si el archivo no tiene recursos, se omite el parámetro load_steps. El motor seguirá cargando el archivo correctamente incluso si load_steps es incorrecto, pero esto afectará a las barras de carga y cualquier otro código que dependa de ese valor.

Estas secciones deben aparecer en orden, pero puede ser difícil distinguirlas. La única diferencia entre ellas es el primer elemento en el encabezado de todos los elementos de la sección. Por ejemplo, el encabezado de todos los recursos externos debe comenzar con [ext_resource .....].

Los archivos TSCN pueden contener comentarios de una sola línea que empiezan con un punto y coma (;). Sin embargo, los comentarios serán descartados cuando se guarde el archivo usando el editor de Godot.

Entradas dentro de los archivos

Un encabezado se parece a [<resource_type> key=value key=value key=value ...] donde resource_type es uno de:

  • ext_resource

  • sub_resource

  • node

  • connection

Debajo de cada encabezado viene cero o más pares de clave = valor. Los valores pueden ser tipos de datos complejos como Arrays, Transforms, Colors, etc. Por ejemplo, un nodo spatial se ve como:

[node name="Cube" type="Spatial" 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 )

El árbol de escenas

¡El árbol de escena está formado por... nodos! El encabezado de cada nodo consiste en su nombre, padre (y en la mayoría de los casos) un tipo. Por ejemplo, [node type="Camera" name="PlayerCamera" parent="Player/Head"]

Otros palabras clave válidas incluyen:

  • instance

  • instance_placeholder

  • owner

  • index (establece el orden de aparición en el árbol. Si está ausente, los nodos heredados tendrán prioridad sobre los nodos regulares)

  • groups

El primer nodo en el archivo, que también es la raíz de la escena, no debe tener una entrada parent=Path/To/Node en su encabezado. Todos los archivos de escena deben tener exactamente una raíz de escena. Si no la tiene, Godot no podrá importar el archivo. La ruta del padre de otros nodos debe ser absoluta, pero no debe contener el nombre de la raíz de la escena. Si el nodo es un hijo directo de la raíz de la escena, la ruta debe ser ".". Aquí tienes un ejemplo del árbol de escena (pero sin contenido de nodos):

[node name="Player" type="Spatial"]             ; The scene root
[node name="Arm" parent="." type="Spatial"]     ; Parented to the scene root
[node name="Hand" parent="Arm" type="Spatial"]
[node name="Finger" parent="Arm/Hand" type="Spatial"]

Similar al recurso interno, el documento para cada nodo está actualmente incompleto. Afortunadamente, es fácil de averiguar porque simplemente puedes guardar un archivo con ese nodo en él. Algunos ejemplos de nodos son:

[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  )


[node type="MeshInstance" name="Sphere" parent="SpherePhysics"]

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  )


[node type="OmniLight" name="Lamp" parent="."]

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 type="Camera" name="Camera" parent="."]

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

Ruta del nodo

Una estructura de árbol no es suficiente para representar toda la escena. Godot utiliza una estructura de NodePath(Path/To/Node) para hacer referencia a otro nodo o atributo del nodo en cualquier lugar del árbol de la escena. Por ejemplo, MeshInstance utiliza NodePath() para apuntar a su esqueleto. Del mismo modo, las pistas de animación utilizan NodePath() para apuntar a propiedades del nodo a animar.

[node name="mesh" type="MeshInstance" parent="Armature001"]

mesh = SubResource(1)
skeleton = NodePath("..:")
[sub_resource id=3 type="Animation"]

...
tracks/0/type = "transform
tracks/0/path = NodePath("Cube:")
...

Esqueleto

El nodo Skeleton hereda del nodo Spatial, pero también puede tener una lista de huesos descritos en pares de clave-valor en el formato bones/Id/Attribute=Value. Los atributos de los huesos consisten en:

  • nombre

  • parent

  • rest

  • pose

  • enabled

  • bound_children

  1. name debe ser el primer atributo de cada hueso.

  2. parent es el índice del hueso padre en la lista de huesos, con este índice se construye un árbol de huesos.

  3. rest es la matriz de transformación del hueso en su posición "de reposo" o "inicial".

  4. pose es la matriz de pose; se usa como base la matriz rest.

  5. bound_children es una lista de NodePath() que apuntan a BoneAttachments pertenecientes a este hueso.

Aquí hay un ejemplo de un nodo esqueleto con dos huesos:

[node name="Skeleton" type="Skeleton" parent="Armature001" index="0"]

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 = [  ]

BoneAttachment

El nodo BoneAttachment es un nodo intermedio que describe algún nodo que está siendo padre de un solo hueso en un nodo Skeleton. El BoneAttachment tiene un atributo bone_name=NombreDelHueso, y el hueso correspondiente que es el padre tiene el nodo BoneAttachment en su lista bound_children.

Un ejemplo de un MeshInstance anidado a un hueso en un 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="MeshInstance" 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)

AnimationPlayer

El AnimationPlayer funciona como una biblioteca de animaciones. Almacena las animaciones listadas en el formato anim/Nombre=SubResource(ResourceId); cada línea se refiere a un recurso de animación. Todos los recursos de animación utilizan el nodo raíz del AnimationPlayer. El nodo raíz se almacena como root_node=NodePath(Path/To/Node).

[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 = [  ]

Recursos

Los recursos son componentes que conforman los nodos. Por ejemplo, un nodo MeshInstance tendrá un recurso ArrayMesh asociado. El recurso ArrayMesh puede ser interno o externo al archivo TSCN.

Las referencias a los recursos se manejan mediante números id en el encabezado del recurso. Los recursos externos e internos se refieren con ExtResource(id) y SubResource(id), respectivamente. Debido a que tienen métodos diferentes para referirse a recursos internos y externos, es posible que un mismo ID se utilice para un recurso interno y externo.

Por ejemplo, para referirse al recurso "[ext_resource id=3 type="PackedScene" path=....]", se usaría "ExtResource(3)".

Recursos externos

Los recursos externos son enlaces a recursos que no están contenidos dentro del archivo TSCN en sí mismo. Un recurso externo consta de una ruta, un tipo y un ID.

Godot siempre genera rutas absolutas relativas al directorio de recursos y, por lo tanto, se les agrega el prefijo res://, pero las rutas relativas a la ubicación del archivo TSCN también son válidas.

Algunos ejemplos de recursos externos son:

[ext_resource path="res://characters/player.dae" type="PackedScene" id=1]
[ext_resource path="metal.tres" type="Material" id=2]

Como los archivos TSCN, un archivo TRES puede contener comentarios de una sola línea que comienzan con un punto y coma (;). Sin embargo, los comentarios serán descartados al guardar el recurso utilizando el editor de Godot.

Recursos internos

Un archivo TSCN puede contener mallas, materiales y otros datos. Estos se encuentran en la sección de recursos internos del archivo. La cabecera de un recurso interno se parece a la de los recursos externos, excepto que no tiene una ruta. Los recursos internos también tienen pares de clave=valor bajo cada cabecera. Por ejemplo, una forma de colisión de cápsula se vería así:

[sub_resource type="CapsuleShape" id=2]

radius = 0.5
height = 3.0

Algunos recursos internos contienen enlaces a otros recursos internos (por ejemplo, una malla que tiene un material). En este caso, el recurso referente debe aparecer antes de la referencia a él. Esto significa que el orden es importante en la sección de recursos internos del archivo.

Lamentablemente, la documentación sobre los formatos de estos subrecursos no está completa. Algunos ejemplos se pueden encontrar inspeccionando archivos de recursos guardados, pero otros solo se pueden encontrar revisando el código fuente de Godot.

ArrayMesh

ArrayMesh consiste de varias superficies, cada una en el formato surface\Index={}. Cada superficie es un conjunto de vértices y un material.

Los archivos TSCN admiten dos formatos de superficie:

  1. Para el formato antiguo, cada superficie tiene tres claves esenciales:

  • primitive

  • arrays

  • morph_arrays

    1. primitive es una variable enumerada, primitive=4 que es PRIMITIVE_TRIANGLES y se usa con frecuencia.

    2. arrays es una matriz bidimensional que contiene:

      1. Array de posiciones de vértices

      2. Arrays de normales

      3. Array de tangentes

      4. Array de colores de vértices

      5. UV array 1

      6. UV array 2

      7. Array de índices de huesos

      8. Array de peso de los huesos

      9. Array de índices de vértices

    3. morph_arrays es un array de morphs. Cada morph es exactamente un arrays, pero sin el array de índices de vértices.

Un ejemplo de ArrayMesh:

[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":[]
}

Animación

Un recurso de animación consiste en pistas (tracks). Además, tiene las propiedades length, loop y step, que se aplican a todas las pistas.

  1. length y step son duraciones expresadas en segundos.

Cada pista está descrita por una lista de pares clave-valor en el formato tracks/Id/Attribute. Cada pista incluye:

  • type

  • path

  • interp

  • keys

  • loop_wrap

  • imported

  • enabled

  1. El atributo type debe ser el primero de cada pista. El valor de type puede ser:

    • transform

    • value

    • method

  2. El atributo path tiene el formato NodePath(Path/To/Node:atributo). Es la ruta hacia el nodo o atributo animado, relativo al nodo raíz definido en el AnimationPlayer.

  3. El atributo interp es el método para interpolar los fotogramas de las keyframes. Es una variable enumerada con uno de los siguientes valores:

    • 0 (constante)

    • 1 (linear)

    • 2 (cubic)

  4. Las keys corresponden a las keyframes. Se presenta como una PoolRealArray(), pero puede tener una estructura diferente para pistas con diferentes tipos.

    • Una pista de transformación utiliza cada 12 números reales en las keys para describir una keyframe. El primer número es la marca de tiempo. El segundo número es la transición seguida de un vector de traslación de 3 números, luego un cuaternión de rotación de 4 números (X, Y, Z, W) y finalmente un vector de escala de 3 números. La transición predeterminada en una pista de transformación es 1.0.

[sub_resource type="Animation" id=2]

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 = PoolRealArray( 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 = PoolRealArray( 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 )