Skip to content

Understanding glTF File Format With a minimum glTF File

A minimum glTF File

json
{
  "scene": 0, // The scene property indicates that scene at index 0 of the scenes array is the default scene to be displayed
  "scenes" : [
    {
      "nodes" : [ 0 ]  // This array contains the index of the node object, referring to nodes at index 0
    }
  ],
  
  "nodes" : [
    {
      "mesh" : 0  // This node contains a 'mesh' property that refers to index 0 in the meshes array
    }
  ],
  
  "meshes" : [
    {
      "primitives" : [  // Array of drawable shapes forming this mesh
        {
          "attributes" : {
            "POSITION" : 1   // Indicates that the position data of this primitive is described by accessor at index 1
          },
          "indices" : 0 // Specifies that the index data for this primitive is described by accessor at index 0
        }
      ]
    }
  ],

  "buffers" : [
    {
      "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=",
      "byteLength" : 44 // Indicates the total length of the buffer data when decoded from Base64
    }
  ],
 
  "bufferViews" : [
    {
      "buffer" : 0,   // Refers to the data source, which is buffer at index 0
      "byteOffset" : 0,  
      "byteLength" : 6, // Specifies that the buffer view encompasses bytes 0 to 6 in the buffer 
      "target" : 34963  // Indicates Element Array Buffer, used for element indices
    },
    {
      "buffer" : 0,
      "byteOffset" : 8,
      "byteLength" : 36, // Specifies that the buffer view encompasses bytes 8 to 44 in the buffer
      "target" : 34962 // Indicates Array Buffer, used for vertex attributes like position
    }
  ],
  "accessors" : [
    {
      "bufferView" : 0,    
      "byteOffset" : 0,
      "componentType" : 5123,  // UNSIGNED_SHORT: each integer occupies 2 bytes
      "count" : 3,   // There are 3 indices
      "type" : "SCALAR",  // Represents scalar values (single value per element)
      "max" : [ 2 ],
      "min" : [ 0 ]
    },
    {
      "bufferView" : 1,
      "byteOffset" : 0,  
      "componentType" : 5126,  // FLOAT: each float occupies 4 bytes
      "count" : 3,  // Three groups of three floats, representing 3 vertices
      "type" : "VEC3", // Represents a 3-component vector
      "max" : [ 1.0, 1.0, 0.0 ], // Maximum values for each component in the vector
      "min" : [ 0.0, 0.0, 0.0 ] // Minimum values for each component in the vector
    }
  ],
  
  "asset" : {
    "version" : "2.0" // Specifies the version of the glTF format
  }
}

In this structure:

  • The scene specifies which scene from the scenes array to render by default. Each scene lists nodes that should be included. These correspond to entries in the nodes array.
  • A node can specify a mesh to display, linking to the meshes array.
  • A mesh contains one or more primitives, each describing a part of the geometry to be rendered.

This hierarchy allows complex scene compositions allowing glTF to represent detailed and sophisticated 3D environments effectively.

Parse the base64 buffer

python
import base64
import struct

# Base64 strings
base64_string = "AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA="

# Decode Base64 to bytes
buffer = base64.b64decode(base64_string)

# Print raw bytes
print("buffer:", buffer)

# Function to interpret bytes as 'unsigned short' (2 bytes) for indices
def unpack_indices(data):
    indices = []
    for i in range(0, len(data), 2):
        index = struct.unpack_from('<H', data, i)[0]
        indices.append(index)
    return indices

# Function to interpret bytes as 'float' (4 bytes) for positions
def unpack_positions(data):
    positions = []
    for i in range(0, len(data), 12):
        x, y, z = struct.unpack_from('<fff', data, i)
        positions.append((x, y, z))
    return positions

# Unpack data
decoded_indices = unpack_indices(buffer[:6])   # The first 6 bytes are for indices
decoded_positions = unpack_positions(buffer[8:44]) # From byte 8 to 44 are for positions

# Print decoded values
print("Decoded Indices:", decoded_indices)
print("Decoded Positions:", decoded_positions)

output:

python
buffer: b'\x00\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x00\x00'
Decoded Indices: [0, 1, 2]
Decoded Positions: [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)]

In 3D graphics, indices and vertex positions define the connections between points to form geometric shapes. In this case, you have three vertex positions, and the indices describe how to connect these vertices to form a triangle.

The indices [0, 1, 2] refer to the vertices in the order they are listed in the vertex array. The connection is as follows:

  • Vertex at index 0 is connected to the vertex at index 1 (forming the edge from (0.0, 0.0, 0.0) to (1.0, 0.0, 0.0)).
  • Vertex at index 1 is connected to the vertex at index 2 (forming the edge from (1.0, 0.0, 0.0) to (0.0, 1.0, 0.0)).
  • Vertex at index 2 is connected back to the vertex at index 0 (forming the edge from (0.0, 1.0, 0.0) back to (0.0, 0.0, 0.0)).

This sequence of connections results in the formation of a triangle, where the points are connected in a closed loop defined by the indices 0, 1, 2, then back to 0, completing the perimeter of the triangle.

Therefore, our minimum glTF file is actually a triangle.