# LibreMetaverse MeshFoundry renderer

`libremetaverse-rendering-mesh-foundry` is the bounded native renderer for
legacy prims, sculpt maps, terrain, and Second Life LLSD mesh assets. It uses
the checked SimpleRenderer/PrimMesher path for legacy geometry and the native
StructuredData binary codec for mesh containers. It does not wrap, load, or
invoke the C# renderer or duplicate the LLSD and image codecs.

## Pipelines

The mapped `MeshFoundry` methods cover:

- simple and face-grouped legacy prim geometry at every `DetailLevel`, always
  retaining finite normals and UVs;
- plane, cylinder, sphere, and torus sculpt maps;
- rectangular height-map terrain with generated normals and UVs;
- packed mesh-asset headers and individually zlib-compressed LLSD sections;
- requested visual LOD selection (`high_lod`, `medium_lod`, `low_lod`, and
  `lowest_lod`) with deterministic highest-to-lowest fallback;
- single-section simple-mesh extraction and physics convex/bounding hulls; and
- default or planar texture transforms, repeats, offsets, rotation, and prim
  scale through the common `IRendering` contract.

Mesh vertices remain in prim-local coordinates. Quantized positions use each
submesh's `PositionDomain`; normals and tangent components use the format's
fixed `[-1, 1]` domain; UV0 and optional UV1 use their independent domains.
Triangle and face order is preserved, while flattened multi-face output rebases
indices without reordering vertices. Missing normals and tangents are generated
deterministically from triangles and UVs. Degenerate UV gradients receive a
stable normal-orthogonal fallback tangent.

The mapped `Face` type has no tangent field. Mesh tangents are therefore kept
without changing the public C# layout in a public `MeshFaceAux` value stored in
`Face.user_data`; recover it with
`face.user_data.downcast_ref::<MeshFaceAux>()`. Its `Vector4.w` is the bitangent
handedness. UV1 remains in `Face.tex_coords1`.

## Rigged meshes and materials

The `skin` section decodes bounded joint names, inverse-bind matrices,
bind-shape matrix, alternative inverse-bind matrices, pelvis offset, and the
joint-position scale lock into the shared `MeshSkinData`. Per-vertex weight
streams accept the protocol's sentinel form and its exactly-four-influence
form, validate every joint index, clamp protocol weights, normalize four
influences, and bind missing vertices to joint zero. The output is directly
consumable by `RiggedSkinMath` and `AnimeshSkinning`.

Each submesh retains its original face ID and the effective inherited
`PrimitiveTextureEntryFace`, including texture, legacy material, and render
material overrides. `NormalizedScale`, face bounds, centers, optional UV1,
weights, normals, tangents, and winding are kept together. Mesh sculpt mirror
and invert flags apply the viewer-compatible reflection, normal inversion, and
XOR winding rule to every face and tangent domain.

## Bounds and failure contract

Assets and compressed/decompressed sections are capped at 64 MiB. Header
sections and submeshes are capped at 256, each face remains within the 16-bit
65,536-vertex index domain, aggregate visual geometry is capped at 1,000,000
vertices and 6,000,000 indices, skins at 512 joints, and convex data at 65,536
hulls and 1,000,000 vertices. All sizes, offsets, index rebasing, buffer strides,
and allocation products use checked arithmetic before slicing or allocation.

Malformed LLSD, zlib data, domains, parallel buffers, matrices, weights,
offsets, and indices return `Error::Rendering` with the source primitive UUID
and a stable operation context. `UnpackMesh` has no primitive parameter and
uses the zero UUID. This crate performs no asset download, scene traversal,
world transform, GPU upload, material shading, texture fetch, or drawing.

## Reproducible gates and benchmark

```console
python3 tools/check_milestone_10_issue_77.py
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-compat-tests --test imaging_meshing_semantics --locked -j1
CARGO_BUILD_JOBS=1 cargo test -p libremetaverse-rendering-mesh-foundry --locked -j1
CARGO_BUILD_JOBS=1 cargo bench -p libremetaverse-rendering-mesh-foundry --bench mesh_decode --profile benchmark --locked -j1
```

The named `benchmark` profile uses bounded-memory moderate optimization because
full release optimization of the generated core surface can exceed small
Gitea-worker memory before the fixture runs. The non-statistical benchmark
builds and decodes a deterministic 57,600-vertex,
342,726-index mesh asset and reports elapsed time plus allocation counts. It is
diagnostic evidence for regressions; correctness and malformed-input tests
remain the release gates.
