io7m | single-page | multi-page | SMF Format Specification 0.16.0-SNAPSHOT

SMF Format Specification 0.16.0-SNAPSHOT

DATE 2016-11-21T22:03:59+00:00
DESCRIPTION Format Specification
IDENTIFIER bab4dfb1-4cca-4aa4-b05a-e77d96135b85
LANGUAGE en
SOURCE https://www.io7m.com/software/smfj/
TITLE SMF Format Specification 0.16.0-SNAPSHOT

Table Of Contents

This section of the specification documents all notational conventions used throughout.
The specification makes reference to the Unicode character set which, at the time of writing, is at version 9.0. The specification often references specific Unicode characters, and does so using the standard notation U+NNNN, where N represents a hexadecimal digit. For example, U+03BB corresponds to the lowercase lambda symbol λ.
The specification gives grammar definitions in ISO/IEC 14977:1996 Extended Backus-Naur form.
Rather than rely on untyped and ambiguous mathematical notation, this documentation expresses all mathematics and type definitions in strict Haskell 2010 with no extensions. All Haskell sources are included along with the documentation and can therefore be executed from the command line GHCi tool in order to interactively check results and experiment with functions.
When used within prose, functions are referred to using fully qualified notation, such as (Vector3f.cross n t). This is the application of the cross function defined in the Vector3f module, to the arguments n and t.
Definitions of binary structures are given as FIX SBE definitions. All values are considered to be in big-endian byte order unless otherwise specified.
This specification uses terms such as MUST, MUST NOT, SHOULD, SHOULD NOT, etc, according to the definitions given in RFC 2119.
The SMF model is a minimalist, portable storage model for triangle mesh data. SMF files are intended to be consumed by 3D rendering engines directly and therefore do not contain any complex interchange features common to formats such as COLLADA [2]. The text and binary encodings of the model are designed to be trivial to parse and to permit the easy construction of extremely fast event-based parsers that do not require loading the entire file into memory for processing.
The format is versioned, and is specified in a manner that allows for implementations to provide strong guarantees of forwards and backwards compatibility as the specification evolves.
The version of the SMF model described by this specification is (2, 0). See versioning for details.
The SMF specification is versioned via a restricted subset of the Semantic Versioning specification. The SMF specification has a major and minor version number, with major version increments denoting incompatible changes, and minor version increments denoting new functionality. There is no patch version number. A version of the specification with major version m and minor version n is denoted as specification version (m, n).
Assuming a version of the SMF specification m, an update to the specification that yields version n such that n > m is considered to be forwards compatible if a parser that supports format version m can read files that were written using format version n.
Assuming a version of the SMF specification m, an update to the specification that yields version n such that n > m is considered to be backwards compatible if a parser that supports format version n can read files that were written using format version m.
The SMF specification is designed such that a correctly-written parser implementation that supports a major version m is able to support the set of versions ∀n. (m, n). This implies full forwards and backwards compatibility for parsers when the major version is unchanged.
Changes that would cause a parser supporting an older version of the specification to fail to read a file written according to a newer version of the specification MUST imply an increment in the major version of the specification.
Changes that would cause a parser supporting a newer version of the specification to fail to read a file written according to an older version of the specification MUST imply an increment in the major version of the specification.
An implication of the above rules is that new features added to the specification must be added in a manner that allows them to be ignored by older parsers, lest the major version of the specification be incremented on every update.
A mesh in the SMF model is a set of triangles.
An attribute in the SMF model is a uniquely-named array of elements of a given type. All attributes within a particular SMF file have the same number of elements. The type of an attribute is a 3-tuple (c, n, s), where c is a component type, n is a component count, and s is a component size expressed in bits.
This version of the specification defines three basic component types: signed integers, unsigned integers, and floating point numbers. Floating point numbers are IEEE 754 floating point values.

2.4.3. Attribute Type

module Attribute where

data ComponentType =
    IntegerSigned
  | IntegerUnsigned
  | FloatingPoint

data AttributeType = AttributeType {
  attribute_component_type  :: ComponentType,
  attribute_component_count :: Word32,
  attribute_component_size  :: Word32
}

data Attribute = Attribute {
  attribute_name :: String,
  attribute_type :: AttributeType
}
Attribute names may be at most 64 characters in length and must conform to the following syntax:

2.4.5. Attribute Name Syntax

attribute_name =
  ? [a-z_.0-9]{1,64} ? ;
Implementations are required to support attributes of at least the following types:

2.4.7. Types

Component Type Component Count Component size (bits)
Signed integer 1 8
Signed integer 2 8
Signed integer 3 8
Signed integer 4 8
Signed integer 1 16
Signed integer 2 16
Signed integer 3 16
Signed integer 4 16
Signed integer 1 32
Signed integer 2 32
Signed integer 3 32
Signed integer 4 32
Signed integer 1 64
Signed integer 2 64
Signed integer 3 64
Signed integer 4 64
Unsigned integer 1 8
Unsigned integer 2 8
Unsigned integer 3 8
Unsigned integer 4 8
Unsigned integer 1 16
Unsigned integer 2 16
Unsigned integer 3 16
Unsigned integer 4 16
Unsigned integer 1 32
Unsigned integer 2 32
Unsigned integer 3 32
Unsigned integer 4 32
Unsigned integer 1 64
Unsigned integer 2 64
Unsigned integer 3 64
Unsigned integer 4 64
IEEE754 floating point 1 16
IEEE754 floating point 2 16
IEEE754 floating point 3 16
IEEE754 floating point 4 16
IEEE754 floating point 1 32
IEEE754 floating point 2 32
IEEE754 floating point 3 32
IEEE754 floating point 4 32
IEEE754 floating point 1 64
IEEE754 floating point 2 64
IEEE754 floating point 3 64
IEEE754 floating point 4 64
A vertex is an abstract object consisting of exactly one element taken from each of the defined attributes. A vertex can essentially be considered to be an array index; The vertex at index n can be considered to be the aggregation of the nth elements of all the defined attributes. Vertices are numbered starting at 0.
A triangle is a 3-tuple of vertices. In the SMF model, a triangle references vertices by their numeric index.
A coordinate system in the SMF model is a set of three axis names and a triangle winding order. The axis names specify which Cartesian coordinate system axes correspond to the right, up, and forward directions. The winding order specifies whether vertices for triangles are given in clockwise or counter-clockwise order. A coordinate system is only valid if the three chosen axes have different axis names. The axisName function relates an axis to an axis name. The axisValid function gives a formal definition of the validity of sets of axes.

2.7.2. Axis

module Axis where

data Axis
  = PositiveX
  | PositiveY
  | PositiveZ
  | NegativeX
  | NegativeY
  | NegativeZ

data AxisName
  = AxisX
  | AxisY
  | AxisZ

axisName :: Axis -> AxisName
axisName PositiveX = AxisX
axisName NegativeX = AxisX
axisName PositiveY = AxisY
axisName NegativeY = AxisY
axisName PositiveZ = AxisZ
axisName NegativeZ = AxisZ

data AxisSystem = AxisSystem {
  axis_right   :: Axis,
  axis_up      :: Axis,
  axis_forward :: Axis
}

axisValid :: (Axis, Axis, Axis) -> Bool
axisValid (right, up, forward) =
  case (axisName right, axisName up, axisName forward) of
    (AxisX, AxisY, AxisZ) -> True
    (AxisZ, AxisX, AxisY) -> True
    (AxisY, AxisZ, AxisX) -> True
    _                     -> False

data WindingOrder
  = WindingOrderClockwise
  | WindingOrderCounterClockwise

data CoordinateSystem = CoordinateSystem {
  coords_axes  :: AxisSystem,
  coords_order :: WindingOrder
}
A schema identifier is an optional identifier that can be inserted into SMF files. Because the SMF model does not predefine any particular attributes, tools that consume SMF files cannot know ahead of time if the file they have just loaded will actually contain the attributes that they are expecting. A schema identifier effectively provides a concrete name for a set of attributes so that tools that process SMF files can perform validation of attributes based on the identifier. It is somewhat analogous to XML namespace [1] declarations; The author of a particular document inserts an XML namespace identifier into their document, and validation tools use this identifier to locate schema definitions against which the document is then validated.
A schema identifier consists of a string and two integer values:

2.8.3. Schema Identifier

data SchemaIdentifier = SchemaIdentifier {
  schema_id            :: String,
  schema_version_major :: Word32,
  schema_version_minor :: Word32
}
The schema_id uniquely identifies the schema, and the schema_version_major and schema_version_minor values identify the version of the schema.
Schema names may be at most 64 characters in length and must conform to the following syntax:

2.8.6. Schema Name Syntax

schema_name =
  ? [a-z_.0-9]{1,64} ? ;
The SMF model supports the inclusion of arbitrary metadata. A metadata value is an opaque data value with a schema identifier specified in the same manner as the schema identifier. Applications may use the metadata facilities to associate small amounts of extra information with mesh data. Typical uses include checksums, copyright information, and generating software package version information.

Footnotes

1
References to this footnote: 1
2
References to this footnote: 1
The SMF/B format is a binary encoding for the SMF model.
An SMF/B file begins with a simple fixed-size header followed by a series of sections containing data and metadata.
All data in an SMF/B file is written in big-endian byte order, with the exception of triangle and vertex data which may be electively written in big-endian or little-endian byte order based on the order specified in the SMF section.
An SMF/B file begins with the following fixed-size structure:

3.2.2. SMF/B Header

<composite name="SMFHeader">
  <type name="magicNumber"
        description="The magic number identifying the section."
        presence="constant"
        primitiveType="uint64">
    0x89534D460D0A1A0A
  </type>
  <type name="versionMajor"
        description="The major version of the specification to which this file conforms."
        primitiveType="uint32"/>
  <type name="versionMinor"
        description="The minor version of the specification to which this file conforms."
        primitiveType="uint32"/>
</composite>
The value of the magicNumber field MUST be 0x89534D460D0A1A0A. Implementations MUST immediately reject any files that do not begin with this magic number. The derivation of this constant is taken almost verbatim from the PNG [1] file format with the characters PNG changed to SMF.
The versionMajor field specifies the major version number of the specification to which the data in the file is expected to conform. The versionMinor field specifies the minor version number of the specification to which the data in the file is expected to conform.
The smf section specifies information common to all sections in an SMF/B file.
An smf section MUST appear exactly once in an SMF/B file.
The smf section MUST be the first section in an SMF/B file. Implementations MUST reject SMF/B files that do not start with an smf section.
The smf section is identified by the magic number 0x534D465F48454144.
The smf section begins with the following structure:

3.3.4.2. SMF Header

<composite name="SMFSectionFields">
  <ref name="fieldsSize"
       type="uint32"/>
  <ref name="schemaIdentifier"
       type="SMFSchemaIdentifier"/>
  <ref name="vertexCount"
       type="uint64"/>
  <type name="triangleCount"
        primitiveType="uint64"
        semanticType="Length"/>
  <type name="triangleSizeBits"
        primitiveType="uint32"
        semanticType="Length"/>
  <ref name="attributeCount"
       type="uint32"/>
  <ref name="coordinateSystem"
       type="SMFCoordinateSystem"/>
  <enum name="SMFByteOrder"
        encodingType="uint32">
    <validValue name="BIG_ENDIAN">0</validValue>
    <validValue name="LITTLE_ENDIAN">1</validValue>
  </enum>
</composite>
The fieldsSize field specifies the total size of the header fields, after which a list of attributes will be declared. The purpose of this field is to allow for forward compatibility: Future updates to this specification may add extra fields to the end of the header structure, and the value of this field allows older implementations to skip those newer fields.
The schemaIdentifier field specifies the schema identifier for the model:

3.3.4.5. Schema Identifier

<composite name="SMFSchemaIdentifier">
  <type name="nameLength"
        description="The number of bytes in the following name array that should be extracted to form a name."
        primitiveType="uint32"/>
  <type name="name"
        primitiveType="uint8"
        characterEncoding="UTF-8"
        length="64"/>
  <type name="versionMajor"
        description="The major version of the schema."
        primitiveType="uint32"/>
  <type name="versionMinor"
        description="The minor version of the schema."
        primitiveType="uint32"/>
</composite>
If the length of the schema_id field is 0, the schema identifier as a whole is ignored.
The vertexCount field specifies the number of vertices that will be specified in a vertices-noninterleaved section. Parsers MUST raise an appropriate error if a non-zero vertex count is specified but no vertices are actually provided in any following section.
The triangleCount field specifies the number of triangles that will be specified in a triangles section. The triangleSizeBits field specifies the size in bits of the individual vertex indices within each triangle. Parsers MUST raise an appropriate error if a non-zero triangle count is specified but no triangles are actually provided in any following section.
The attributeCount field specifies the number of attributes that will be declared.
The coordinateSystem field specifies the coordinate system of the model:

3.3.4.11. Coordinate System

<enum name="SMFAxis"
      encodingType="uint8">
  <validValue name="AXIS_POSITIVE_X">0</validValue>
  <validValue name="AXIS_POSITIVE_Y">1</validValue>
  <validValue name="AXIS_POSITIVE_Z">2</validValue>
  <validValue name="AXIS_NEGATIVE_X">3</validValue>
  <validValue name="AXIS_NEGATIVE_Y">4</validValue>
  <validValue name="AXIS_NEGATIVE_Z">5</validValue>
</enum>

<enum name="SMFWindingOrder"
      encodingType="uint8">
  <validValue name="FACE_WINDING_ORDER_CLOCKWISE">0</validValue>
  <validValue name="FACE_WINDING_ORDER_COUNTER_CLOCKWISE">1</validValue>
</enum>

<composite name="SMFCoordinateSystem">
  <ref name="right"
       type="SMFAxis"/>
  <ref name="up"
       type="SMFAxis"/>
  <ref name="forward"
       type="SMFAxis"/>
  <ref name="windingOrder"
       type="SMFWindingOrder"/>
</composite>
The axis and winding order values are related to integer values via the following functions:

3.3.4.13. Coordinate System Mapping

import qualified Axis as A

axisOrdinal :: A.Axis -> Integer
axisOrdinal A.PositiveX = 0
axisOrdinal A.PositiveY = 1
axisOrdinal A.PositiveZ = 2
axisOrdinal A.NegativeX = 3
axisOrdinal A.NegativeY = 4
axisOrdinal A.NegativeZ = 5

windingOrdinal :: A.WindingOrder -> Integer
windingOrdinal A.WindingOrderClockwise        = 0
windingOrdinal A.WindingOrderCounterClockwise = 1
The byteOrder field specifies the endianness of the values in the vertex and triangle.
Directly following the end of the header structure is a series of attribute declarations:

3.3.4.16. Attribute

<composite name="SMFSchemaAttribute">
  <type name="nameLength"
        description="The number of bytes in the following name array that should be extracted to form a name."
        primitiveType="uint32"/>

  <type name="name"
        primitiveType="uint8"
        characterEncoding="UTF-8"
        length="64"/>

  <enum name="componentKind"
        encodingType="uint32">
    <validValue name="ELEMENT_TYPE_INTEGER_SIGNED">0</validValue>
    <validValue name="ELEMENT_TYPE_INTEGER_UNSIGNED">1</validValue>
    <validValue name="ELEMENT_TYPE_FLOATING">2</validValue>
  </enum>

  <type name="componentCount"
        description="The number of components in a single attribute value."
        minValue="1"
        maxValue="4"
        primitiveType="uint32"/>

  <type name="componentSizeBits"
        description="The size in bits of components in a single attribute value."
        minValue="1"
        maxValue="64"
        primitiveType="uint32"/>
</composite>
The name field specifies the name of the attribute. The componentKind field specifies the type of the attribute components. The componentCount field specifies the number of components in each value in the attribute, and the componentSizeBits field specifies the size in bits of each component value.
Attribute component types are related to integer values via the following function:

3.3.4.19. Attribute Type Mapping

import qualified Attribute as A

attributeTypeOrdinal :: A.ComponentType -> Integer
attributeTypeOrdinal A.IntegerSigned   = 0
attributeTypeOrdinal A.IntegerUnsigned = 1
attributeTypeOrdinal A.FloatingPoint   = 2
Directly following the header, an SMF/B file consists of a series of sections. A section begins with the following fixed-size structure:

3.4.1.2. Section header

<composite name="SMFSection">
  <type name="magicNumber"
        description="The magic number identifying the section."
        primitiveType="uint64"/>
  <type name="size"
        description="The size of the section data"
        primitiveType="uint64"
        semanticType="Length"/>
</composite>
The id field of the section header identifies the type of the section. The size field of the section header specifies the data size; the size in octets of the data that follows the section header. The data size necessarily includes any trailing padding octets that may be inserted in order to guarantee that the start of the next section (if any) is correctly aligned. This implies that the data size of any given section MUST be a multiple of the alignment size.
Implementations MUST ignore any content inside a section of an unrecognized type [1]. As sections explicitly state the size of their own data, implementations can simply seek forwards in the file by the specified data size to reach the next section.
The first section in an SMF/B file MUST be an smf section. The last section in an SMF/B file MUST be an end section.
Sections MUST be aligned to 16 octet boundaries. As the section header is defined to be exactly 16 octets, this implies that the data within a section will also be aligned to a 16 octet boundary [2].
This version of the specification defines the following sections:
Note: This subsection is informative.
The design of sections allows for implementations to quickly enumerate all sections within a file using the following strategy:

3.4.4.3. Enumerating Sections

void showSections()
{
  // Read SMF header
  long smf = readUnsigned64();
  int major = readUnsigned32();
  int minor = readUnsigned32();

  // Read all sections
  while (bytesRemaining()) {
    long offset = currentOffset();
    long magic = readUnsigned64();
    long size = readUnsigned64();

    System.out.printf(
    "section: %s %s %s\n",
    Long.toUnsignedString(offset),
    Long.toUnsignedString(magic, 16),
    Long.toUnsignedString(size));

    seek(size);
  }
}
The bytesAvailable method is assumed to return true if there are any bytes remaining in the file. The currentOffset method is assumed to return the current read position in octets within the file. The readUnsigned64 method is assumed to read a 64-bit big-endian integer from the current file, advancing the current read position by 8 octets. The readUnsigned32 method is assumed to read a 32-bit big-endian integer from the current file, advancing the current read position by 4 octets.

Footnotes

1
Ignoring unrecognized sections allows for forwards compatibility: An implementation supporting version m can read a file of version n, where m < n, and ignore any new sections specified by format n that it does not understand.
References to this footnote: 1
2
The requirement for data to be aligned to 16 octet boundaries is intended to facilitate implementations using vector instruction sets such as SSE to efficiently operate on memory-mapped SMF/B files.
References to this footnote: 1
The metadata section specifies arbitrary metadata.
A metadata section can appear [0, n] times in an SMF/B file, for any n.
The metadata section has no requirements on ordering.
The metadata section is identified by the magic number 0x534D465F4D455441.
The data of a metadata section starts with the following fixed-size header:

3.5.4.2. Metadata Header

<composite name="SMFMeta">
  <ref name="metaSchemaIdentifier"
       type="SchemaIdentifier"/>
  <ref name="metaSize"
       type="uint32"/>
</composite>
The metaSchemaIdentifier, field is intended to provide type information for the metadata. It has the same semantics and purpose as schema identifiers. The metaSize field specifies the size in octets of the metadata.
The triangles section specifies triangle data.
A triangles section can appear [0, 1] times in an SMF/B file.
The triangles section has no requirements on ordering.
The triangles section is identified by the magic number 0x534D465F54524953.
Triangles are specified as an array of 3-element unsigned integer vectors, with each integer component being of the size declared in the preceding smf section. No padding is inserted between triangles, but padding may need to be added at the end of the array in order to ensure proper section alignment. All values are stored in either big-endian or little-endian form depending on the order specified in the SMF section.
The vertices-noninterleaved section specifies vertex data for all declared attributes.
A vertices-noninterleaved section can appear [0, 1] times in an SMF/B file.
The vertices-noninterleaved section has no requirements on ordering.
The vertices-noninterleaved section is identified by the magic number 0x534D465F56444E49.
The data for each attribute is given in full in the order in which the attributes were declared in the header. Specifically, in a file containing v vertices and a list of attributes a of length n, the data of the section will contain v values of the type specified in a !! 0, followed by v values of the type specified by a !! 1, and so on up to a !! (n - 1). All values are stored in either big-endian or little-endian form depending on the order specified in the SMF section.
The start of the data for each attribute is aligned to the next 16 octet boundary regardless of type. The alignment is achieved by inserting padding octets at the end of the data for each attribute so that the start of the data for the next attribute, or the start of the next section, occurs on a 16 octet boundary.
The end section indicates the end of an SMF/B file.
An end section must appear exactly once in an SMF/B file.
The end section MUST be the last section in an SMF/B file. Implementations MUST reject SMF/B files that do not contain a terminating end section.
The end section is identified by the magic number 0x534D465F454E4421.
The end section contains no data. The specified data size for the section MUST be 0.
A diagram of an example SMF/B file is as follows:
The SMF/T format is a text encoding for the SMF model. Data specified in the text encoding is separated into explicitly delimited sections.
The format is line-based, and lines (excluding the very first line in the file) containing only whitespace, or lines beginning with U+0023 # are ignored. In a given line, whitespace between tokens is insignificant.
The smf section specifies the major and minor version of the specification to which the rest of the file is expected to conform. The first argument specifies the major version, and the second argument specifies the minor version. Implementations MUST immediately reject files that are of unsupported major versions, and MUST halt processing of the rest of the file if the version numbers cannot be parsed.
The command takes the following arguments:

4.2.1.3. smf Arguments

  1. The non-negative SMF major version
  2. The non-negative SMF minor version

4.2.1.4. smf

smf =
  "smf" , integer-unsigned , integer-unsigned ;
The contents of an smf section are a series of subcommands with at most one subcommand appearing on each line within the section. A subcommand consists of the subcommand's name and a series of zero or more arguments. Implementations MUST ignore subcommands that have unrecognized names, but SHOULD log warning messages to indicate that unrecognized subcommands have been specified.
This version of the specification defines the following smf subcommands:
The attribute subcommand specifies an attribute that will appear in the model.

4.2.3.2. attribute

attribute_name_raw =
  ? [a-z_.0-9]{1,64} ? ;

attribute_name_quoted =
  """ , attribute_name_raw , """ ;

attribute_name =
  attribute_name_raw | attribute_name_quoted ;

attribute_type =
  "integer-signed" | "integer-unsigned" | "float" ;

smf_attribute =
  "attribute" , attribute_name , attribute_type , integer-unsigned , integer-unsigned ;
The subcommand takes the following arguments:

4.2.3.4. attributes Arguments

  1. The name of the attribute
  2. The type of the components in the attribute
  3. The non-negative number of components in the attribute
  4. The non-negative size in bits of an individual component
The subcommand is allowed to appear any number of times, but all specified attribute names must be unique.
The coordinates subcommand specifies the coordinate space of the mesh data that will appear in the model.

4.2.4.2. coordinates

axis =
  "+x" | "-x" | "+y" | "-y" | "+z" | "-z" ;

winding_order =
  "clockwise" | "counter-clockwise" ;

smf_coordinates =
  "coordinates" , <axis> , <axis> , <axis> , <winding_order> ;
The subcommand takes the following arguments:

4.2.4.4. coordinates Arguments

  1. The axis for the right direction
  2. The axis for the up direction
  3. The axis for the forward direction
  4. The triangle winding order
If the subcommand does not appear in the section, implementations MUST behave as if the following subcommand had been specified:

4.2.4.6. coordinates Default

coordinates +x +y -z counter-clockwise
The endianness subcommand specifies the byte order of data that will appear in the file. Because byte ordering is irrelevant in an SMF/T file, the effect of evaluating this subcommand is to set the byte order in the parsed SMF model to the specified value. This will affect subsequent serializations of the model to formats with which byte ordering is relevant (such as SMF/B) .

4.2.5.2. endianness

byte_order =
  "big" | "little" ;

smf_attribute =
  "endianness" , byte_order ;
The subcommand takes the following arguments:

4.2.5.4. attributes Arguments

  1. The name of the byte order
The subcommand is allowed to appear at most once. If the command does not appear, implementations MUST behave as if big-endian byte ordering was specified.
The schema command specifies the schema identifier for the file.

4.2.6.2. schema

smf_schema =
  "schema" , schema_name , integer-unsigned , integer-unsigned ;
The command takes the following arguments:

4.2.6.4. schema Arguments

  1. The schema ID string
  2. The non-negative schema major version
  3. The non-negative schema minor version
The triangles subcommand specifies the number of triangles that will appear in the model, and the size of the vertex indices in bits.

4.2.7.2. triangles

smf_triangles =
  "triangles" , integer-unsigned , integer-unsigned ;
The subcommand takes the following arguments:

4.2.7.4. triangles Arguments

  1. The non-negative triangle count
  2. The non-negative size in bits of a triangle vertex index
If the subcommand does not appear in the section, implementations MUST behave as if the following subcommand had been specified:
Parsers MUST raise an appropriate error if a non-zero triangle count is specified but no triangles are actually provided in any following section.
The vertices subcommand specifies the number of vertices that will appear in the model for each attribute.

4.2.8.2. vertices

smf_vertices =
  "vertices" , integer-unsigned ;
The subcommand takes the following arguments:

4.2.8.4. vertices Arguments

  1. The non-negative number of vertices that will appear in the file
If the subcommand does not appear in the section, implementations MUST behave as if the following subcommand had been specified:
Parsers MUST raise an appropriate error if a non-zero vertex count is specified but no vertices are actually provided in any following section.
A section is started with a section command and terminated with a corresponding end command. Implementations MUST ignore any content between and including an unrecognized section command and the corresponding end command[1].
The first line of an SMF/T file MUST begin an smf section.
A section is terminated with an end command. Implementations MUST raise errors if an end-of-file condition is encountered before an end command is given for the current section.

4.3.1.4. end

end =
  "end" ;
The command takes no arguments.
This version of the specification defines the following sections:

Footnotes

1
Ignoring unrecognized sections allows for forwards compatibility: An implementation supporting version m can read a file of version n, where m < n, and ignore any new sections specified by format n that it does not understand.
References to this footnote: 1
The metadata section specifies an item of optional metadata that will be attached to the model.

4.4.1.2. metadata

metadata =
  "metadata" , schema_name , integer-unsigned , integer-unsigned , integer-unsigned ;
The command takes the following arguments:

4.4.1.4. meta Arguments

  1. The schema ID
  2. The major version of the schema
  3. The minor version of the schema
  4. The non-negative number of lines of metadata that will follow the command
For any v, s, and n, the command meta v s n will indicate that the next n non-empty, non-commented lines will contain RFC4648 base64url encoded data of a type with vendor v and schema s. Implementations MUST raise errors if there are not exactly n lines of data specified before the section's end command.
The triangles section specifies triangle data that will appear in the model.

4.5.1.2. triangles

data_command_triangles =
  "triangles" ;
The command takes no arguments.
In a file that is specified to contain k triangles, the next k non-empty, non-commented lines will contain exactly 3 whitespace-separated unsigned integer values each representing the index of a vertex. Implementations MUST raise errors if there are not exactly k triangles specified before section's end command.
The vertices-noninterleaved section specifies the vertex data that will appear in the model.
The contents of a vertices-noninterleaved section are a series of subcommands with at most one subcommand appearing on each line within the section. A subcommand consists of the subcommand's name and a series of zero or more arguments. Implementations MUST ignore subcommands that have unrecognized names, but SHOULD log warning messages to indicate that unrecognized subcommands have been specified.
This version of the specification defines the following vertices-noninterleaved subcommands:
The attribute subcommand indicates the start of data for an attribute.

4.6.3.2. attribute

vertices_noninterleaved_attribute =
  "attribute" , attribute_name ;
The command takes the following arguments:

4.6.3.4. attribute Arguments

  1. The name of the attribute
The named attribute must have been declared in the smf section; Implementations MUST raise errors when encountering attribute subcommands that name attributes that were not declared in the smf section.
For an attribute a that specifies c components of type t, in a file that is specified to contain v vertices, the next v non-empty, non-commented lines will contain exactly c whitespace-separated values of type t. Implementations MUST raise errors if there are not exactly v vertex values specified before section's end command or the start of the next attribute subcommand, whichever occurs first.
The following is a complete example of an SMF/T file:

4.7.2. smf

smf 1 0
schema com.io7m.example.smf 1 0
vertices 9
triangles 4 32
coordinates +x +y -z counter-clockwise
attribute "POSITION" float 3 32
attribute "NORMAL" float 3 32
attribute "UV:UVMap" float 2 32
attribute "GROUP:group0" float 1 32
end

#
# This is a comment and will be ignored.
#

vertices-noninterleaved
attribute "POSITION"
0.000000000000000 0.000000000000000 0.000000000000000
1.000000000000000 0.000000000000000 0.000000000000000
0.000000000000000 0.000000000000000 -2.000000000000000
1.000000000000000 0.000000000000000 -2.000000000000000
2.000000000000000 0.000000000000000 0.000000000000000
2.000000000000000 0.000000000000000 -2.000000000000000
1.000000000000000 0.000000000000000 0.000000000000000
1.000000000000000 0.000000000000000 -2.000000000000000
1.000000000000000 0.000000000000000 0.000000000000000
attribute "NORMAL"
0.000000000000000 1.000000000000000 0.000000000000000
0.000000000000000 0.999999940395355 0.000000000000000
0.000000000000000 1.000000000000000 0.000000000000000
0.000000000000000 1.000000000000000 0.000000000000000
0.000000000000000 1.000000000000000 0.000000000000000
0.000000000000000 1.000000000000000 0.000000000000000
0.000000000000000 0.999999940395355 0.000000000000000
0.000000000000000 1.000000000000000 0.000000000000000
0.000000000000000 0.999999940395355 0.000000000000000
attribute "UV:UVMap"
0.112528264522552 0.912521243095398
0.112528264522552 0.712549567222595
0.512471735477448 0.912521243095398
0.696853816509247 0.614087224006653
0.396896183490753 0.464108407497406
0.696853816509247 0.464108407497406
0.396896213293076 0.614087224006653
0.512471735477448 0.712549448013306
0.396896213293076 0.614087224006653
attribute "GROUP:group0"
0.000000000000000
0.300000011920929
0.200000002980232
0.400000005960465
0.600000023841858
0.699999988079071
0.300000011920929
0.400000005960465
0.300000011920929
end

triangles
1 2 0
6 5 3
1 7 2
8 4 5
end

metadata com.example.metadata.example0 1 0 1
aGVsbG8taGVsbG8K
end

metadata com.example.metadata.example3 2 0 5
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1
Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWpr
bG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch
oqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX
2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w==
end
This section provides a brief summary of specification changes over time.

5.2. History

  • 1.0 Initial specification.
  • 2.0 Optional endianness, redesigned binary encoding.
io7m | single-page | multi-page | SMF Format Specification 0.16.0-SNAPSHOT