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-fossNav 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
└── ...| Object | Role |
|---|---|
Scene | Augstākā līmeņa konteineris. Satur root_node, asset_info, animation_clips un sub_scenes. |
Node | Nosaukts 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. |
Transform | Lokā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)) # 0Scene 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)) # 1Alternatī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") # STLLai 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_nodeover manual attachment. It sets the parent reference automatically and is less error‑prone. - Read
global_transformafter 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_nodeswill produce unpredictable behaviour. Collect nodes first, then modify. - Control points use
Vector4, notVector3. Always passw=1for ordinary vertex positions;w=0represents a direction vector (not a point). mesh.control_pointsreturns a copy. Thecontrol_pointsproperty returnslist(self._control_points)— appending to the returned list does not modify the mesh. Always append tomesh._control_pointsdirectly 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ēma | Risinā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īšanu | transform.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ēšanas | Daž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ž None | Nosaukums 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). |