Kaip naršyti 3D scenos grafą Python

Kaip naršyti 3D scenos grafą Python

Scenos grafas Aspose.3D FOSS yra medžio Node objektų, kurių šaknis yra scene.root_node. Kiekvienas 3D failas, nesvarbu ar jis įkeltas iš OBJ, glTF, STL, COLLADA ar 3MF, sukuria tą pačią medžio struktūrą. Žinant, kaip jį naršyti, galite peržiūrėti geometriją, suskaičiuoti daugiakampius, filtruoti objektus pagal tipą ir apdoroti konkrečias sudėtingos scenos dalis.

Žingsnis po žingsnio vadovas

1 žingsnis: Įdiegti ir importuoti

Įdiekite Aspose.3D FOSS iš PyPI:

pip install aspose-3d-foss

Importuokite reikiamas klases:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

Visos viešosios klasės yra aspose.threed arba jos subpaketuose (aspose.threed.entities, aspose.threed.utilities).


2 žingsnis: Įkelti sceną iš failo

Naudokite statinį Scene.from_file() metodą atverti bet kurį palaikomą formatą. Formatą aptinka automatiškai pagal failo plėtinį:

scene = Scene.from_file("model.gltf")

Taip pat galite atidaryti su aiškiai nurodytais įkėlimo parametrais:

from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions

options = ObjLoadOptions()
options.enable_materials = True

scene = Scene()
scene.open("model.obj", options)

Įkėlus, scene.root_node yra medžio šaknis. Visi importuoti mazgai yra šio mazgo vaikai arba palikuonys.


3 žingsnis: Parašyti rekursinę perėjimo funkciją

Paprastiausias perėjimas aplanko kiekvieną mazgą gylio pirmumo tvarka:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

def traverse(node, depth=0):
    prefix = "  " * depth
    entity_name = type(node.entity).__name__ if node.entity else "-"
    print(f"{prefix}[{entity_name}] {node.name}")
    for child in node.child_nodes:
        traverse(child, depth + 1)

scene = Scene.from_file("model.gltf")
traverse(scene.root_node)

Pavyzdinis išvestis:

[-]
  [-] Armature
    [Mesh] Body
    [Mesh] Eyes
  [-] Ground
    [Mesh] Plane

<button class=“hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50” title=“Copy code”

<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>

Pastaba: Šakninis mazgas turi tuščią pavadinimą (""), todėl pirmoje eilutėje rodomas [-] be pavadinimo po jo.

node.child_nodes grąžina vaikus įterpimo tvarka (tvarka, kuria importuotojas arba naudotojas juos pridėjo).


4 žingsnis: Gauti entiteto savybes kiekviename mazge

Naudokite node.entity gauti pirmąjį mazgui priklausantį elementą, arba node.entities kad iteruotumėte visus. Patikrinkite tipą prieš prieinant prie formatui būdingų savybių:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

def print_entity_details(node, depth=0):
    indent = "  " * depth
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh = entity
            print(f"{indent}Mesh '{node.name}':")
            print(f"{indent}  vertices  : {len(mesh.control_points)}")
            print(f"{indent}  polygons  : {mesh.polygon_count}")
            print(f"{indent}  cast_shadows   : {mesh.cast_shadows}")
            print(f"{indent}  receive_shadows: {mesh.receive_shadows}")
    for child in node.child_nodes:
        print_entity_details(child, depth + 1)

scene = Scene.from_file("model.gltf")
print_entity_details(scene.root_node)

node.transform.translation, node.transform.rotation, ir node.transform.scaling suteikia vietinės erdvės transformaciją. node.global_transform suteikia įvertintą pasaulio erdvės rezultatą (su global_transform.scale pasaulio erdvės masteliui).


Žingsnis 5: Filtruoti mazgus pagal objekto tipą

Norėdami veikti tik su konkrečiais objektų tipais, pridėkite isinstance apsaugą traversalo viduje:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

def find_mesh_nodes(node, results=None):
    if results is None:
        results = []
    for entity in node.entities:
        if isinstance(entity, Mesh):
            results.append(node)
            break   # One match per node is enough
    for child in node.child_nodes:
        find_mesh_nodes(child, results)
    return results

scene = Scene.from_file("model.gltf")
mesh_nodes = find_mesh_nodes(scene.root_node)
print(f"Found {len(mesh_nodes)} mesh node(s)")
for n in mesh_nodes:
    print(f"  {n.name}")

Šis modelis naudingas masinėms operacijoms: taikyti transformaciją kiekvienam mesh mazgui, keisti medžiagas arba eksportuoti po-medžius.


Žingsnis 6: Surinkti visus mesh ir išspausdinti viršūnių skaičių

Sujunkite mesh statistiką vienu praeitimu:

from aspose.threed import Scene
from aspose.threed.entities import Mesh

def collect_meshes(node, results=None):
    if results is None:
        results = []
    if isinstance(node.entity, Mesh):
        results.append((node.name, node.entity))
    for child in node.child_nodes:
        collect_meshes(child, results)
    return results

scene = Scene.from_file("model.gltf")
meshes = collect_meshes(scene.root_node)

print(f"Total meshes: {len(meshes)}")
total_verts = 0
total_polys = 0

for name, mesh in meshes:
    verts = len(mesh.control_points)
    polys = mesh.polygon_count
    total_verts += verts
    total_polys += polys
    print(f"  {name}: {verts} vertices, {polys} polygons")

print(f"Scene totals: {total_verts} vertices, {total_polys} polygons")

Dažnos problemos

ProblemaSprendimas
AttributeError: 'NoneType' object has no attribute 'polygons'Apsauga su if node.entity is not None arba isinstance(node.entity, Mesh) prieš prieinant prie tinklelio savybių. Mazgai be objektų grąžina None.
Traversalas sustoja ankstiĮsitikinkite, kad rekursija pasiekia node.child_nodes. Jei iteruojate tik scene.root_node.child_nodes (ne rekursyviai), praleidžiate visus palikuonius.
Tinklelis trūksta surinktų rezultatųPatikrinkite, ar failo formatas išsaugo hierarchiją. OBJ supaprastina visą geometriją į vieną mazgo lygį. Naudokite glTF arba COLLADA gilioms hierarchijoms.
node.entity grąžina tik pirmą objektąNaudokite node.entities (pilnas sąrašas) kai mazgas turi kelis elementus. node.entity yra trumpinys, reiškiantis node.entities[0] kai entities nėra tuščias.
collect_meshes grąžina 0 rezultatų įkeltam STLSTL failai paprastai sukuria vieną plokščią mazgą su viena mesh entity tiesiai po juo root_node. Patikrinkite root_node.child_nodes[0].entity tiesiogiai.
Mazgo pavadinimai yra tuščios eilutėsKai kurie formatai (binary STL, kai kurie OBJ failai) neįrašo objektų pavadinimų. Mazgai turės tuščius name eilučių; vietoj to naudokite indeksą identifikavimui.

Dažnai užduodami klausimai

Kiek gili gali būti scenos grafas?

Nėra griežto apribojimo. Python numatytoji rekursijos riba (1000 kadrų) taikoma rekursiniams perėjimo funkcijoms. Labai gilioms hierarchijoms konvertuokite rekursiją į aiškų steką:

from collections import deque
from aspose.threed import Scene

scene = Scene.from_file("deep.gltf")
queue = deque([(scene.root_node, 0)])

while queue:
    node, depth = queue.popleft()
    print("  " * depth + node.name)
    for child in node.child_nodes:
        queue.append((child, depth + 1))

Ar galiu modifikuoti medį perėjimo metu?

Nedėkite ir nepašalinkite mazgų iš child_nodes kai juos iteruojate. Surinkite mazgus, kuriuos reikia modifikuoti, pirmame etape, o tada atlikite pakeitimus antrame etape.

Kaip rasti konkretų mazgą pagal pavadinimą?

Naudokite node.get_child(name) rasti tiesioginį vaiką pagal pavadinimą. Giliau ieškant, pereikite medį su pavadinimo filtru:

def find_by_name(root, name):
    if root.name == name:
        return root
    for child in root.child_nodes:
        result = find_by_name(child, name)
        if result:
            return result
    return None

target = find_by_name(scene.root_node, "Wheel_FL")

Ar node.entity visada grąžina Mesh?

Ne. Mazgas gali turėti bet kokio tipo objektą: Mesh, Camera, Light, arba pasirinktinius objektus. Visada patikrinkite su isinstance(node.entity, Mesh) prieš naudodami tik Mesh specifines savybes.

Kaip gauti mazgo poziciją pasaulinėje erdvėje?

Skaityti node.global_transform.translation. Tai yra apskaičiuota pozicija pasaulio erdvėje, atsižvelgiant į visus tėvų transformavimus. Ji yra tik skaityti; modifikuokite node.transform.translation perkelti mazgą.

Ar galiu suskaičiuoti bendrą daugiakampių skaičių scenoje be traversalo rašymo?

Ne tiesiogiai per API; nėra scene.total_polygon_count savybė. Naudokite collect_meshes ir sumuokite mesh.polygon_count per rezultatus, kaip parodyta 6 žingsnyje.

 Lietuvių