2.9. Geometry Payload

The geometry channel carries every server-allocated resource that is not video, video-tag or audio. It is the reliable / ordered SCTP data channel labelled "geometry" (stream index 80) (if using EFP framing), or "geometry_unframed" (if not using framing). All multi-byte fields are little-endian and structs are 1-byte packed.

Reference implementations:

2.9.1. Channel framing

Each SCTP message on the geometry channel contains a single payload chunk:

Table 2.38 Geometry chunk header

Bytes

Type

Description

8

size_t

payloadSize – length, in bytes, of everything that follows the size field (i.e. type + uid + body).

1

GeometryPayloadType

Chunk type (see table below).

8

avs::uid

Resource uid this chunk belongs to. RemoveNodes is the only type that omits this field.

The payloadSize is not a self-delimiting framing prefix at the SCTP level; SCTP already preserves message boundaries. It is informational and is used by the decoder for sanity checking. Implementations that want to bundle multiple chunks per SCTP message can do so by emitting them back-to-back: the decoder advances offset by 8 + payloadSize after each chunk.

2.9.2. Payload types

Table 2.39 GeometryPayloadType

Id

Name

Body

0

Invalid

Reserved.

1

Mesh

1-byte MeshCompressionType (0 none, 1 Draco), followed by version/name/Draco buffer. See Mesh payload.

2

Material

Material name + PBR descriptor and texture-uid references. See Material payload.

3

MaterialInstance

Currently unused on the wire; reserved. See MaterialInstance payload.

4

Texture

uint16 name length, name, 4-byte TextureCompression, then the codec-specific payload bytes (e.g. raw KTX2/Basis container). See Texture payload.

5

Animation

Skeletal animation: name, duration, per-bone position and rotation keyframe lists. See Animation payload.

6

Node

uint16 name length, name, avs::Transform (already converted to the client’s axes), stationary flag, holder client id, priority, parent uid, then the type-specific data (mesh, light, link, …). See Node payload.

7

Skeleton

Name + bone-uid array. See Skeleton payload.

8

FontAtlas

Font texture uid + glyph maps. See FontAtlas payload.

9

TextCanvas

Text-canvas description (font uid, layout, colour, text). See TextCanvas payload.

10

TexturePointer

uint16 URL length + URL bytes. The body is the HTTP(S) URL of an out-of-band Texture; see TexturePointer payload and HTTP Service.

11

MeshPointer

uint16 URL length + URL bytes. As above but for Meshes. See MeshPointer payload.

12

MaterialPointer

Reserved; not currently emitted. See MaterialPointer payload.

13

RemoveNodes

uint16 count followed by that many avs::uid values to delete from the client’s scene. Has no resource uid in the header. See RemoveNodes payload.

2.9.3. Node payload

A Node chunk describes a single scene-graph entry: its transform, parent, streaming metadata and an optional component record that attaches renderable data (mesh, light, text canvas) or a link. The chunk body that follows the standard geometry chunk header (payloadSize, GeometryPayloadType::Node, uid) is laid out as a fixed-size node header followed by zero or one component records.

See e.g. avs::Node

2.9.3.1. Node header

Table 2.40 Node header

Field

Type

Size (bytes)

Description

name length

uint16

2

UTF-8 byte count. The string is not null-terminated.

name

uint8[name length]

variable

Node name. Useful for diagnostics; the client does not key on it.

localTransform

avs::Transform

40

vec3 position (12) + vec4 rotation (16) + vec3 scale (12). Already converted to the client’s avs::AxesStandard by the encoder.

stationary

uint8

1

0 = dynamic, non-zero = stationary. Stationary nodes are not expected to receive movement updates.

holder_client_id

uint64 (avs::uid)

8

Owning client id, or 0 if the node is not held by any client.

priority

int32

4

Streaming priority. The server may withhold the mesh of nodes below the client’s minimum priority.

parentID

uint64 (avs::uid)

8

Parent node uid, or 0 if the node is a scene-graph root.

numComponents

uint8

1

Number of component records that follow. Currently 0 or 1: if 0 the node has no renderable data and the chunk ends here; if 1 exactly one component record follows. This is a count to leave room for additional components in future versions without bumping the header layout.

Once name has been consumed, the remainder of the header is a fixed 62-byte tail:

packet-beta title Node header tail (byte offsets from end of name) 0-39: "localTransform (avs::Transform)" 40-40: "stationary" 41-48: "holder_client_id" 49-52: "priority" 53-60: "parentID" 61-61: "numComponents"

2.9.3.2. Component record

A component record is present only when numComponents != 0 and begins with a single byte identifying the type:

Table 2.41 Component header

Field

Type

Size

Description

data_type

uint8 (avs::NodeDataType)

1

One of Mesh (2), Light (3), TextCanvas (4), Link (7). Values Invalid (0), None (1), Unused1 (5), SkeletonUnused (6) and Script (8) are reserved and not currently emitted.

The body that follows the data_type byte depends on the type, and is one of the four layouts below.

2.9.3.2.1. Mesh component

Table 2.42 Mesh component body

Field

Type

Size (bytes)

Description

data_uid

uint64 (avs::uid)

8

Mesh resource uid. Must be non-zero; the mesh itself is sent as a separate Mesh or MeshPointer chunk.

skeletonID

uint64 (avs::uid)

8

Skeleton resource uid, or 0 for an unskinned mesh.

joint_indices count

uint16

2

Number of entries in the joint-indices array.

joint_indices

int16[count]

n

Per-bone joint indices into the referenced skeleton. -1 marks an unmapped bone.

animations count

uint16

2

Number of entries in the animations array.

animations

uint64[count]

n

Animation resource uids associated with this node.

materials count

uint16

2

Number of entries in the materials array.

materials

uint64[count]

n

Material resource uids; one per submesh.

lightmapScaleOffset

vec4

16

Lightmap UV transform: xy scale, zw offset.

globalIlluminationUid

uint64 (avs::uid)

8

Texture uid for the GI / lightmap texture, or 0 if the node is not lightmapped.

2.9.3.2.2. Light component

Table 2.43 Light component body

Field

Type

Size (bytes)

Description

lightColour

vec4

16

Linear-space colour (r, g, b, a). Alpha is unused by the reference renderer.

lightRadius

float

4

Light source radius, in metres.

lightRange

float

4

Maximum effective distance of the light, in metres.

lightDirection

vec3

12

Forward direction in the client’s axes (already converted by the encoder).

lightType

uint8

1

Light-type tag (point / spot / directional / area). See avs::Node’s lightType field.

The Light component body is 37 bytes:

packet-beta title Light component body (byte offsets after data_type) 0-15: "lightColour" 16-19: "lightRadius" 20-23: "lightRange" 24-35: "lightDirection" 36-36: "lightType"

2.9.3.2.3. TextCanvas component

Table 2.44 TextCanvas component body

Field

Type

Size (bytes)

Description

data_uid

uint64 (avs::uid)

8

TextCanvas resource uid. The canvas contents are delivered separately as a TextCanvas chunk (id 9).

2.9.3.3. Notes

  • String fields obey the file-wide convention from Coding conventions: uint16-length-prefixed UTF-8, never null-terminated.

  • All vectors, quaternions and Transform values are already in the client’s avs::AxesStandard when they reach the wire.

  • The numComponents byte is a forward-compatibility hook. Senders currently write 0 or 1; future revisions can attach multiple components to one node without a wire-level layout change.

2.9.4. Mesh payload

A Mesh chunk carries a compressed mesh resource (geometry + skin binding). Only MeshCompressionType::DRACO is currently emitted; uncompressed meshes are not supported on the wire.

In-memory representation: avs::CompressedMesh.

Table 2.46 Mesh body

Field

Type

Size (bytes)

Description

meshCompressionType

uint8 (avs::MeshCompressionType)

1

0 = NONE (not supported on the wire), 1 = DRACO.

version

uint16

2

Mesh-payload version. Currently 1.

dracoVersion

int32

4

Draco bitstream version number. Currently 1.

name length

uint16

2

UTF-8 byte count.

name

uint8[name length]

variable

Mesh name.

invBindDataSize

uint64

8

Length in bytes of the inverse-bind-matrix block that follows. 0 if the mesh is unskinned.

invBindData

uint8[invBindDataSize]

variable

Tightly packed array of mat4 (16 floats = 64 bytes each), one per joint. Omitted when invBindDataSize == 0.

submeshCount

uint32

4

Number of submesh records that follow.

submeshes

see below

variable

submeshCount records.

Each submesh record is:

Table 2.47 Submesh record

Field

Type

Size (bytes)

Description

bufferSize

uint64

8

Length of the Draco buffer that follows.

buffer

uint8[bufferSize]

variable

Raw Draco-encoded submesh bytes; see the Draco bitstream specification.

2.9.5. Material payload

A Material chunk carries a PBR (metallic-roughness) material descriptor and a list of extension records. Texture resources referenced by avs::uid are sent as separate Texture or TexturePointer chunks.

In-memory representation: avs::Material.

Table 2.48 Material body

Field

Type

Size (bytes)

Description

name length

uint16

2

UTF-8 byte count.

name

uint8[name length]

variable

Material name.

materialMode

uint8 (avs::MaterialMode)

1

0 UNKNOWN, 1 OPAQUE, 2 TRANSPARENT, 3 MASKED.

baseColorTexture

TextureAccessor

25

PBR base colour texture (see TextureAccessor below).

baseColorFactor

vec4

16

Linear-space multiplier (r, g, b, a).

metallicRoughnessTexture

TextureAccessor

25

Metallic + roughness texture.

metallicFactor

float

4

Multiplier for the metallic channel.

roughnessMultiplier

float

4

Multiplier for the roughness channel.

roughnessOffset

float

4

Constant added to roughness after the multiplier.

normalTexture

TextureAccessor

25

Normal map; the union member is interpreted as scale. If the client did not declare normals support in the handshake, index is forced to 0.

occlusionTexture

TextureAccessor

25

Ambient occlusion map; the union member is interpreted as strength. If the client did not declare ambientOcclusion support, index is forced to 0.

emissiveTexture

TextureAccessor

25

Emissive map.

emissiveFactor

vec3

12

Linear-space emissive colour.

doubleSided

uint8

1

Non-zero if the material should be rendered on both sides.

lightmapTexCoordIndex

uint8

1

Index of the UV channel used for lightmaps.

extensionCount

uint64

8

Number of material extensions that follow.

extensions

see below

variable

extensionCount extension records.

inlineTextureCount

uint64

8

Number of inline Texture chunks that follow this material on the wire. Currently always 0; texture resources are sent independently.

TextureAccessor is a 25-byte fixed-size record:

Table 2.49 TextureAccessor

Field

Type

Size (bytes)

Description

index

uint64 (avs::uid)

8

Texture resource uid, or 0 if absent.

texCoord

uint8

1

UV channel index (TEXCOORD_n).

tiling.x

float

4

UV tiling on the U axis.

tiling.y

float

4

UV tiling on the V axis.

scale / strength

float

4

Union: normal-map scale or occlusion strength. Other slots ignore the value but the field is always present.

Each extension record begins with a 4-byte identifier:

Table 2.50 Extension record

Field

Type

Size (bytes)

Description

id

uint32 (avs::MaterialExtensionIdentifier)

4

0 = SIMPLE_GRASS_WIND.

body

id-specific

variable

Layout defined by the named extension.

2.9.6. MaterialInstance payload

Reserved. MaterialInstance (id 3) is not currently emitted; the body is unspecified and receivers must skip the chunk by advancing past payloadSize bytes.

2.9.7. Texture payload

A Texture chunk carries an inline compressed texture. Large textures (over ~1 MiB) are normally promoted to a TexturePointer payload instead.

In-memory representation: avs::Texture.

Table 2.51 Texture body

Field

Type

Size (bytes)

Description

name length

uint16

2

UTF-8 byte count.

name

uint8[name length]

variable

Texture name (often the source file stem).

compression

uint32 (avs::TextureCompression)

4

0 UNCOMPRESSED (rejected on the wire), 1 PNG, 2 MULTIPLE_PNG, 3 KTX, 4 JPEG.

compressedData

uint8[…]

variable

Codec-specific payload. Its length is implied by payloadSize - (2 + nameLength + 4).

The MULTIPLE_PNG format is a face/mip array (length-prefixed PNG blobs); the byte-exact layout is defined by the texture-cache writer in the reference server.

2.9.8. Animation payload

An Animation chunk carries a per-bone keyframe track.

In-memory representation: teleport::core::Animation.

Table 2.52 Animation body

Field

Type

Size (bytes)

Description

name length

uint16

2

UTF-8 byte count.

name

uint8[name length]

variable

Animation name.

duration

float

4

Animation length in seconds. Must be >= 0.

boneTrackCount

uint64

8

Number of bone tracks that follow.

tracks

see below

variable

boneTrackCount bone tracks.

Each bone track is:

Table 2.53 Bone track

Field

Type

Size (bytes)

Description

boneIndex

int16

2

Index of the bone within the referencing skeleton; -1 means unmapped.

positionKeyframeCount

uint16

2

Number of position keyframes.

positionKeyframes

{float time, vec3 value}[count]

16·n

Position keyframes in the client’s axes. Time in seconds; value in metres.

rotationKeyframeCount

uint16

2

Number of rotation keyframes.

rotationKeyframes

{float time, vec4 value}[count]

20·n

Rotation keyframes. vec4 is a quaternion (x, y, z, w).

2.9.9. Skeleton payload

A Skeleton chunk gives the ordered list of bone-node uids that make up a skeletal hierarchy. The bones themselves are normal scene nodes sent as Node payload chunks.

In-memory representation: avs::Skeleton.

Table 2.54 Skeleton body

Field

Type

Size (bytes)

Description

name length

uint16

2

UTF-8 byte count.

name

uint8[name length]

variable

Skeleton name.

boneCount

uint64

8

Number of bone uids that follow.

boneIDs

uint64[boneCount]

8·n

Bone-node uids, in the order expected by the Mesh component’s joint_indices.

2.9.10. FontAtlas payload

A FontAtlas chunk carries the glyph-position metadata for a font, keyed by point size. The actual glyph bitmap is delivered as a separate Texture whose uid is given here.

In-memory representation: teleport::core::FontAtlas.

Table 2.55 FontAtlas body

Field

Type

Size (bytes)

Description

font_texture_uid

uint64 (avs::uid)

8

Texture uid of the font bitmap.

mapCount

uint8

1

Number of font-size maps that follow. Maximum 255.

maps

see below

variable

mapCount font maps.

Each font map is:

Table 2.56 Font map

Field

Type

Size (bytes)

Description

pointSize

uint16

2

Font size key for this map.

lineHeight

float

4

Line height in pixels for this size.

glyphCount

uint16

2

Number of glyphs that follow.

glyphs

Glyph[glyphCount]

30·n

Packed glyph records (see below).

A Glyph is a 30-byte packed record:

packet-beta title Glyph (byte offsets) 0-1: "index" 2-3: "x0" 4-5: "y0" 6-7: "x1" 8-9: "y1" 10-13: "xOffset" 14-17: "yOffset" 18-21: "xAdvance" 22-25: "xOffset2" 26-29: "yOffset2"

  • index, x0, y0, x1, y1 are uint16 and describe the bounding box of the glyph in the font texture.

  • xOffset, yOffset, xAdvance, xOffset2, yOffset2 are float and describe placement and advance, in the canvas’s local units.

2.9.11. TextCanvas payload

A TextCanvas chunk carries the renderable contents of a text canvas referenced by a Node’s TextCanvas component.

In-memory representation: teleport::core::TextCanvas.

Table 2.57 TextCanvas body

Field

Type

Size (bytes)

Description

font_uid

uint64 (avs::uid)

8

FontAtlas uid this canvas renders with.

pointSize

int32

4

Selected point size; must match one of the FontAtlas’s pointSize keys.

lineHeight

float

4

Per-canvas line-height override, in the canvas’s local units.

colour

vec4

16

Foreground colour (r, g, b, a).

text length

uint16

2

UTF-8 byte count of the text payload.

text

uint8[text length]

variable

UTF-8 text. Not null-terminated.

The fixed 32-byte head:

packet-beta title TextCanvas head (byte offsets) 0-7: "font_uid" 8-11: "pointSize" 12-15: "lineHeight" 16-31: "colour"

2.9.12. TexturePointer payload

A TexturePointer chunk delivers a Texture indirectly: it carries an HTTP(S) URL that the client fetches and then decodes as a Texture payload. See HTTP Service for the fetch and caching rules.

Table 2.58 TexturePointer body

Field

Type

Size (bytes)

Description

url length

uint16

2

UTF-8 byte count.

url

uint8[url length]

variable

Absolute URL (http:// / https://) or path relative to the cache’s defaultURLRoot.

Example body bytes for the URL "/a/b.ktx2":

09 00                                    -- url length = 9
2F 61 2F 62 2E 6B 74 78 32               -- "/a/b.ktx2"

2.9.13. MeshPointer payload

A MeshPointer chunk has the identical layout to TexturePointer payload, but the body fetched from the URL is decoded as a Mesh payload.

Table 2.59 MeshPointer body

Field

Type

Size (bytes)

Description

url length

uint16

2

UTF-8 byte count.

url

uint8[url length]

variable

Absolute URL or relative path; see HTTP Service.

2.9.14. MaterialPointer payload

Reserved. MaterialPointer (id 12) is not currently emitted; receivers must skip the chunk by advancing past payloadSize bytes. The intended body matches TexturePointer payload but for Material resources.

2.9.15. RemoveNodes payload

The RemoveNodes chunk instructs the client to delete one or more scene nodes. It is the only payload type whose chunk header omits the uid field — there is no single resource this chunk is “about”.

Table 2.60 RemoveNodes body

Field

Type

Size (bytes)

Description

count

uint16

2

Number of node uids that follow. Must be > 0; senders skip emitting the chunk entirely when there is nothing to remove.

nodeIDs

uint64[count]

8·n

Node uids to delete. Each delete is idempotent and silently succeeds if the node is unknown.

Example body removing two nodes with uids 0x11 and 0x22:

02 00                                    -- count = 2
11 00 00 00 00 00 00 00                  -- uid 0x11
22 00 00 00 00 00 00 00                  -- uid 0x22

2.9.16. Resource lifecycle

For every chunk other than RemoveNodes, TexturePointer, MeshPointer and MaterialPointer, the server records the uid as “in flight” and waits for the client to confirm receipt by sending ReceivedResourcesMessage (id 4) on the reliable client-to-server channel. See Messages from Client to Server.

For pointer chunks, the server treats the resource as delivered as soon as the chunk leaves the encoder; the actual asset is then fetched out-of-band over HTTPS. The client is still expected to send ReceivedResourcesMessage once the HTTP body has been decoded.

If the client’s decoder fails (e.g. corrupt data, missing dependency, decoder panic), the client emits ResourceLostMessage (id 5) to ask the server to re-send. The server should treat the uid as “not yet received” and re-encode it on the next streaming pass.

2.9.17. Axis conversion

Every geometric value (positions, transforms, vectors and quaternions) is converted from the server’s avs::AxesStandard to the client’s standard inside the encoder, using avs::ConvertTransform. The client therefore reads geometry data in its own axes, regardless of how the source scene is authored. The server’s axes are reported in SetupCommand.axesStandard for diagnostic purposes only.

2.9.18. Coding conventions

  • Strings are uint16-length-prefixed UTF-8, never null-terminated.

  • Variable-length arrays are size_t-length-prefixed.

  • Quaternions are vec4_packed (x, y, z, w); positions are vec3_packed in metres.

  • Compression flags (MeshCompressionType, TextureCompression) are 1-byte enums whose layout is part of the wire format; new values must bump protocol_version.