io7m-smfj 0.1.0 Specification
Notational Conventions
Unicode
The specification makes reference to the Unicode character set which, at the time of writing, is at version 8.0.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 λ.
EBNF
The specification gives grammar definitions in ISO/IEC 14977:1996 Extended Backus-Naur form.
Haskell
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.
JPRA
Definitions of binary structures are given as jpra language definitions. All values are considered to be in big-endian byte order unless otherwise specified.
Model
Overview
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 [0]. 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.
Mesh
A mesh in the SMF model is a set of triangles.
Attributes
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.
Implementations are required to support attributes of at least the following types:
  • 8/16/32/64-bit signed integer vectors of 1-4 components
  • 8/16/32/64-bit unsigned integer vectors of 1-4 components
  • 16/32/64-bit floating point vectors of 1-4 components
Vertices
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 of the defined attributes. Vertices are numbered starting at 0.
Triangles
A triangle is a 3-tuple of vertices. In the SMF model, a triangle references vertices by their numeric index.
Schema ID
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 four integer values:
data SchemaIdentifier = SchemaIdentifier {
  vendor_id            :: Word32,
  schema_id            :: Word32,
  schema_version_major :: Word32,
  schema_version_minor :: Word32
}
The vendor_id uniquely identifiers the vendor, the schema_id uniquely identifies the file type, and the schema_version_major and schema_version_minor values identify the version of the schema.
SMFT - Text Encoding
Overview
The SMFT format is a text encoding for the SMF model. The format is separated into a header and data section.
The format is line-based, with one command per line. Lines (excluding the very first line in the file) containing only whitespace, or lines beginning with U+0023 # are ignored.
Header
The first line of an SMFT file MUST consist of an smf command. After the initial command, the header consists of any number of header commands and is terminated by the data command.
A header command is one of the following:
Header - smf
The smf command 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 are required to immediately reject files that are not of supported versions, and are required to halt processing of the rest of the file in the case of syntax errors.
The command takes the following arguments:
  • The non-negative SMF major version
  • The non-negative SMF minor version
header_command_smf =
  "smf" , integer-unsigned , integer-unsigned ;
Header - vertices
The vertices command specifies the number of vertices that will appear in the data section for each attribute.
header_command_vertices =
  "vertices" , integer-unsigned ;
The command takes the following arguments:
  • The non-negative number of vertices that will appear in the file
The command is required to appear exactly once in the header.
Header - schema
The schema command specifies the schema identifier for the file.
header_command_schema =
  "schema" , integer-unsigned-base16 , integer-unsigned-base16 , integer-unsigned , integer-unsigned ;
The command takes the following arguments:
  • The vendor ID as a hexadecimal integer
  • The schema ID as a hexadecimal integer
  • The non-negative schema major version
  • The non-negative schema minor version
The command is allowed to appear at most once in the header. If the command is not specified, a default schema identifier consisting of all zeroes is implicitly defined.
Header - triangles
The triangles command specifies the number of triangles that will appear in the data section of the file, and the size of the vertex indices in bits.
header_command_triangles =
  "triangles" , integer-unsigned , integer-unsigned ;
The command takes the following arguments:
  • The non-negative triangle count
  • The non-negative size in bits of a triangle vertex index
The command is required to appear exactly once in the header.
Header - attribute
The attribute command specifies an attribute that will appear in the data section of the file.
attribute_name =
  ? [\p{IsAlphabetic}\p{IsDigit}_\-\.]{1,64} ? ;

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

header_command_attribute =
  "attribute" , attribute_name , attribute_type , integer-unsigned , integer-unsigned ;
The command takes the following arguments:
  • The name of the attribute
  • The type of the components in the attribute
  • The non-negative number of components in the attribute
  • The non-negative size in bits of an individual component
The command is allowed to appear any number of times in the header, but all specified attribute names must be unique.
Data
The data section of the file specifies the data for attributes and triangles. The section begins with the data command and consists of both data and data commands.
A data command is one of the following:
Data - data
The data command indicates the start of the data section.
data_command_data =
  "data" ;
The command takes no arguments.
Data - attribute
The attribute command indicates the start of data for an attribute.
data_command_attribute =
  "attribute" , attribute_name ;
The command takes the following arguments:
  • The name of the attribute
The named attribute must have been declared in the header. 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.
Data - triangles
The triangles command indicates the start of triangle data.
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.
Example
The following is a complete example of an SMFT file:
smf 1 0
vendor 696f376d a0b0c0d0 1 0
attribute "POSITION" float 3 32
attribute "NORMAL" float 3 32
attribute "UV:UVMap" float 2 32
attribute "GROUP:group0" float 1 32
vertices 9
triangles 4 32
data
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.400000005960464
0.600000023841858
0.699999988079071
0.300000011920929
0.400000005960464
0.300000011920929
triangles
1 2 0
6 5 3
1 7 2
8 4 5
SMFB - Binary Encoding
Header
The SMFB format is a binary encoding for the SMF model. The format is separated into a header and data section.
All SMFB files begin with a fixed header. The start of the header uses a fixed magic number and a major/minor version number pair indicating the major and minor versions of the specification that defines the file structure.
[record SMFBHeaderStart
  ([field magic           [integer unsigned 64]]
   [field version_major   [integer unsigned 32]]
   [field version_minor   [integer unsigned 32]])]
The magic number MUST always be 0x89534d460d0a1a0a; implementations are required to immediately reject any files that do not begin with this magic number. The derivation of this constant is taken almost verbatim from the PNG [2] file format with the characters PNG changed to SMF .
The header continues with an encoding of the schema ID.
[record SMFBV1VendorSchemaID
  ([field vendor_id                   [integer unsigned 32]]
   [field vendor_schema_id            [integer unsigned 32]]
   [field vendor_schema_version_major [integer unsigned 32]]
   [field vendor_schema_version_minor [integer unsigned 32]])]
The header continues by specifying the number of vertices present in the file, the number of triangles present in the file, the size in bits of the individual triangle indices, and the number of attributes present in the file. Two fields are reserved for future use.
[record SMFBV1VendorSchemaID
  ([field vendor_id                   [integer unsigned 32]]
   [field vendor_schema_id            [integer unsigned 32]]
   [field vendor_schema_version_major [integer unsigned 32]]
   [field vendor_schema_version_minor [integer unsigned 32]])]

[record SMFBV1Counts
  ([field vertex_count             [integer unsigned 64]]
   [field triangle_count           [integer unsigned 64]]
   [field triangle_index_size_bits [integer unsigned 32]]
   [field reserved_0               [integer unsigned 32]]
   [field attribute_count          [integer unsigned 32]]
   [field reserved_1               [integer unsigned 32]])]
The header concludes by giving definitions for exactly n attributes, where n is the attribute count specified in the previous section of the header.
[record SMFBV1Attribute
  ([field name            [string 64 "UTF-8"]]
   [field component_kind  [integer unsigned 32]]
   [field component_count [integer unsigned 32]]
   [field component_size  [integer unsigned 32]])]
The header is constructed with no implicit padding and no particular alignment. However, the individual header structures are constructed such that their sizes are always a multiple of 8. This ensures that the data that immediately follows the header also starts on an 8 octet boundary.
Data
After the header, 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 n attributes a, the file 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.
The start of the data for each attribute is aligned to the next 8 octet boundary regardless of type. The alignment is achieved by inserting padding octets at the end of the previous attribute. For example, the following diagram shows how the data is aligned for a file containing three vertices with two attributes A and B. Attribute A consists of three 16-bit floating-point components, and attribute B consists for four 32-bit floating-point components. As the file has three vertices, 3 * 3 = 9 values are placed into the file first, consuming 3 * 3 * 2 = 18 octets. Because the next octet, 18, is not divisible by 8, it's necessary to insert 6 octets of padding so that the data for B will start on the next 8 octet boundary.
All values are stored in big-endian form.