Python에서 3D 모델 로드하는 방법
Aspose.3D FOSS for Python은 네이티브 종속성 없이 3D 파일을 열 수 있는 간단한 API를 제공합니다. 파일을 Scene 객체에 로드한 후, 노드 계층을 탐색하고 씬의 모든 메시에 대한 원시 기하학 데이터를 읽을 수 있습니다.
단계별 가이드
1단계: 패키지 설치
PyPI에서 Aspose.3D FOSS를 설치하십시오. 추가 시스템 라이브러리는 필요하지 않습니다.
pip install aspose-3d-foss지원되는 Python 버전: 3.7, 3.8, 3.9, 3.10, 3.11, 3.12.
2단계: Scene 클래스를 가져오기
Scene 클래스는 모든 3D 데이터의 최상위 컨테이너입니다. 필요한 로드 옵션 클래스와 함께 가져오세요.
from aspose.threed import Scene
from aspose.threed.formats import ObjLoadOptions모든 public 클래스는 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 (binary 및 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는 0개 이상의 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 참조를 참조하십시오.
일반적인 문제 및 해결 방법
Scene.from_file() 호출 시 FileNotFoundError
경로는 절대 경로이거나 런타임 시 작업 디렉터리에 대해 올바른 상대 경로여야 합니다. pathlib.Path을 사용하여 신뢰할 수 있는 경로를 구축하십시오:
from pathlib import Path
from aspose.threed import Scene
path = Path(__file__).parent / "assets" / "model.obj"
scene = Scene.from_file(str(path))STL 파일을 로드한 후 mesh.polygons가 비어 있습니다
STL 파일은 삼각형을 원시 면으로 저장하며, 인덱스된 메시는 사용하지 않습니다. 로드한 후에는 해당 면들로부터 다각형이 합성됩니다. polygons이 비어 있으면, len(mesh.control_points)을 확인하십시오; 카운트가 3의 배수이면 기하학이 인덱스되지 않은 형태로 저장된 것이며, 연속된 세 개의 정점이 하나의 삼각형을 형성합니다.
좌표계 불일치 (모델이 회전되거나 반전된 것으로 보임)
다른 도구들은 서로 다른 규칙(Y-up vs Z-up, left-hand vs right-hand)을 사용합니다. 로드 후 ObjLoadOptions.flip_coordinate_system = True를 설정하거나 루트 노드의 Transform에 회전을 적용하십시오.
AttributeError: 'NoneType' object has no attribute 'polygons'
노드의 엔터티 목록에는 메시가 아닌 엔터티(카메라, 조명)가 포함될 수 있습니다. 캐스팅하기 전에 항상 isinstance(entity, Mesh) 로 보호하십시오.
자주 묻는 질문 (FAQ)
어떤 3D 형식을 로드할 수 있나요?
OBJ (Wavefront), STL (binary and ASCII), glTF 2.0 / GLB, COLLADA (DAE), 및 3MF. FBX 파일 토큰화는 부분적으로 지원되지만 전체 파싱은 아직 완료되지 않았습니다.
OBJ 파일을 로드하면 .mtl 재질도 로드됩니까?
예, ObjLoadOptions.enable_materials = True(기본값)일 때입니다. 라이브러리는 .obj 파일과 동일한 디렉터리에서 .mtl 파일을 찾습니다. .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를 공유하지 않는 한 안전합니다.