CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pywavefront

Python library for importing Wavefront .obj files and generating interleaved vertex data ready for rendering.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

mesh-operations.mddocs/

Mesh Operations

Mesh organization and face data collection for 3D geometry analysis and processing. Meshes group materials and optionally collect triangulated face topology.

Capabilities

Mesh Objects

Meshes organize materials and optionally store face topology data for 3D geometry analysis.

class Mesh:
    def __init__(
        self,
        name: str = None,
        has_faces: bool = False
    ):
        """
        Create a mesh object for grouping materials and faces.
        
        Parameters:
        - name: Optional mesh name (from 'o' statements in .obj files)
        - has_faces: Whether to collect triangulated face data
        
        Attributes:
        - name: str - Mesh name (None for anonymous meshes)
        - materials: List[Material] - Materials used by this mesh
        - has_faces: bool - Whether face data is collected
        - faces: List[List[int]] - Triangle face data (vertex indices) if has_faces=True
        """
    
    def add_material(self, material: Material) -> None:
        """
        Add a material to the mesh if not already present.
        
        Parameters:
        - material: Material object to associate with this mesh
        """
        
    def has_material(self, material: Material) -> bool:
        """
        Check if mesh already contains a specific material.
        
        Parameters:
        - material: Material to check for
        
        Returns:
        - bool: True if material is already in the mesh
        """

Face Data Collection

When collect_faces=True is used during loading, meshes collect triangulated face topology data.

Face Data Structure:

  • Each face is represented as a list of 3 vertex indices (triangulated)
  • Indices reference positions in the global wavefront.vertices array
  • N-gon faces are automatically triangulated using fan triangulation
  • Face data enables topology analysis, normal calculation, and mesh processing

Triangulation Algorithm: For faces with n vertices (n ≥ 3) in order v₁, v₂, v₃, ..., vₙ:

  1. First triangle: (v₁, v₂, v₃)
  2. Additional triangles: (vⱼ, v₁, vⱼ₋₁) for j > 3

This creates a triangle fan from the first vertex, suitable for convex polygons.

Mesh Organization

PyWavefront maintains meshes in two collections:

Named Meshes (wavefront.meshes):

  • Dictionary mapping mesh names to Mesh objects
  • Created from 'o' (object) statements in .obj files
  • Accessible by name for specific mesh operations

Mesh List (wavefront.mesh_list):

  • Ordered list of all meshes including anonymous ones
  • Preserves order of appearance in .obj file
  • Includes meshes without explicit names

Usage Examples

# Access all meshes
for mesh in scene.mesh_list:
    print(f"Mesh: {mesh.name or 'Anonymous'}")
    print(f"Materials: {len(mesh.materials)}")
    
    if mesh.has_faces:
        print(f"Faces: {len(mesh.faces)}")

# Access named meshes
if 'body' in scene.meshes:
    body_mesh = scene.meshes['body']
    print(f"Body mesh has {len(body_mesh.materials)} materials")

# Analyze face topology (when collect_faces=True)
scene = pywavefront.Wavefront('model.obj', collect_faces=True)

for mesh in scene.mesh_list:
    if mesh.has_faces:
        print(f"Mesh {mesh.name}: {len(mesh.faces)} triangular faces")
        
        # Access individual faces
        for i, face in enumerate(mesh.faces[:5]):  # First 5 faces
            v1_idx, v2_idx, v3_idx = face
            # Get vertex positions
            v1 = scene.vertices[v1_idx]
            v2 = scene.vertices[v2_idx] 
            v3 = scene.vertices[v3_idx]
            print(f"Face {i}: vertices {v1_idx}, {v2_idx}, {v3_idx}")

# Material analysis per mesh
for mesh_name, mesh in scene.meshes.items():
    print(f"\nMesh: {mesh_name}")
    
    for material in mesh.materials:
        vertex_count = len(material.vertices) // get_vertex_size(material.vertex_format)
        print(f"  Material {material.name}: {vertex_count} vertices")
        print(f"  Format: {material.vertex_format}")
        
def get_vertex_size(format_str):
    """Helper to calculate vertex size from format string."""
    components = format_str.split('_')
    size = 0
    for comp in components:
        if comp == 'T2F': size += 2  # Texture coordinates
        elif comp == 'C3F': size += 3  # Colors
        elif comp == 'N3F': size += 3  # Normals
        elif comp == 'V3F': size += 3  # Positions
    return size

# Mesh creation and manipulation
new_mesh = pywavefront.Mesh("custom_mesh", has_faces=True)
new_mesh.add_material(some_material)
scene.add_mesh(new_mesh)

# Check for material overlap between meshes
mesh1 = scene.meshes['mesh_a']
mesh2 = scene.meshes['mesh_b']

shared_materials = []
for mat1 in mesh1.materials:
    if mesh2.has_material(mat1):
        shared_materials.append(mat1.name)
        
print(f"Shared materials: {shared_materials}")

Face Data Applications

Face topology data enables various 3D processing tasks:

Mesh Analysis:

  • Surface area calculation
  • Volume computation for closed meshes
  • Connectivity analysis
  • Edge detection and boundary identification

Normal Calculation:

  • Per-face normal computation
  • Vertex normal averaging
  • Smooth vs. flat shading determination

Mesh Processing:

  • Subdivision algorithms
  • Decimation and simplification
  • Mesh repair and validation
  • UV unwrapping preparation

Collision Detection:

  • Bounding box generation
  • Ray-triangle intersection
  • Mesh-mesh collision detection
  • Spatial acceleration structure building
# Example: Calculate face normals
import numpy as np

def calculate_face_normal(v1, v2, v3):
    """Calculate normal vector for a triangle face."""
    edge1 = np.array(v2) - np.array(v1)
    edge2 = np.array(v3) - np.array(v1) 
    normal = np.cross(edge1, edge2)
    return normal / np.linalg.norm(normal)

# Calculate normals for all faces
scene = pywavefront.Wavefront('model.obj', collect_faces=True)

for mesh in scene.mesh_list:
    if mesh.has_faces:
        face_normals = []
        
        for face in mesh.faces:
            v1_idx, v2_idx, v3_idx = face
            v1 = scene.vertices[v1_idx * 3:(v1_idx * 3) + 3]  # x,y,z
            v2 = scene.vertices[v2_idx * 3:(v2_idx * 3) + 3]
            v3 = scene.vertices[v3_idx * 3:(v3_idx * 3) + 3]
            
            normal = calculate_face_normal(v1, v2, v3)
            face_normals.append(normal)
            
        print(f"Calculated {len(face_normals)} face normals for {mesh.name}")

Install with Tessl CLI

npx tessl i tessl/pypi-pywavefront

docs

configuration.md

file-loading.md

index.md

materials.md

mesh-operations.md

visualization.md

tile.json