Scenegraf
En scenegraf er den grundlæggende datamodel for Aspose.3D FOSS til Python. Hver 3D-fil, uanset om den er indlæst fra disk eller konstrueret i hukommelsen, repræsenteres som et træ af Node objekter med rod i Scene.root_node. Hvert knudepunkt kan indeholde underknuder og en eller flere Entity objekter (meshes, kameraer, lys). At forstå scenegrafen giver dig direkte adgang til geometrien, materialerne og de rumlige transformationer for hvert objekt i en scene.
Installation og opsætning
Installer biblioteket fra PyPI:
pip install aspose-3d-fossIngen native udvidelser, compilere eller ekstra systempakker er påkrævet. For fulde installationsinstruktioner, se den Installationsvejledning.
Oversigt: Koncepter for scenegraf
Scenegrafen i Aspose.3D FOSS følger et enkelt indeholdshierarki:
Scene
└── root_node (Node)
├── child_node_A (Node)
│ ├── entity: Mesh
│ └── transform: translation, rotation, scale
├── child_node_B (Node)
│ └── child_node_C (Node)
│ └── entity: Mesh
└── ...| Objekt | Rolle |
|---|---|
Scene | Topniveau-beholder. Indeholder root_node, asset_info, animation_clips, og sub_scenes. |
Node | Navngivet træknude. Har en forælder, nul eller flere børn, nul eller flere enheder, og en lokal Transform. |
Entity | Geometri eller sceneobjekt knyttet til en knude. Almindelige enhedstyper: Mesh, Camera, Light. |
Transform | Lokalrum position, rotation og skalering for en knude. Verdensrum-resultatet læses fra global_transform. |
Trin for trin: Bygning af en scenegraf programmatisk
Trin 1: Opret en scene
En ny Scene begynder altid med en tom root_node:
from aspose.threed import Scene
scene = Scene()
print(scene.root_node.name) # "" (empty string — root node has no name by default)
print(len(scene.root_node.child_nodes)) # 0Scene er indgangspunktet for alt: indlæsning af filer, gemning af filer, oprettelse af animationsklip og adgang til nodetræet.
Trin 2: Opret under‑noder
Brug create_child_node(name) til at tilføje navngivne noder til træet:
from aspose.threed import Scene
scene = Scene()
parent = scene.root_node.create_child_node("parent")
child = parent.create_child_node("child")
print(parent.name) # "parent"
print(child.parent_node.name) # "parent"
print(len(scene.root_node.child_nodes)) # 1Alternativt kan du oprette en selvstændig Node og vedhæft den eksplicit:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)Begge tilgange giver det samme resultat. create_child_node er mere kortfattet til inline‑konstruktion.
Trin 3: Opret en Mesh‑entity og vedhæft den
En Mesh gemmer vertexdata (control_points) og ansigtstopologi (polygons). Opret en, tilføj geometri, og vedhæft den derefter til en node:
from aspose.threed import Scene, Node
from aspose.threed.entities import Mesh
from aspose.threed.utilities import Vector3, Vector4
scene = Scene()
parent = scene.root_node.create_child_node("parent")
child = parent.create_child_node("child")
##Create a quad mesh (four vertices, one polygon)
# Note: control_points returns a copy of the internal list; append to
# _control_points directly to actually add vertices. This is a known
# library limitation — a public add_control_point() API is not yet available.
mesh = Mesh("cube")
mesh._control_points.append(Vector4(0, 0, 0, 1))
mesh._control_points.append(Vector4(1, 0, 0, 1))
mesh._control_points.append(Vector4(1, 1, 0, 1))
mesh._control_points.append(Vector4(0, 1, 0, 1))
mesh.create_polygon(0, 1, 2, 3)
child.add_entity(mesh)
print(f"Mesh name: {mesh.name}")
print(f"Vertex count: {len(mesh.control_points)}")
print(f"Polygon count: {mesh.polygon_count}")Vector4(x, y, z, w) repræsenterer en homogen koordinat. Brug w=1 til almindelige punktpositioner.
create_polygon(*indices) accepterer vertexindekser og registrerer ét ansigt i polygonlisten. Angiv tre indekser for en trekant, fire for en firkant.
Trin 4: Indstil node‑transformationer
Hver node har en Transform som styrer dens position, orientering og størrelse i lokalt rum:
from aspose.threed.utilities import Vector3, Quaternion
##Translate the node 2 units along the X axis
child.transform.translation = Vector3(2.0, 0.0, 0.0)
##Scale the node to half its natural size
child.transform.scaling = Vector3(0.5, 0.5, 0.5)
##Rotate 45 degrees around the Y axis using Euler angles
child.transform.euler_angles = Vector3(0.0, 45.0, 0.0)Transformationer er kumulative: et underordneds nodes verdensrumposition er sammensætningen af dens egen transformation med alle forældrenodes transformationer. Læs det evaluerede verdensrumresultat fra node.global_transform (uforanderlig, skrivebeskyttet).
Trin 5: Gå igennem scenegrafen rekursivt
Gå gennem hele træet ved at rekursivt gennemløbe node.child_nodes:
def traverse(node, depth=0):
indent = " " * depth
entity_type = type(node.entity).__name__ if node.entity else "None"
print(f"{indent}{node.name} [{entity_type}]")
for child in node.child_nodes:
traverse(child, depth + 1)
traverse(scene.root_node)Eksempeloutput for scenen oprettet ovenfor (rotnodens navn er en tom streng):
[None]
parent [None]
child [Mesh]For scener med flere enheder pr. node, iterer node.entities i stedet for node.entity:
def traverse_full(node, depth=0):
indent = " " * depth
entity_names = [type(e).__name__ for e in node.entities] or ["None"]
print(f"{indent}{node.name} [{', '.join(entity_names)}]")
for child in node.child_nodes:
traverse_full(child, depth + 1)
traverse_full(scene.root_node)Trin 6: Gem scenen
Videregiv en filsti til scene.save(). Formatet udledes af filendelsen:
scene.save("scene.gltf") # JSON glTF 2.0
scene.save("scene.glb") # Binary GLB container
scene.save("scene.obj") # Wavefront OBJ
scene.save("scene.stl") # STLFor format‑specifikke indstillinger, send et save‑options‑objekt som det andet argument:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)Tips og bedste praksis
- Navngiv hver node. At give noder meningsfulde navne gør fejlsøgning af traversal meget lettere og sikrer, at navne bevares i den eksporterede fil.
- Én mesh pr. node. At holde entiteter 1:1 med noder forenkler transformationer og kollisionsforespørgsler.
- Brug
create_child_nodei stedet for manuel tilknytning. Den sætter forældre‑referencen automatisk og er mindre fejlbehæftet. - Læs
global_transformefter opbygning af hierarkiet. Resultatet i verdensrum er kun stabilt, når alle forældre‑transformationer er indstillet. - Mutér ikke træet under traversal. Tilføjelse eller fjernelse af undernoder under iteration
child_nodesvil give uforudsigelig opførsel. Saml noder først, og modificér derefter. - Kontrolpunkter bruger
Vector4, ikkeVector3. Giv altid passw=1for almindelige vertexpositioner;w=0repræsenterer en retningsvektor (ikke et punkt). mesh.control_pointsreturnerer en kopi. Dencontrol_pointsegenskab returnererlist(self._control_points)— at tilføje til den returnerede liste ændrer ikke mesh’en. Tilføj altid tilmesh._control_pointsdirekte når du bygger geometri programmatisk. Dette er en kendt biblioteksbegrænsning; en offentlig mutations-API findes endnu ikke.
Almindelige problemer
| Problem | Løsning |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Beskyt med if node.entity is not None før du får adgang til entitetsegenskaber. En node uden entiteter har entity = None. |
Mesh vises ved origo på trods af indstillingen translation | transform.translation anvender en lokal forskydning. Hvis forældrenoden selv har en ikke-identitets-transformation, kan verdenspositionen afvige. Tjek global_transform. |
Underordnede noder mangler efter scene.save() / genindlæsning | Nogle formater (OBJ) flader hierarkiet ud. Brug glTF eller COLLADA for at bevare det fulde nodetræ. |
polygon_count er 0 efter mesh.create_polygon(...) | Bekræft at de vertex-indekser, der sendes til create_polygon er inden for intervallet (0 til len(control_points) - 1). |
Node.get_child(name) returnerer None | Navnet er case-sensitive. Bekræft den nøjagtige navnestreng, der blev brugt på oprettelsestidspunktet. |
| Traversal besøger noder i uventet rækkefølge | child_nodes returnerer børn i indsættelsesrækkefølge (rækkefølgen add_child_node / create_child_node blev kaldet). |