0
# Mesh Operations
1
2
Mesh organization and face data collection for 3D geometry analysis and processing. Meshes group materials and optionally collect triangulated face topology.
3
4
## Capabilities
5
6
### Mesh Objects
7
8
Meshes organize materials and optionally store face topology data for 3D geometry analysis.
9
10
```python { .api }
11
class Mesh:
12
def __init__(
13
self,
14
name: str = None,
15
has_faces: bool = False
16
):
17
"""
18
Create a mesh object for grouping materials and faces.
19
20
Parameters:
21
- name: Optional mesh name (from 'o' statements in .obj files)
22
- has_faces: Whether to collect triangulated face data
23
24
Attributes:
25
- name: str - Mesh name (None for anonymous meshes)
26
- materials: List[Material] - Materials used by this mesh
27
- has_faces: bool - Whether face data is collected
28
- faces: List[List[int]] - Triangle face data (vertex indices) if has_faces=True
29
"""
30
31
def add_material(self, material: Material) -> None:
32
"""
33
Add a material to the mesh if not already present.
34
35
Parameters:
36
- material: Material object to associate with this mesh
37
"""
38
39
def has_material(self, material: Material) -> bool:
40
"""
41
Check if mesh already contains a specific material.
42
43
Parameters:
44
- material: Material to check for
45
46
Returns:
47
- bool: True if material is already in the mesh
48
"""
49
```
50
51
### Face Data Collection
52
53
When `collect_faces=True` is used during loading, meshes collect triangulated face topology data.
54
55
**Face Data Structure:**
56
- Each face is represented as a list of 3 vertex indices (triangulated)
57
- Indices reference positions in the global `wavefront.vertices` array
58
- N-gon faces are automatically triangulated using fan triangulation
59
- Face data enables topology analysis, normal calculation, and mesh processing
60
61
**Triangulation Algorithm:**
62
For faces with n vertices (n ≥ 3) in order v₁, v₂, v₃, ..., vₙ:
63
1. First triangle: (v₁, v₂, v₃)
64
2. Additional triangles: (vⱼ, v₁, vⱼ₋₁) for j > 3
65
66
This creates a triangle fan from the first vertex, suitable for convex polygons.
67
68
### Mesh Organization
69
70
PyWavefront maintains meshes in two collections:
71
72
**Named Meshes (`wavefront.meshes`):**
73
- Dictionary mapping mesh names to Mesh objects
74
- Created from 'o' (object) statements in .obj files
75
- Accessible by name for specific mesh operations
76
77
**Mesh List (`wavefront.mesh_list`):**
78
- Ordered list of all meshes including anonymous ones
79
- Preserves order of appearance in .obj file
80
- Includes meshes without explicit names
81
82
### Usage Examples
83
84
```python
85
# Access all meshes
86
for mesh in scene.mesh_list:
87
print(f"Mesh: {mesh.name or 'Anonymous'}")
88
print(f"Materials: {len(mesh.materials)}")
89
90
if mesh.has_faces:
91
print(f"Faces: {len(mesh.faces)}")
92
93
# Access named meshes
94
if 'body' in scene.meshes:
95
body_mesh = scene.meshes['body']
96
print(f"Body mesh has {len(body_mesh.materials)} materials")
97
98
# Analyze face topology (when collect_faces=True)
99
scene = pywavefront.Wavefront('model.obj', collect_faces=True)
100
101
for mesh in scene.mesh_list:
102
if mesh.has_faces:
103
print(f"Mesh {mesh.name}: {len(mesh.faces)} triangular faces")
104
105
# Access individual faces
106
for i, face in enumerate(mesh.faces[:5]): # First 5 faces
107
v1_idx, v2_idx, v3_idx = face
108
# Get vertex positions
109
v1 = scene.vertices[v1_idx]
110
v2 = scene.vertices[v2_idx]
111
v3 = scene.vertices[v3_idx]
112
print(f"Face {i}: vertices {v1_idx}, {v2_idx}, {v3_idx}")
113
114
# Material analysis per mesh
115
for mesh_name, mesh in scene.meshes.items():
116
print(f"\nMesh: {mesh_name}")
117
118
for material in mesh.materials:
119
vertex_count = len(material.vertices) // get_vertex_size(material.vertex_format)
120
print(f" Material {material.name}: {vertex_count} vertices")
121
print(f" Format: {material.vertex_format}")
122
123
def get_vertex_size(format_str):
124
"""Helper to calculate vertex size from format string."""
125
components = format_str.split('_')
126
size = 0
127
for comp in components:
128
if comp == 'T2F': size += 2 # Texture coordinates
129
elif comp == 'C3F': size += 3 # Colors
130
elif comp == 'N3F': size += 3 # Normals
131
elif comp == 'V3F': size += 3 # Positions
132
return size
133
134
# Mesh creation and manipulation
135
new_mesh = pywavefront.Mesh("custom_mesh", has_faces=True)
136
new_mesh.add_material(some_material)
137
scene.add_mesh(new_mesh)
138
139
# Check for material overlap between meshes
140
mesh1 = scene.meshes['mesh_a']
141
mesh2 = scene.meshes['mesh_b']
142
143
shared_materials = []
144
for mat1 in mesh1.materials:
145
if mesh2.has_material(mat1):
146
shared_materials.append(mat1.name)
147
148
print(f"Shared materials: {shared_materials}")
149
```
150
151
### Face Data Applications
152
153
Face topology data enables various 3D processing tasks:
154
155
**Mesh Analysis:**
156
- Surface area calculation
157
- Volume computation for closed meshes
158
- Connectivity analysis
159
- Edge detection and boundary identification
160
161
**Normal Calculation:**
162
- Per-face normal computation
163
- Vertex normal averaging
164
- Smooth vs. flat shading determination
165
166
**Mesh Processing:**
167
- Subdivision algorithms
168
- Decimation and simplification
169
- Mesh repair and validation
170
- UV unwrapping preparation
171
172
**Collision Detection:**
173
- Bounding box generation
174
- Ray-triangle intersection
175
- Mesh-mesh collision detection
176
- Spatial acceleration structure building
177
178
```python
179
# Example: Calculate face normals
180
import numpy as np
181
182
def calculate_face_normal(v1, v2, v3):
183
"""Calculate normal vector for a triangle face."""
184
edge1 = np.array(v2) - np.array(v1)
185
edge2 = np.array(v3) - np.array(v1)
186
normal = np.cross(edge1, edge2)
187
return normal / np.linalg.norm(normal)
188
189
# Calculate normals for all faces
190
scene = pywavefront.Wavefront('model.obj', collect_faces=True)
191
192
for mesh in scene.mesh_list:
193
if mesh.has_faces:
194
face_normals = []
195
196
for face in mesh.faces:
197
v1_idx, v2_idx, v3_idx = face
198
v1 = scene.vertices[v1_idx * 3:(v1_idx * 3) + 3] # x,y,z
199
v2 = scene.vertices[v2_idx * 3:(v2_idx * 3) + 3]
200
v3 = scene.vertices[v3_idx * 3:(v3_idx * 3) + 3]
201
202
normal = calculate_face_normal(v1, v2, v3)
203
face_normals.append(normal)
204
205
print(f"Calculated {len(face_normals)} face normals for {mesh.name}")
206
```