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:
Encoder —
teleport::server::GeometryEncoder(Teleport/TeleportServer/GeometryEncoder.cpp).Decoder —
teleport::clientrender::GeometryDecoder(Teleport/ClientRender/GeometryDecoder.cpp).Payload-type enum —
avs::GeometryPayloadType(libavstream/common_exports.h).
2.9.1. Channel framing¶
Each SCTP message on the geometry channel contains a single payload chunk:
Bytes |
Type |
Description |
|---|---|---|
8 |
size_t |
|
1 |
GeometryPayloadType |
Chunk type (see table below). |
8 |
avs::uid |
Resource uid this chunk belongs to. |
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¶
Id |
Name |
Body |
|---|---|---|
0 |
Invalid |
Reserved. |
1 |
Mesh |
1-byte |
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 |
|
5 |
Animation |
Skeletal animation: name, duration, per-bone position and rotation keyframe lists. See Animation payload. |
6 |
Node |
|
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 |
|
11 |
MeshPointer |
|
12 |
MaterialPointer |
Reserved; not currently emitted. See MaterialPointer payload. |
13 |
RemoveNodes |
|
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¶
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
name length |
|
2 |
UTF-8 byte count. The string is not null-terminated. |
name |
|
variable |
Node name. Useful for diagnostics; the client does not key on it. |
localTransform |
|
40 |
|
stationary |
|
1 |
|
holder_client_id |
|
8 |
Owning client id, or |
priority |
|
4 |
Streaming priority. The server may withhold the mesh of nodes below the client’s minimum priority. |
parentID |
|
8 |
Parent node uid, or |
numComponents |
|
1 |
Number of component records that follow. Currently |
Once name has been consumed, the remainder of the header is a fixed 62-byte tail:
2.9.3.2. Component record¶
A component record is present only when numComponents != 0 and begins with a single byte identifying the type:
Field |
Type |
Size |
Description |
|---|---|---|---|
data_type |
|
1 |
One of |
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¶
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
data_uid |
|
8 |
Mesh resource uid. Must be non-zero; the mesh itself is sent as a separate |
skeletonID |
|
8 |
Skeleton resource uid, or |
joint_indices count |
|
2 |
Number of entries in the joint-indices array. |
joint_indices |
|
2·n |
Per-bone joint indices into the referenced skeleton. |
animations count |
|
2 |
Number of entries in the animations array. |
animations |
|
8·n |
Animation resource uids associated with this node. |
materials count |
|
2 |
Number of entries in the materials array. |
materials |
|
8·n |
Material resource uids; one per submesh. |
lightmapScaleOffset |
|
16 |
Lightmap UV transform: |
globalIlluminationUid |
|
8 |
Texture uid for the GI / lightmap texture, or |
2.9.3.2.2. Light component¶
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
lightColour |
|
16 |
Linear-space colour |
lightRadius |
|
4 |
Light source radius, in metres. |
lightRange |
|
4 |
Maximum effective distance of the light, in metres. |
lightDirection |
|
12 |
Forward direction in the client’s axes (already converted by the encoder). |
lightType |
|
1 |
Light-type tag (point / spot / directional / area). See |
The Light component body is 37 bytes:
2.9.3.2.3. TextCanvas component¶
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
data_uid |
|
8 |
TextCanvas resource uid. The canvas contents are delivered separately as a |
2.9.3.2.4. Link component¶
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
url length |
|
2 |
UTF-8 byte count of the target URL. |
url |
|
variable |
Target URL activated when the user follows the link. |
query length |
|
2 |
UTF-8 byte count of the query string. |
query |
|
variable |
Optional query string appended to the URL on activation; empty for links that take no parameters. |
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
Transformvalues are already in the client’savs::AxesStandardwhen they reach the wire.The
numComponentsbyte is a forward-compatibility hook. Senders currently write0or1; 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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
meshCompressionType |
|
1 |
|
version |
|
2 |
Mesh-payload version. Currently |
dracoVersion |
|
4 |
Draco bitstream version number. Currently |
name length |
|
2 |
UTF-8 byte count. |
name |
|
variable |
Mesh name. |
invBindDataSize |
|
8 |
Length in bytes of the inverse-bind-matrix block that follows. |
invBindData |
|
variable |
Tightly packed array of |
submeshCount |
|
4 |
Number of submesh records that follow. |
submeshes |
see below |
variable |
|
Each submesh record is:
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
bufferSize |
|
8 |
Length of the Draco buffer that follows. |
buffer |
|
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
name length |
|
2 |
UTF-8 byte count. |
name |
|
variable |
Material name. |
materialMode |
|
1 |
|
baseColorTexture |
|
25 |
PBR base colour texture (see TextureAccessor below). |
baseColorFactor |
|
16 |
Linear-space multiplier |
metallicRoughnessTexture |
|
25 |
Metallic + roughness texture. |
metallicFactor |
|
4 |
Multiplier for the metallic channel. |
roughnessMultiplier |
|
4 |
Multiplier for the roughness channel. |
roughnessOffset |
|
4 |
Constant added to roughness after the multiplier. |
normalTexture |
|
25 |
Normal map; the union member is interpreted as |
occlusionTexture |
|
25 |
Ambient occlusion map; the union member is interpreted as |
emissiveTexture |
|
25 |
Emissive map. |
emissiveFactor |
|
12 |
Linear-space emissive colour. |
doubleSided |
|
1 |
Non-zero if the material should be rendered on both sides. |
lightmapTexCoordIndex |
|
1 |
Index of the UV channel used for lightmaps. |
extensionCount |
|
8 |
Number of material extensions that follow. |
extensions |
see below |
variable |
|
inlineTextureCount |
|
8 |
Number of inline |
TextureAccessor is a 25-byte fixed-size record:
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
index |
|
8 |
Texture resource uid, or |
texCoord |
|
1 |
UV channel index ( |
tiling.x |
|
4 |
UV tiling on the U axis. |
tiling.y |
|
4 |
UV tiling on the V axis. |
scale / strength |
|
4 |
Union: normal-map |
Each extension record begins with a 4-byte identifier:
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
id |
|
4 |
|
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
name length |
|
2 |
UTF-8 byte count. |
name |
|
variable |
Texture name (often the source file stem). |
compression |
|
4 |
|
compressedData |
|
variable |
Codec-specific payload. Its length is implied by |
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
name length |
|
2 |
UTF-8 byte count. |
name |
|
variable |
Animation name. |
duration |
|
4 |
Animation length in seconds. Must be |
boneTrackCount |
|
8 |
Number of bone tracks that follow. |
tracks |
see below |
variable |
|
Each bone track is:
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
boneIndex |
|
2 |
Index of the bone within the referencing skeleton; |
positionKeyframeCount |
|
2 |
Number of position keyframes. |
positionKeyframes |
|
16·n |
Position keyframes in the client’s axes. Time in seconds; value in metres. |
rotationKeyframeCount |
|
2 |
Number of rotation keyframes. |
rotationKeyframes |
|
20·n |
Rotation keyframes. |
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
name length |
|
2 |
UTF-8 byte count. |
name |
|
variable |
Skeleton name. |
boneCount |
|
8 |
Number of bone uids that follow. |
boneIDs |
|
8·n |
Bone-node uids, in the order expected by the Mesh component’s |
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
font_texture_uid |
|
8 |
Texture uid of the font bitmap. |
mapCount |
|
1 |
Number of font-size maps that follow. Maximum |
maps |
see below |
variable |
|
Each font map is:
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
pointSize |
|
2 |
Font size key for this map. |
lineHeight |
|
4 |
Line height in pixels for this size. |
glyphCount |
|
2 |
Number of glyphs that follow. |
glyphs |
|
30·n |
Packed glyph records (see below). |
A Glyph is a 30-byte packed record:
index, x0, y0, x1, y1areuint16and describe the bounding box of the glyph in the font texture.xOffset, yOffset, xAdvance, xOffset2, yOffset2arefloatand 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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
font_uid |
|
8 |
FontAtlas uid this canvas renders with. |
pointSize |
|
4 |
Selected point size; must match one of the FontAtlas’s |
lineHeight |
|
4 |
Per-canvas line-height override, in the canvas’s local units. |
colour |
|
16 |
Foreground colour |
text length |
|
2 |
UTF-8 byte count of the text payload. |
text |
|
variable |
UTF-8 text. Not null-terminated. |
The fixed 32-byte head:
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
url length |
|
2 |
UTF-8 byte count. |
url |
|
variable |
Absolute URL ( |
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.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
url length |
|
2 |
UTF-8 byte count. |
url |
|
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”.
Field |
Type |
Size (bytes) |
Description |
|---|---|---|---|
count |
|
2 |
Number of node uids that follow. Must be |
nodeIDs |
|
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 arevec3_packedin metres.Compression flags (
MeshCompressionType,TextureCompression) are 1-byte enums whose layout is part of the wire format; new values must bump protocol_version.