Sahne Grafiği

Bir scene graph, Aspose.3D FOSS for Python’ın temel veri modelidir. Diskten yüklenmiş ya da bellek içinde oluşturulmuş olsun, her 3D dosya Node nesnelerinden oluşan ve Scene.root_node köküne sahip bir ağaç olarak temsil edilir. Her düğüm, alt düğümler ve bir veya daha fazla Entity nesnesi (mesh’ler, kameralar, ışıklar) tutabilir. Scene graph‘ı anlamak, sahnedeki her nesnenin geometri, malzeme ve uzamsal dönüşümlerine doğrudan erişim sağlar.

Kurulum ve Yapılandırma

Kütüphaneyi PyPI’dan yükleyin:

pip install aspose-3d-foss

Yerel uzantılar, derleyiciler veya ek sistem paketleri gerekmez. Tam kurulum talimatları için, Installation Guide bölümüne bakın.


Genel Bakış: Sahne Grafiği Kavramları

Aspose.3D FOSS’taki sahne grafiği, basit bir kapsama hiyerarşisini izler:

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
SceneÜst düzey kapsayıcı. root_node, asset_info, animation_clips ve sub_scenes içerir.
NodeAdlandırılmış ağaç düğümü. Bir ebeveyni, sıfır veya daha fazla çocuğu, sıfır veya daha fazla varlığı ve yerel bir Transform vardır.
EntityBir düğüme bağlı geometri veya sahne nesnesi. Yaygın varlık türleri: Mesh, Camera, Light.
TransformBir düğüm için yerel uzay konumu, dönüş ve ölçek. Dünya uzayı sonucu global_transform‘den okunur.

Adım Adım: Programlı Olarak Bir Sahne Grafiği Oluşturma

Adım 1: Sahne Oluştur

Yeni bir Scene her zaman boş bir root_node ile başlar:

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 her şey için giriş noktasıdır: dosyaları yükleme, dosyaları kaydetme, animasyon klipleri oluşturma ve düğüm ağacına erişme.


Adım 2: Çocuk Düğümler Oluştur

Ağaçta adlandırılmış düğümler eklemek için create_child_node(name) kullanın:

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

Alternatif olarak, bağımsız bir Node oluşturun ve bunu açıkça ekleyin:

from aspose.threed import Scene, Node

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

Her iki yaklaşım da aynı sonucu üretir. create_child_node satır içi oluşturma için daha özlüdür.


Adım 3: Bir Mesh Varlığı Oluşturun ve Ekleyin

Bir Mesh, köşe verilerini (control_points) ve yüz topolojisini (polygons) depolar. Bir tane oluşturun, geometri ekleyin ve ardından bir düğüme ekleyin:

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) homojen bir koordinatı temsil eder. Normal nokta konumları için w=1 kullanın.

create_polygon(*indices) vertex indekslerini kabul eder ve çokgen listesine bir yüzey kaydeder. Bir üçgen için üç indeks, bir dörtgen için dört indeks gönderin.


Adım 4: Düğüm Dönüşümlerini Ayarla

Her düğümün, konumunu, yönelimini ve yerel uzaydaki boyutunu kontrol eden bir Transform vardır:

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)

Dönüşümler toplu olarak uygulanır: bir alt düğümün dünya‑uzayı konumu, kendi dönüşümü ile tüm üst düğüm dönüşümlerinin birleşimidir. Değerlendirilmiş dünya‑uzayı sonucunu node.global_transform‘den okuyun (değiştirilemez, yalnızca okunabilir).


Adım 5: Sahne Grafiğini Rekürsif Olarak Gezin

node.child_nodes üzerinden yineleme yaparak tüm ağacı dolaşın:

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)

Yukarıda oluşturulan sahne için örnek çıktı (kök düğüm adı boş bir dizedir):

 [None]
  parent [None]
    child [Mesh]

Bir düğüm başına birden fazla varlık içeren sahneler için, node.entities‘i node.entity yerine yineleyin:

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)

Adım 6: Sahneyi Kaydet

scene.save() öğesine bir dosya yolu gönderin. Biçim, dosya uzantısından çıkarılır:

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

Biçim‑özel seçenekler için, ikinci argüman olarak bir save-options nesnesi geçirin:

from aspose.threed.formats import GltfSaveOptions

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

İpuçları ve En İyi Uygulamalar

  • Her düğüme isim verin. Düğümlere anlamlı isimler vermek, gezinme hatalarını ayıklamayı çok daha kolaylaştırır ve isimlerin dışa aktarılan dosyada korunmasını sağlar.
  • Her düğümde bir ağ. Varlıkları düğümlerle 1:1 tutmak, dönüşümleri ve çarpışma sorgularını basitleştirir.
  • Manuel eklemenin yerine create_child_node kullanın. Bu, ebeveyn referansını otomatik olarak ayarlar ve daha az hata yapma olasılığı taşır.
  • Hiyerarşiyi oluşturduktan sonra global_transform okuyun. Dünya uzayı sonucu, tüm üst düğüm dönüşümleri ayarlandıktan sonra ancak kararlı olur.
  • Gezi sırasında ağacı değiştirmeyin. child_nodes üzerinde yineleme yaparken alt düğüm eklemek veya kaldırmak, öngörülemeyen davranışlara yol açar. Önce düğümleri toplayın, ardından değişiklik yapın.
  • Kontrol noktaları Vector4 kullanır, Vector3 değil. Normal köşe konumları için her zaman w=1 gönderin; w=0 bir yön vektörünü temsil eder (nokta değil).
  • mesh.control_points bir kopya döndürür. control_points özelliği list(self._control_points) döndürür — döndürülen listeye ekleme yapmak ağı modeli değiştirmez. Geometriyi programlı olarak oluştururken her zaman doğrudan mesh._control_points ekleyin. Bu, bilinen bir kütüphane sınırlamasıdır; henüz bir genel değişiklik API’si mevcut değildir.

Yaygın Sorunlar

SorunÇözüm
AttributeError: 'NoneType' object has no attribute 'polygons'if node.entity is not None ile varlık özelliklerine erişmeden önce koruma ekleyin. Varlığı olmayan bir düğüm entity = None içerir.
Mesh appears at the origin despite setting translationtransform.translation yerel bir offset uygular. Eğer üst düğümün kendisi kimlik olmayan bir dönüşüme sahipse, dünya konumu farklı olabilir. global_transform kontrol edin.
Child nodes missing after scene.save() / reloadBazı formatlar (OBJ) hiyerarşiyi düzleştirir. Tam düğüm ağacını korumak için glTF veya COLLADA kullanın.
polygon_count is 0 after mesh.create_polygon(...)mesh.create_polygon(...) sonrası polygon_count 0’dır. create_polygon‘ye geçirilen köşe indekslerinin (0 ile len(control_points) - 1 arasında) aralık içinde olduğundan emin olun.
Node.get_child(name) returns Noneİsim büyük/küçük harfe duyarlıdır. Oluşturma sırasında kullanılan tam isim dizesini doğrulayın.
Traversal visits nodes in unexpected orderchild_nodes çocukları ekleme sırasına göre döndürür (add_child_node / create_child_node çağrıldığı sırada).
 Türkçe