CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyassimp

Python bindings for the Open Asset Import Library (ASSIMP) enabling 3D model loading, processing, and export

Pending
Overview
Eval results
Files

materials.mddocs/

Materials and Textures

Material properties, texture management, and shader parameter access. PyAssimp provides comprehensive access to material systems including property queries, texture mapping, and material type identification with support for various shading models and material workflows.

Capabilities

Material Properties

Material property access through a specialized dictionary interface supporting semantic keys.

class Material:
    """
    Material definition with properties and textures.
    
    Attributes:
    - properties: PropertyGetter, material property dictionary with semantic access
    """
    properties: PropertyGetter

class PropertyGetter(dict):
    """
    Material property dictionary with semantic key support.
    
    Supports both simple key access and semantic tuple keys (key, semantic_index).
    Material properties are organized by property type and semantic meaning.
    """
    def __getitem__(self, key):
        """
        Access property by key or (key, semantic) tuple.
        
        Parameters:
        - key: str or tuple, property name or (name, semantic_index)
        
        Returns:
        Property value (type varies by property)
        """
    
    def keys(self):
        """
        Iterate over property keys.
        
        Yields:
        str: Property key names
        """
    
    def items(self):
        """
        Iterate over property key-value pairs.
        
        Yields:
        tuple: (key, value) pairs
        """

Usage examples:

import pyassimp

scene = pyassimp.load("textured_model.obj")

for i, material in enumerate(scene.materials):
    print(f"Material {i}:")
    
    # Access all properties
    for key, value in material.properties.items():
        print(f"  {key}: {value}")
    
    # Access specific properties
    properties = material.properties
    
    # Common material properties
    if 'diffuse' in properties:
        diffuse_color = properties['diffuse']
        print(f"  Diffuse color: {diffuse_color}")
    
    if 'specular' in properties:
        specular_color = properties['specular']
        print(f"  Specular color: {specular_color}")
    
    if 'shininess' in properties:
        shininess = properties['shininess']
        print(f"  Shininess: {shininess}")
    
    # Texture properties (using semantic indices)
    try:
        diffuse_texture = properties[('file', 1)]  # Diffuse texture
        print(f"  Diffuse texture: {diffuse_texture}")
    except KeyError:
        pass
    
    try:
        normal_texture = properties[('file', 2)]  # Normal map
        print(f"  Normal texture: {normal_texture}")
    except KeyError:
        pass

pyassimp.release(scene)

Texture Data

Embedded texture data and texture information.

class Texture:
    """
    Embedded texture data.
    
    Attributes:
    - achformathint: str, format hint/extension (e.g., "jpg", "png")
    - data: array, texture pixel data
    - width: int, texture width in pixels
    - height: int, texture height in pixels
    """
    achformathint: str
    data: array
    width: int
    height: int

Usage examples:

import pyassimp

scene = pyassimp.load("model_with_embedded_textures.blend")

if scene.textures:
    for i, texture in enumerate(scene.textures):
        print(f"Embedded texture {i}:")
        print(f"  Format hint: {texture.achformathint}")
        print(f"  Dimensions: {texture.width}x{texture.height}")
        print(f"  Data size: {len(texture.data) if texture.data else 0}")
        
        # Save embedded texture
        if texture.data and texture.achformathint:
            filename = f"texture_{i}.{texture.achformathint}"
            # Note: texture.data contains pixel data that may need format conversion
            print(f"  Can be saved as: {filename}")

pyassimp.release(scene)

Material Types and Properties

Common material property identification and texture type constants.

# Texture type constants (from pyassimp.material)
aiTextureType_NONE = 0x0      # No texture
aiTextureType_DIFFUSE = 0x1   # Diffuse color texture
aiTextureType_SPECULAR = 0x2  # Specular color texture  
aiTextureType_AMBIENT = 0x3   # Ambient color texture
aiTextureType_EMISSIVE = 0x4  # Emissive color texture
aiTextureType_HEIGHT = 0x5    # Height map
aiTextureType_NORMALS = 0x6   # Normal map
aiTextureType_SHININESS = 0x7 # Shininess map
aiTextureType_OPACITY = 0x8   # Opacity map
aiTextureType_DISPLACEMENT = 0x9  # Displacement map
aiTextureType_LIGHTMAP = 0xA  # Light map
aiTextureType_REFLECTION = 0xB # Reflection map
aiTextureType_UNKNOWN = 0xC    # Unknown texture type

Usage examples:

import pyassimp
from pyassimp.material import *

def analyze_material_textures(material):
    """Analyze texture usage in a material."""
    
    texture_types = {
        'diffuse': aiTextureType_DIFFUSE,
        'specular': aiTextureType_SPECULAR,
        'normal': aiTextureType_NORMALS,
        'height': aiTextureType_HEIGHT,
        'opacity': aiTextureType_OPACITY,
        'emissive': aiTextureType_EMISSIVE
    }
    
    found_textures = {}
    
    for name, tex_type in texture_types.items():
        try:
            # Look for texture file property with semantic index
            texture_path = material.properties[('file', tex_type)]
            found_textures[name] = texture_path
        except KeyError:
            pass
    
    return found_textures

# Usage
scene = pyassimp.load("complex_material.dae")

for i, material in enumerate(scene.materials):
    print(f"Material {i} textures:")
    textures = analyze_material_textures(material)
    
    for tex_type, path in textures.items():
        print(f"  {tex_type}: {path}")

pyassimp.release(scene)

Advanced Material Usage

Material Property Extraction

import pyassimp

def extract_material_info(material):
    """Extract common material properties."""
    
    props = material.properties
    material_info = {}
    
    # Color properties
    color_props = ['diffuse', 'specular', 'ambient', 'emissive']
    for prop in color_props:
        if prop in props:
            material_info[prop + '_color'] = props[prop]
    
    # Scalar properties  
    scalar_props = ['shininess', 'opacity', 'refracti']
    for prop in scalar_props:
        if prop in props:
            material_info[prop] = props[prop]
    
    # String properties
    string_props = ['name']
    for prop in string_props:
        if prop in props:
            material_info[prop] = props[prop]
    
    # Texture paths
    texture_semantics = {
        'diffuse_map': 1,
        'specular_map': 2, 
        'ambient_map': 3,
        'emissive_map': 4,
        'height_map': 5,
        'normal_map': 6,
        'shininess_map': 7,
        'opacity_map': 8,
        'displacement_map': 9,
        'lightmap': 10,
        'reflection_map': 11
    }
    
    for map_name, semantic in texture_semantics.items():
        try:
            texture_path = props[('file', semantic)]
            material_info[map_name] = texture_path
        except KeyError:
            pass
    
    return material_info

# Usage
scene = pyassimp.load("pbr_model.gltf")

for i, material in enumerate(scene.materials):
    info = extract_material_info(material)
    print(f"Material {i}:")
    for key, value in info.items():
        print(f"  {key}: {value}")

pyassimp.release(scene)

Material-Mesh Mapping

import pyassimp

def create_material_mesh_mapping(scene):
    """Create mapping between materials and meshes that use them."""
    
    material_usage = {}
    
    for mat_idx in range(len(scene.materials)):
        material_usage[mat_idx] = {
            'material': scene.materials[mat_idx],
            'meshes': []
        }
    
    for mesh_idx, mesh in enumerate(scene.meshes):
        mat_idx = mesh.materialindex
        if mat_idx < len(scene.materials):
            material_usage[mat_idx]['meshes'].append({
                'index': mesh_idx,
                'mesh': mesh,
                'vertex_count': len(mesh.vertices) if mesh.vertices else 0,
                'face_count': len(mesh.faces) if mesh.faces else 0
            })
    
    return material_usage

# Usage
scene = pyassimp.load("multi_material_model.obj")

mapping = create_material_mesh_mapping(scene)

for mat_idx, info in mapping.items():
    material = info['material']
    meshes = info['meshes']
    
    print(f"Material {mat_idx}: {len(meshes)} meshes")
    
    # Show material properties
    if 'diffuse' in material.properties:
        print(f"  Diffuse: {material.properties['diffuse']}")
    
    # Show mesh usage
    total_vertices = sum(mesh['vertex_count'] for mesh in meshes)
    total_faces = sum(mesh['face_count'] for mesh in meshes)
    print(f"  Total vertices: {total_vertices}")
    print(f"  Total faces: {total_faces}")

pyassimp.release(scene)

PBR Material Detection

import pyassimp

def detect_pbr_workflow(material):
    """Detect if material uses PBR workflow and which type."""
    
    props = material.properties
    
    # Look for PBR-specific properties
    pbr_indicators = {
        'metallic_roughness': ['metallic', 'roughness'],
        'specular_glossiness': ['specular', 'glossiness'],
        'base_color': ['base_color', 'basecolor']
    }
    
    workflow = None
    properties_found = {}
    
    for workflow_name, prop_names in pbr_indicators.items():
        found_props = []
        for prop in prop_names:
            if prop in props:
                found_props.append(prop)
                properties_found[prop] = props[prop]
        
        if found_props:
            workflow = workflow_name
            break
    
    # Look for PBR texture maps
    pbr_textures = {}
    pbr_texture_semantics = {
        'basecolor_map': ('file', 1),
        'metallic_map': ('file', 12),  # Assuming extended semantics
        'roughness_map': ('file', 13),
        'normal_map': ('file', 6),
        'occlusion_map': ('file', 14),
        'emissive_map': ('file', 4)
    }
    
    for tex_name, (key, semantic) in pbr_texture_semantics.items():
        try:
            texture_path = props[(key, semantic)]
            pbr_textures[tex_name] = texture_path
        except KeyError:
            continue
    
    return {
        'workflow': workflow,
        'properties': properties_found,
        'textures': pbr_textures
    }

# Usage
scene = pyassimp.load("pbr_model.gltf")

for i, material in enumerate(scene.materials):
    pbr_info = detect_pbr_workflow(material)
    
    print(f"Material {i}:")
    print(f"  PBR workflow: {pbr_info['workflow']}")
    print(f"  Properties: {pbr_info['properties']}")
    print(f"  Textures: {pbr_info['textures']}")

pyassimp.release(scene)

Texture Path Resolution

import pyassimp
import os

def resolve_texture_paths(scene, model_directory):
    """Resolve relative texture paths to absolute paths."""
    
    resolved_textures = {}
    
    for mat_idx, material in enumerate(scene.materials):
        material_textures = {}
        
        # Get all texture properties
        for key, value in material.properties.items():
            if isinstance(key, tuple) and key[0] == 'file':
                texture_path = value
                
                # Resolve relative paths
                if not os.path.isabs(texture_path):
                    resolved_path = os.path.join(model_directory, texture_path)
                    resolved_path = os.path.normpath(resolved_path)
                else:
                    resolved_path = texture_path
                
                # Check if file exists  
                exists = os.path.exists(resolved_path)
                
                material_textures[key[1]] = {
                    'original_path': texture_path,
                    'resolved_path': resolved_path,
                    'exists': exists
                }
        
        if material_textures:
            resolved_textures[mat_idx] = material_textures
    
    return resolved_textures

# Usage
model_path = "models/house/house.obj"
model_dir = os.path.dirname(model_path)

scene = pyassimp.load(model_path)
texture_info = resolve_texture_paths(scene, model_dir)

for mat_idx, textures in texture_info.items():
    print(f"Material {mat_idx} textures:")
    for semantic, info in textures.items():
        status = "✓" if info['exists'] else "✗"
        print(f"  Semantic {semantic}: {info['original_path']} → {info['resolved_path']} {status}")

pyassimp.release(scene)

Material Property Reference

Common material properties you might encounter:

Color Properties

  • diffuse: Diffuse color (RGB or RGBA)
  • specular: Specular color
  • ambient: Ambient color
  • emissive: Emissive/self-illumination color
  • transparent: Transparency color

Scalar Properties

  • shininess: Specular shininess/power
  • opacity: Material opacity (1.0 = opaque, 0.0 = transparent)
  • refracti: Refractive index
  • reflectivity: Reflectivity factor

String Properties

  • name: Material name
  • shadingm: Shading model identifier

Texture Properties (by semantic index)

  • Semantic 1: Diffuse texture
  • Semantic 2: Specular texture
  • Semantic 3: Ambient texture
  • Semantic 4: Emissive texture
  • Semantic 5: Height map
  • Semantic 6: Normal map
  • Semantic 7: Shininess map
  • Semantic 8: Opacity map
  • Semantic 9: Displacement map
  • Semantic 10: Light map
  • Semantic 11: Reflection map

Performance Considerations

  1. Property Access: Use direct key lookup rather than iteration when possible
  2. Memory Usage: Material properties are loaded into memory; large property sets increase RAM usage
  3. Texture Loading: Embedded textures are loaded into memory; external textures are referenced by path
  4. Property Iteration: Iterating over all properties can be slow for materials with many properties
  5. Semantic Indices: Using semantic indices is more reliable than key names across different file formats

Install with Tessl CLI

npx tessl i tessl/pypi-pyassimp

docs

index.md

materials.md

math-utilities.md

post-processing.md

scene-data.md

scene-loading.md

tile.json