How the Meshoptimizer Index Codec Rotates Triangles for Better Compression

The meshoptimizer index codec rotates triangles so that the vertex most likely to exist in the FIFO cache appears first, reducing the number of bits required to encode each triangle.

The meshoptimizer library provides high-performance algorithms for mesh optimization, including a sophisticated index buffer codec that achieves significant compression ratios by exploiting spatial and temporal coherence. At the heart of this codec lies a triangle rotation strategy that reorders vertex indices to maximize cache hits before encoding. This article examines the rotation mechanism implemented in src/indexcodec.cpp and explains how it minimizes encoded output size.

Triangle Rotation Logic in the Encoder

The codec processes triangles sequentially and attempts to predict which vertices have already been seen recently. When a triangle cannot be matched to an existing edge in the FIFO cache, the encoder applies a rotation heuristic to determine the optimal vertex ordering.

The rotateTriangle Helper Function

In src/indexcodec.cpp (lines 27-32), the static function rotateTriangle determines how many positions to rotate the triangle based on which vertex matches the expected next value:

static int rotateTriangle(unsigned int a, unsigned int b, unsigned int c, unsigned int next)
{
    (void)a;
    return (b == next) ? 1 : (c == next ? 2 : 0);
}

This function compares the second vertex (b) and third vertex (c) against the next parameter, which represents the vertex the codec expects to process next. If b equals next, the function returns 1 (rotate once). If c equals next, it returns 2 (rotate twice). Otherwise, it returns 0 (no rotation). The first vertex a is explicitly unused in this calculation because the rotation logic prioritizes making either b or c the first vertex in the encoded output.

The Rotation Table Mapping

The codec uses a static lookup table defined at line 97 to map rotation indices to vertex orderings:

static const int rotations[] = {0, 1, 2, 0, 1};

This array allows the encoder to fetch the appropriate permutation indices without complex branching. When rotateTriangle returns a value, the encoder uses it as an offset into this table to determine which physical vertex indices correspond to the logical a, b, and c positions in the compression algorithm.

Integration in the Encoding Loop

When the encoder encounters a triangle that does not match the edge FIFO (indicated by fer < 0), it falls back to the rotation path (lines 51-56 in src/indexcodec.cpp):

int rotation = rotateTriangle(indices[i + 0], indices[i + 1], indices[i + 2], next);
const int* order = rotations + rotation;

unsigned int a = indices[i + order[0]],
             b = indices[i + order[1]],
             c = indices[i + order[2]];

The encoder retrieves the rotation index, applies the mapping from the rotations array, and reorders the vertices accordingly. This ensures that the vertex most likely to be in the 16-entry vertex FIFO appears in the first position (a), while the subsequent vertices form an edge that may already exist in the edge FIFO.

Why Rotation Improves Compression Efficiency

The triangle rotation strategy directly impacts three compression mechanisms in the codec. By maximizing the probability of cache hits, the encoder can omit or reduce the size of several data fields in the output stream.

Vertex-FIFO Hit Optimization

The codec maintains a 16-entry vertex FIFO cache. When the first vertex of a triangle (a) matches the expected next value, the encoder can emit the triangle with fea = 0, indicating that no FIFO index needs to be encoded for the first vertex. This saves a 4-bit field in the triangle's code byte. Rotation ensures that the vertex continuing the current strip appears first, making this optimization applicable to the majority of triangles in well-optimized meshes.

Edge-FIFO Reference Efficiency

After rotation, the edge formed by the first two vertices (a-b) is more likely to already exist in the edge FIFO. When this edge is found in the cache, the encoder can reference it using a small 4-bit index (fe) rather than encoding two full vertex indices. This reduces the per-triangle overhead from 6 bytes (three 16-bit indices) to potentially 1 byte (the code byte plus a 4-bit edge reference).

Delta-Encoding Benefits

Vertices not present in the FIFO are delta-encoded relative to the previous vertex. By rotating the triangle so that the vertex continuing the strip appears first, the delta between consecutive vertices is often zero or a small integer. Since the codec uses variable-length encoding for these deltas, smaller values require fewer bits, further reducing the compressed size.

Practical Implementation Example

The following example demonstrates how to use the meshoptimizer index codec, which automatically applies triangle rotation during encoding:

#include "meshoptimizer.h"
#include <vector>
#include <cstdio>

int main()
{
    // Example index buffer (12 indices = 4 triangles)
    std::vector<unsigned int> indices = {
        0, 1, 2,
        2, 1, 3,   // rotated to make vertex 2 the next expected one
        3, 4, 5,
        5, 4, 6
    };

    // Allocate a buffer that is guaranteed to be large enough
    size_t bufSize = meshopt_encodeIndexBufferBound(indices.size(), 7);
    std::vector<unsigned char> buffer(bufSize);

    // Encode – the codec will rotate the second triangle automatically
    size_t encoded = meshopt_encodeIndexBuffer(buffer.data(), bufSize,
                                               indices.data(), indices.size());

    printf("Encoded %zu bytes out of %zu\n", encoded, bufSize);

    // Decode back to verify correctness
    std::vector<unsigned int> decoded(indices.size());
    int err = meshopt_decodeIndexBuffer(decoded.data(), decoded.size(),
                                        sizeof(unsigned int),
                                        buffer.data(), encoded);
    return err;
}

In this example, the second triangle (2, 1, 3) is automatically rotated by the codec so that vertex 2 (the expected next value) becomes the first vertex. This produces a more compact encoding than the original ordering would have allowed, demonstrating how the rotation logic operates transparently during the encoding process.

Summary

  • The rotateTriangle function in src/indexcodec.cpp determines rotation by checking if the second or third vertex matches the expected next value, returning 0, 1, or 2 respectively.
  • The rotations array maps these indices to vertex orderings, allowing the encoder to permute triangle vertices efficiently.
  • Rotation maximizes FIFO hits by ensuring the vertex most likely to be in the 16-entry vertex cache appears first, often eliminating the need to encode a FIFO index.
  • Edge-FIFO utilization improves because the rotated ordering makes the first edge more likely to exist in the edge cache, allowing 4-bit references instead of full vertex indices.
  • Delta compression benefits from sequential vertices having smaller differences, which the variable-length encoding represents with fewer bits.

Frequently Asked Questions

What is the purpose of triangle rotation in mesh compression?

Triangle rotation reorders the vertices of each triangle so that the vertex most likely to already exist in the codec's FIFO cache appears in the first position. This maximizes cache hits and allows the encoder to omit or reduce the size of vertex references, significantly improving compression ratios for indexed geometry.

How does the rotateTriangle function determine the rotation amount?

The function checks if the second vertex (b) equals the expected next value (returning 1), or if the third vertex (c) equals next (returning 2). If neither matches, it returns 0 for no rotation. This logic, implemented in lines 27-32 of src/indexcodec.cpp, prioritizes placing the continuing vertex from the mesh strip into the first position.

What is the maximum rotation amount applied to a triangle?

The maximum rotation is 2 positions, which moves the third vertex to the first position. This is sufficient because the codec only needs to check which of the three vertices matches the expected next value; any of the three vertices can be rotated into the first position with at most two cyclic permutations.

Where is the rotation logic implemented in the meshoptimizer source code?

The core rotation logic resides in src/indexcodec.cpp, specifically in the rotateTriangle helper function (lines 27-32) and the encoding loop (lines 51-56) that uses the static rotations array (line 97). The public API that exposes this functionality is defined in include/meshoptimizer.h and implemented through meshopt_encodeIndexBuffer.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →