Graf scene

A scene graph je osnovni model podataka Aspose.3D FOSS for Python. Svaki 3D fajl, bilo da je učitan sa diska ili konstruisan u memoriji, predstavlja se kao stablo Node objekata čiji je koren Scene.root_node. Svaki čvor može da sadrži podčvorove i jedan ili više Entity objekata (meshes, cameras, lights). Razumevanje scene graph‑a daje vam direktan pristup geometriji, materijalima i prostornim transformacijama svakog objekta u sceni.

Instalacija i podešavanje

Instalirajte biblioteku sa PyPI:

pip install aspose-3d-foss

Nisu potrebna nikakva izvorna proširenja, kompajleri ili dodatni sistemski paketi. Za potpune upute za instalaciju, pogledajte Vodič za instalaciju.


Pregled: Koncepti grafova scene

Graf scena u Aspose.3D FOSS prati jednostavnu hijerarhiju sadržavanja:

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
SceneKontejner najvišeg nivoa. Sadrži root_node, asset_info, animation_clips i sub_scenes.
NodeNazvani čvor stabla. Ima roditelja, nula ili više dece, nula ili više entiteta i lokalni Transform.
EntityGeometrijski ili scenični objekat prikačen čvoru. Uobičajeni tipovi entiteta: Mesh, Camera, Light.
TransformLokalni položaj, rotacija i skala u prostoru za čvor. Rezultat u svetskom prostoru se čita iz global_transform.

Korak po korak: Programatsko izgradnja Scene Graph-a

Korak 1: Kreirajte scenu

Novi Scene uvek počinje praznim 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 ulazna tačka za sve: učitavanje fajlova, čuvanje fajlova, kreiranje animacionih klipova i pristupanje čvorovnom stablu.


Korak 2: Kreiranje podčvorova

Koristite create_child_node(name) da biste dodali imenovane čvorove u stablo:

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

Alternativno, kreirajte samostalni Node i priložite ga eksplicitno:

from aspose.threed import Scene, Node

scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)

Oba pristupa daju isti rezultat. create_child_node je koncizniji za inline konstrukciju.


Korak 3: Kreirajte Mesh Entity i prikačite ga

Mesh čuva podatke o vrhovima (control_points) i topologiju površina (polygons). Kreirajte jedan, dodajte geometriju, a zatim ga prikačite na čvor:

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) predstavlja homogenu koordinatu. Koristite w=1 za regularne položaje tačaka.

create_polygon(*indices) prihvata indekse vrhova i registruje jedno lice u listi poligona. Prosledite tri indeksa za trougao, četiri za četvorougao.


Korak 4: Postavljanje Node Transforms

Svaki čvor ima Transform koji kontroliše njegov položaj, orijentaciju i veličinu u lokalnom prostoru:

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)

Transformacije su kumulativne: pozicija čvora u svetskom prostoru je sastav njegove sopstvene transformacije i svih transformacija pretka. Pročitajte izračunati rezultat u svetskom prostoru iz node.global_transform (nepromenljivo, samo za čitanje).


Korak 5: Rekurzivno pretraživanje grafika scene

Prođite kroz celo stablo rekurzivno kroz 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)

Primer izlaza za scenu izgrađenu iznad (ime korenskog čvora je prazan string):

 [None]
  parent [None]
    child [Mesh]

Za scene sa više entiteta po čvoru, iterirajte node.entities umesto 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)

Korak 6: Sačuvajte scenu

Prosledite putanju do scene.save(). Format se zaključuje iz ekstenzije fajla:

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

Za opcije specifične za format, prosledite objekat save-options kao drugi argument:

from aspose.threed.formats import GltfSaveOptions

opts = GltfSaveOptions()
scene.save("scene.gltf", opts)

Saveti i najbolje prakse

  • Imenujte svaki čvor. Davanje čvorovima smislenih imena olakšava otklanjanje grešaka pri prolazima i osigurava da se imena sačuvaju u izvezenom fajlu.
  • Jedan mesh po čvoru. Održavanje entiteta 1:1 sa čvorovima pojednostavljuje transformacije i upite o sudaru.
  • Koristite create_child_node umesto ručnog povezivanja. Automatski postavlja referencu na roditelja i manje je podložno greškama.
  • Pročitajte global_transform nakon izgradnje hijerarhije. Rezultat u svetskom prostoru je stabilan tek kada su postavljene sve transformacije pretka.
  • Ne menjajte stablo tokom prolaza. Dodavanje ili uklanjanje čvorova deteta tokom iteracije child_nodes će izazvati nepredvidivo ponašanje. Prvo sakupite čvorove, zatim izmenite.
  • Kontrolne tačke koriste Vector4, a ne Vector3. Uvek prosledite w=1 za obične pozicije vrhova; w=0 predstavlja vektorski smer (nije tačka).
  • mesh.control_points vraća kopiju. Svojstvo control_points vraća list(self._control_points) — dodavanje u vraćenu listu ne menja mesh. Uvek dodajte direktno u mesh._control_points prilikom programskog izgradnje geometrije. Ovo je poznato ograničenje biblioteke; javni API za mutacije još ne postoji.

Česti problemi

IssueResolution
AttributeError: 'NoneType' object has no attribute 'polygons'Zaštitite sa if node.entity is not None pre pristupa svojstvima entiteta. Čvor bez entiteta ima entity = None.
Mesh appears at the origin despite setting translationtransform.translation primenjuje lokalni pomeraj. Ako roditeljski čvor sam po sebi ima transformaciju koja nije identitet, svetska pozicija može biti drugačija. Proverite global_transform.
Child nodes missing after scene.save() / reloadNeki formati (OBJ) izravnavaju hijerarhiju. Koristite glTF ili COLLADA da biste sačuvali kompletno stablo čvorova.
polygon_count is 0 after mesh.create_polygon(...)Proverite da li su indeksi vrhova prosleđeni u create_polygon unutar opsega (0 do len(control_points) - 1).
Node.get_child(name) returns NoneIme je osetljivo na veličinu slova. Potvrdite tačan string imena koji je korišćen prilikom kreiranja.
Traversal visits nodes in unexpected orderchild_nodes vraća decu u redosledu umetanja (redosled u kome je add_child_node / create_child_node pozvan).
 Српски