Graf scény
A scene graph je základný dátový model Aspose.3D FOSS pre Python. Každý 3D súbor, či už načítaný z disku alebo vytvorený v pamäti, je reprezentovaný ako strom objektov Node s koreňom v Scene.root_node. Každý uzol môže obsahovať poduzly a jeden alebo viac objektov Entity (sieťové modely, kamery, svetlá). Pochopenie scene graph vám poskytuje priamy prístup k geometrii, materiálom a priestorovým transformáciám každého objektu v scéne.
Inštalácia a nastavenie
Nainštalujte knižnicu z PyPI:
pip install aspose-3d-fossNie sú potrebné žiadne natívne rozšírenia, kompilátory ani ďalšie systémové balíky. Pre úplné inštrukcie inštalácie si pozrite Inštalačná príručka.
Prehľad: Koncepty grafu scény
Graf scény v Aspose.3D FOSS nasleduje jednoduchú hierarchiu kontajnerov:
Scene
└── root_node (Node)
├── child_node_A (Node)
│ ├── entity: Mesh
│ └── transform: translation, rotation, scale
├── child_node_B (Node)
│ └── child_node_C (Node)
│ └── entity: Mesh
└── ...| Object | Role |
|---|---|
Scene | Kontajner najvyššej úrovne. Obsahuje root_node, asset_info, animation_clips a sub_scenes. |
Node | Pomenovaný uzol stromu. Má rodiča, nula alebo viac potomkov, nula alebo viac entít a lokálny Transform. |
Entity | Geometria alebo objekt scény pripojený k uzlu. Bežné typy entít: Mesh, Camera, Light. |
Transform | Lokálna pozícia, rotácia a mierka v priestore pre uzol. Výsledok vo svetovom priestore sa číta z global_transform. |
Krok za krokom: Programové vytváranie grafu scény
Krok 1: Vytvoriť scénu
Nový Scene vždy začína prázdnym 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 je vstupným bodom pre všetko: načítavanie súborov, ukladanie súborov, vytváranie animačných klipov a prístup k stromu uzlov.
Krok 2: Vytvoriť podriadené uzly
Použite create_child_node(name) na pridanie pomenovaných uzlov do stromu:
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)) # 1Alternatívne vytvorte samostatný Node a pripojte ho explicitne:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)Oba prístupy produkujú rovnaký výsledok. create_child_node je stručnejší pre inline konštrukciu.
Krok 3: Vytvorte Mesh entitu a pripojte ju
Mesh uchováva dáta vrcholov (control_points) a topológiu plôch (polygons). Vytvorte jeden, pridajte geometriu a potom ho pripojte k uzlu:
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) predstavuje homogénnu súradnicu. Použite w=1 pre bežné pozície bodov.
create_polygon(*indices) akceptuje indexy vrcholov a zaregistruje jednu tvár v zozname polygonov. Zadajte tri indexy pre trojuholník, štyri pre štvoruholník.
Krok 4: Nastaviť transformácie uzlov
Každý uzol má Transform, ktorý riadi jeho polohu, orientáciu a veľkosť v lokálnom priestore:
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)Transformácie sú kumulatívne: pozícia uzla v svetovom priestore je zložením jeho vlastnej transformácie so všetkými transformáciami predkov. Prečítajte vyhodnotený výsledok v svetovom priestore z node.global_transform (nemenný, len na čítanie).
Krok 5: Rekurzívne prechádzanie grafu scény
Prejdite celý strom rekurzívnym prechádzaním cez 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)Príklad výstupu pre vyššie zostavenú scénu (názov koreňového uzla je prázdny reťazec):
[None]
parent [None]
child [Mesh]Pre scény s viacerými entitami na uzol iterujte node.entities namiesto 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)Krok 6: Uložiť scénu
Zadajte cestu k súboru do scene.save(). Formát sa odvodí z prípony súboru:
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") # STLPre možnosti špecifické pre formát odovzdajte objekt save-options ako druhý argument:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)Tipy a osvedčené postupy
- Pomenujte každý uzol. Dávať uzlom zmysluplné názvy uľahčuje ladenie prechádzok a zabezpečuje, že názvy zostanú zachované v exportovanom súbore.
- Jeden mesh na uzol. Udržiavanie entít 1:1 s uzlami zjednodušuje transformácie a dotazy na kolízie.
- Použite
create_child_nodenamiesto manuálneho pripojenia. Automaticky nastaví referenciu na rodiča a je menej náchylné na chyby. - Prečítajte
global_transformpo zostavení hierarchie. Výsledok vo svetovom priestore je stabilný až po nastavení všetkých transformácií predkov. - Nemeníte strom počas prechádzky. Pridávanie alebo odstraňovanie poduzlov počas iterácie
child_nodesspôsobí nepredvídateľné správanie. Najprv zozbierajte uzly, potom ich upravte. - Kontrolné body používajú
Vector4, nieVector3. Vždy odovzdajtew=1pre bežné pozície vrcholov;w=0predstavuje smerový vektor (nie bod). mesh.control_pointsvracia kópiu. Vlastnosťcontrol_pointsvracialist(self._control_points)— pridávanie do vráteného zoznamu neovplyvňuje mesh. Vždy pridávajte priamo domesh._control_pointspri programatickom vytváraní geometrie. Toto je známa obmedzenosť knižnice; verejné API na mutáciu ešte neexistuje.
Bežné problémy
| Issue | Resolution |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Ochránte pomocou if node.entity is not None pred prístupom k vlastnostiam entity. Uzol bez entít má entity = None. |
Mesh appears at the origin despite setting translation | transform.translation aplikuje lokálny posun. Ak má rodičovský uzol sám neidentickú transformáciu, svetová pozícia sa môže líšiť. Skontrolujte global_transform. |
Child nodes missing after scene.save() / reload | Niektoré formáty (OBJ) sploštia hierarchiu. Použite glTF alebo COLLADA na zachovanie celej stromovej štruktúry uzlov. |
polygon_count is 0 after mesh.create_polygon(...) | Overte, že indexy vrcholov odovzdané do create_polygon sú v rozsahu (0 až len(control_points) - 1). |
Node.get_child(name) returns None | Názov je citlivý na veľkosť písmen. Potvrďte presný reťazec názvu použitý pri vytváraní. |
| Traversal visits nodes in unexpected order | child_nodes vracia deti v poradí vkladania (v poradí, v akom bolo volané add_child_node / create_child_node). |