Skip to main content

Writing a Converter

This guide provides a technical overview of the FTL format — the native asset format used by the Faster Than Light (FTL) engine, which powers the 3dverse platform.

It explains how to structure intermediary files that conform to FTL’s schemas and naming conventions, so they can be correctly ingested and finalized by the 3dverse backend.

While this is not a tutorial on building a plugin for a specific tool (e.g., Blender or a CAD suite), it gives you the necessary format-level guidance to integrate your own exporter or asset pipeline into the FTL ecosystem.

You’ll learn how to emit compliant files for Mesh, Material, and Scene assets — and how those assets are validated and assembled into a live 3dverse project.

What is FTL?

FTL stands for Faster Than Light — it is both the name of the real-time engine that powers the 3dverse platform, and the internal asset format used to feed it.

FTL defines how assets are structured, serialized, referenced, and streamed within the engine. It is designed to support real-time, collaborative 3D experiences at scale.

Unlike traditional formats like FBX, GLTF, or OBJ, FTL is not a monolithic file format. Instead, it defines a modular, backend-native representation of assets composed of three parts:

  1. Dependencies – which other assets this one references
  2. Description – a JSON document conforming to a versioned schema
  3. Payload – raw binary content (e.g., geometry, textures)

Each part is stored separately:

  • Dependencies → in a graph database
  • Descriptions → in a document store
  • Payloads → in blob/object storage (e.g. filesystem, S3)

This structure allows FTL to support:

  • Massive scenes with modular, reusable components
  • Efficient streaming of assets on demand
  • Cloud-native pipelines for real-time collaboration and live updates

As a converter author, your job is to generate intermediary files that follow this structure and comply with the FTL schemas, allowing them to be finalized and integrated into the 3dverse platform.

Why FTL?

If you're coming from formats like FBX or GLTF, the FTL model may feel unusual at first — but it's designed for production-scale cloud pipelines.

Most traditional formats embed everything into a single opaque blob. FTL, by contrast, makes every asset composable, versionable, and inspectable from the ground up — making it ideal for collaborative, distributed workflows.

For converter developers, this means:

  • No need to bundle complex references
  • Each part (description, payload) is authored independently
  • Validation and dependency linking happen automatically in the backend

Converter Workflow

When writing a converter, your responsibility is to output intermediary files for assets. These files are then ingested and finalized inside the 3dverse backend.

Typical flow:

  1. Local conversion Original file → Your Converterdesc/payload intermediary files

  2. Validation & UUID verification UUIDs are validated for uniqueness and authorization.

    • Asset and entity UUIDs must not be duplicated.
    • If a UUID already exists in 3dverse, it must be writable by your API key.
    • The HTTP request must include the appropriate overwrite mode.
  3. Finalization The intermediary files are packaged and sent to the backend via the PUT /folders/{folder_id}/packages API.

Important

Converters must not attempt to generate dependency files themselves. Dependencies are always inferred internally from descriptions.

Intermediary File Naming

Each file must follow this pattern:

{part_type}.{asset_type}.{uuid}

Example:

desc.scene.c80b0e1c-fe9e-4c34-9c33-c407465aec8d
  • part_typedesc, payload
  • asset_typemesh, material, scene
  • uuid → UUID v4
note

Some asset types support additional payload formats, such as mipmaps or histogram files. See the Asset Import API for the complete naming pattern.

Mesh Assets

A Mesh asset consists of:

  • Description (desc.mesh.{uuid}) → Mesh schema
  • Payload (payload.mesh.{uuid}) → binary geometry

Key points:

  • Meshes do not depend on any other asset.
  • Description declares submeshes, vertex channels, index ranges, etc.
  • Payload contains the actual vertex/index buffers.
  • The payload must match the layout defined in the description:
    • Vertex channels must be packed in the correct format (e.g. float32, vec3).
    • Index buffers must reference valid ranges and respect declared submeshes.
  • The format is little-endian, tightly packed, with no metadata in the payload itself.

Pitfalls to avoid:

  • Only one UV channel currently supported.
  • Unsupported vertex color channels may be dropped.
  • Index buffers must be valid and AABBs consistent.

Converters should emit both the description and the payload and rely on validation tools to ensure consistency.

Example

A minimal desc.mesh.{uuid} file for a unit cube might look like this:

{
"uuid": "11111111-1111-1111-1111-111111111111",
"name": "Cube",
"payloadSize": 720,
"submeshes": [
{
"aabb": {
"min": [-0.5, -0.5, -0.5],
"max": [0.5, 0.5, 0.5]
},
"channels": [
{
"semantic": "index",
"elementCount": 1,
"elementType": 1,
"elementSize": 4,
"dataOffset": 0,
"dataSize": 144
},
{
"semantic": "position",
"elementCount": 3,
"elementType": 2,
"elementSize": 4,
"dataOffset": 144,
"dataSize": 288
},
{
"semantic": "normal",
"elementCount": 3,
"elementType": 2,
"elementSize": 4,
"dataOffset": 432,
"dataSize": 288
}
],
"indexCount": 36,
"vertexCount": 24
}
]
}

This corresponds to a cube with 24 unique vertices (4 per face × 6 faces) and 36 indices (12 triangles), using 32-bit float vectors for positions and normals, and 32-bit uint for indices. The offsets and sizes must match your binary payload.mesh.{uuid} exactly.

Material Assets

A Material asset consists only of:

Key points:

  • Materials always depend on a Shader.
  • The description specifies:
    • Shader reference (by UUID)
    • Parameter values to bind (scalars, vectors, textures)
  • No payload file is needed.

Important:

  • The dependency file is generated internally, not by the converter.
  • Materials must reference a shader that exists and matches its descriptor (i.e. required inputs must be provided).

Converters should focus on generating compliant desc.material JSON with correct parameters.

Example

This example defines a simple opaque red material using the default PBR shader provided by 3dverse.

{
"uuid": "22222222-2222-2222-2222-222222222222",
"name": "Red Opaque Material",
"shaderRef": "f2a549e5-4f72-4cef-a5ab-48873c209e0c",
"dataJson": {
"albedo": [1.0, 0.0, 0.0, 1.0],
"metallic": 0.0,
"roughness": 0.5
},
"isDoubleSided": false
}

Field breakdown

FieldDescription
uuidUnique Material identifier (UUID v4)
nameHuman-readable name
shaderRefPoints to the default OPAQUE PBR shader (f2a549e5-4f72-4cef-a5ab-48873c209e0c)
dataJsonParameters defined by the shader descriptor
isDoubleSidedWhether the Material renders both front and back faces

Need transparency?

Use the default transparent PBR shader instead:

"shaderRef": "a740058e-27a0-48e3-af37-70ae93cc0b67"

Scene Assets

A Scene asset is the most complex. It consists of:

A Scene is essentially an ECS graph: entities described by components.

Core components to support:

ComponentPurpose
euidEntity UUID (must be unique and valid UUID v4)
debug_nameHuman-readable name
lineageParent/child relationship
local_transformTransform relative to the parent
mesh_refReference to a Mesh asset
material_refReference to a Material asset
scene_refReference to another Scene

Notes:

  • Scene descriptions are JSON; components must comply with provided schemas.
  • Each entity must have a unique EUID (UUID v4).
  • Converters may generate them deterministically or randomly.
  • EUIDs are never modified by the backend — duplicates within a Scene or across assets will result in validation failure.

Example

This Scene defines a single entity using the unit cube mesh and red opaque Material defined earlier.

{
"uuid": "33333333-3333-3333-3333-333333333333",
"name": "Scene with Cube",
"aabb": {
"min": [-0.5, -0.5, -0.5],
"max": [0.5, 0.5, 0.5]
},
"entities": [
{
"euid": {
"value": "00000000-1111-2222-3333-444444444444"
},
"debug_name": {
"value": "Cube Entity"
},
"lineage": {
"ordinal": 0,
"parentUUID": "00000000-0000-0000-0000-000000000000"
},
"local_transform": {
"position": [0.0, 0.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"orientation": [0.0, 0.0, 0.0, 1.0]
},
"mesh_ref": {
"value": "11111111-1111-1111-1111-111111111111",
"submeshIndex": 0,
"shadowCastingMode": 1
},
"material_ref": {
"value": "22222222-2222-2222-2222-222222222222"
},
"local_aabb": {
"min": [-0.5, -0.5, -0.5],
"max": [0.5, 0.5, 0.5]
}
}
]
}

Packaging Your Output

As a converter developer, your plugin must output a ZIP archive of FTL-compliant intermediary files, ready to be sent to the 3dverse backend via the Import Assets API.

Your ZIP package should contain:

  1. Files at the root of the archive, named using the format:
desc.{asset_type}.{uuid}
payload.{asset_type}.{uuid}
  1. For Mesh assets:
  • A desc.mesh.{uuid} JSON file compliant with the Mesh schema.
  • A payload.mesh.{uuid} binary file matching the declared vertex/index layout.
  1. For Material assets:
  • A desc.material.{uuid} JSON file with a valid shader reference and parameters.
  • No payload is required.
  1. For Scene assets:
  • A desc.scene.{uuid} JSON file describing entities and components.
  • Use valid UUID v4 strings.
  • Ensure no two entities or assets share the same UUID.
  • If uploading an asset that already exists, the backend requires overwrite permissions and a matching overwrite mode.
UUID Validation Rules
  • All UUIDs must be valid UUID v4.
  • UUIDs must be globally unique across your payload.
  • No two assets (or two entities in the same Scene) can share the same UUID.
  • If a UUID already exists in 3dverse, you must:
    • Have permission to overwrite it with your API key
    • Set the appropriate overwrite mode in your HTTP request:
      • only-specified with an overwrite.json file
      • or always to replace all
How to Submit

To upload your intermediary files:

  • Zip them together into a flat archive. Example:

    my_export.zip
    ├── desc.mesh.11111111-1111-1111-1111-111111111111
    ├── payload.mesh.11111111-1111-1111-1111-111111111111
    ├── desc.material.22222222-2222-2222-2222-222222222222
    ├── desc.scene.33333333-3333-3333-3333-333333333333
    ├── overwrite.json (if using `only-specified` mode)
  • Send it via the PUT /folders/{folder_id}/packages endpoint.

Refer to the asset API documentation for full details on supported types and overwrite behavior.

Summary of Design Decisions

  • Split storage (dep / desc / payload) chosen for scalability and flexibility.
  • UUIDs must be globally unique and valid. They are verified, not regenerated.
  • Schemas are the single source of truth — your converter must comply strictly.
  • Converters focus on producing valid intermediaries, not on database integration.
  • Validation tools (provided by 3dverse) will catch schema and payload mismatches.
Ready to go

With this, you can start building a converter for your tool (Blender, CAD, etc.) that exports directly into FTL intermediaries.

Glossary

TermDefinition
FTLFaster Than Light engine + asset format used by 3dverse
UUIDUniversally unique identifier (must be v4, no duplicates)
DescriptionJSON metadata file describing an asset
PayloadBinary file containing raw data (vertex data, pixel colors, etc.)
DependencyInternal asset reference (inferred, not authored)