Scene Graph
A kohtausgrafi on Aspose.3D FOSS:n Python perusdatamalli. Jokainen 3D‑tiedosto, ladattu levystä tai rakennettu muistissa, esitetään puuna, jossa on Node objekteja, joiden juuri on Scene.root_node. Jokainen solmu voi sisältää alisolmuja ja yhden tai useamman Entity objektia (meshes, cameras, lights). Kohtausgrafiikan ymmärtäminen antaa suoran pääsyn jokaisen objektin geometriaan, materiaaleihin ja tilallisiin muunnoksiin kohtauksessa.
Asennus ja käyttöönotto
Asenna kirjasto PyPI:stä:
pip install aspose-3d-fossEi vaadi natiiveja laajennuksia, kääntäjiä tai lisäjärjestelmäpaketteja. Täydelliset asennusohjeet löytyvät Asennusopas.
Yleiskatsaus: Kohtausgrafiikan käsitteet
Kohtausgrafiikka Aspose.3D FOSS:ssa noudattaa yksinkertaista sisällytyshierarkiaa:
Scene
└── root_node (Node)
├── child_node_A (Node)
│ ├── entity: Mesh
│ └── transform: translation, rotation, scale
├── child_node_B (Node)
│ └── child_node_C (Node)
│ └── entity: Mesh
└── ...| Objekti | Rooli |
|---|---|
Scene | Ylätason säiliö. Sisältää root_node, asset_info, animation_clips, ja sub_scenes. |
Node | Nimetty puusolmu. Siinä on vanhempi, nolla tai useampi lapsi, nolla tai useampi entiteetti, ja paikallinen Transform. |
Entity | Geometria tai kohtausobjekti, joka on liitetty solmuun. Yleisiä entiteettityyppejä: Mesh, Camera, Light. |
Transform | Paikallisen tilan sijainti, kierto ja skaalaus solmulle. Maailman tilan tulos luetaan global_transform. |
Vaihe vaiheelta: Kohtausgrafiikan rakentaminen ohjelmallisesti
Vaihe 1: Luo kohtaus
Uusi Scene alkaa aina tyhjällä 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 on kaiken sisäänkäynti: tiedostojen lataaminen, tiedostojen tallentaminen, animaatioleikkeiden luominen ja solmupuun käyttö.
Vaihe 2: Luo alisolmuja
Käytä create_child_node(name) lisätäksesi nimettyjä solmuja puuhun:
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)) # 1Vaihtoehtoisesti luo itsenäinen Node ja liitä se eksplisiittisesti:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)Molemmat lähestymistavat tuottavat saman tuloksen. create_child_node on tiiviimpi sisäkkäiseen rakenteeseen.
Vaihe 3: Luo Mesh‑entiteetti ja liitä se
A Mesh tallentaa vertex data (control_points) ja kasvojen topologia (polygons). Luo yksi, lisää geometria, ja liitä se solmuun:
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) edustaa homogeenista koordinaattia. Käytä w=1 säännöllisille pisteasennuksille.
create_polygon(*indices) Hyväksyy vertex indices ja rekisteröi yhden face polygon listassa. Anna kolme indeksiä trianglelle, neljä quadille.
Vaihe 4: Aseta solmun transformit
Jokaisella solmulla on Transform joka ohjaa sen sijaintia, suuntausta ja kokoa paikallisessa tilassa:
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)Transformit ovat kumulatiivisia: lapsisolmun maailmanavaruuden sijainti on sen oman transformin ja kaikkien esivanhempien transformien koostuma. Lue arvioitu maailmanavaruuden tulos kohteesta node.global_transform (muuttumaton, vain luku).
Vaihe 5: Kulje kohtausgraafia rekursiivisesti
Käy läpi koko puu rekursiolla 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)Esimerkkituloste yllä rakennetulle kohtaukselle (juurisolmun nimi on tyhjä merkkijono):
[None]
parent [None]
child [Mesh]Kun kohtauksissa on useita entiteettejä per solmu, iteroi node.entities sen sijaan 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)Vaihe 6: Tallenna kohtaus
Anna tiedostopolku scene.save(). Muoto päätellään tiedostopäätteestä:
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") # STLMuotoon liittyviä asetuksia varten, anna tallennusasetusobjekti toisena argumenttina:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)Vinkkejä ja parhaita käytäntöjä
- Nimeä jokainen solmu. Solmuille merkityksellisten nimien antaminen tekee läpikäyntien virheenkorjauksesta paljon helpompaa ja varmistaa, että nimet säilyvät viedyssä tiedostossa.
- Yksi verkko per solmu. Entiteettien pitäminen 1:1-suhteessa solmuihin yksinkertaistaa muunnoksia ja törmäyshakuja.
- Käytä
create_child_nodemanuaalisen liittämisen sijaan. Se asettaa vanhempareferenssin automaattisesti ja on vähemmän virhealtinen. - Lue
global_transformhierarkian rakentamisen jälkeen. Maailmankoordinaattien tulos on vakaa vasta, kun kaikki vanhempien muunnokset on asetettu. - Älä muuta puuta läpikäynnin aikana. Lapsisolmujen lisääminen tai poistaminen iteraation aikana
child_nodestuottaa ennakoimattoman käyttäytymisen. Kerää solmut ensin, sitten muokkaa. - Ohjauspisteet käyttävät
Vector4, eiVector3. Aina annaw=1tavallisia vertex-sijainteja varten;w=0edustaa suuntavektoria (ei pistettä). mesh.control_pointspalauttaa kopion. Secontrol_pointsominaisuus palauttaalist(self._control_points)— palautettuun listaan lisääminen ei muuta verkkoa. Lisää ainamesh._control_pointssuoraan, kun rakennat geometriaa ohjelmallisesti. Tämä on tunnettu kirjaston rajoitus; julkista mutaatio-API:a ei vielä ole.
Yleisiä ongelmia
| Ongelma | Ratkaisu |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Suojautua if node.entity is not None ennen kuin käytät entiteettien ominaisuuksia. Solmulla ilman entiteettejä on entity = None. |
Verkko ilmestyy origoon, vaikka asetetaan translation | transform.translation soveltaa paikallista siirtymää. Jos vanhempisolmulla on itse ei-identiteettitransformaatio, maailman sijainti voi poiketa. Tarkista global_transform. |
Alisolmut puuttuvat jälkeen scene.save() / lataa uudelleen | Jotkut formaatit (OBJ) litistävät hierarkian. Käytä glTF:ää tai COLLADA:a säilyttääksesi koko solmutason puun. |
polygon_count on 0 jälkeen mesh.create_polygon(...) | Vahvista, että vertex-indeksit, jotka on annettu create_polygon ovat sallitulla alueella (0 kohteeseen len(control_points) - 1). |
Node.get_child(name) palauttaa None | Nimi on kirjainkoolla riippuvainen. Vahvista tarkka nimimerkkijono, jota käytettiin luontiajankohtana. |
| Traversointi käy läpi solmut odottamattomassa järjestyksessä | child_nodes palauttaa lapset lisäysjärjestyksessä (järjestys add_child_node / create_child_node kutsuttiin). |