Meshes in DiGeo#

The DiGeo library represents meshes using PyTorch tensors to enable GPU acceleration and differentiable operations. Under the hood, it uses the trimesh library to parse and load 3D models from files.

Loading Meshes#

  1. From a file with digeo.load_mesh_from_file(). Uses trimesh internally to read standard 3D formats (like .obj).

  2. From an existing trimesh.Trimesh object with digeo.load_mesh_from_trimesh(). Useful if you have already loaded or generated a mesh using trimesh.

  3. From vertices and faces by directly creating a digeo.Mesh object. This requires providing the vertex positions and face indices as PyTorch tensors or Numpy arrays.

When loading meshes, you can specify the device (CPU or GPU) and data type (e.g., float32), allowing for efficient processing in subsequent operations.

from digeo import load_mesh_from_file, load_mesh_from_trimesh
import trimesh
import torch

# Load directly from a file
mesh = load_mesh_from_file("path/to/mesh.obj", device="cuda", dtype=torch.float32)

# Load from an existing trimesh object
trimesh_obj = trimesh.creation.icosphere()
mesh = load_mesh_from_trimesh(trimesh_obj, device="cuda", dtype=torch.float32)

# Using vertices and faces to create a Mesh
vertices = torch.randn((100, 3))
faces = torch.randint(0, 100, (200, 3))
mesh = Mesh(vertices, faces, device="cuda", dtype=torch.float32)

Batching Meshes#

To process multiple meshes simultaneously, you can group them into a MeshBatch object. A MeshBatch inherits from Mesh and concatenates the vertices and faces of multiple meshes into a single large mesh with disconnected components.

When working with batched meshes, any points on the mesh (represented by MeshPointBatch) must also be batched to align with the shifted face indices of the MeshBatch.

from digeo import MeshBatch
from digeo.ops import uniform_sampling

# Load individual meshes
mesh1 = load_mesh_from_file("mesh1.obj", device="cuda")
mesh2 = load_mesh_from_file("mesh2.obj", device="cuda")

# Create a MeshBatch
mesh_batch = MeshBatch([mesh1, mesh2])

# Sample points on individual meshes
points1 = uniform_sampling(mesh1, num_points=100)
points2 = uniform_sampling(mesh2, num_points=100)

# Batch the points using the MeshBatch
batched_points = mesh_batch.batch_points([points1, points2])

# Process the batched points with the MeshBatch
# For example, trace geodesics on the batched mesh with the batched points
new_batched_points, _ = trace_geodesics(mesh_batch, batched_points, directions)

# Unbatch the points back to individual meshes
unbatched_points = mesh_batch.unbatch_points(new_batched_points)
new_points1, new_points2 = unbatched_points[0], unbatched_points[1]

MeshPoints#

Points on the mesh are represented by the MeshPoint class, which encodes a point as a face index and barycentric coordinates within that face. This representation allows for efficient interpolation of face attributes (like normals) at the point location.

However, it is prefered to use directly the MeshPointBatch class, which is a batch of MeshPoint objects, as it is more efficient to process points in batches, especially when tracing geodesics or computing gradients.

To create a MeshPointBatch, you can use the sampling functions provided in src/digeo/ops/sampling.py, such as uniform_sampling. It is also possible to create a MeshPointBatch from explicit face indices and uv coordinates.

It is also possible to convert these meshpoints to 3d coordinates using MeshPointBatch.interpolate().

from digeo import MeshPointBatch, uniform_sampling

# Sample points on the mesh
points = uniform_sampling(mesh, num_points=100)

# Alternatively, create a MeshPointBatch from explicit face indices and uv coordinates
face_indices = torch.tensor([0, 1, 2])
bary_coords = torch.tensor([[0.2, 0.3, 0.5], [0.1, 0.1, 0.8], [0.4, 0.4, 0.2]])
# We convert the bary_coords to UV coords
uv_coords = bary_coords[:, 1:]
points = MeshPointBatch(face_indices, uv_coords)

# Interpolate the 3D coordinates of the points
point_3d = points.interpolate(mesh)