Python library for importing Wavefront .obj files and generating interleaved vertex data ready for rendering.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Mesh organization and face data collection for 3D geometry analysis and processing. Meshes group materials and optionally collect triangulated face topology.
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
"""When collect_faces=True is used during loading, meshes collect triangulated face topology data.
Face Data Structure:
wavefront.vertices arrayTriangulation Algorithm: For faces with n vertices (n ≥ 3) in order v₁, v₂, v₃, ..., vₙ:
This creates a triangle fan from the first vertex, suitable for convex polygons.
PyWavefront maintains meshes in two collections:
Named Meshes (wavefront.meshes):
Mesh List (wavefront.mesh_list):
# 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 topology data enables various 3D processing tasks:
Mesh Analysis:
Normal Calculation:
Mesh Processing:
Collision Detection:
# 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