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

Ei 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
    └── ...
ObjektiRooli
SceneYlätason säiliö. Sisältää root_node, asset_info, animation_clips, ja sub_scenes.
NodeNimetty puusolmu. Siinä on vanhempi, nolla tai useampi lapsi, nolla tai useampi entiteetti, ja paikallinen Transform.
EntityGeometria tai kohtausobjekti, joka on liitetty solmuun. Yleisiä entiteettityyppejä: Mesh, Camera, Light.
TransformPaikallisen 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))  # 0

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

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

Muotoon 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_node manuaalisen liittämisen sijaan. Se asettaa vanhempareferenssin automaattisesti ja on vähemmän virhealtinen.
  • Lue global_transform hierarkian 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_nodes tuottaa ennakoimattoman käyttäytymisen. Kerää solmut ensin, sitten muokkaa.
  • Ohjauspisteet käyttävät Vector4, ei Vector3. Aina anna w=1 tavallisia vertex-sijainteja varten; w=0 edustaa suuntavektoria (ei pistettä).
  • mesh.control_points palauttaa kopion. Se control_points ominaisuus palauttaa list(self._control_points) — palautettuun listaan lisääminen ei muuta verkkoa. Lisää aina mesh._control_points suoraan, kun rakennat geometriaa ohjelmallisesti. Tämä on tunnettu kirjaston rajoitus; julkista mutaatio-API:a ei vielä ole.

Yleisiä ongelmia

OngelmaRatkaisu
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 translationtransform.translation soveltaa paikallista siirtymää. Jos vanhempisolmulla on itse ei-identiteettitransformaatio, maailman sijainti voi poiketa. Tarkista global_transform.
Alisolmut puuttuvat jälkeen scene.save() / lataa uudelleenJotkut 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 NoneNimi on kirjainkoolla riippuvainen. Vahvista tarkka nimi­merkkijono, jota käytettiin luonti­ajankohtana.
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).
 Suomi