Hoe 3D-modellen te laden in Python

Hoe 3D-modellen te laden in Python

Aspose.3D FOSS voor 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 knooppunt-hiërarchie lopen en ruwe geometriegegevens 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-klasse

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

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

Alle openbare klassen bevinden zich onder aspose.threed of zijn sub-packages (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).


Stap 3: Laad een bestand

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

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

Of maak een Scene instantie en roep aan open(); 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 Scene-knooppunten

Een geladen scène is een boom van Node objecten met als wortel scene.root_node. Loop 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 dragen Entity objecten (meshes, camera’s, lichten). Controleer node.entities om te zien wat er is gekoppeld.


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 bevatten de positie en w is de homogene coördinaat (normaal 1.0).

mesh.polygons is een lijst van lijsten met gehele getallen, waarbij elke binnenste lijst de geordende reeks van controlepuntindices 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 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 lijkt leeg, controleer len(mesh.control_points); als het aantal een veelvoud van 3 is, wordt de geometrie opgeslagen in niet-geïndexeerde vorm en vormt elke opeenvolgende drie vertices één driehoek.

Coördinatensysteem mismatch (model lijkt geroteerd of gespiegeld)

Verschillende tools gebruiken verschillende conventies (Y-up vs Z-up, left-hand vs right-hand). Stel ObjLoadOptions.flip_coordinate_system = True of pas een rotatie toe op de root node’s Transform na het laden.

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

De entiteitslijst van een node kan niet-mesh entiteiten bevatten (camera’s, lichten). Altijd controleren 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 de .mtl material?

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

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

Ja. scene.open() accepteert elk bestand-achtig object met een .read() methode naast een bestandspadstring. Geef een open binaire stream door (bijv., io.BytesIO) direct. Scene.from_file() accepteert alleen een bestandspadstring.

Hoe haal ik oppervlaktennormen op?

Na het laden, controleer mesh.get_element(VertexElementType.NORMAL). Dit retourneert een VertexElementNormal wiens data lijst bevat één normaalvector per referentie, 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?

Elke Scene object is onafhankelijk. Het laden van afzonderlijke bestanden in afzonderlijke Scene instanties van afzonderlijke threads zijn veilig zolang je niet een enkele Scene over threads zonder externe vergrendeling.

 Nederlands