Γράφημα Σκηνής

Α διάγραμμα σκηνής είναι το θεμελιώδες μοντέλο δεδομένων του Aspose.3D FOSS για Python. Κάθε αρχείο 3D, είτε φορτώνεται από δίσκο είτε κατασκευάζεται στη μνήμη, αναπαρίσταται ως δέντρο από Node αντικείμενα ριζωμένα στο Scene.root_node. Κάθε κόμβος μπορεί να περιέχει θυγατρικούς κόμβους και ένα ή περισσότερα Entity αντικείμενα (meshes, cameras, lights). Η κατανόηση του διαγράμματος σκηνής σας δίνει άμεση πρόσβαση στη γεωμετρία, τα υλικά και τους χωρικούς μετασχηματισμούς κάθε αντικειμένου σε μια σκηνή.

Εγκατάσταση και Ρύθμιση

Εγκαταστήστε τη βιβλιοθήκη από το PyPI:

pip install aspose-3d-foss

Δεν απαιτούνται εγγενείς επεκτάσεις, μεταγλωττιστές ή πρόσθετα πακέτα συστήματος. Για πλήρεις οδηγίες εγκατάστασης, δείτε το Οδηγός Εγκατάστασης.


Επισκόπηση: Έννοιες Γραφήματος Σκηνής

Το γράφημα σκηνής στο Aspose.3D FOSS ακολουθεί μια απλή ιεραρχία περιεχομένου:

Scene
└── root_node  (Node)
    ├── child_node_A  (Node)
    │   ├── entity: Mesh
    │   └── transform: translation, rotation, scale
    ├── child_node_B  (Node)
    │   └── child_node_C  (Node)
    │       └── entity: Mesh
    └── ...
ΑντικείμενοΡόλος
SceneΚοντέινερ ανώτερου επιπέδου. Περιέχει root_node, asset_info, animation_clips, και sub_scenes.
NodeΟνομαστικός κόμβος δέντρου. Έχει γονέα, μηδέν ή περισσότερα παιδιά, μηδέν ή περισσότερες οντότητες, και ένα τοπικό Transform.
EntityΓεωμετρία ή αντικείμενο σκηνής προσαρτημένο σε έναν κόμβο. Συνηθισμένοι τύποι οντοτήτων: Mesh, Camera, Light.
TransformΘέση, περιστροφή και κλίμακα σε τοπικό χώρο για έναν κόμβο. Το αποτέλεσμα σε παγκόσμιο χώρο διαβάζεται από global_transform.

Βήμα-βήμα: Δημιουργία Γραφήματος Σκηνής Προγραμματιστικά

Βήμα 1: Δημιουργία Σκηνής

Ένα νέο Scene πάντα ξεκινά με ένα κενό 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 είναι το σημείο εισόδου για τα πάντα: φόρτωση αρχείων, αποθήκευση αρχείων, δημιουργία κλιπ ανιματισμού και πρόσβαση στο δέντρο κόμβων.


Βήμα 2: Δημιουργία Υποκόμβων

Χρησιμοποιήστε create_child_node(name) για να προσθέσετε ονομασμένους κόμβους στο δέντρο:

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

Εναλλακτικά, δημιουργήστε ένα αυτόνομο Node και συνδέστε το ρητά:

from aspose.threed import Scene, Node

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

Και οι δύο προσεγγίσεις παράγουν το ίδιο αποτέλεσμα. create_child_node είναι πιο συνοπτικό για ενσωματωμένη κατασκευή.


Βήμα 3: Δημιουργία Οντότητας Mesh και Σύνδεσή της

Ένα Mesh αποθηκεύει δεδομένα κορυφών (control_points) και τοπολογία προσώπων (polygons). Δημιουργήστε ένα, προσθέστε γεωμετρία, και στη συνέχεια συνδέστε το σε έναν κόμβο:

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) αντιπροσωπεύει μια ομογενή συντεταγμένη. Χρησιμοποιήστε w=1 για κανονικές θέσεις σημείων.

create_polygon(*indices) δέχεται δείκτες κορυφών και καταχωρεί ένα πρόσωπο στη λίστα πολυγώνων. Περνάτε τρεις δείκτες για ένα τρίγωνο, τέσσερις για ένα τετράπλευρο.


Βήμα 4: Ορισμός Μετασχηματισμών Κόμβου

Κάθε κόμβος έχει ένα Transform που ελέγχει τη θέση, την προσανατολισμό και το μέγεθός του στον τοπικό χώρο:

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)

Οι μετασχηματισμοί είναι αθροιστικοί: η θέση του παιδικού κόμβου στον παγκόσμιο χώρο είναι η σύνθεση του δικού του μετασχηματισμού με όλους τους μετασχηματισμούς των προγόνων. Διαβάστε το αξιολογημένο αποτέλεσμα του παγκόσμιου χώρου από node.global_transform (αμετάβλητο, μόνο για ανάγνωση).


Βήμα 5: Διέλευση του Γραφήματος Σκηνής Αναδρομικά

Περιηγηθείτε σε όλο το δέντρο επαναλαμβάνοντας μέσω 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)

Παράδειγμα εξόδου για τη σκηνή που δημιουργήθηκε παραπάνω (το όνομα του ριζικού κόμβου είναι κενό string):

 [None]
  parent [None]
    child [Mesh]

Για σκηνές με πολλαπλές οντότητες ανά κόμβο, κάντε επανάληψη node.entities αντί για 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: Αποθήκευση της Σκηνής

Περάστε μια διαδρομή αρχείου στο scene.save(). Η μορφή προκύπτει από την επέκταση του αρχείου:

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

Για επιλογές ειδικές για τη μορφή, περάστε ένα αντικείμενο save-options ως δεύτερο όρισμα:

from aspose.threed.formats import GltfSaveOptions

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

Συμβουλές και Καλές Πρακτικές

  • Ονομάστε κάθε κόμβο. Η ανάθεση σημασιολογικών ονομάτων στους κόμβους καθιστά την αποσφαλμάτωση των διασχίσεων πολύ πιο εύκολη και διασφαλίζει ότι τα ονόματα διατηρούνται στο εξαγόμενο αρχείο.
  • Ένα πλέγμα ανά κόμβο. Η διατήρηση των οντοτήτων 1:1 με τους κόμβους απλοποιεί τις μετασχηματίσεις και τις ερωτήσεις σύγκρουσης.
  • Χρησιμοποιήστε create_child_node αντί για χειροκίνητη προσάρτηση. Ορίζει αυτόματα την αναφορά γονέα και είναι λιγότερο επιρρεπής σε σφάλματα.
  • Διαβάστε global_transform μετά την κατασκευή της ιεραρχίας. Το αποτέλεσμα στον παγκόσμιο χώρο είναι σταθερό μόνο όταν έχουν οριστεί όλες οι μετασχηματίσεις των προγόνων.
  • Μην τροποποιείτε το δέντρο κατά τη διάρκεια της διασχίσης. Η προσθήκη ή αφαίρεση παιδικών κόμβων κατά την επανάληψη child_nodes θα προκαλέσει απρόβλεπτη συμπεριφορά. Συλλέξτε πρώτα τους κόμβους, έπειτα τροποποιήστε.
  • Τα σημεία ελέγχου χρησιμοποιούν Vector4, όχι Vector3. Πάντα περάστε w=1 για κανονικές θέσεις κορυφών; w=0 αντιπροσωπεύει ένα διάνυσμα κατεύθυνσης (όχι σημείο).
  • mesh.control_points επιστρέφει ένα αντίγραφο. Το control_points η ιδιότητα επιστρέφει list(self._control_points) — η προσθήκη στη λίστα που επιστρέφεται δεν τροποποιεί το πλέγμα. Πάντα προσθέτετε στο mesh._control_points απευθείας όταν δημιουργείτε γεωμετρία προγραμματιστικά. Αυτό είναι γνωστό περιορισμό της βιβλιοθήκης· ένα δημόσιο API μεταβολής δεν υπάρχει ακόμη.

Κοινά Προβλήματα

ΠρόβλημαΕπίλυση
AttributeError: 'NoneType' object has no attribute 'polygons'Προστατέψτε με if node.entity is not None πριν προσπελάσετε τις ιδιότητες οντοτήτων. Ένας κόμβος χωρίς οντότητες έχει entity = None.
Το πλέγμα εμφανίζεται στην αρχή παρά το ότι έχει οριστεί translationtransform.translation εφαρμόζει τοπική μετατόπιση. Εάν ο γονικός κόμβος έχει μετασχηματισμό διαφορετικό από την ταυτότητα, η παγκόσμια θέση μπορεί να διαφέρει. Ελέγξτε global_transform.
Τα θυγατρικά κόμβοι λείπουν μετά scene.save() / επαναφόρτωσηΟρισμένες μορφές (OBJ) ισοπεδώνουν την ιεραρχία. Χρησιμοποιήστε glTF ή COLLADA για να διατηρήσετε το πλήρες δέντρο κόμβων.
polygon_count είναι 0 μετά mesh.create_polygon(...)Επαληθεύστε ότι οι δείκτες κορυφών που περνιούνται στο create_polygon είναι εντός εύρους (0 προς len(control_points) - 1).
Node.get_child(name) επιστρέφει NoneΤο όνομα είναι ευαίσθητο σε πεζά/κεφαλαία. Επιβεβαιώστε το ακριβές όνομα που χρησιμοποιήθηκε κατά τη δημιουργία.
Η διάσχιση επισκέπτεται κόμβους με απρόσμενη σειράchild_nodes επιστρέφει τα παιδιά με τη σειρά εισαγωγής (η σειρά add_child_node / create_child_node ονομάστηκε).
 Ελληνικά