Timbermesh overview - mechanistry/timbermesh GitHub Wiki
Intro
Timbermesh is an open-source 3D file format that allows for storage of geometry (mesh) information usable in various 3D software. It was created with the primary goal of simplifying animation pipeline and improving performance in the Unity game engine. It allows to take advantage of VAT (Vertex Animation Texture) animations that can replace existing animation implementations, increasing rendering performance and simplifying model creation process. Due to its open nature (based on Google Protocol Buffers) and serialization structure, Timbermesh allows for an easy integration not only with Unity, but also other 3D engines, design software and various tools.
Its lightweight design, simplified creation pipeline and highly performant animations make it an ideal solution for game developers seeking to optimize their workflow and improve the performance of their games. This documentation provides the technical details of Timbermesh, including its format specification and animation capabilities.
Technical overview
Timbermesh files internally use the standardized Google Protocol Buffers mechanism to serialize data. This allows for easy implementation (e.g., as an export plugin or parser) regardless of programming language or operating system, due to its language and platform neutrality. Protocol Buffers also ensure a low memory footprint, fast deserialization time, large flexibility, and an easy-to-read format description.
Timbermesh files are compressed by a GZip (zlib) compatible algorithm using standard compression parameters and a default compression ratio. It is possible to use a different compression ratio, but doing so would deviate from the standard implementation and could cause compatibility issues. GZip compression is widely used and has many implementations for most modern programming languages and frameworks (usually as part of standard library packages, such as zlib in Python or DeflateStream in .NET).
Model overview
Timbermesh model is composed of nodes, where each node is equivalent to a single logical object or geometry in typical 3D editor software like Blender or Maya. Nodes are arranged in hierarchy, creating parent-child relationships (note that circular references are not allowed). Topmost nodes (without parents) are called root nodes. Nodes are placed in a cartesian coordinate system relative either to model space (if node is a root) or to their parents. Timbermesh model space assumes a left-handed, Y-up coordinate system.
Each node may contain:
-
Relation in hierarchy (required) - given as reference to node parent (if available).
-
Spatial information (required) - position, rotation and scale. As previously stated, they are relative either to the parent node or model space itself (in case of root nodes).
-
Geometry (optional) - given as a list of meshes. All meshes are composed of vertices, triangles and (optional) material. Mesh vertices are defined as a collection of properties (e.g. position, color or normal vector) - where every mesh vertex has the same set of properties.
-
Animations (optional) - each node can be independently animated. Animation can either change node position/rotation/scale in space or modify node geometry (by changing various vertex properties). For more information about animations see Animations below.
Animations
Timbermesh supports two types of animation: Vertex Animation and Node Animation. Both of them are "snapshot animations", meaning that they do not use keyframe interpolation to calculate object shape during animation - instead for every animation frame they store a snapshot of all data required to animate a node (or its mesh) at given frame time. Those snapshots may be then interpolated for smooth movement.
Vertex Animation
The Vertex Animation system animates node geometry by changing properties of mesh vertices. For every animation frame default implementation should store vertex offsets (relative to the mesh rest pose) along with their rotation, derived from normal and tangent vectors, but it can also change other vertex properties (e.g. colors). This also enables various mesh modification techniques such as UV animation, shape morphing or blending. It was created with VAT implementation in mind, where such animation data may be easily stored in texture and be used to animate geometry in shader. Because of that, it also allows to merge multiple meshes into a single one and animate it as a whole - reducing draw calls required to render a mesh and significantly boosting performance. It can also be used to replace complicated skeleton animations (which are usually CPU and GPU expensive), at the cost of blending between animation states - as it directly modifies vertex position without knowledge about bone constraints.
The downside of Vertex Animation is the large amount of data that needs to be stored. For each frame, each vertex has its own copy of properties. In case of VAT implementation this may result in large VRAM usage. Because of that VAT animations are good for animating small, mildly detailed meshes with reasonably simple animations.
Node Animation
The Node Animation system animates nodes by directly affecting their spatial data (either relative to parent or model). Every animation frame stores only the node position, rotation, and scale. During playback, these can be used to interpolate the node position. This animation system takes up much less VRAM space in comparison to vertex animation, but is much simpler and more limited in scope. However, in certain cases it can significantly simplify entire animation system while still meeting all movement requirements, such as when animating mechanical parts.
Comparison
Vertex Animation
Pros ๐๐ผ | Cons ๐๐ผ |
---|---|
Allows for less draw calls and simplifies rendering | Large memory footprint |
Supports most of animation techniques | Difficult to make smooth animation blending |
Can replace skeleton animation |
Node Animation
Pros ๐๐ผ | Cons ๐๐ผ |
---|---|
Very simple implementation | Allows only for a limited range of movements |
Small memory requirements | |
Provides good performance |
Protocol buffer format
This section contains a detailed description of each protocol buffer message used by Timbermesh file format. Raw definition compatible with Google proto3 format can be found here: https://github.com/mechanistry/timbermesh/blob/main/proto/model.proto.
More information about Google Protocol Buffers are available in official documentation and can be found at: https://protobuf.dev/programming-guides/proto3/.
All string literals within a file are case sensitive and should be encoded in UTF-8 standard.
- Model
- version - Model version number.
- name - Model name.
- nodes - Flat list of all nodes stored in a model. Order of nodes is important as each node refers to its parent by index in this list.
- Node
- parent - Index of parent node in list of all model nodes (must be -1 for root nodes).
- name - Name of node, does not need to be unique.
- position - Node position relative to its parent node (or in global model space if node is a root).
- rotation - Node rotation relative to its parent node (or in global model space if node is a root).
- scale - Node scale relative to its parent node (or in global model space if node is a root).
- vertexCount - Number of vertices creating this node geometry. If a node doesnโt have any mesh this value should always be zero.
- vertexProperties - List of properties assigned to this node mesh (if mesh exists). Number of property values in each set should always be equal to the vertexCount value.
- meshes - List of all meshes created by node vertices.
- vertexAnimations - List of all vertex animations.
- nodeAnimations - List of all node animations.
- Mesh
- indices - List of vertex indices that build up mesh geometry. Values should be grouped by 3 (each triplet for a single mesh triangle). All values should be less than the vertexCount value of the node.
- material - Name of material used by this mesh (can be an empty string if the mesh doesnโt have any material assigned).
- VertexAnimation
- name - Animation name, should be unique within all animations in a single node (but does not to be unique within the entire model).
- framerate - Animation frame rate (given as an amount of frames shown per second of playback).
- animatedVertexCount - Amount of vertices that are being animated. Should be equal or less than vertexCount of given node mesh.
- frames - List of animation frames (must be organized in order from first to last frame).
- VertexAnimationFrame
- vertexProperties - List of properties that change in the current frame. Number of property values in each set should always be equal to the node vertexCount value.
- NodeAnimation
- name - Animation name, should be unique within all animations in a single node (but does not to be unique within the entire model).
- framerate - Animation frame rate (given as an amount of frames shown per second of playback).
- frames - List of animation frames (must be organized in order from first to last frame).
- NodeAnimationFrame
- position - Node position relative to its parent node (or in global model space if node is a root) during this animation frame.
- rotation - Node rotation relative to its parent node (or in global model space if node is a root) during this animation frame.
- scale - Node scale relative to its parent node (or in global model space if node is a root) during this animation frame.
- VertexProperty
- name - Property name that reflects what type of vertex attribute this property refers to. There are several standard names reserved for default mesh representation (see Predefined vertex properties below), but users can also use their own (e.g. when adding custom properties).
- scalarType - Scalar type of single value component. By default it is SCALAR_TYPE_UNSPECIFIED and must be set to one of values:
- SCALAR_TYPE_UNSIGNED_BYTE (8 bit unsigned byte value)
- SCALAR_TYPE_UNSIGNED_INT (32 bit unsigned integer value)
- SCALAR_TYPE_INT (32 bit signed integer value)
- SCALAR_TYPE_FLOAT (32 bit signed float value)
- SCALAR_TYPE_DOUBLE (64 bit signed double value)
- scalarTypeDimension - Number of value components in a single vertex property (size of property value vector).
- data - Array of raw binary data containing property values stored in order from first to last vertex.
Predefined vertex properties
As stated before, users implementing Timbermesh format can select any name for vertex property. However several names are already predefined and form a standard mesh description (they should be used to represent a typical mesh). Users may still enforce using a different name (or different value format), but this will be considered a deviation from standard implementation (which might cause compatibility issues).
Hereโs a complete list of predefined properties:
Name | Scalar format | Description |
---|---|---|
position | Vector of 3 SCALAR_TYPE_FLOAT values | Position of vertex in node space |
normal | Vector of 3 SCALAR_TYPE_FLOAT values | Vertex normal vector in node space |
tangent | Vector of 4 SCALAR_TYPE_FLOAT values | Vertex tangent vector in node space |
color | Vector of 4 SCALAR_TYPE_FLOAT values | Vertex color |
uv0 | Vector of 2 SCALAR_TYPE_FLOAT values | Vertex first texture coordinates |
uv1 | Vector of 2 SCALAR_TYPE_FLOAT values | Vertex second texture coordinates |
uv2 | Vector of 2 SCALAR_TYPE_FLOAT values | Vertex third texture coordinates |
uv3 | Vector of 2 SCALAR_TYPE_FLOAT values | Vertex fourth texture coordinates |
offset | Vector of 3 SCALAR_TYPE_FLOAT values | Vertex offset relative to mesh rest pose |
rotation | Vector (quaternion) of 4 SCALAR_TYPE_FLOAT values | Vertex rotation relative to mesh rest pose |