Skip to content

Feature Request: Add first-class support for vertex_tangents to support normal mapping in GLB exports #2494

@greenbrettmichael

Description

@greenbrettmichael

Description

I would like to request adding a vertex_tangents attribute to the trimesh.Trimesh object and updating the GLTF/GLB exporter to include these tangents when saving.

Motivation

The glTF 2.0 specification strongly recommends (and in practice requires for correct rendering) that meshes with a normal map include a TANGENT vertex attribute. This attribute is a VEC4 where the w component indicates the handedness of the tangent basis.

Currently, trimesh handles PBR materials (including normal textures) but does not appear to calculate or export the tangent vectors required to display them correctly. Without tangents, viewers (like three.js, Babylon.js, or model-viewer) must attempt to calculate them at runtime, which often fails or produces lighting artifacts because they lack the original geometry context or use a different algorithm than the MikkTSpace standard.

Proposed Changes

  1. Add vertex_tangents Property:
    Add a vertex_tangents property to trimesh.base.Trimesh, similar to vertex_normals.

    • It should store a (n, 4) float array.
    • Ideally, include a method to calculate these tangents (e.g., mesh.calculate_tangents()) using the MikkTSpace algorithm, which is the standard for glTF.
  2. Update GLTF Exporter:
    Modify trimesh.exchange.gltf.export_gltf (and internal helper functions) to check for vertex_tangents.

    • If present, add the TANGENT accessor to the mesh primitive attributes in the exported GLB/GLTF file.

Example Use Case

import trimesh
import numpy as np

# Load a mesh that has a PBR material with a normal map
mesh = trimesh.load('model_with_normal_map.obj')

# Current behavior: Tangents are ignored or missing
# mesh.vertex_tangents  # -> AttributeError or None

# Desired behavior:
# Calculate tangents if they don't exist
if mesh.visual.material.normalTexture is not None:
    mesh.calculate_tangents() 

print(mesh.vertex_tangents.shape) 
# -> (N, 4)

# Export to GLB with TANGENT attribute included
mesh.export('model_with_tangents.glb')

Additional Context

  • glTF Spec Reference: glTF 2.0 Specification - Meshes states: "When normals and tangents are specified, the client implementations SHOULD compute the bitangent... using the handedness stored in the w component of the tangent."
  • Why Custom Attributes Aren't Enough: While we can hack this by adding _TANGENT to vertex_attributes manually, having it as a first-class citizen ensures that operations like subdivision, slicing, or concatenation preserve or re-calculate the tangents correctly. _TANGENT isn't recognized by importers as the TANGENT attribute and requires modifying the gltf json after export from trimesh.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions