Comment charger des modèles 3D dans Python

Comment charger des modèles 3D dans Python

Aspose.3D FOSS pour Python fournit une API simple pour ouvrir des fichiers 3D sans aucune dépendance native. Après avoir chargé un fichier dans un Scene objet, vous pouvez parcourir la hiérarchie des nœuds et lire les données de géométrie brute de chaque maillage de la scène.

Guide étape par étape

Étape 1 : Installer le package

Installez Aspose.3D FOSS depuis PyPI. Aucune bibliothèque système supplémentaire n’est requise.

pip install aspose-3d-foss

Versions Python prises en charge : 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


Étape 2 : Importer la classe Scene

Le Scene class est le conteneur de niveau supérieur pour toutes les données 3D. Importez‑la avec toutes les classes d’options de chargement dont vous avez besoin.

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

Toutes les classes publiques se trouvent sous aspose.threed ou ses sous‑packages (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).


Étape 3 : Charger un fichier

Utilisez le static Scene.from_file() méthode pour ouvrir n’importe quel format pris en charge. La bibliothèque détecte automatiquement le format à partir de l’extension du fichier.

##Automatic format detection
scene = Scene.from_file("model.obj")

Alternativement, créez un Scene instance et appelez open(); utile lorsque vous souhaitez passer des options de chargement ou gérer les erreurs explicitement :

scene = Scene()
scene.open("model.obj")

Les deux méthodes prennent en charge les fichiers OBJ, STL (binaire et ASCII), glTF 2.0 / GLB, COLLADA (DAE) et 3MF.


Étape 4 : Parcourir les nœuds de la scène

Une scène chargée est un arbre de Node objets enracinés à scene.root_node. Parcourez récursivement pour trouver tous les nœuds :

from aspose.threed import Scene, Node

scene = Scene.from_file("model.obj")

def walk(node: Node, depth: int = 0) -> None:
    indent = "  " * depth
    print(f"{indent}Node: {node.name!r}")
    for child in node.child_nodes:
        walk(child, depth + 1)

walk(scene.root_node)

Chaque Node peut contenir zéro ou plusieurs Entity objets (maillages, caméras, lumières). Vérifiez node.entities pour voir ce qui est attaché.


Étape 5 : accéder aux données de sommets et de polygones

Convertissez l’entité d’un nœud en Mesh et lisez ses points de contrôle (positions des sommets) et ses polygones (listes d’indices de faces) :

from aspose.threed import Scene
from aspose.threed.entities import Mesh

scene = Scene.from_file("model.obj")

for node in scene.root_node.child_nodes:
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh: Mesh = entity
            print(f"Mesh '{node.name}': "
                  f"{len(mesh.control_points)} vertices, "
                  f"{len(mesh.polygons)} polygons")

            # First vertex position
            if mesh.control_points:
                v = mesh.control_points[0]
                print(f"  First vertex: ({v.x:.4f}, {v.y:.4f}, {v.z:.4f})")

            # First polygon face (list of control-point indices)
            if mesh.polygons:
                print(f"  First polygon: {mesh.polygons[0]}")

mesh.control_points est une liste de Vector4 objets ; x, y, z conserver la position et w est la coordonnée homogène (normalement 1.0).

mesh.polygons est une liste de listes d’entiers, où chaque liste interne est l’ensemble ordonné des indices de points de contrôle pour une face.


Étape 6 : appliquer les options de chargement spécifiques au format

Pour un contrôle fin sur la façon dont un fichier OBJ est interprété, passez un ObjLoadOptions instance à scene.open():

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

options = ObjLoadOptions()
options.flip_coordinate_system = True   # Convert right-hand Y-up to Z-up
options.scale = 0.01                    # Convert centimetres to metres
options.enable_materials = True         # Load .mtl material file
options.normalize_normal = True         # Normalize all normals to unit length

scene = Scene()
scene.open("model.obj", options)

Pour les fichiers STL, la classe équivalente est StlLoadOptions. Pour glTF, utilisez GltfLoadOptions. Voir le référence API pour une liste complète.


Problèmes courants et solutions

FileNotFoundError lors de l’appel Scene.from_file()

Le chemin doit être absolu ou correctement relatif au répertoire de travail à l’exécution. Utilisez pathlib.Path pour construire des chemins fiables :

from pathlib import Path
from aspose.threed import Scene

path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))

mesh.polygons est vide après le chargement d’un fichier STL

Les fichiers STL stockent les triangles sous forme de facettes brutes, pas d’un maillage indexé. Après le chargement, les polygones sont synthétisés à partir de ces facettes. Si polygons apparaît vide, vérifiez len(mesh.control_points); si le nombre est un multiple de 3, la géométrie est stockée sous forme non indexée et chaque triple consécutif de sommets forme un triangle.

Incohérence du système de coordonnées (le modèle apparaît tourné ou reflété)

Différents outils utilisent des conventions différentes (Y-up vs Z-up, main gauche vs main droite). Définissez ObjLoadOptions.flip_coordinate_system = True ou appliquez une rotation au root node’s Transform après le chargement.

AttributeError: 'NoneType' object has no attribute 'polygons'

La liste d’entités d’un nœud peut contenir des entités non maillage (caméras, lumières). Protégez toujours avec isinstance(entity, Mesh) avant le transtypage.


Foire aux questions (FAQ)

Quels formats 3D puis-je charger ?

OBJ (Wavefront), STL (binaire et ASCII), glTF 2.0 / GLB, COLLADA (DAE) et 3MF. La tokenisation des fichiers FBX est partiellement prise en charge, mais l’analyse complète n’est pas encore terminée.

Le chargement d’un fichier OBJ charge-t-il également le .mtl matériau ?

Oui, quand ObjLoadOptions.enable_materials = True (par défaut). La bibliothèque recherche le .mtl fichier dans le même répertoire que le .obj fichier. Si le .mtl est manquant, la géométrie est toujours chargée et un avertissement est émis.

Puis-je charger un fichier à partir d’un flux d’octets au lieu d’un chemin ?

Oui. scene.open() accepte tout objet de type fichier avec un .read() méthode en plus d’une chaîne de chemin de fichier. Passez un flux binaire ouvert (par ex., io.BytesIO) directement. Scene.from_file() n’accepte qu’une chaîne de chemin de fichier.

Comment obtenir les normales de surface ?

Après le chargement, vérifiez mesh.get_element(VertexElementType.NORMAL). Cela renvoie un VertexElementNormal dont data la liste contient un vecteur normal par référence, mappé selon mapping_mode et reference_mode.

from aspose.threed.entities import Mesh, VertexElementType

normals = mesh.get_element(VertexElementType.NORMAL)
if normals:
    print(normals.data[0])  # First normal vector

La bibliothèque est-elle thread‑safe pour le chargement de plusieurs fichiers simultanément ?

Chaque Scene objet est indépendant. Charger des fichiers séparés dans des Scene instances provenant de threads séparés est sûr tant que vous ne partagez pas un seul Scene entre les threads sans verrouillage externe.

 Français