LightWave 3D objects are stored on disk as 3D meshes consistsing of points, polygons (which can be faces, curves or patches), and surfaces. Files can contain a single connected mesh or several disjoint meshes describing a single logical object. An object file may also contain one or more surface definitions with no mesh.
This document describes the high-level format of the file, and describes in detail all the elements of the file. There is a sample file at the end displayed as an annotated dump.
LightWave 3D object files are binary files composed of a series of bytes in the range of 0 to 255. The format conforms to the IFF ("Interchange File Format") specification with a FORM-type of LWOB. The full IFF format specification can be found in "EA IFF 85 Standard for Interchange Format Files," but a simplified description is included here.
The atomic (or lowest-level) types in the file are listed below along with their type name (I2, F4, etc.). These are used interpret sequences of bytes in the file.
The basic structural element in an IFF file is the Chunk. This is given by the structure:
Each chunk starts with an ID tag which indicates the type of chunk, and an unsigned four-byte integer which indicates the length of the data. After that follows length bytes of data and an optional pad byte if length is odd. The pad byte, if included, should have a value of zero.
LightWave objects also have the Sub-chunk (or mini-chunk) structural element which is just like a chunk except that the length is given by a two-byte unsigned integer. Sub-chunks are specific to LightWave objects, not general IFF files.
For the purposes of this document, chunks and sub-chunks are written as "id-tag { data }". The entire file can be an example of this notation, since it is itself a FORM chunk containing the ID tag LWOB and a series of data chunks.
Very roughly, object files start with the four bytes "FORM" followed by a four-byte integer giving the length of the file (not including the first 8 bytes) and the four bytes of the FORM-type, "LWOB". The remainder of the data is a series of chunks, each given by a four-character type and a four-byte length for the data which follows. The contents of each chunk is determined by its type.
The chunks can occur in any order except when the data in a chunk depends on knowing the values in previous chunks, in which case the dependant chunk must occur after the chunk it depends upon. The Layout and Modeler portions of LightWave 3D write their chunks in slightly different orders, so it is important for parsers to support order-independence.
The LightWave format has some composite datatypes that is uses consistently which are built from the fundamental types.
The following section describes all the chunks that can be found in a LightWave object file. Their order here is not important, except that chunks which define values in other chunks are listed first.
This chunk contains a list of the X, Y, and Z coordinates of all the points in an object. Since each coordinate has three components, and each component is stored as a four byte floating point number, the number of points in an object can be determined by dividing the size in bytes of the PNTS chunk by 12.
By convention, the +X direction is to the right or east, the +Y direction is upward, and the +Z direction is forward or north. For models of real-world objects, the unit size is usually considered to be one meter. The coordinates are specified relative to an object's pivot point. See the LightWave Modeler manual for more information about LightWave 3D's geometric conventions.
Points in the PNTS chunk are numbered in the order they occur, starting with zero. This index is then used by polygons to define their vertices. The PNTS chunk must be before the POLS, CRVS, and PCHS chunks in the file.
This chunk contains a list of the names of all the surfaces in an object. Each surface name appears as a null-terminated character string. If the length of the string (including the null) is odd, an extra null byte is added. Surface names should be read from the file until as many bytes as the chunk size specifies have been read.
In LightWave 3D terminology, a "surface" is defined as a named set of shading attributes. Each polygon contains a reference to the surface used to color the polygon. The names as listed in the SRFS chunk are numbered in the order they occur, starting from 1, and this index is used by polygons to define their surface. The SRFS chunk must be before the POLS, CRVS, and PCHS chunks in the file.
This chunk contains a list of all the polygons in an object. Each entry consists of a short integer specifying the number of vertices in the polygon followed by that many short integers specifying the vertices themselves (as indices into the points list) followed by a short integer specifying which surface is used by the polygon (as an index into the surfaces list). The number of vertices in a polygon currently may vary from one to 200. The vertex list for each polygon should begin at a convex vertex and proceed clockwise as seen from the visible side of the polygon (LightWave 3D polygons are single-sided, except for those whose surfaces have the double- sided flag set). Polygons should be read from the file until as many bytes as the chunk size specifies have been read.
Since the points in the PNTS chunk are referenced using two-byte integers, the effective maximum number of points in a LightWave object file is 65,536. This is an inherient limitation of this current format.
A negative surface number for a polygon indicates that the polygon has detail polygons (which are drawn on top of the main polygon and may be coplanar with it). In this case, the next number in the file is a short integer specifying how many detail polygons belong to the current polygon. This is followed by a list of those detail polygons, where each entry is of the same format as described above for regular polygons (except that the detail polygons cannot have details of their own). The list of regular polygons then resumes. To determine which surface is used by a polygon with a negative surface number, the absolute value of that number should be used. Note, however, that detail polygons are mostly obsolete so even though they may be recognized by LightWave and old files contain them, they should be ignored.
This chunk contains a list of all the spline curves in an object. Each entry consists of a short integer specifying the number of vertices in a curve followed by that many short integers specifying the vertices themselves in sequential order, followed by a short integer specifying which surface is used by the curve, followed by another short integer specifying bit-flags associated with the curve. If bit zero is set then the first point is a continuity control point, and if bit one is set then the last point is. The point and surface indices are as they are in the POLS chunk, except that curves cannot have details. Curves should be read from the file until as many bytes as the chunk size specifies have been read.
This chunk contains a list of all the MetaNURBS(tm) patches in an object. The entries are the same as the POLS chunk, except that patches cannot have details. Patches should be read from the file until as many bytes as the chunk size specifies have been read.
MetaNURBS patches are currently limited to four vertices. Other numbers of vertices will load and save but will not display. MetaNURBS patches are also limited to Modeler only and do not display in Layout or render.
Each SURF chunk describes the surface attributes of a particular surface. These chunks begin with the name of the surface being described. Following the name is a series of sub-chunks, which are like normal IFF chunks except that their sizes are specified by short integers instead of longs. It is likely that the variety of sub-chunks will grow as new surface attributes are added to the program, but any unknown sub-chunks may be skipped over by using the size. Sub-chunks should be read from the file until as many bytes as the chunk size specifies have been read.
The SURF surface sub-chunks create a simple but varied language for describing surface parameters of LightWave objects. These chunks mostly correspond to the settings in the program's interface, so a complete understanding of the program is very useful for interpreting these chunks.
The following surface sub-chunks define the base characteristics of a surface. These are values that are independent of texturing and correspond roughly to the main Surface control panel in Layout. Even if the effects of textures and shaders overrides these settings completely in final rendering, as many of these should still be specified as possible since they are also used for previewing and real-time rendering.
This defines the base color of the surface, which is the color that lies under all the other texturing attributes.
Surface flags are stored in a short integer whose bits specify various options for the current surface. Currently only the nine least significant bits are used. The options that set bits indicate are (starting with the least significant bit): Luminous, Outline, Smoothing, Color Highlights, Color Filter, Opaque Edge, Transparent Edge, Sharp Terminator, Double Sided, Additive, and Shadow Alpha.
The two edge transparency bits should not both be set. The luminous bit has been superceded by the LUMI and VLUM chunks which specify an explicit luminosity percentage. If no explicit luminosity is specified, the Luminous bit sets the luminosity to 100%.
These sub-chunks specify the base level of the surface's luminosity, diffuse, specular, reflection, or transparency settings. Each setting has a fixed-point and a floating-point form, but if both are present the floating-point form should take precedence. The fixed-point value should be rounded to the nearest half percent. Even though the floating-point form is prefered, the convention is to write both sub-chunks to a surface description to support older parsers. If any of these sub-chunks are absent for a surface, a value of zero is assumed. The LUMI or VLUM sub-chunk overrides the Luminous bit of the FLAG sub-chunk.
REFL and SPEC sub-chunks may have an incorrect length of 4 instead of 2. Even though that bug has been fixed, there may still be object files around with this error.
Glossiness is stored as a short integer and is only needed if the specular setting in the SPEC or VSPC sub-chunk is non-zero. A value of 16 indicates low glossiness, 64 indicates medium glossiness, 256 indicates high glossiness, and 1024 indicates maximum glossiness. This parameter is related to the "specular exponent" used in many lighting models.
GLOS sub-chunks may have have an incorrect length of 4 instead of 2. Even though that bug has been fixed, there may still be object files around with this error.
The reflection mode is a numeric code that describes how reflections are handled for this surface and is only meaningful if the reflectivity of the surface is non-zero. If the mode is 0, then only the backdrop colors in the scene are reflected. If the mode is 1, it is the same as 0 except that raytracing is used for objects in the scene when it is enabled. If the mode is 2 and an image is provided by the RIMG sub-chunk, then the image wrapped spherically around the scene is reflected. If the mode is 3, it is the same as 2 except that raytracing is used when enabled. If there is no RFLT sub-chunk, a value of 3 is assumed.
The reflection image is wrapped around the scene and is used for reflection mapping if the RFLT mode is set to use an image and the reflectivity of the surface is non-zero. If the RFLT mode setting expects an image and there is no RIMG chunk, then the backdrop colors are reflected. See the section on Image Options for more on specifying image names.
This angle is the heading angle of the reflection map seam. If missing, a value of zero is assumed.
The surface's refractive index is defined as the ratio of the speed of light in a vacuum to the speed of light in the material. Since light is fastest in a vacuum, this value should therefore be greater than or equal to 1.0.
The edge transparency threshold of the current surface should lie between 0.0 and 1.0.
This specifies the maximum angle between two adjacent polygons that can be smooth shaded. Polygons with a greater angle between them will appear to meet at a sharp seam.
Surface textures in LightWave correspond closely to the Textures panel accessed though the Surfaces panel. They are algorithmic or image-based modulations of one of the shading parameters. Any number of textures may be used and their effect is cumulative.
The presence of one of these sub-chunks indicates that the current surface has a color, diffuse, specular, reflection, transparency, luminosity, or bump texture. The contents of the sub-chunk is a character string specifying the texture type as shown on the control panel. Once one of these sub-chunks is encountered within a SURF chunk, all subsequent texture-related sub-chunks are considered to pertain to the current texture, until another one of these texture strating sub-chunks is read. There may be any number of textures for each parameter, and the textures are layered in the order they are read.
This short integer has bits which specify various options for the current texture. Currently only the seven least significant bits are used. The options that set bits indicate are (starting with the least significant bit): X Axis, Y Axis, Z Axis, World Coords, Negative Image, Pixel Blending, and Antialiasing. Note that only one of the three axis bits should be set.
These sub-chunks each consist of a vector for the current texture's size, center, falloff, or velocity. If missing, the center, falloff and velocity are assumed to be zero. The size should always be specified for any texture.
This specifies the modifying color used by a CTEX texture.
This specifies the modifying value of a DTEX, STEX, RTEX, TTEX, or LTEX texture as a fixed-point percentage.
This specifies the amplitude of the current BTEX bump texture as a floating-point percentage.
The TFPn sub-chunks each contain a floating point number that specifies one of the special texture type-specific parameters (such as Contrast, Turbulence, Wavelength, etc). The TIPn sub-chunks are similar but are used for integer parameters (such as number of Frequencies, Wave Sources, etc). Which sub-chunk is used to record a particular parameter depends on the order in which that parameter's button appears on the control panel. The total number of possible sub-chunks may increase in the future, but the totals for 5.0 are 4 floating-point and 1 integer.
The TSPn sub-chunks are an obsolete form of the TFPn sub-chunks. The TFRQ sub-chunk is an obsolete form of TIP0 from a time when the number of fractal noise frequencies or number of wave sources was the only integer algorithmic parameter. LightWave will still parse these older sub-chunks but it no longer writes them.
This specifies an image name to be used for image texture mapping. See the section on Image Options for more on specifying image names.
This specifies an image name to be used as the alpha map for this texture. See the section on Image Options for more on specifying image names.
This specifies how image-based textures will interpret the color of areas outside the image. If the mode is 0, then outside the image is considered to be black. If the mode is 1, areas outside the image are clamped to the closest image edge value. If the mode is 2, then the image repeats outside the image area resulting in a uniform tiling. If the mode is 3, then the image repeats but with mirroring, so that each each adjacent repetition is reversed. If no wrap options are specified, 2 is assumed.
This specifies the strength of antialiasing used for the texture. If the Antialiasing bit is set in the TFLG sub-chunk and this sub-chunk is missing, 100% is assumed. This setting currently only affects image-based textures.
This specifies how opaque this texture is with respect to the textures before it. If no opacity is specified, 100% is assumed.
Plug-in shaders are applied to a surface after all built-in textures. They are evaluated in the order specified in the surface description and there can currently be up to four.
Plug-in shaders are identified by their server name, which is stored in this sub-chunk.
A shader can store up to 65,535 bytes of data to describe its settings, and these are stored directly in this sub-chunk without modification. The length and contents of this chunk will depend completely on the shader specified by the previous SHDR chunk.
Images are specified, either by the RIMG, TIMG, or TALP sub-chunks, as a filename string. If this is a simple filename, then it represents a still image. However, if the last part of the string is " (sequence)", then the first part of the string specifies the prefix of an image sequence and the actual filename is generated by appending the three digit frame number to the prefix when loading the image file for each frame. If the last part of the string is " (clip)", then the string is the name of a Flyer clip.
The following sub-chunks may be used to set options for images. Each of these sub-chunks refers to image immediately preceeding it in the SURF data.
This only applies if the image is a sequence. The offset is added to the frame number to get the image number for that frame. The loop-length is the number of frames before the sequence starts at the beginning again. The least significant flag bit indicates that the sequence does loop, and the next bit indicates that the images are interlaced and should be treated as fields.
This only applies if the image is a Flyer clip. The two values are the clip endpoints stored in Flyer timecode format.
Only color-mapped images can use color cycling. The cycle-speed can be +1, -1 or 0, but 0 means the sub-chunk can be ignored. The low-index and high-index give the color table range to cycle through while rendering.
A simple object (with somewhat complex surfaces) is listed below to illustrate some of the features of a FORM LWOB. The object is an image-mapped square polygon and a transparent, reflective, bumpy yellow triangle. They are made from 5 points in the XY plane. The annotations are laid out like a disassembly, with left collumn showing the binary file data listed as two hexadecimal digits per byte, the center collumn showing a symbolic breakdown of the file, and the far right showing commentary on each line.
464f524d 000001fe FORM { IFF file header; FORM chunk file length is 510 + 8 bytes 4c574f42 LWOB FORM type ID for LW OBjects
504e5453 0000003c PNTS { 60 bytes / 12 = 5 points 00000000 3f800000 00000000 0.0, 1.0, 0.0 point 0 40200000 3f800000 00000000 2.5, 1.0, 0.0 point 1 40200000 bf800000 00000000 2.5, -1.0, 0.0 point 2 00000000 bf800000 00000000 0.0, -1.0, 0.0 point 3 c0000000 00000000 00000000 -2.0, 0.0, 0.0 point 4 }
53524653 00000012 SRFS { 54 72 69 61 6e 67 6c 65 00 00 "Triangle" surface 1 53 71 75 61 72 65 00 00 "Square" surface 2 }
504f4c53 00000016 POLS { 0003 3 polygon 1 has 3 vertices 0003 0004 0000 3, 4, 0 polygon 1 vertex list 0001 1 polygon 1 uses "Triangle" surface 0004 4 polygon 2 has 4 vertices 0000 0001 0002 0003 0, 1, 2, 3 polygon 2 vertex list 0002 2 polygon 2 uses "Square" surface }
53555246 000000c8 SURF { start defintion of "Triangle" 54 72 69 61 6e 67 6c 65 00 00 "Triangle" surface settings 434f4c52 0004 COLR { base color is yellow f0 b4 00 00 240, 180, 0 } 464c4147 0002 FLAG { surface is double-sided 0100 [00100000000] } 44494646 0002 DIFF { fixed 60% diffuse 009a 154 } 56444946 0004 VDIF { float 60% diffuse 3f19999a 0.6 } 53504543 0002 SPEC { fixed 80% specular 00cd 205 } 56535043 0004 VSPC { float 80% specular 3f4ccccd 0.8 } 474c4f53 0002 GLOS { "High" glossiness 0100 256 } 5245464c 0002 REFL { fixed 20% reflective 0033 51 } 5652464c 0004 VRFL { float 20% reflective 3e4ccccd 0.2 } 52464c54 0002 RFLT { reflect backdrop colors and 0001 1 raytraced objects } 5452414e 0002 TRAN { fixed 40% transparent 0066 102 } 5654524e 0004 VTRN { float 40% transparent 3ecccccd 0.4 } 52494e44 0004 RIND { refractive index 3f800000 1.2 1.2 } 42544558 000e BTEX { start bump map 46 72 61 63 74 61 6c 20 using "Fractal Bumps" algorithm 42 75 6d 70 73 00 "Fractal Bumps" } 54464c47 0002 TFLG { Y-axis; world-coords; 006a [1101010] pixel blending; antialiasing } 5453495a 000c TSIZ { texture size 3dcccccd 3dcccccd 3dcccccd 0.1, 0.1, 0.1 } 54414153 0004 TAAS { texture antializing strength 3f800000 1.0 100% } 54414d50 0004 TAMP { bump amplitude 3f000000 1.5 150% } 54495030 0002 TIP0 { first integer parameter 0003 3 3 fractal noise frequences } } end of SURF chunk
53555246 000000a6 SURF { start definition of "Square" 53 71 75 61 72 65 00 00 "Square" surface settings 434f4c52 0004 COLR { base color is white c8 c8 c8 00 200, 200, 200 } 464c4147 0002 FLAG { all surface flags clear 0000 [000000000] } 44494646 0002 DIFF { fixed 100% diffuse 0100 256 } 56444946 0004 VDIF { float 100% diffuse 3f800000 1.0 } 43544558 0012 CTEX { start color map using 50 6c 61 6e 61 72 20 49 6d "Planar Image Map" algorithm 61 67 65 20 4d 61 70 00 00 "Planar Image Map" } 54494d47 0012 TIMG { static image used for mapping 49 6d 61 67 65 73 5c 6d 69 72 61 67 65 2e 69 66 66 00 "Images\mirage.iff" } 54575250 0004 TWRP { image will tile in both directions 0002 0002 2, 2 } 54464c47 0002 TFLG { Z-axis; pixel blending; 0064 [1100100] antialiasing } 5453495a 000c TSIZ { image size (only X and Y matter) 40200000 40000000 3f800000 2.5, 2.0, 1.0 } 54435452 000c TCTR { image center (only X and Y matter) 3fa00000 00000000 00000000 1.25, 0.0, 0.0 } 54414153 0004 TAAS { texture antialiasing strength 3f800000 1.0 100% } 54434c52 0004 TCLR { texture color is black (unused) 00 00 00 00 0, 0, 0 } } end of SURF chunk } end of FORM chunk and file