Wie man einen 3D-Szenengraph in Python durchläuft
Die Szene Grafik in Aspose.3D FOSS ist ein Baum von Node Objekte, die auf der scene.root_node.Jede 3D-Datei, ob von OBJ, glTF, STL, COLLADA oder 3MF geladen, erzeugt die gleiche Baumstruktur. Wenn Sie wissen, wie man sie durchläuft, können Sie Geometrie überprüfen, Polygone zählen, Objekte nach Typ filtern und bestimmte Teile einer komplexen Szene verarbeiten.
Schritt für Schritte
Schritt 1: Installieren und Importieren
Installieren Sie Aspose.3D FOSS von PyPI:
asposefoss/3d is not yet published — build from source until it ships. See the project README for build instructions.
Importieren Sie die benötigten Klassen:
from aspose.threed import Scene
from aspose.threed.entities import MeshDie öffentlichen Klassen leben unter der aspose.threed oder deren Unterpakete (aspose.threed.entities, aspose.threed.utilities).
Schritt 2: Laden Sie eine Szene aus einer Datei ein
Verwenden Sie die statische Scene.from_file() Das Format wird automatisch von der Dateierweiterung erkannt:
scene = Scene.from_file("model.gltf")Sie können auch mit expliziten Ladeoptionen öffnen:
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.enable_materials = True
scene = Scene()
scene.open("model.obj", options)Nach dem Laden, scene.root_node ist die Wurzel des Baumes. Alle importierten Knoten sind Kinder oder Nachkommen dieses Knotes.
Schritt 3: Eine rekursive Durchlauffunktion schreiben
Die einfachste Durchfahrt besucht jeden Knoten in der ersten Größenordnung:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def traverse(node, depth=0):
prefix = " " * depth
entity_name = type(node.entity).__name__ if node.entity else "-"
print(f"{prefix}[{entity_name}] {node.name}")
for child in node.child_nodes:
traverse(child, depth + 1)
scene = Scene.from_file("model.gltf")
traverse(scene.root_node)Beispiel:
[-]
[-] Armature
[Mesh] Body
[Mesh] Eyes
[-] Ground
[Mesh] Plane<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Code kopieren”
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
Anmerkung: Der Wurzelknoten hat einen leeren Namen (
""), so daß die erste Zeile zeigt, dass der[-]Ohne Namen.
node.child_nodes Kinder in der Reihenfolge des Einsatzes (die Reihung, in die sie vom Importeur oder dem Nutzer hinzugefügt wurden) zurückgegeben werden.
Schritt 4: Zugriff auf Entitätseigenschaften für jeden Knoten
Verwendung node.entity die erste Entität an einen Knoten anzubinden, oder node.entities Überprüfen Sie den Typ, bevor Sie auf formatspezifische Eigenschaften zugreifen:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def print_entity_details(node, depth=0):
indent = " " * depth
for entity in node.entities:
if isinstance(entity, Mesh):
mesh = entity
print(f"{indent}Mesh '{node.name}':")
print(f"{indent} vertices : {len(mesh.control_points)}")
print(f"{indent} polygons : {mesh.polygon_count}")
print(f"{indent} cast_shadows : {mesh.cast_shadows}")
print(f"{indent} receive_shadows: {mesh.receive_shadows}")
for child in node.child_nodes:
print_entity_details(child, depth + 1)
scene = Scene.from_file("model.gltf")
print_entity_details(scene.root_node)node.transform.translation, node.transform.rotation,und . node.transform.scaling Geben Sie die Lokalraum-Transformation. node.global_transform Das ausgewertete Weltraum-Ergebnis (mit global_transform.scale für den Weltraum-Skala).
Schritt 5: Filterknoten nach Entitätstyp
Um nur auf bestimmten Entitätstypen zu arbeiten, fügen Sie eine isinstance Schutz im Inneren des Durchgangs:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def find_mesh_nodes(node, results=None):
if results is None:
results = []
for entity in node.entities:
if isinstance(entity, Mesh):
results.append(node)
break # One match per node is enough
for child in node.child_nodes:
find_mesh_nodes(child, results)
return results
scene = Scene.from_file("model.gltf")
mesh_nodes = find_mesh_nodes(scene.root_node)
print(f"Found {len(mesh_nodes)} mesh node(s)")
for n in mesh_nodes:
print(f" {n.name}")Dieses Muster ist nützlich für Massenoperationen: die Anwendung einer Transformation auf jeden Mesh-Knoten, das Ersetzen von Materialien oder der Export von Unterbäumen.
Schritt 6: Sammeln Sie alle Meshes und Drucken Sie die Vertex-Zählungen
Aggregate Maschenstatistiken in einem einzigen Durchgang:
from aspose.threed import Scene
from aspose.threed.entities import Mesh
def collect_meshes(node, results=None):
if results is None:
results = []
if isinstance(node.entity, Mesh):
results.append((node.name, node.entity))
for child in node.child_nodes:
collect_meshes(child, results)
return results
scene = Scene.from_file("model.gltf")
meshes = collect_meshes(scene.root_node)
print(f"Total meshes: {len(meshes)}")
total_verts = 0
total_polys = 0
for name, mesh in meshes:
verts = len(mesh.control_points)
polys = mesh.polygon_count
total_verts += verts
total_polys += polys
print(f" {name}: {verts} vertices, {polys} polygons")
print(f"Scene totals: {total_verts} vertices, {total_polys} polygons")Häufige Probleme
| Ausgabe | Entschließung |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Bewachtungs- und Überwachungsanlagen if node.entity is not None oder isinstance(node.entity, Mesh) Knoten ohne Entitäten kehren zurück. None. |
| Durchfahrt stoppt frühzeitig | Sicherstellen, dass die Rekursion in den node.child_nodes.Wenn Sie nur wiederholen , scene.root_node.child_nodes (nicht rekursiv) verpasst man alle Nachkommen. |
| Aus den erhobenen Ergebnissen fehlende Netzmaschen | Überprüfen Sie, ob das Dateifort der Hierarchie entspricht. OBJ flacht alle Geometrie in eine einzige Knotenstufe ein. Verwenden glTF oder COLLADA für tiefe Hierarchien. |
node.entity gibt nur die erste Entität zurück. | Verwendung node.entities (die vollständige Liste) wenn ein Knoten mehrere Entitäten trägt. node.entity ist eine Abkürzung für: node.entities[0] - Wann? entities ist nicht leer. |
collect_meshes gibt 0 Ergebnisse für eine geladene STL zurück. | STL-Dateien erzeugen typischerweise einen einzigen flachen Knoten mit einer Mascheneinheit direkt unter root_node.- Die Scheckkarte . root_node.child_nodes[0].entity direkt. Ich bin nicht hier. |
| Knotennamen sind leere Zeichenketten . | Einige Formate (binäre STL, einige OBJ-Dateien) speichern keine Objektnamen. Knoten haben leere name Strings; stattdessen den Index zur Identifizierung verwenden. |
Häufig gestellte Fragen
Wie tief kann ein Szenengraph sein?
Es gibt keine harte Grenze. Die Standard-Rekursionsgrenze von Python (1000 Frames) gilt für rekursive Durchlauffunktionen. Für sehr tiefe Hierarchien konvertieren Sie die Rekursion in einen expliziten Stack:
from collections import deque
from aspose.threed import Scene
scene = Scene.from_file("deep.gltf")
queue = deque([(scene.root_node, 0)])
while queue:
node, depth = queue.popleft()
print(" " * depth + node.name)
for child in node.child_nodes:
queue.append((child, depth + 1))Kann ich den Baum während der Durchfahrt verändern?
Hinzufügen oder Entfernen von Knoten aus der child_nodes Sammeln Sie die Knoten, um in einem ersten Pass zu ändern und dann Änderungen im zweiten Pass anwenden.
Wie finde ich einen bestimmten Knoten nach Namen?
Verwendung node.get_child(name) Für eine tiefe Suche durchqueren Sie den Baum mit einem Namensfilter:
def find_by_name(root, name):
if root.name == name:
return root
for child in root.child_nodes:
result = find_by_name(child, name)
if result:
return result
return None
target = find_by_name(scene.root_node, "Wheel_FL")Ist es so ? node.entity Gibst du immer einen Mesh zurück?
Nein. Ein Knoten kann jeden Entitätstyp aufnehmen: Mesh, Camera, Light, oder Zollstellen. isinstance(node.entity, Mesh) vor der Verwendung von maschenspezifischen Eigenschaften.
Wie bekomme ich die Weltraumposition eines Knoten?
Lesen Sie . node.global_transform.translation. Dies ist die bewertete Position im Weltraum, wobei alle Vorfahren-Transformationen berücksichtigt werden. Es ist nur für den Leser verfügbar; modifizieren node.transform.translation Um den Knoten neu zu positionieren.
Kann ich die Gesamtzahl der Polygone in einer Szene zählen, ohne eine Durchlaufschrift zu schreiben?
Nicht direkt über die API; es gibt keine scene.total_polygon_count Eigentum und Nutzung. collect_meshes und Summe mesh.polygon_count Die Ergebnisse werden in Schritt 6 dargestellt.