File Format for Mobile 3D Graphics API


Abstract

This specification defines a 3D Graphics File Format that complements the Mobile 3D Graphics API (M3G). The file format is provided as a compact and standardised way of populating a scene graph.

Contents


1 Important Notes

The data here are not serialized by Java's own serialization mechanism. They are serialized by the M3G serialization mechanism, which produces and loads data streams conforming to the M3G file format specification.

For more details of the mechanisms for loading a M3G compliant file, please refer to the documentation for the Loader class.

2 MIME Type and File Extension

The MIME type for this file format is application/m3g. The file extension (for systems that do not support MIME type queries) is .m3g, to match the lowest level name in the package hierarchy.

3 Data Types

3.1 Fundamental Data Types

There are several data types which are regarded as fundamental. These are as follows:
 

Type Name
Description
Byte
A single, unsigned 8-bit byte.
Int16
A signed 16 bit value, stored as two bytes, lower byte first.
UInt16
An unsigned 16 bit value, stored as two bytes, lower byte first.
Int32
A signed 32 bit value, stored as four bytes, lowest byte first.
UInt32
An unsigned 32 bit value, stored as four bytes, lowest byte first.
Float32
A single precision floating point value, in 32-bit format as defined by IEEE-754. This is stored as four bytes, with the least significant byte of the mantissa first, and the exponent byte last.
Note that only normal numeric values and positive 0 can be stored. Special values such as denormals, infinities, NaNs, negative 0, and indefinite values are disallowed and must be treated as errors.
String
A null-terminated Unicode string, coded as UTF-8.
Boolean
A single byte with the value 0 (false) or 1 (true). Other values are disallowed and must be treated as errors.

3.2 Compound Data Types

In order to avoid having to repeatedly specify sequences of the same types many times, some compound data types are defined for convenience. The composition of these is listed to show both their makeup and the order in which the simple elements are to be serialized. These are as follows:
 

Type Name
Description
Composition
Vector3D
A 3D vector.
Float32 x;
Float32 y;
Float32 z;
Matrix
A 4x4 generalized matrix. The 16 elements of the matrix are output in the same order as they are retrieved using the API Transform.get method. In other words, in this order:
  0  1  2  3
  4  5  6  7
  8  9  10 11
  12 13 14 15
Float32 elements[16];
ColorRGB
A color, with no alpha information. Each component is scaled so that 0x00 is 0.0, and 0xFF is 1.0.
Byte red;
Byte green;
Byte blue;
ColorRGBA
A color, with alpha information. Each component is scaled so that 0x00 is 0.0, and 0xFF is 1.0. The alpha value is scaled so that 0x00 is completely transparent, and 0xFF is completely opaque.
Byte red;
Byte green;
Byte blue;
Byte alpha;
ObjectIndex
The index of a previously encountered object in the file. Although this is serialized as a single unsigned integer, it is included in the compound type list because of the additional semantic information embodied in its type. A value of 0 is reserved to indicate a null reference; actual object indices start from 1. Object indices must refer only to null or to an object which has already been created during the input deserialization of a file - they must be less than or equal to the index of the object in which they appear. Other values are disallowed and must be treated as errors.
UInt32 index;
Type[]
A variable-length array of any type is always output in a counted form, with the count first. Each element is then output in index order, starting from 0. The last element has index (count-1). If the array is empty, then only a 0 count is output.
UInt32 count;
Type arrayValue[0];
Type arrayValue[1];
...etc.
Type[count]
Arrays with an explicit length are either always have the same constant number of elements, or this count is specified elsewhere, so only the elements are output. Each element is then output in index order, starting from 0. The last element has index (length-1). If the array is empty, then nothing is output.
Type arrayValue[0];
Type arrayValue[1];
...etc.

4 File Structure

The file consists of the file identifier, followed by one or more sections. Thus the overall file structure looks like this:
 
  File Identifier
Section 0 File Header Object
Section 1 External Reference Objects
Section 2 Scene Objects
Section 3 Scene Objects
... ...
Section n Scene Objects
The reason for having different sections is that some of the objects, such as the mesh objects, should be compressed to reduce file size, whereas other objects, such as the header object, should not be compressed. The header object must be kept uncompressed since it should be easy to read quickly.

The first section, Section 0, must be present, must be uncompressed and must contain only the header object. This object contains information about the file as a whole, and is discussed in detail in Section 10.1.

If there are external references in the file, then these must all appear in a single section immediately following the header section. This section may be compressed or uncompressed. External references allow scenes to be built up from a collection of separate files, and are discussed in detail in Section 10.2.

Following these are an unspecified number of sections containing scene objects.

The file must contain the header section, plus at least one other non-empty section (containing at least one object). It is possible to have a file consisting solely of external references, or solely of scene objects.

A file containing no objects at all is not a valid M3G file, and must be treated as an error.

5 File Identifier

The file identifier is a unique set of bytes that will differentiate the file from other types of files. It consists of 12 bytes, as follows:
Byte[12] FileIdentifier = { 0xAB, 0x4A, 0x53, 0x52, 0x31, 0x38, 0x34, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
This can also be expressed using C-style character definitions as:
Byte[12] FileIdentifier = { '«', 'J', 'S', 'R', '1', '8', '4', '»', '\r', '\n', '\x1A', '\n' }
The rationale behind the choice values in the identifier is based on the rationale for the identifier in the PNG specification. This identifier both identifies the file as a M3G file and provides for immediate detection of common file-transfer problems. A decoder may further verify that the next byte is 0 (this is the first byte of the mandatory uncompressed header section). This will catch bad transfers that drop or alter zero bytes.

6 Section

A section is a data container for one or more objects. The section header determines if the objects are compressed or not, how much object data there is, and also contains a checksum.

In this document, we will talk about "sections that are compressed" and "sections that are uncompressed". In reality, we will mean "sections where the objects are compressed", and "sections where the objects are uncompressed".

Each section has the following structure:

Byte                        CompressionScheme
UInt32 TotalSectionLength
UInt32 UncompressedLength
Byte[TotalSectionLength-13] Objects
UInt32 Checksum
We will now go through the individual parts of the section.

6.1 CompressionScheme

This field tells how the Objects field in this section is compressed. It also specifies what checksum algorithm is used. Currently, only the Adler32 checksum is mandatory. Compression only applies to the Object data, and not to the other fields in the section.

CompressionScheme must be one of the following values:

 
0 Uncompressed, Adler32 Checksum
1 ZLib compression, 32 k buffer size, Adler32 Checksum
2...255 Reserved
Example:
Byte CompressionScheme = 1;
indicates that the Objects field in the section is compressed using zlib with 32 k buffer size.

The values 2...255 are reserved for future releases and are disallowed. A loader that follows the specification must report an error if they are found.

6.2 TotalSectionLength

This is the total length of the section in bytes; from the start of this section to the start of the next section.

Example:

UInt32 TotalSectionLength = 2056
indicates that this section, including the CompressionScheme, TotalSectionLength, UncompressedLength, Objects and Checksum fields, will be 2056 bytes in length.

6.3 UncompressedLength

Knowing the size of the decompressed data ahead of time can be used to make Zlib inflation much easier and less memory hungry. Therefore, the size of the compressed part of the section (in bytes) before compression (or after decompression) is serialized as part of the section information. Since it is only the Objects field that can be compressed, UncompressedLength contains the length of the Objects field after decompression. If no compression is specified for this section, this equals the actual number of bytes serialized in the Objects array.

A value of 0 in this field is legal - the section is simply ignored. However, it is recommended that any process that creates a file should check for 0 length sections and eliminate them to reduce file size.

Example:

UInt32 UncompressedLength = 4560
Means that in this section, after decompression, the Objects field is 4560 bytes in length.

6.4 Objects

The objects in each section are serialized as an array of bytes, one after the other. This array of bytes will either be compressed (if CompressionScheme is 1) or it will be uncompressed. If it is compressed, it is compressed as a single chunk of data, not as separate objects. Zero bits must be padded in the end to make the Objects field byte aligned.

The structure of each individual object's data is documented in Section 10 and Section 11.

6.5 Checksum

To be able to verify that the section was correctly loaded, there is a 32-bit checksum of all the data in the section. The checksum algorithm is specified by the CompressionScheme field. Currently, only the Adler32 checksum is mandatory. The checksum is calculated using all preceding bytes in the section, i.e. the CompressionScheme,TotalSectionLength, UncompressedLength, and the actual serialized data in the Objects field (i.e. in its compressed form if compression is specified).

Example:

UInt32 Checksum = 0xffe806a3
On limited devices, we might not be able to afford to load an entire section before interpreting it. Thus the loader may start interpreting the objects before knowing that the section as a whole is correct. However, the checksums are still useful in that we at least know afterwards that there was an otherwise undetected error if the checksums differed.

Even on a system that can afford to load an entire section before loading it, it is possible to have errors in the file. The content creation program can have a defect, the transmission of the file could be error-prone, or the file could have been altered as part of a deliberate attack on the device. Thus it is important that the loader tries to detect errors also in files that have correct checksums.

The loader implementation may decide not to compute (and/or check) the checksum. Thus, a file with erroneous checksums is not guaranteed to be rejected. However, a file with erroneous checksums is not a M3G compliant file and must not pass a strict verification test.

7 Object Structure

The object data stored in each section is first decompressed and then interpreted as a sequence of objects. This separates the act of decompression from the interpretation of the data. All data documented in this section is assumed already to be in its uncompressed form.

Each object in the file represents one object in the scene graph tree, and is stored in a chunk. The structure of an object chunk is as follows:

Byte          ObjectType
UInt32 Length
Byte[Length] Data

7.1 ObjectType

This field describes what type of object has been serialized. For instance, we could have a Camera node, a Mesh node or a Texture2D object. Section 12 includes a table that shows the correspondence between ObjectType values and the actual object types. The ObjectType field must hold a valid value as defined in Section 12. The reserved object types (values 23..254) must be treated as errors.

The values 0 and 0xFF are special: 0 represents the header object, and 0xFF represents an external reference.

Example:

Byte ObjectType = 14
This means that the current object is a Mesh object (see Section 12).

7.2 Length

This contains the length of the Data array, in bytes. Note that a value of 0 in this field may be legal; some objects require no additional data over and above their mere presence.

Example:

UInt32 Length = 2032
indicates that the Data field of this object spans 2032 bytes in the (decompressed) file.

7.3 Data

This is data that is specific for the object. It is up to the loader to interpret this data according to the object type and populate the object accordingly. Detailed information on the data for each object type is documented in Section 10 and Section 11.

For instance, if the object just contained a single color, the Data would be a 3 byte long array, where the first byte represents the red component, the second byte the green component, and the third byte the blue component.

Attempts to read off the end of an object's data are disallowed and must be signalled as errors. An example of this would be an object with a reported length of 32 bytes, but which internally specifies an array with 65537 members.

Conversely, the deserialization code for each object may also check that each byte of the data belongs to a valid interpretation. Additional bytes after the end of an object's valid data are disallowed. This condition may be difficult to determine on the target platform, but any file which contains "extra" data in object chunks is not a M3G compliant file and must not pass a strict verification test.

8 Object Ordering

All the objects in the scene graph are serialized in leaf-first order, or reference based order as it is also called. Before serializing a specific object, all other objects referenced by that object must already have been serialized. Objects may refer to themselves if this is allowed by the scene data structures.

By definition, the root of the tree will be sent last.

Note that cycles are not allowed in the file format. There is one special case where they are allowed in the run-time scene graph, namely Node alignment. Before a scene graph containing cyclic references can be written into a file, the cycles must be broken. This can be done by inserting dummy target nodes as children of the original alignment targets. For example, if a leaf node is aligned to the World, an empty Group with an identity transformation is inserted as a child of the World, and the alignment redirected to that.

Given a scene graph with no cycles, it is possible to use a "leaves first" strategy for output - start by serializing all the objects that do not reference other objects, and then all the objects that refer to the objects already sent, and so it continues until all objects are sent.

Alternatively, a "depth first" strategy can be used, where each object recursively applies the following procedure, to build up a table of references in the correct order. (It is assumed that the table is initially empty.)

BuildReferenceTable:

for each reference in this object,
    call BuildReferenceTable on the referred object

if this object is not already in the reference table,
    append this object to the reference table.
Each object can then be serialized from the reference table in order.

For example, assume that we have the following tree structure:

Tree with node A as root, B under A, C and D under B, E under A again, and F under E

One valid ordering of the objects is C D F B E A. This is the ordering that occurs if the "leaves first" method is used. Note that other leaf-first orderings are also valid, for instance F D C E B A.

The "depth-first" method produces valid orderings where interior nodes in the graph may be sent before all the leaves have been sent. An ordering produced by the depth-first method discussed above might be C D B F E A.

The only important thing is that any objects referenced by a particular object are sent before the object itself.

With this flexibility, the ordering of references can be forced by the file creator if this is advantageous. For example, if we wish textures to be sent in a separate section that is uncompressed. Thus, if we have the following tree:

Tree with node A as root, B under A, T1 and D under B, E under A again, and T2 under E

where T1 and T2 are textures, we can send the scene graph using, for instance:

 
Identifier   File Identifier (see Section 5)
Section 0 Uncompressed File Header Object
Section 1 Uncompressed T1 T2
Section 2 Compressed D B E A
Other orderings are also possible, for instance:
 
Identifier   File Identifier (see Section 5)
Section 0 Uncompressed File Header Object
Section 1 Uncompressed T1 T2
Section 2 Compressed D E B A
or even (with a naive file creator):
 
Identifier   File Identifier (see Section 5)
Section 0 Uncompressed File Header Object
Section 1 Uncompressed T1
Section 2 Compressed D
Section 3 Uncompressed T2
Section 4 Compressed B E A
Because multiple root-level objects are allowed in the file format, there is no obvious end point in the data. In order that the loader can determine that the file has ended, the total length of the file is stored in the header. Reading from the file is ended when the total number of bytes is reached. At this point, any objects not yet linked into the scene graph are treated as root-level objects and returned to the application.

8.1 Object References

Each object serialized, including the header object, is given an index, in order, starting from 1. The 0 index is used to indicate a null reference. This index is unrelated to the user ID for an object.

A reference to an object is serialized as an integer containing its index within the file. The serialization order constraint can be expressed as follows:

For an object with index i, a reference index r within it is only valid if r <= i. Invalid reference indices must be treated as an error.

An object reference must refer to an object of a valid type for the reference involved. For example, the reference from an Appearance object to a Material object must actually refer to a Material object. If the referred object type is incorrect, this must be treated as an error.

8.2 Shared Objects

Shared objects are handled the same way as normal objects. We only need to make sure that a shared object is sent before both of the objects that reference it.

For instance, for the following tree of references, where X is a shared object

Tree with node A as root, B under A, X and D under B, E under A again, and X shared under E

A possible ordering would be D X B E A. Both the leaves-first and the depth-first algorithms described above will generate valid orderings.

8.3 Subtree Loading

With reference based order, it will be more difficult to load an arbitrary subtree from a file than with, e.g., root-first order. However, it is still possible, using two passes of the file. Assume that the subtree is defined as "object number X and all its children". In the first pass, only the node references are decoded, and an empty tree of the complete scene graph is created. From this graph, we find node X and make a list of all its descendants in the subtree. During the second pass, we simply load all the objects in the list. The last object that was loaded will be the root of the subtree.

In the case where rewinding the stream is not possible, it is also possible to do subtree loading in just one pass. This is achieved by loading everything up until the root node of the desired subtree, and then letting the garbage collection remove everything that is not referred to by the subtree. However, such an implementation would consume more memory than the two-pass implementation above. In the worst case, this is no different from loading the entire scene. For example, if the file contains a 3D map of the whole world and all you want is a specific house, you may still need to load the entire world and then delete everything but the house, if the house is the last object in the file.

9 Error Handling

There are several points at which an error may be detected. These include, but are not limited to: In particular, if values read from the loaded file would cause an immediate exception when passed to the API (e.g. all the attenuation parameters on a light are 0.0, or an image is too large for the implementation to handle), then this must be treated as an error.

If combinations of values are read that may cause a deferred exception, (e.g. a material and light are both present, but there are no normals specified), then this must not be treated as an error by the Loader. The application must be given the opportunity to take action after loading, in order to avoid these exceptions.

If any kind of error is detected during loading, the required action is for the Loader to abort this download, and that of any pending external references, clear up any temporary data structures, and throw an exception. If this file is being used as an external reference, then this is also treated as an error in the file that is attempting to load it. (This definition is, of course, recursive.)

The practical upshot of this is that any error detected in any of the files that may make up a world being loaded must result in a safe abort of the loading process and the throwing of a single exception to the main application.

It is up to the application what action, if any, is taken in the event of a loading error. Options range from an apologetic alert to the user ("Download failed!"), up through sophisticated error recovery schemes involving alternate file locations, or even different content in extreme cases.

10 Special Object Data

The data for the "special" object types is documented here.

10.1 Header Object

Object Type: 00
Superclass data: none
Followed by:
Byte[2] VersionNumber
Boolean hasExternalReferences
UInt32  TotalFileSize
UInt32  ApproximateContentSize
String  AuthoringField
There must be exactly one Header object in a file, and it must be the only object in the first section of the file, which in turn must be uncompressed. Due to its position in the file, it will always be assigned object index 1.

VersionNumber is a unique version number identifying variants of the file fomat. Only one variant is currently specified: version number 1.0. This must be indicated by VersionNumber = {1, 0}. The first number is major revision number, followed by minor revision.

hasExternalReferences is a boolean that describes whether this file is self-contained or includes URIs for other files, such as textures or geometries. If this is false, the file is self-contained. If it is true, then it indicates that the immediately following section of the file will contain the external reference objects needed to specify these external links. See Section 10.2 for more details.

TotalFileSize is the total size of the file, from the start to the end. It will be used in the loading, so it must be correct. (That is, it is not a hint.) For example, a file of size 6783 would define this field as TotalFileSize = 6783.

ApproximateContentSize contains the total number of bytes required to dowload the entire scene, including external links such as textures and geometry. This is provided as a hint, so that the user can know how much data he/she will pay for before loading the entire scene. The ApproximateContentSize field is also necessary in order to produce a good progress bar during the loading of the scene.

It should be noted that this information is only a hint. For instance, the file sizes of the objects that this file is linked to might have changed. Due to this, it is called "approximate" content size. Note that ApproximateContentSize should be equal to TotalFileSize if ExternalFiles is false.

For example, a file of 6083 bytes, with an external reference to another file of 10700 bytes would set ApproximateContentSize = 16783.

AuthoringField consists of a single nul-terminated UTF-8 string. The content of the string is not defined, and may include any information that the authoring environment wishes to place into it. Its most common purpose is mainly to make it possible to put a copyright note on the file, for example: AuthoringField = "Blast4Fun (C) 2003 Extreme Games Inc."

Note that if the string just contains numbers and letters from the English alphabet, the UTF-8 encoding will be the same as ASCII encoding.

10.2 External Reference

ObjectType: 0xFF (255)
Superclass data: none
Followed by:
String  URI
Instead of storing an object in-place, it is possible to have an external reference, in the form of a URI. This is stored in the object data as a single, nul-terminated UTF-8 string.

Relative URIs are relative to the file in which they are found, as usual. For example, a URI of "http://www.gamesforfun.com/objs/redcar.m3g", indicates another file in the M3G file format, at an absolute address, and "bluecartexture.png" indicates a PNG file in the same location as the current file.

If an external reference cannot be loaded, this will result in an error, causing the parent file to be "unloadable".

Loops of external references (e.g. file A references file B which in turn references file A again) are illegal and will result in a loading error.

The loader must only indicate that the loading of a file is complete when all external references within it have also been successfully loaded and the references type checked.

External references may appear only within their own section within the file. If present, this appears immediately after the file header section. It may be compressed or uncompressed.

In order to facilitate type checking of external references, loading of the externally referenced file must complete before reading any objects which could refer to it. This is one of the main reasons for ensuring that external references are in their own section, which occurs before sections containing objects of other types. For example, if the external reference is referred to as if it were an Appearance object, then the check that it is indeed an Appearance can occur only after loading the referred file.

External reference loading must support both M3G and PNG file types in order to satisfy the specification. An external reference to any other type of file must be treated as an error. To stress the point, even if a particular format (e.g., JPEG) is otherwise supported by the Loader, it must still reject any M3G files that reference JPEG images.

For M3G format files, the external reference must be able to load another M3G format file containing a single root-level object. If more than one root-level object is defined, then the first root-level object will be used, and the other objects and their descendants discarded.

For PNG format files, the external reference must be able to reference a valid PNG file, in which case the object created is a single instance of Image2D.

In all cases, once loading completes, the single root-level object loaded from the file effectively replaces the external reference object in the object index table. References to that index will then nominate the root-level object.

11 Per-Class Data

The data for each class in the API is now presented in alphabetical order. Where a class is a subclass, the superclass's data is always output first, and this information is taken to be part of the data for the class as a whole.

Classes without a serialized form (e.g. Graphics3D) are shown here for completeness, but are indicated as "not a serializable class".

Detailed information about each field is not given - it should be assumed that the data have the same meanings as those assigned in the API. Where data is serialized in a form which is different from the way it is specified in the API, this alternate form is documented here.

Any values which would be invalid as arguments to the corresponding methods in the API are also invalid in the file and must be reported as errors. For example, a negative value in the light attenuation fields is disallowed by the API and is therefore also disallowed in the file format.

11.1 AnimationController

ObjectType: 01
Superclass data: Object3D
Followed by:
Float32       speed;
Float32       weight;
Int32         activeIntervalStart;
Int32         activeIntervalEnd;
Float32       referenceSequenceTime;
Int32         referenceWorldTime;

11.2 AnimationTrack

ObjectType: 02
Superclass data: Object3D
Followed by:
ObjectIndex   keyframeSequence;
ObjectIndex   animationController;
UInt32        propertyID;
The propertyID field must hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.3 Appearance

ObjectType: 03
Superclass data: Object3D
Followed by:
Byte          layer;
ObjectIndex   compositingMode;
ObjectIndex   fog;
ObjectIndex   polygonMode;
ObjectIndex   material;
ObjectIndex[] textures;
These are simply references to each of the objects aggregated together to form an appearance.

There are as many texture objects in the textures array as there are active texture units for this appearance. The texture units are loaded sequentially from unit 0. If the implementation supports more texture units than are specified, these are left in their default, inactive state, with a null texture.

If more textures are specified than are supported by the implementation, then this must be treated as an error, as it would be in the API. The application can then decide on an appropriate course of action to handle this case.

11.4 Background

ObjectType: 04
Superclass data: Object3D
Followed by:
ColorRGBA     backgroundColor;
ObjectIndex   backgroundImage;
Byte          backgroundImageModeX;
Byte          backgroundImageModeY;
Int32         cropX;
Int32         cropY;
Int32         cropWidth;
Int32         cropHeight;
Boolean       depthClearEnabled;
Boolean       colorClearEnabled;
The backgroundImageModeX and backgroundImageModeY fields must each hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.5 Camera

ObjectType: 05
Superclass data: Node
Followed by:
Byte          projectionType;

IF projectionType==GENERIC, THEN

    Matrix        projectionMatrix;

ELSE

    Float32       fovy;
    Float32       AspectRatio;
    Float32       near;
    Float32       far;

END
The projectionType field must hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.6 CompositingMode

ObjectType: 06
Superclass data: Object3D
Followed by:
Boolean       depthTestEnabled;
Boolean       depthWriteEnabled;
Boolean       colorWriteEnabled;
Boolean       alphaWriteEnabled;
Byte          blending;
Byte          alphaThreshold;
Float32       depthOffsetFactor;
Float32       depthOffsetUnits;
The blending field must hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

The alphaThreshold field is stored as a byte to save space. It is mapped so that 0x00 is equivalent to 0.0 (completely transparent), and 0xFF is equivalent to 1.0 (completely opaque).

11.7 Fog

ObjectType: 07
Superclass data: Object3D
Followed by:
ColorRGB      color;
Byte          mode;

IF mode==EXPONENTIAL, THEN

    Float32       density;

ELSE IF mode==LINEAR, THEN

    Float32       near;
    Float32       far;

END
The mode field must hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.8 Graphics3D

Not a serializable class.

11.9 Group

ObjectType: 09
Superclass data: Node
Followed by:
ObjectIndex[] children;

11.10 Image2D

ObjectType: 10
Superclass data: Object3D
Followed by:
Byte          format;
Boolean       isMutable;
UInt32        width;
UInt32        height;

IF isMutable==false, THEN

    Byte[]        palette;
    Byte[]        pixels;
END
The format field must hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

For a palettised format, the pixels array contains a single palette index per pixel, and the palette array will contain up to 256 entries, each consisting of a pixel specifier appropriate to the format chosen.

For a non-palettised format, the palette array will be empty, and the pixels array contains a pixel specifier appropriate to the format chosen.

In a pixel specifier, each byte is scaled such that 0 represents the value 0.0 and 255 represents the value 1.0. The different formats require different data to be serialized, as follows:

The meaning of the components is given in the documentation for the Image2D class.

11.11 IndexBuffer

ObjectType: none (abstract base class)
Superclass data: Object3D
Followed by: no data (abstract class)

11.12 KeyframeSequence

ObjectType: 19
Superclass data: Object3D
Followed by:
Byte          interpolation;
Byte          repeatMode;
Byte          encoding;
UInt32        duration;
UInt32        validRangeFirst;
UInt32        validRangeLast;

UInt32        componentCount;
UInt32        keyframeCount;

IF encoding == 0

    FOR each key frame...

        UInt32                  time;
        Float32[componentCount] vectorValue;

    END

ELSE IF encoding == 1

    Float32[componentCount] vectorBias;
    Float32[componentCount] vectorScale;

    FOR each key frame...

        UInt32               time;
        Byte[componentCount] vectorValue;

    END

ELSE IF encoding == 2

    Float32[componentCount] vectorBias;
    Float32[componentCount] vectorScale;

    FOR each key frame...

        UInt32                 time;
        UInt16[componentCount] vectorValue;

    END

END
The interpolation and repeatMode fields must each hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

All of the vectorValue arrays are the same size, so a separate count is stored outside the individual keyframe's data rather than with each array.

The encoding field indicates the encoding scheme to be used for the keyframe data. Only the nominated values above are allowed. Other values must be treated as errors.

11.13 Light

ObjectType: 12
Superclass data: Node
Followed by:
Float32       attenuationConstant;
Float32       attenuationLinear;
Float32       attenuationQuadratic;
ColorRGB      color;
Byte          mode;
Float32       intensity;
Float32       spotAngle;
Float32       spotExponent;
The mode field must hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.14 Loader

Not a serializable class.

11.15 Material

ObjectType: 13
Superclass data: Object3D
Followed by:
ColorRGB      ambientColor;
ColorRGBA     diffuseColor;
ColorRGB      emissiveColor;
ColorRGB      specularColor;
Float32       shininess;
Boolean       vertexColorTrackingEnabled;

11.16 Mesh

ObjectType: 14
Superclass data: Node
Followed by:
ObjectIndex   vertexBuffer;

UInt32        submeshCount;

FOR each submesh...

    ObjectIndex   indexBuffer;
    ObjectIndex   appearance;

END

11.17 MorphingMesh

ObjectType: 15
Superclass data: Mesh
Followed by:
UInt32        morphTargetCount;

FOR each target buffer...

    ObjectIndex   morphTarget;
    Float32       initialWeight;

END

11.18 Node

ObjectType: none (abstract base class)
Superclass data: Transformable
Followed by:
Boolean       enableRendering;
Boolean       enablePicking;
Byte          alphaFactor;
UInt32        scope;

Boolean       hasAlignment;

IF hasAlignment==TRUE, THEN

      Byte          zTarget;
      Byte          yTarget;

      ObjectIndex   zReference;
      ObjectIndex   yReference;

END
The zTarget and yTarget fields must each hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

The alphaFactor field is stored as a byte to save space. It is mapped so that 0x00 is equivalent to 0.0 (fully transparent), and 255 is equivalent to 1.0 (fully opaque).

If the hasAlignment field is false, the omitted fields are initialized to their default values.

11.19 Object3D

ObjectType: none (abstract base class)
Superclass data: none
Followed by:
UInt32           userID;

ObjectIndex[]    animationTracks;

UInt32           userParameterCount;

FOR each user parameter...

    UInt32        parameterID;
    Byte[]        parameterValue;

END
The userID field may be any value.

The user parameter data contains enough data to create a java.util.Hashtable object. This contains key/value pairs, with the key being the parameterID, and the value being the parameterValue byte array. The meanings of the IDs, and the contents of the byte arrays, are defined by the application and may have any value.

The behaviour of the java.util.Hashtable class does not allow multiple objects with the same key. Therefore, duplicate parameterID values are not allowed and must be reported as an error.

If an object has no user parameters, the userParameterCount field must be 0. In this case, the user object in the resulting Object3D instance must be set to null, rather than indicating a Hashtable object with no content. The Hashtable containing the parameters, if it exists, can be retrieved through the API using the getUserObject method.

11.20 PolygonMode

ObjectType: 08
Superclass data: Object3D
Followed by:
Byte          culling;
Byte          shading;
Byte          winding;
Boolean       twoSidedLightingEnabled;
Boolean       localCameraLightingEnabled;
Boolean       perspectiveCorrectionEnabled;
The culling, shading and winding fields must each hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.21 RayIntersection

Not a serializable class.

11.22 SkinnedMesh

ObjectType: 16
Superclass data: Mesh
Followed by:
ObjectIndex   skeleton;

UInt32        transformReferenceCount;

FOR each bone reference...

    ObjectIndex   transformNode;
    UInt32        firstVertex;
    UInt32        vertexCount;
    Int32         weight;

END

11.23 Sprite

ObjectType: 18
Superclass data: Node
Followed by:
ObjectIndex   image;
ObjectIndex   appearance;

Boolean       isScaled;

Int32         cropX;
Int32         cropY;
Int32         cropWidth;
Int32         cropHeight;

11.24 Texture2D

ObjectType: 17
Superclass data: Transformable
Followed by:
ObjectIndex   image;

ColorRGB      blendColor;
Byte          blending;

Byte          wrappingS;
Byte          wrappingT;

Byte          levelFilter;
Byte          imageFilter;
The levelFilter, imageFilter, wrappingS, wrappingT, and blending fields must each hold a valid enumerated value, as specified in the class definition. Other values must be treated as errors.

11.25 Transform

Not a serializable class.

11.26 Transformable

ObjectType: none (abstract base class)
Superclass data: Object3D
Followed by:
Boolean       hasComponentTransform;

IF hasComponentTransform==TRUE, THEN

    Vector3D      translation;
    Vector3D      scale;
    Float32       orientationAngle;
    Vector3D      orientationAxis;

END

Boolean       hasGeneralTransform;

IF hasGeneralTransform==TRUE, THEN

    Matrix        transform;

END
If either hasComponentTransform or hasGeneralTransform is false, the omitted fields will be initialized to their default values (equivalent to an identity transform in both cases).

11.27 TriangleStripArray

ObjectType: 11
Superclass data: IndexBuffer
Followed by:
Byte       encoding;

IF encoding == 0, THEN

    UInt32        startIndex;

ELSE IF encoding == 1, THEN

    Byte          startIndex;

ELSE IF encoding == 2, THEN

    UInt16        startIndex;

ELSE IF encoding == 128, THEN

    UInt32[]      indices;

ELSE IF encoding == 129, THEN

    Byte[]        indices;

ELSE IF encoding == 130, THEN

    UInt16[]      indices;

END

UInt32[]      stripLengths;
Bit 7 of the encoding field is equivalent to the explicit property on the index buffer, and will be 1 if the index buffer was constructed with explicit indices, or 0 if constructed with implicit indices. The other bits indicate the width of each index field. 0 indicates that the "raw" integer values are written, 1 indicates that a single byte will suffice, and 2 indicates that a 16 bit integer is sufficient to hold all the given index values. Values for the encoding field other than those explicitly nominated above are not allowed and must be treated as errors.

11.28 VertexArray

ObjectType: 20
Superclass data: Object3D
Followed by:
Byte          componentSize;
Byte          componentCount;
Byte          encoding;

UInt16        vertexCount;

FOR each vertex...

    IF componentSize==1, THEN

        IF encoding==0, THEN

              Byte[componentCount] components;

        ELSE IF encoding==1, THEN

              Byte[componentCount] componentDeltas;

        END

    ELSE

        IF encoding==0, THEN

              Int16[componentCount] components;

        ELSE IF encoding==1, THEN

              Int16[componentCount] componentDeltas;

        END

    END

END
The componentSize and componentCount fields must each hold a valid value, as specified in the constructor definition. Other values must be treated as errors.

The encoding field indicates the encoding scheme to be used for the keyframe data. Only the nominated values above are allowed. Other values must be treated as errors.

11.29 VertexBuffer

ObjectType: 21
Superclass data: Object3D
Followed by:
ColorRGBA     defaultColor;

ObjectIndex   positions;
Float32[3]    positionBias;
Float32       positionScale;

ObjectIndex   normals;
ObjectIndex   colors;

UInt32        texcoordArrayCount;

FOR each texture coordinate array...

       ObjectIndex   texCoords;
       Float32[3]    texCoordBias;
       Float32       texCoordScale;

END
If a texture coordinate array has only two components, the corresponding texCoordBias[2] element must be 0.0.

Null texture coordinate arrays are never serialized, regardless of their position. A single texture coordinate array will therefore always be serialized as belonging to texturing unit 0, regardless of its original unit it was assigned to.

There are as many references in the texture coordinates array as there are active texture units for this geometry. The texture coordinate references are loaded sequentially from texture unit 0. If the implementation supports more texture units than are specified, these are left in their default, inactive state, with a null texture coordinate reference and an undefined bias and scale.

If more texture coordinate references are specified than are supported by the implementation, then this must be treated as an error, as it would be in the API. The application can then decide on an appropriate course of action to handle this case.

11.30 World

ObjectType: 22
Superclass data: Group
Followed by:
ObjectIndex   activeCamera;
ObjectIndex   background;

12 ObjectType Values

This list shows what object type a specific ObjectType value maps to.
 
ObjectType value Object Type
00 Header Object
01 AnimationController
02 AnimationTrack
03 Appearance
04 Background
05 Camera
06 CompositingMode
07 Fog
08 PolygonMode
09 Group
10 Image2D
11 TriangleStripArray
12 Light
13 Material
14 Mesh
15 MorphingMesh
16 SkinnedMesh
17 Texture2D
18 Sprite
19 KeyframeSequence
20 VertexArray
21 VertexBuffer
22 World
23 ... 254 Reserved for use in future versions of the file format
255 External Reference
Note that Object3D, Transformable, Node, and IndexBuffer are abstract classes and cannot be instantiated directly. They therefore do not appear in this list.