Ainas grafiks

Scene graph ir pamata datu modelis Aspose.3D FOSS for Python. Katrs 3D fails, neatkarīgi no tā, vai tas ir ielādēts no diska vai izveidots atmiņā, tiek attēlots kā koks no Node objektiem, kura sakne ir Scene.root_node. Katrs mezgls var saturēt bērna mezglus un vienu vai vairākus Entity objektus (tīkli, kameras, gaismas). Izpratne par scene graph ļauj tieši piekļūt ģeometrijai, materiāliem un telpiskajiem transformācijas datiem katram objektam ainas vidū.

Instalēšana un iestatīšana

Instalējiet bibliotēku no PyPI:

pip install aspose-3d-foss

Nav nepieciešami iebūvēti paplašinājumi, kompilatori vai papildu sistēmas pakotnes. Lai iegūtu pilnas instalācijas instrukcijas, skatiet Installation Guide.


Pārskats: Scēnas grafika koncepcijas

Scēnas grafiks Aspose.3D FOSS seko vienkāršai ietilpības hierarhijai:

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
SceneAugstākā līmeņa konteineris. Satur root_node, asset_info, animation_clips un sub_scenes.
NodeNosaukts koka mezgls. Ir vecāks, nulle vai vairāk bērnu, nulle vai vairāk vienību un lokāls Transform.
EntityĢeometrijas vai ainas objekts, kas pievienots mezglam. Biežāk sastopamie vienību veidi: Mesh, Camera, Light.
TransformLokālās telpas pozīcija, rotācija un mērogs mezglam. Pasaules telpas rezultāts tiek nolasīts no global_transform.

Soli pa solim: Scēnas grafika izveide programmatiski

1. solis: Izveidot ainu

Jauns Scene vienmēr sākas ar tukšu 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 ir ieejas punkts visam: failu ielāde, failu saglabāšana, animācijas klipu izveide un piekļuve mezglu kokam.


2. solis: Izveidot bērna mezglus

Izmantojiet create_child_node(name), lai pievienotu nosauktus mezglus koka struktūrai:

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īvi izveidojiet neatkarīgu Node un pievienojiet to skaidri:

from aspose.threed import Scene, Node

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

Abas pieejas dod vienādu rezultātu. create_child_node ir kodolīgāka inline konstrukcijai.


3. solis: Izveidot režģa vienību un pievienot to

Mesh glabā virsotņu datus (control_points) un seju topoloģiju (polygons). Izveidojiet vienu, pievienojiet ģeometriju, pēc tam pievienojiet to mezglam:

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) apzīmē homogēno koordinātu. Izmantojiet w=1 regulāriem punktu pozīcijām.

create_polygon(*indices) pieņem virsotnes indeksus un reģistrē vienu seju daudzstūra sarakstā. Nododiet trīs indeksus trīsstūrim, četrus – četrstūrim.


4. solis: Iestatīt mezgla transformācijas

Katram mezglam ir Transform, kas kontrolē tā pozīciju, orientāciju un izmēru lokālajā telpā:

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ācijas ir kumulatīvas: bērna mezgla pasaules telpas pozīcija ir tās pašas transformācijas un visām priekšgājējām transformācijām kompozīcija. Nolasiet novērtēto pasaules telpas rezultātu no node.global_transform (nemaināms, tikai lasāms).


5. solis: Rekursīvi pārlūkot ainas grafu

Pārlūkojiet visu koku, rekursīvi caur 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)

Piemēra izvade augstāk izveidotajai ainai (saknes mezgla nosaukums ir tukšs):

 [None]
  parent [None]
    child [Mesh]

Scenās, kur katram mezglam ir vairākas vienības, iterējiet node.entities nevis 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)

6. solis: Saglabāt ainu

Padodiet faila ceļu uz scene.save(). Formāts tiek noteikts no faila paplašinājuma:

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

Lai norādītu formāta specifiskās opcijas, kā otro argumentu nododiet saglabāšanas opciju objektu:

from aspose.threed.formats import GltfSaveOptions

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

Padomi un labākās prakses

  • Name every node. Giving nodes meaningful names makes debugging traversals far easier and ensures names are preserved in the exported file.
  • One mesh per node. Keeping entities 1:1 with nodes simplifies transforms and collision queries.
  • Use create_child_node over manual attachment. It sets the parent reference automatically and is less error‑prone.
  • Read global_transform after building the hierarchy. The world‑space result is only stable once all ancestor transforms are set.
  • Do not mutate the tree during traversal. Adding or removing child nodes while iterating child_nodes will produce unpredictable behaviour. Collect nodes first, then modify.
  • Control points use Vector4, not Vector3. Always pass w=1 for ordinary vertex positions; w=0 represents a direction vector (not a point).
  • mesh.control_points returns a copy. The control_points property returns list(self._control_points) — appending to the returned list does not modify the mesh. Always append to mesh._control_points directly when building geometry programmatically. This is a known library limitation; a public mutation API does not yet exist.

Biežāk sastopamās problēmas

ProblēmaRisinājums
AttributeError: 'NoneType' object has no attribute 'polygons'Aizsargājiet ar if node.entity is not None pirms piekļūstat elementa īpašībām. Mezglam bez elementiem ir entity = None.
Mesh parādās izcelsnē, neskatoties uz translation iestatīšanutransform.translation piemēro lokālu nobīdi. Ja vecāka mezglam pašam ir neidentitātes transformācija, pasaules pozīcija var atšķirties. Pārbaudiet global_transform.
Bērnu mezgli trūkst pēc scene.save() / pārlādēšanasDaži formāti (OBJ) izlīdzina hierarhiju. Izmantojiet glTF vai COLLADA, lai saglabātu pilnu mezglu koku.
polygon_count ir 0 pēc mesh.create_polygon(...)Pārliecinieties, ka virsotnes indeksi, kas nodoti create_polygon, ir diapazonā (0 līdz len(control_points) - 1).
Node.get_child(name) atgriež NoneNosaukums ir reģistrjūtīgs. Apstipriniet precīzu nosaukuma virkni, kas izmantota izveides brīdī.
Traversēšana apmeklē mezglus negaidītā secībāchild_nodes atgriež bērnus ievietošanas secībā (secība, kādā tika izsaukts add_child_node / create_child_node).
 Latviešu