Pythonで3Dモデルをロードする方法

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

すべてのパブリッククラスは aspose.threed またはそのサブパッケージ(aspose.threed.entitiesaspose.threed.formatsaspose.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は、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_pointsVector4 オブジェクトのリストです; xyz は位置を表し、w は同次座標です(通常は 1.0)。

mesh.polygons は整数のリストのリストであり、各内部リストは 1 つの面に対する制御点インデックスの順序付けられた集合です。


ステップ 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 の倍数であれば、ジオメトリはインデックスなし形式で格納されており、連続する 3 つの頂点が 1 つの三角形を構成します。

座標系の不一致(モデルが回転または鏡像になっている)

異なるツールは異なる規約(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(デフォルト)の場合です。ライブラリは.mtlファイルを.objファイルと同じディレクトリで探します。.mtlが欠落している場合でも、ジオメトリはロードされ、警告が出力されます。

パスの代わりにバイトストリームからファイルを読み込むことはできますか?

はい。scene.open() は、ファイルパス文字列に加えて .read() メソッドを持つ任意のファイルライクオブジェクトを受け入れます。開いたバイナリストリーム(例: io.BytesIO)を直接渡してください。Scene.from_file() はファイルパス文字列のみ受け付けます。

サーフェス法線を取得するにはどうすればよいですか?

ロードした後、mesh.get_element(VertexElementType.NORMAL) を確認してください。これにより、VertexElementNormal が返され、その data リストには、mapping_modereference_mode に従ってマッピングされた参照ごとに 1 つの法線ベクトルが含まれます。

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をスレッド間で共有しない限り安全です。

 日本語