Mesh/Mesh Asset Format
Overview
This document is a specification of the mesh asset format used by Second Life. At its highest level, a mesh asset is a collection of data blocks spread across a file. The sizes, locations, and types of the data elements are described in the header at the beginning of the file. This document will describe the format of the header and each of the data block types it may reference.
Header
The header is an uncompressed, binary encoded LLSD map containing the following elements:
Versioning and Creator Information
- "version" -- integer -- A version number for the entire asset, filled in by simulator post-import. Three least significant digits are reserved for the minor version, with major version changes indicating a format change that is not backwards compatible and should not be parsed by viewers that don't specifically support that version. For example, if the integer "1" is present, the version is 0.001. A viewer that can parse version 0.001 can also parse versions up to 0.999, but not 1.0 (integer 1000).
- "creator" -- UUID -- agent id of the agent that uploaded the asset, filled in by simulator
- "date" -- LLDate (as LLSD, seconds since epoch UTC) of upload, filled in by simulator
Level of Detail
Up to four level of detail blocks may be specified. Level of detail block names are:
- "high_lod" -- Highest level of detail mesh for this asset, MUST be provided
- "medium_lod" -- Optional, medium level of detail
- "low_lod" -- Optional, low level of detail, must NOT be present if "medium_lod" is absent
- "lowest_lod" -- Optional, lowest level of detail, must NOT be present if "low_lod" is absent
Each level of detail entry must contain the following elements:
- "offset" -- integer -- the offset (in bytes) of the level of detail block in the asset starting from the end of the header
- "size" -- integer -- the size (in bytes) of the level of detail block
Physics
Each physics entry must contain a "hash" for validation purposes. The hash is filled in by the simulator at the time of upload and used for every subsequent load to verify that the asset is valid. Each of these entries must also contain an "offset" and "size" as described by the "Level of Detail" section above. There are four physics data blocks:
- "physics_mesh" -- Optional, a mesh representing the physical shape of this asset
- "physics_convex" -- Required, a single convex hull encompassing the entire mesh asset plus an (optional) set of convex hulls (known as a convex decomposition) for more detailed collisions. If the "physics_convex" data block contains such a decomposition, "physics_mesh" MUST be absent, as it will never be used.
- "physics_havok" -- Filled in by simulator at upload time, contains some cached data specific to Havok to improve load time performance on the simulator.
- "physics_cost_data" -- Data needed to determine the physics cost of this asset for any given scale -- contains a single LLMatrix3 ("mesh") in LLSD format defining mesh physics cost constants if a mesh is used.
Skin
A "skin" block may be present and must contain an "offset" and "size" as described in the Level of Detail section above. See below for details.
Mesh Level of Detail Block
Each level of detail block is a gzip'd binary encoded LLSD list of submeshes. Each submesh will map to a "face" in Second Life, and each level of detail MUST have the same number of submeshes. Each level of detail must have a lower triangle count than any higher level of detail. Each submesh contains the following fields:
- "NoGeometry" -- optional, boolean, must be true -- If present, there is no geometry for this submesh. This submesh is just a placeholder to let the simulator/viewer know that there is a texture entry for this submesh with no geometry at this LoD. No additional fields may be specified if "NoGeometry" is present.
- "Position" -- required, LLSD byte array containing 16-bit unsigned position values (6 bytes per position).
- "PositionDomain" "Min"/"Max" -- optional, LLSD vector3 pair specifying domain of position values (domain is presumed to be [-0.5, 0.5] if "PositionDomain" is not specified). Domain must be within [-0.501, 0.501].
- "Normal" -- optional, LLSD byte array containing 16-bit unsigned normal values ([-1.0, 1.0] domain, not necessarily unit normals). Must be the same length as "Position" array.
- "TexCoord0" -- optional, LLSD byte array containing 16-bit unsigned texture coordinate values (2 dimensional, 4 bytes per texture coordinate). Must have the same number of texture coordinates as there are position values.
- "TexCoord0Domain" "Min"/"Max" -- required if "TexCoord0" is present, vector2 pair denoting domain to unpack 16-bit texture coordinate values into.
- "TriangleList" -- required, LLSD byte array containing 16-bit unsigned vertex indices. Must contain at least one reference to every vertex position and must NOT contain indices larger than the number of vertex positions. No single triangle in the list may reference the same vertex twice (no degenerate triangles).
- "Weights" - required if "skin" block is present in header. LLSD byte array containing joint influences.
Vertex Weight Encoding
Each weight entry corresponds to a position in the Position array. Each entry may contain up to 4 influences. Each influence is defined as a 8-bit unsigned joint index followed by a 16-bit unsigned weight value (least significant byte first). A joint index of “0xFF” indicates there are no more influences for this entry. Other than 0xFF, no joint index may have a value >31. If 4 influences have been read for the current entry, it is assumed there are no more influences for this entry.
For example, an entry denoting weights to joints 2 and 3 would contain the values:
0x02 0xWWWW 0x03 0xWWWW 0xFF
Where 0xWWWW represents some arbitrary weight value. An entry denoting weights to joints 1, 2, 3, and 5 would contain the values:
0x01 0xWWWW 0x02 0xWWWW 0x03 0xWWWW 0x05 0xWWWW
No terminating joint index is needed when 4 values are specified. The number of joint influence entries must match the number of positions from the "Position" array above.
Skin Block
The skin block may optionally alter all or part of the default Second Life skeleton. A partial joint list will contain a subset of these joints: mPelvis, mTorso, mChest, mNeck, mHead, mCollarLeft, mShoulderLeft, mElbowLeft, mWristLeft, mCollarRight, mShoulderRight, mElbowRight, mWristRight, mHipRight, mKneeRight, mFootRight, mHipLeft, mKneeLeft, mFootLeft where a full avatar replacement will contain and reference all of these joints.
The skin block is a gzip'd binary encoded LLSD map with the following entries:
- "joint_names" -- joint name list for this skin. Will contain joint names from the above list.
- "bind_shape_matrix" -- matrix that transforms from model space to world space. One per skin.
- "inverse_bind_matrix" -- matrix that transforms from skin local space(world space) to joint local space. One per joint having influences.
- "alt_inverse_bind_matrix" -- optional, if joint offsets are used then the alternate bind matrix will contain translational information that will override the default Second Life skeleton. Rotational and scaling components are at this moment, unused.
- "pelvis_offset" -- optional, used to provide a pelvis fixup for avatar rigs that alter the default Second Life skeleton.
Physics Mesh Block
A physics mesh block is the same as a Level of Detail block above, but with the additional constraint that it must not contain any degenerate triangles at 0.5x0.5x0.5 scale as defined by the function ll_is_degenerate in llfloatermodelpreview.cpp.
Physics Convex Block
This block define a single convex hull in "BoundingVerts" and a list of convex hulls in "HullList" and "Positions". Setting the physics shape type of a mesh object to "Prim" will use (in order of preference) the "HullList", the "physics_mesh" level of detail, or the "BoundingVerts" as the physical shape of the object.
- "HullList" -- optional, LLSD byte array, list of U8's indicating the number of points in each hull. Here, '0' means 256 points.
- "Positions" -- required if "HullList" is present, LLSD byte array, list of U16's of hull points -- decompress to domain specified by "Min"/"Max". Must contain as many points as the sum of points denoted by "HullList"
- "BoundingVerts" -- required, LLSD byte array, list of U16's of points in single convex hull approximation of entire shape -- decompress to domain specified by "Min"/"Max"
- "Min"/"Max" -- optional, LLSD vector3 pair, domain to unpack position values into. If unspecified, domain is presumed to be [-0.5, 0.5].
"HullList" and "Positions" must NOT be present if header contains a "physics_mesh" entry.
Physics Havok Block
Filled in and used only by simulator.