Как да заредим 3D модели в Python
Aspose.3D FOSS за Python предоставя прост API за отваряне на 3D файлове без никакви native зависимости. След зареждане на файл в Scene обект, можете да обхождате йерархията на възлите и да четете сурови геометрични данни за всеки mesh в сцената.
Ръководство стъпка по стъпка
Стъпка 1: Инсталирайте пакета
Инсталирайте Aspose.3D FOSS от PyPI. Не са необходими допълнителни системни библиотеки.
pip install aspose-3d-fossПоддържани версии на Python: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
Стъпка 2: Импортирайте класа Scene
Класът Scene class е контейнерът от най-високо ниво за всички 3D данни. Импортирайте го заедно с всички класове за опции за зареждане, от които се нуждаете.
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptionsВсички публични класове се намират под aspose.threed или неговите подпакети (aspose.threed.entities, aspose.threed.formats, aspose.threed.utilities).
Стъпка 3: Заредете файл
Използвайте статичния Scene.from_file() метод, за да отворите всеки поддържан формат. Библиотеката автоматично открива формата от разширението на файла.
##Automatic format detection
scene = Scene.from_file("model.obj")Алтернативно, създайте Scene инстанция и извикайте open(); полезно, когато искате да предадете опции за зареждане или да обработвате грешки изрично:
scene = Scene()
scene.open("model.obj")И двата метода поддържат файлове OBJ, STL (бинарен и ASCII), glTF 2.0 / GLB, COLLADA (DAE) и 3MF.
Стъпка 4: Обхождане на възлите в сцената
Заредената сцена е дърво от Node обекти, коренящи се в scene.root_node. Итерайте рекурсивно, за да намерите всички възли:
from aspose.threed import Scene, Node
scene = Scene.from_file("model.obj")
def walk(node: Node, depth: int = 0) -> None:
indent = " " * depth
print(f"{indent}Node: {node.name!r}")
for child in node.child_nodes:
walk(child, depth + 1)
walk(scene.root_node)Всеки Node може да носи нула или повече Entity обекти (мрежи, камери, светлини). Проверете node.entities за да видите какво е прикрепено.
Стъпка 5: Достъп до данните за върхове и полигони
Кастнете ентитета на възела към Mesh и прочетете неговите контролни точки (позиции на върховете) и полигони (списъци с индекси на лицата):
from aspose.threed import Scene
from aspose.threed.entities import Mesh
scene = Scene.from_file("model.obj")
for node in scene.root_node.child_nodes:
for entity in node.entities:
if isinstance(entity, Mesh):
mesh: Mesh = entity
print(f"Mesh '{node.name}': "
f"{len(mesh.control_points)} vertices, "
f"{len(mesh.polygons)} polygons")
# First vertex position
if mesh.control_points:
v = mesh.control_points[0]
print(f" First vertex: ({v.x:.4f}, {v.y:.4f}, {v.z:.4f})")
# First polygon face (list of control-point indices)
if mesh.polygons:
print(f" First polygon: {mesh.polygons[0]}")mesh.control_points е списък от Vector4 обекти; x, y, z носят позицията и w е хомогенната координата (обикновено 1.0).
mesh.polygons е списък от списъци от цели числа, където всеки вътрешен списък е подреденият набор от индекси на контролни точки за едно лице.
Стъпка 6: Прилагане на специфични за формата опции за зареждане
За фино контролиране на начина, по който се интерпретира OBJ файл, предайте ObjLoadOptions екземпляр на scene.open():
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions
options = ObjLoadOptions()
options.flip_coordinate_system = True # Convert right-hand Y-up to Z-up
options.scale = 0.01 # Convert centimetres to metres
options.enable_materials = True # Load .mtl material file
options.normalize_normal = True # Normalize all normals to unit length
scene = Scene()
scene.open("model.obj", options)За STL файлове еквивалентният клас е StlLoadOptions. За glTF използвайте GltfLoadOptions. Вижте справка за API за пълен списък.
Чести проблеми и решения
FileNotFoundError при извикване Scene.from_file()
Пътят трябва да бъде абсолютен или правилно относителен спрямо работната директория по време на изпълнение. Използвайте pathlib.Path за изграждане на надеждни пътища:
from pathlib import Path
from aspose.threed import Scene
path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))mesh.polygons е празен след зареждане на STL файл
STL файловете съхраняват триъгълници като необработени фасети, а не като индексирана мрежа. След зареждане полигоните се синтезират от тези фасети. Ако polygons изглежда празен, проверете len(mesh.control_points); ако броят е кратен на 3, геометрията се съхранява в неиндексирана форма и всяка последователна тройка върхове образува един триъгълник.
Несъответствие в координатната система (моделът изглежда завъртян или огледален)
Различните инструменти използват различни конвенции (Y‑up срещу Z‑up, лява ръка срещу дясна ръка). Задайте ObjLoadOptions.flip_coordinate_system = True или приложете завъртане към кореновия възел Transform след зареждане.
AttributeError: 'NoneType' object has no attribute 'polygons'
Списъкът с обекти на възел може да съдържа не‑мрежови обекти (камери, светлини). Винаги проверявайте с isinstance(entity, Mesh) преди преобразуване.
Често задавани въпроси (FAQ)
Кои 3D формати мога да зареждам?
OBJ (Wavefront), STL (двоичен и ASCII), glTF 2.0 / GLB, COLLADA (DAE) и 3MF. Токенизирането на FBX файлове се поддържа частично, но пълният парсинг все още не е завършен.
Зарежда ли зареждането на OBJ файл също и .mtl материал?
Да, когато ObjLoadOptions.enable_materials = True (по подразбиране). Библиотеката търси the .mtl файл в същата директория като .obj файл. Ако .mtl липсва, геометрията все още се зарежда и се издава предупреждение.
Мога ли да заредя файл от байтов поток вместо от път?
Да. scene.open() приема всеки обект, подобен на файл, с .read() метод в допълнение към низ, представляващ път към файл. Предайте отворен двоичен поток (например,., io.BytesIO) директно. Scene.from_file() приема само низ, представляващ път към файл.
Как да получа нормалите на повърхността?
След зареждане, проверете mesh.get_element(VertexElementType.NORMAL). Това връща VertexElementNormal чийто data списъкът съдържа един нормален вектор за всяка референция, съпоставен според mapping_mode и reference_mode.
from aspose.threed.entities import Mesh, VertexElementType
normals = mesh.get_element(VertexElementType.NORMAL)
if normals:
print(normals.data[0]) # First normal vectorДали библиотеката е безопасна за многопоточност при зареждане на множество файлове едновременно?
Всеки Scene обект е независим. Зареждането на отделни файлове в отделни Scene екземпляри от отделни нишки е безопасно, стига да не споделяте един Scene в различни нишки без външно заключване.