Zig library for loading, generating, processing and optimising triangle meshes.
Under the hood this library uses below C/C++ libraries:
All memory allocations go through user-supplied, Zig allocator.
As an example program please see procedural mesh (wgpu).
Example build.zig:
pub fn build(b: *std.Build) void {
    const exe = b.addExecutable(.{ ... });
    const zmesh = b.dependency("zmesh", .{});
    exe.root_module.addImport("zmesh", zmesh.module("root"));
    exe.linkLibrary(zmesh.artifact("zmesh"));
}Now in your code you may import and use zmesh:
const zmesh = @import("zmesh");
pub fn main() !void {
    ...
    zmesh.init(allocator);
    defer zmesh.deinit();
    var custom = zmesh.Shape.init(indices, positions, normals, texcoords);
    defer custom.deinit();
    var disk = zmesh.Shape.initParametricDisk(10, 2);
    defer disk.deinit();
    disk.invert(0, 0);
    var cylinder = zmesh.Shape.initCylinder(10, 4);
    defer cylinder.deinit();
    cylinder.merge(disk);
    cylinder.translate(0, 0, -1);
    disk.invert(0, 0);
    cylinder.merge(disk);
    cylinder.scale(0.5, 0.5, 2);
    cylinder.rotate(math.pi * 0.5, 1.0, 0.0, 0.0);
    cylinder.unweld();
    cylinder.computeNormals();
    ...
}const zmesh = @import("zmesh");
pub fn main() !void {
    zmesh.init(allocator);
    defer zmesh.deinit();
    //
    // Load mesh
    //
    const data = try zmesh.io.zcgltf.parseAndLoadFile(content_dir ++ "cube.gltf");
    defer zmesh.io.zcgltf.freeData(data);
    var mesh_indices = std.ArrayListUnmanaged(u32){};
    var mesh_positions = std.ArrayListUnmanaged([3]f32){};
    var mesh_normals = std.ArrayListUnmanaged([3]f32){};
    try zmesh.io.zcgltf.appendMeshPrimitive(
        allocator,
        data,
        0, // mesh index
        0, // gltf primitive index (submesh index)
        &mesh_indices,
        &mesh_positions,
        &mesh_normals, // normals (optional)
        null, // texcoords (optional)
        null, // tangents (optional)
    );
    ...
    //
    // Optimize mesh
    //
    const Vertex = struct {
        position: [3]f32,
        normal: [3]f32,
    };
    var remap = std.ArrayListUnmanaged(u32){};
    try remap.resize(allocator, src_indices.items.len);
    const num_unique_vertices = zmesh.opt.generateVertexRemap(
        remap.items, // 'vertex remap' (destination)
        src_indices.items, // non-optimized indices
        Vertex, // Zig type describing your vertex
        src_vertices.items, // non-optimized vertices
    );
    var optimized_vertices = std.ArrayListUnmanaged(Vertex){};
    try optimized_vertices.resize(allocator, num_unique_vertices);
    zmesh.opt.remapVertexBuffer(
        Vertex, // Zig type describing your vertex
        optimized_vertices.items, // optimized vertices (destination)
        src_vertices.items, // non-optimized vertices (source)
        remap.items, // 'vertex remap' generated by generateVertexRemap()
    );
    // More optimization steps are available - see `zmeshoptimizer.zig` file.
}