Wings 3D/User Manual/Wings File Format
The Wings3D file format (version 2).
[edit | edit source]See http://wings3d.com/ for the Open Source modeller written in Erlang.
Important source files to look at: .../src/wings.hrl and .../src/wings_ff_wings.erl.
One more reference is the exporter implemented in Perl which is outdated.
Please note, that there is current development of Wings in progress changing also the file format, so it is advisable to stall work on this document, until the file format stabilized again.
Overall structure
[edit | edit source]A *.wings file has 3 main parts:
- 1. Header
- The header is 15 bytes long and essentially ASCII, formed by the human readeable string "#!WINGS-1.0" followed by the hex bytes 0x0D, 0x0A, 0x1A, 0x04 aka "\r\n^Z^D" aka CR, LF, SUB and EOT (Ctrl-Z being the "end of input" char in Windows and Ctrl-D the same in UNIX).
- 2. Length of data
- A 4 byte unsigned integer giving the size of the entire file, stored in big-endian format. This value should match the size of the file on disk, otherwise the file is missing data.
- 3. Data
- The main part of the file in Erlang's External Term Format as of section 8.1; specifically it is a Zlib-compressed term as indicated by the first two bytes 131, 80. The next 4 bytes thus are an unsigned integer giving the size of the uncompressed data. The rest of the file is the actual compressed data/term.
- When processing *.wings files with Erlang or a language having a full blown interface (in the Erlang distribution exists a C interface and a Java interface, there is also a Python interface elsewhere) you just feed the whole data part to the appropriate function/method.
- But when dealing with *.wings files in some other language, these 6 bytes might be seen as additional header information, the rest must be uncompressed and then further be handled to reconstruct the nested structure of the term.
To get an impression of how a simple Wings model looks in Erlang syntax (after uncompressing; manually reindented and commented/linked to emphasize structure and help):
{ wings, 2, % The Topmost Tuple { [ % Shapes { object, "tetrahedron1", { winged, [ [ { color_rt, <<0,0,0,0,0,0,0,0,63,128,0,0>> }, { color_lt, <<63,128,0,0,0,0,0,0,0,0,0,0>> }, { edge, 0,1,2,0,4,2,1,3 } ], [ { color_rt, <<0,0,0,0,63,128,0,0,0,0,0,0>> }, { color_lt, <<0,0,0,0,0,0,0,0,63,128,0,0>> }, { edge, 0,2,0,3,3,0,2,5 } ], [ { color_rt, <<63,128,0,0,0,0,0,0,0,0,0,0>> }, { color_lt, <<0,0,0,0,63,128,0,0,0,0,0,0>> }, { edge, 0,3,3,2,5,1,0,4 } ], [ { color_rt, <<0,0,0,0,0,0,0,0,63,128,0,0>> }, { edge, 1,2,1,0,5,4,0,1 } ], [ { color_lt, <<63,128,0,0,0,0,0,0,0,0,0,...>> }, { edge, 1,3,2,1,2,0,3,5 } ], [ { color_rt, <<0,0,0,0,63,128,0,0,0,0,...>> }, { edge, 2,3,1,3,4,3,1,2 } ] ], [ [], [], [], [] ], [ [ <<0,0,0,0,0,0,0,0,63,241,107,40,245,93,114,...>> ], [ <<0,0,0,0,0,0,0,0,191,225,107,40,245,93,...>> ], [ <<191,240,0,0,0,0,0,0,191,225,107,40,245,...>> ], [ <<63,240,0,0,0,0,0,0,191,225,107,40,...>> ] ], [] }, [ { plugin_states, [] }, { mode, vertex} ] } ], % Shapes end [ % Materials { default, [ { maps, [] }, { opengl, [ { ambient, { 1.0,1.0,1.0,1.0 } }, { diffuse, { 1.0,1.0,1.0,1.0 } }, { emission, { 0.0,0.0,0.0,0.0 } }, { shininess, 1.0 }, { specular, { 1.0,1.0,1.0,1.0 } } ] } ] } ], % Materials end [ % Props { scene_prefs, [] }, { plugin_states, [] } ] % Props end } }
You'll spot the two essential compound Erlang datatypes tuple (indicated by braces as in {something,someother,somemore}) and list (indicated by square brackets [elem1,elem2,...], some atoms (the all lower case strings without quotes in this sample, e.g. wings, edge, plugin_states), a string ("tetrahedron1"), some binary data (indicated by double angle brackets with byte values inbetween <<0,1,2,3>>) and some integer and float values. And you see, this is all heavily nested (e.g. a list of lists of tuples).
External Term Format
[edit | edit source]When you can't use an existing interface (or want to write a new one), you have to know the external format of Erlang terms, which is very well documented here. Note, that you then have to deal with a stream of bytes and must reconstruct the nested structure yourself somehow. For reference, the Erlang/OTP source code has a module called "jinterface" that gives a Java implementation of a set of classes that can read this format.
To process *.wings files you should be prepared to at least be able to deal with
- 8.4 SMALL_INTEGER_EXT
- 8.5 INTEGER_EXT
- 8.6 FLOAT_EXT
- 8.7 ATOM_EXT
- 8.11 SMALL_TUPLE_EXT
- 8.13 NIL_EXT
- 8.14 STRING_EXT
- 8.15 LIST_EXT
- 8.16 BINARY_EXT
The Topmost Tuple
[edit | edit source]The topmost data structure after expanding is a tagged tuple with 3 elements:
- The atom 'wings', identifying this as a wings file.
- The integer (2) indicating the file version.
- another tuple having 3 elements, namely Shapes, Materials and Props.
A compliant loader should check for the presence of the first 2 elements, and verify that the third element is a 3-tuple.
Shapes
[edit | edit source]The Shapes section consists of a list of objects, each of which is a quadruple (4-tuple) describing every separate object. The object quadruple has the following format:
- The tag atom 'object'.
- A string with the name of the object, such as "tetrahedron1"
- Another quadruple containing the object's actual data, tagged by the atom 'winged' (as in the winged-edge data structure). This tuple is described in the next section.
- A list containing information about plug-in states and selection mode for this object. Generally, this isn't used in an importer/exporter.
The Winged Edge Datastructure
[edit | edit source]Wings3D uses the winged edge data structure enabling it to be exceptionally fast on all operations involving neighborhood operations (e.g. edge looping). The *.wings file format stores these winged edge data structures directly, therefore the majority of the data is stored within the edges themselves rather than in tables (such as *.3ds or *.X file formats).
The tagged tuple 'winged' contains all of the data required to define the object's geometry. It takes the following format:
- The atom 'winged' (tag)
- A list containing a series of lists, each defining an edge.
- A list containing a series of lists, each defining a mapping between a face and a material.
- A list containing a series of lists, each defining a vertex.
- A list, usage unknown as of now -- nil in examples.
In the first list (edge data), a series of lists is found, with each list defining an edge. In each edge's list, the following tuples may appear. Note that a tuple may not appear more than once in each edge's list or else Wings will be unable load it, and the order in which they appear does not matter.
- Tagged 9-tuple 'edge': Defines the winged edge data structure and must be present. Has the format of:
- The atom 'edge' (tag)
- Start Vertex
- End Vertex
- Left Face
- Right Face
- Left Predecessor
- Left successor
- Right predecessor
- Right successor
- Tagged 2-tuple 'color_rt': Defines right vertex edge color. Has the format of:
- The atom 'color_rt' (tag)
- 12-byte binary blob representing the color value in RGB notation. Each component is stored as a 32-bit single precision floating point value.
- Tagged 2-tuple 'color_lt': Defines left vertex edge color. Has the format of:
- The atom 'color_lt' (tag)
- 12-byte binary blob representing the color value in RGB notation. Each component is stored as a 32-bit single precision floating point value.
- Tagged 2-tuple 'uv_rt': Defines right vertex UV coordinate. Has the format of:
- The atom 'uv_rt' (tag)
- 16-byte binary blob representing the UV coordinate, with 8-byte floating point values for each number.
- Tagged 2-tuple 'uv_lt': Defines left vertex UV coordinate. Has the format of:
- The atom 'uv_lt' (tag)
- 16-byte binary blob representing the UV coordinate, with 8-byte floating point values for each number.
As of Wings 1.1.15, both UV and vertex colors may be found in this list. Previously, only one or the other could be found.
In the second list (material data), there is an entry for every face defined by the previously read winged edge structures.
- If the face has no material (the default material), then the item in the list is nil.
- If the face has a material, the entry is a list containing a tagged 2-tuple in the format of:
- The atom 'material' (tag)
- The atom of the material name, such as 'tetrahedron1_auv'. This is a reference to the materials section.
Materials
[edit | edit source]Sample material structure
[ [ "Cylinder1_auv", [ [ "maps", [ [ "diffuse", 3 ] ] ], [ "opengl", [ [ "ambient", [ 1,1,1,1 ] ], [ "diffuse", [ 1,1,1,1 ] ], [ "emission", [ 0,0,0,0 ] ], [ "shininess", 1 ], [ "specular", [ 1,1,1,1 ] ], [ "vertex_colors", "ignore" ] ] ] ] ], [ "default", [ [ "maps" ], [ "opengl", [ [ "ambient", [ 0.789853807692308,0.813333333333333,0.694044444444444,1 ] ], [ "diffuse", [ 0.789853807692308,0.813333333333333,0.694044444444444,1 ] ], [ "emission", [ 0,0,0,1 ] ], [ "shininess", 0 ], [ "specular", [ 0,0,0,1 ] ], [ "vertex_colors", "set" ] ] ] ] ] ]
Props
[edit | edit source]Sample props data:
[ [ "scene_prefs" ], [ "plugin_states", [ [ "wings_shape", [ "no_folder", [ [ "no_folder", [ "open", [ 1, [ 1,"nil","nil" ] ] ] ] ] ] ] ] ], [ "current_view", 0 ], [ "views", [ [ "view", [ [ "name", "current_view" ], [ "aim", [ 0,0,0 ] ], [ "distance_to_aim", 3.14266478310898 ], [ "azimuth", -284.25 ], [ "elevation", 78.5 ], [ "tracking", [ 0,0 ] ], [ "fov", 45 ], [ "hither", 0.1 ], [ "yon", 10000 ] ] ] ] ], [ "images", [ [ 3, [ [ "name", "auvBG" ], [ "width", 256 ], [ "height", 256 ], [ "samples_per_pixel", 3 ], [ "mask_size", 0 ], [ "pixels", /* BinaryNode of 196608 bytes. */ ] ] ] ] ] ]