Scenos grafas
scene graph yra pagrindinis duomenų modelis Aspose.3D FOSS for Python. Kiekvienas 3D failas, nesvarbu, ar įkeliamas iš disko, ar sukuriamas atmintyje, yra atvaizduojamas kaip Node objektų medis, kurio šaknis yra Scene.root_node. Kiekvienas mazgas gali turėti vaikų mazgus ir vieną ar kelis Entity objektus (tinklus, kameras, šviesas). Suprasdami scene graph gausite tiesioginę prieigą prie geometrijos, medžiagų ir erdvinės transformacijos kiekvieno scenos objekto.
Įdiegimas ir nustatymas
Įdiekite biblioteką iš PyPI:
pip install aspose-3d-fossNereikia jokių natūralių plėtinių, kompiliatorių ar papildomų sistemos paketų. Daugiau diegimo instrukcijų rasite Įdiegimo vadovas.
Apžvalga: Scenos grafiko koncepcijos
Scenos grafikas Aspose.3D FOSS seka paprastą turinio hierarchiją:
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 | Viršutinio lygio konteineris. Laiko root_node, asset_info, animation_clips ir sub_scenes. |
Node | Pavadintas medžio mazgas. Turi tėvą, nulį arba daugiau vaikų, nulį arba daugiau objektų ir vietinį Transform. |
Entity | Geometrijos arba scenos objektas, prisegtas prie mazgo. Dažniausios objektų rūšys: Mesh, Camera, Light. |
Transform | Vietinės erdvės pozicija, sukimas ir mastelis mazgui. Pasaulinės erdvės rezultatas gaunamas iš global_transform. |
Žingsnis po žingsnio: Scenos grafiko kūrimas programiškai
Žingsnis 1: Sukurti sceną
Naujas Scene visada prasideda tuščiu 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 yra įėjimo taškas viskam: failų įkėlimui, failų išsaugojimui, animacijos klipų kūrimui ir mazgų medžio prieigai.
Žingsnis 2: sukurti vaikų mazgus
Naudokite create_child_node(name), kad pridėtumėte pavadintus mazgus į medį:
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)) # 1Alternatyviai sukurkite atskirą Node ir prisekite jį aiškiai:
from aspose.threed import Scene, Node
scene = Scene()
node = Node("standalone")
scene.root_node.add_child_node(node)Abu metodai duoda tą patį rezultatą. create_child_node yra glaustesnis įterptam konstrukcijai.
Žingsnis 3: Sukurkite tinklo objektą ir jį prijunkite
Mesh saugo viršūnių duomenis (control_points) ir veidų topologiją (polygons). Sukurkite vieną, pridėkite geometriją, tada prijunkite jį prie mazgo:
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) reiškia homogeninę koordinatę. Naudokite w=1 įprastoms taško pozicijoms.
create_polygon(*indices) priima vertex indices ir registruoja vieną face į polygon list. Pateikite tris indeksus triangle, keturis – quad.
Žingsnis 4: Nustatyti mazgo transformacijas
Kiekvienas mazgas turi Transform, kuris valdo jo padėtį, orientaciją ir dydį vietinėje erdvėje:
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)Transformacijos yra kumuliatyvios: vaiko mazgo pasaulio erdvės pozicija yra jo pačios transformacijos su visomis tėvų transformacijomis sudėtis. Perskaitykite įvertintą pasaulio erdvės rezultatą iš node.global_transform (nekintamas, tik skaitymui).
Žingsnis 5: Rekursyviai pereiti scenos grafiką
Eikite per visą medį rekursiškai per 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)Pavyzdinis išvesties rezultatas scenai, sukurtai aukščiau (šaknies mazgo pavadinimas yra tuščia eilutė):
[None]
parent [None]
child [Mesh]Scenoms, kuriose viename mazge yra keli objektai, kartokite node.entities vietoj 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)Žingsnis 6: Išsaugoti sceną
Pateikite failo kelią scene.save(). Formatas nustatomas pagal failo plėtinį:
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") # STLFormatui būdingoms parinktimoms perduokite save‑options objektą kaip antrąjį argumentą:
from aspose.threed.formats import GltfSaveOptions
opts = GltfSaveOptions()
scene.save("scene.gltf", opts)Patarimai ir geriausios praktikos
- 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.
Dažnos problemos
| Issue | Resolution |
|---|---|
AttributeError: 'NoneType' object has no attribute 'polygons' | Apsaugokite naudodami if node.entity is not None prieš prieigą prie objektų savybių. Mazgas be objektų turi entity = None. |
Mesh appears at the origin despite setting translation | transform.translation taiko vietinį poslinkį. Jei tėvo mazgas turi ne identišką transformaciją, pasaulinė padėtis gali skirtis. Patikrinkite global_transform. |
Child nodes missing after scene.save() / reload | Kai kurie formatai (OBJ) išlygina hierarchiją. Naudokite glTF arba COLLADA, kad išsaugotumėte visą mazgų medį. |
polygon_count is 0 after mesh.create_polygon(...) | Patikrinkite, ar create_polygon perduoti viršūnių indeksai yra diapazone (0 iki len(control_points) - 1). |
Node.get_child(name) returns None | Pavadinimas yra skiriamas didžiosiomis ir mažosiomis raidėmis. Patikrinkite tikslų pavadinimo eilutę, naudotą kūrimo metu. |
| Traversal visits nodes in unexpected order | child_nodes grąžina vaikus įterpimo tvarka (ta tvarka, kai buvo iškviesta add_child_node / create_child_node). |