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-foss

Nie 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
    └── ...
ObjectRole
SceneKontajner najvyššej úrovne. Obsahuje root_node, asset_info, animation_clips a sub_scenes.
NodePomenovaný uzol stromu. Má rodiča, nula alebo viac potomkov, nula alebo viac entít a lokálny Transform.
EntityGeometria alebo objekt scény pripojený k uzlu. Bežné typy entít: Mesh, Camera, Light.
TransformLoká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))  # 0

Scene 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))     # 1

Alternatí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")    # STL

Pre 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_node namiesto manuálneho pripojenia. Automaticky nastaví referenciu na rodiča a je menej náchylné na chyby.
  • Prečítajte global_transform po 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_nodes spôsobí nepredvídateľné správanie. Najprv zozbierajte uzly, potom ich upravte.
  • Kontrolné body používajú Vector4, nie Vector3. Vždy odovzdajte w=1 pre bežné pozície vrcholov; w=0 predstavuje smerový vektor (nie bod).
  • mesh.control_points vracia kópiu. Vlastnosť control_points vracia list(self._control_points) — pridávanie do vráteného zoznamu neovplyvňuje mesh. Vždy pridávajte priamo do mesh._control_points pri programatickom vytváraní geometrie. Toto je známa obmedzenosť knižnice; verejné API na mutáciu ešte neexistuje.

Bežné problémy

IssueResolution
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 translationtransform.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() / reloadNiektoré 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 (0len(control_points) - 1).
Node.get_child(name) returns NoneNá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 orderchild_nodes vracia deti v poradí vkladania (v poradí, v akom bolo volané add_child_node / create_child_node).
 Slovenčina