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
Material properties, texture management, and vertex data organization. Materials contain interleaved vertex arrays formatted for direct use with OpenGL rendering pipelines.
Materials store both visual properties and vertex geometry data in a format optimized for modern rendering.
class Material:
def __init__(
self,
name: str,
is_default: bool = False,
has_faces: bool = False
):
"""
Create a new material object.
Parameters:
- name: Material name as defined in .mtl file
- is_default: Whether this is an auto-generated default material
- has_faces: Whether to collect face topology data
Attributes:
- name: str - Material name
- vertex_format: str - Vertex data format (e.g., "T2F_N3F_V3F")
- vertices: List[float] - Interleaved vertex data array
- diffuse: List[float] - Diffuse color [r, g, b, a] (0.0-1.0)
- ambient: List[float] - Ambient color [r, g, b, a] (0.0-1.0)
- specular: List[float] - Specular color [r, g, b, a] (0.0-1.0)
- emissive: List[float] - Emissive color [r, g, b, a] (0.0-1.0)
- transparency: float - Transparency value (0.0=opaque, 1.0=transparent)
- shininess: float - Specular shininess factor
- optical_density: float - Optical density for refraction
- illumination_model: int - Illumination model (0-10)
- texture: Texture - Diffuse texture map
- texture_ambient: Texture - Ambient texture map
- texture_specular_color: Texture - Specular color texture
- texture_specular_highlight: Texture - Specular highlight texture
- texture_alpha: Texture - Alpha/transparency texture
- texture_bump: Texture - Bump/normal map texture
"""
@property
def has_normals(self) -> bool:
"""Check if vertex data contains normal vectors."""
@property
def has_uvs(self) -> bool:
"""Check if vertex data contains texture coordinates."""
@property
def has_colors(self) -> bool:
"""Check if vertex data contains vertex colors."""
@property
def vertex_size(self) -> int:
"""Calculate how many float values each vertex contains in the interleaved data."""
def pad_light(self, values: List[float]) -> List[float]:
"""
Accept an array of up to 4 values, and return an array of 4 values.
Pads with zeroes to length 4 if input is shorter.
"""
def set_alpha(self, alpha: float) -> None:
"""Set alpha/transparency value on all four lighting attributes."""
def set_diffuse(self, values: List[float] = None) -> None:
"""Set diffuse color with automatic padding to 4 values."""
def set_ambient(self, values: List[float] = None) -> None:
"""Set ambient color with automatic padding to 4 values."""
def set_specular(self, values: List[float] = None) -> None:
"""Set specular color with automatic padding to 4 values."""
def set_emissive(self, values: List[float] = None) -> None:
"""Set emissive color with automatic padding to 4 values."""
def set_texture(self, name: str, search_path: str) -> None:
"""Set diffuse texture map."""
def set_texture_ambient(self, name: str, search_path: str) -> None:
"""Set ambient texture map."""
def set_texture_specular_color(self, name: str, search_path: str) -> None:
"""Set specular color texture map."""
def set_texture_specular_highlight(self, name: str, search_path: str) -> None:
"""Set specular highlight texture map."""
def set_texture_alpha(self, name: str, search_path: str) -> None:
"""Set alpha/transparency texture map."""
def set_texture_bump(self, name: str, search_path: str) -> None:
"""Set bump/normal map texture."""
def unset_texture(self) -> None:
"""Remove the diffuse texture reference."""Materials organize vertex data in interleaved arrays optimized for GPU rendering:
Supported Vertex Formats:
V3F - Positions only (x, y, z)T2F_V3F - Texture coordinates + positions (u, v, x, y, z)N3F_V3F - Normals + positions (nx, ny, nz, x, y, z)T2F_N3F_V3F - Texture coords + normals + positions (u, v, nx, ny, nz, x, y, z)C3F_V3F - Vertex colors + positions (r, g, b, x, y, z)T2F_C3F_V3F - Texture coords + colors + positions (u, v, r, g, b, x, y, z)The vertex format string describes the exact layout of the interleaved vertex array, allowing direct use with OpenGL vertex buffer objects (VBOs).
Materials support the full range of Wavefront material properties:
Color Properties:
Kd) - Base surface colorKa) - Ambient lighting responseKs) - Specular highlight colorKe) - Self-illumination colorSurface Properties:
d or Tr) - Surface transparency (0.0-1.0)Ns) - Specular exponent for highlightsNi) - Index of refraction for transparent materialsillum) - Lighting calculation method (0-10)Illumination Models:
class Texture:
def __init__(self, name: str, search_path: str):
"""
Create a texture.
Parameters:
- name: Texture name possibly with path and options as it appears in the material
- search_path: Absolute or relative path where the texture might be located
Attributes:
- path: str - File path to texture image
- options: TextureOptions - Texture loading and sampling options
- image: Any - Used externally by visualization module
"""
@property
def name(self) -> str:
"""The texture path as it appears in the material."""
@name.setter
def name(self, value: str) -> None:
"""Set the texture name."""
@property
def options(self) -> 'TextureOptions':
"""Options for this texture."""
@property
def file_name(self) -> str:
"""
Obtain the file name of the texture.
Handles Windows/relative paths and extracts just the filename.
"""
@property
def path(self) -> str:
"""Full path: search_path + name."""
@path.setter
def path(self, value: str) -> None:
"""Set the texture path."""
@property
def image_name(self) -> str:
"""Legacy property name for texture path as it appears in material."""
@image_name.setter
def image_name(self, value: str) -> None:
"""Legacy setter for texture name."""
def find(self, path: str = None) -> str:
"""
Find the texture in the configured search path.
Searches recursively in subdirectories if texture not found at direct path.
Parameters:
- path: Override the search path
Returns:
- str: Full path to found texture file
Raises:
- FileNotFoundError: If texture cannot be located
"""
def exists(self) -> bool:
"""Check if the texture file exists at the configured path."""
class TextureOptions:
def __init__(self):
"""
Container for texture loading options and parameters with default values.
Attributes:
- name: str - Texture name (default: "default")
- blendu: str - U-direction blending ("on"/"off", default: "on")
- blendv: str - V-direction blending ("on"/"off", default: "on")
- bm: float - Bump multiplier for bump maps (default: 1.0)
- boost: float - Boost factor for mip-mapping (default: 0.0)
- cc: str - Color correction ("on"/"off", default: "off")
- clamp: str - Clamping mode ("on"/"off", default: "off")
- imfchan: str - Channel for scalar/bump textures (default: "l")
- mm: Tuple[float, float] - Range modification (base, gain, default: (0.0, 1.0))
- o: Tuple[float, float, float] - Origin offset (u, v, w, default: (0.0, 0.0, 0.0))
- s: Tuple[float, float, float] - Scale factors (u, v, w, default: (1.0, 1.0, 1.0))
- t: Tuple[float, float, float] - Turbulence factors (u, v, w, default: (0.0, 0.0, 0.0))
- texres: str - Texture resolution specification
"""Texture Types:
map_Kd - Diffuse color texture (most common)map_Ka - Ambient color texturemap_Ks - Specular color texturemap_Ns - Specular highlight texturemap_d - Alpha/transparency texturemap_bump or bump - Bump/normal map textureclass MaterialParser:
"""
Parser for .mtl material library files.
Automatically invoked when processing mtllib statements in .obj files.
"""The MaterialParser handles:
# Access material properties
for name, material in scene.materials.items():
print(f"Material: {name}")
print(f"Diffuse color: {material.diffuse}")
print(f"Has texture: {material.texture is not None}")
print(f"Transparency: {material.transparency}")
print(f"Illumination model: {material.illumination_model}")
# Check vertex data format and content
material = scene.materials['wood_material']
print(f"Vertex format: {material.vertex_format}")
print(f"Has normals: {material.has_normals}")
print(f"Has UVs: {material.has_uvs}")
print(f"Has colors: {material.has_colors}")
print(f"Vertex count: {len(material.vertices)}")
# Access interleaved vertex data for rendering
if material.vertex_format == "T2F_N3F_V3F":
# Data layout: [u, v, nx, ny, nz, x, y, z, u, v, nx, ny, nz, x, y, z, ...]
vertices = material.vertices
vertex_size = material.vertex_size # Automatically calculated: 8 floats per vertex
for i in range(0, len(vertices), vertex_size):
u, v = vertices[i], vertices[i+1] # Texture coordinates
nx, ny, nz = vertices[i+2], vertices[i+3], vertices[i+4] # Normal
x, y, z = vertices[i+5], vertices[i+6], vertices[i+7] # Position
# Material property modification
material.diffuse = [1.0, 0.5, 0.2, 1.0] # Set orange diffuse color
material.shininess = 128.0 # Increase shininess
material.transparency = 0.8 # Make semi-transparent
# Working with textures
if material.texture:
print(f"Diffuse texture: {material.texture.path}")
print(f"Texture exists: {material.texture.exists()}")
print(f"Texture filename: {material.texture.file_name}")
if material.texture_bump:
print(f"Bump map: {material.texture_bump.path}")
# Material property setters with validation
material.set_diffuse([0.8, 0.2, 0.1]) # Will be padded to [0.8, 0.2, 0.1, 0.0]
material.set_ambient([0.1, 0.1, 0.1, 1.0]) # Full RGBA specification
material.set_alpha(0.75) # Sets alpha on all lighting properties
# Texture management
material.set_texture("wood_diffuse.jpg", "/path/to/textures")
material.set_texture_bump("wood_normal.jpg", "/path/to/textures")
# Calculate vertex size dynamically
vertex_size = material.vertex_size
triangles_count = len(material.vertices) // vertex_size // 3
print(f"Material has {triangles_count} triangles")
# Working with texture options
if material.texture:
options = material.texture.options
print(f"Texture clamp mode: {options.clamp}")
print(f"Bump multiplier: {options.bm}")
# Find texture file in search paths
try:
texture_path = material.texture.find()
print(f"Found texture at: {texture_path}")
except FileNotFoundError:
print("Texture file not found in search paths")Install with Tessl CLI
npx tessl i tessl/pypi-pywavefront