Hoe 3D-modellen te laden in Python

Hoe 3D-modellen te laden in Python

Aspose.3D FOSS for Python biedt een eenvoudige API voor het openen van 3D‑bestanden zonder native afhankelijkheden. Na het laden van een bestand in een Scene‑object kun je door de knoophierarchie lopen en ruwe geometrie‑gegevens lezen voor elke mesh in de scène.

Stapsgewijze handleiding

Stap 1: Installeer het pakket

Installeer Aspose.3D FOSS vanaf PyPI. Er zijn geen extra systeembibliotheken vereist.

pip install aspose-3d-foss

Ondersteunde Python‑versies: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.


Stap 2: Importeer de Scene Class

De Scene-klasse is de top-level container voor alle 3D-gegevens. Importeer deze samen met alle laadoptieklassen die u nodig heeft.

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

Alle openbare klassen bevinden zich onder aspose.threed of in de sub‑pakketten daarvan (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).


Stap 3: Een bestand laden

Gebruik de statische Scene.from_file()‑methode om elk ondersteund formaat te openen. De bibliotheek detecteert het formaat automatisch op basis van de bestandsextensie.

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

Maak eventueel een Scene-instantie aan en roep open() aan; handig wanneer je laadopties wilt doorgeven of fouten expliciet wilt afhandelen:

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

Beide methoden ondersteunen OBJ, STL (binair en ASCII), glTF 2.0 / GLB, COLLADA (DAE) en 3MF‑bestanden.


Stap 4: Doorloop scèneknopen

Een geladen scène is een boom van Node objecten met wortel scene.root_node. Itereer recursief om alle knooppunten te vinden:

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)

Elke Node kan nul of meer Entity objecten (meshes, camera’s, lichten) dragen. Controleer node.entities om te zien wat er is bevestigd.


Stap 5: Toegang tot Vertex‑ en Polygon‑gegevens

Cast een node’s entiteit naar Mesh en lees de controlepunten (vertexposities) en polygonen (vlakindexlijsten):

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 is een lijst van Vector4 objecten; x, y, z dragen de positie en w is de homogene coördinaat (normaal 1.0).

mesh.polygons is een lijst van lijsten van gehele getallen, waarbij elke interne lijst de geordende set van controlepunt‑indices voor één vlak is.


Stap 6: Toepassen van formaat‑specifieke laadopties

Voor fijnmazige controle over hoe een OBJ‑bestand wordt geïnterpreteerd, geef een ObjLoadOptions‑instantie door aan 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)

Voor STL‑bestanden is de equivalente klasse StlLoadOptions. Voor glTF gebruik je GltfLoadOptions. Zie de API‑referentie voor een volledige lijst.


Veelvoorkomende problemen en oplossingen

FileNotFoundError bij het aanroepen van Scene.from_file()

Het pad moet absoluut zijn of correct relatief ten opzichte van de werkmap tijdens runtime. Gebruik pathlib.Path om betrouwbare paden te bouwen:

from pathlib import Path
from aspose.threed import Scene

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

mesh.polygons is leeg na het laden van een STL‑bestand

STL-bestanden slaan driehoeken op als ruwe facetten, niet als een geïndexeerde mesh. Na het laden worden polygonen gesynthetiseerd uit die facetten. Als polygons leeg lijkt, controleer len(mesh.control_points); als het aantal een veelvoud van 3 is, wordt de geometrie opgeslagen in niet-geïndexeerde vorm en vormt elk opeenvolgend drietal vertices één driehoek.

Coördinatensysteem mismatch (model lijkt gedraaid of gespiegeld)

Verschillende tools gebruiken verschillende conventies (Y‑up vs Z‑up, linkshandig vs rechtshandig). Stel ObjLoadOptions.flip_coordinate_system = True in of pas een rotatie toe op de Transform van de rootnode na het laden.

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

De entiteitslijst van een knoop kan niet‑mesh‑entiteiten bevatten (camera’s, lampen). Bescherm altijd met isinstance(entity, Mesh) voordat je cast.


Veelgestelde vragen (FAQ)

Welke 3D-formaten kan ik laden?

OBJ (Wavefront), STL (binair en ASCII), glTF 2.0 / GLB, COLLADA (DAE) en 3MF. FBX-bestandstokenisatie wordt gedeeltelijk ondersteund, maar volledige parsing is nog niet voltooid.

Laadt het laden van een OBJ‑bestand ook het .mtl‑materiaal?

Ja, wanneer ObjLoadOptions.enable_materials = True (de standaard). De bibliotheek zoekt het .mtl‑bestand in dezelfde map als het .obj‑bestand. Als de .mtl ontbreekt, wordt de geometrie nog steeds geladen en wordt er een waarschuwing uitgegeven.

Kan ik een bestand laden vanuit een byte‑stream in plaats van een pad?

Ja. scene.open() accepteert elk bestand‑achtig object met een .read()‑methode naast een bestandspad‑string. Geef een geopende binaire stream (bijv. io.BytesIO) direct door. Scene.from_file() accepteert alleen een bestandspad‑string.

Hoe krijg ik oppervlaknormaalvectoren?

Na het laden, controleer mesh.get_element(VertexElementType.NORMAL). Dit retourneert een VertexElementNormal waarvan de data‑lijst één normaalvector per referentie bevat, gemapt volgens mapping_mode en reference_mode.

from aspose.threed.entities import Mesh, VertexElementType

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

Is de bibliotheek thread-safe voor het gelijktijdig laden van meerdere bestanden?

Elk Scene-object is onafhankelijk. Het laden van afzonderlijke bestanden in afzonderlijke Scene-instanties vanuit afzonderlijke threads is veilig, zolang je geen enkel Scene over threads deelt zonder externe vergrendeling.

 Nederlands