Pythonで3Dモデルを変換する方法

Pythonで3Dモデルを変換する方法

Aspose.3D FOSS for Python を使用したフォーマット変換は、2 段階のプロセスです。まず Scene オブジェクトにロードし、次に目的の出力フォーマットに保存します。すべてのジオメトリは共通のインメモリ表現で保持されているため、フォーマット固有の中間ステップは不要です。以下のセクションでは、最も一般的な変換と動作するコードを示します。

ステップバイステップガイド

ステップ 1: パッケージをインストール

pip install aspose-3d-foss

システムライブラリ、コンパイラ、または追加のランタイム依存関係は必要ありません。


ステップ 2: ソースモデルをロードする

最も簡単なケースでは Scene.from_file() を使用します: 形式はファイル拡張子から自動的に検出されます。

from aspose.threed import Scene

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

座標系やマテリアルの読み込みを制御する必要がある OBJ ファイルの場合は、scene.open()ObjLoadOptions を使用してください:

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

options = ObjLoadOptions()
options.flip_coordinate_system = True  # Convert to Z-up if needed
options.enable_materials = True        # Load the accompanying .mtl file
options.normalize_normal = True

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

両方のアプローチは、次の保存ステップのために同一のSceneオブジェクトを生成します。


ステップ 3: ロードされたシーンを検査

変換を実行する前に、ジオメトリが正しく読み込まれたか確認する価値があります。ファイルが欠落している、サポートされていない FBX 機能、または .mtl ファイルのパス問題のいずれも、空のシーンを生成する可能性があります。

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

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

mesh_count = 0
total_vertices = 0

def count_meshes(node) -> None:
    global mesh_count, total_vertices
    for entity in node.entities:
        if isinstance(entity, Mesh):
            mesh_count += 1
            total_vertices += len(entity.control_points)
    for child in node.child_nodes:
        count_meshes(child)

count_meshes(scene.root_node)
print(f"Loaded {mesh_count} mesh(es), {total_vertices} total vertices")

if mesh_count == 0:
    raise ValueError("Scene contains no geometry: check the source file path and format")

ステップ 4: ターゲット形式に保存

出力パスを指定して scene.save() を呼び出します。バイナリと ASCII の出力、座標軸、圧縮を制御するために、フォーマット固有の save-options オブジェクトを渡します。

OBJ から STL(バイナリ)

from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

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

save_opts = StlSaveOptions()
##StlSaveOptions defaults to binary output, which is more compact.

scene.save("model.stl", save_opts)
print("Saved model.stl")

OBJ から glTF 2.0 へ

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

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

save_opts = GltfSaveOptions()

scene.save("model.gltf", save_opts)
print("Saved model.gltf")

自己完結型の GLB バイナリとして保存し、.gltf と外部バッファーを使用しないようにするには、出力拡張子を .glb に変更します:

scene.save("model.glb", save_opts)

OBJ から 3MF

from aspose.threed import Scene
from aspose.threed.formats import ThreeMfSaveOptions

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

save_opts = ThreeMfSaveOptions()

scene.save("model.3mf", save_opts)
print("Saved model.3mf")

STL から glTF 2.0

同じパターンはソース形式に関係なく適用されます:

from aspose.threed import Scene
from aspose.threed.formats import GltfSaveOptions

scene = Scene.from_file("input.stl")
scene.save("output.gltf", GltfSaveOptions())
print("Saved output.gltf")

ステップ5: 出力を確認する

保存後、出力ファイルが存在し、サイズがゼロでないことを確認してください。より徹底的なチェックとして、ファイルを再読み込みし、メッシュ数を比較します:

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

output_path = "model.stl"

##Basic file-system check
size = os.path.getsize(output_path)
print(f"Output file size: {size} bytes")
if size == 0:
    raise RuntimeError("Output file is empty: save may have failed silently")

##Round-trip verification: reload and count geometry
def _iter_nodes(node):
    yield node
    for child in node.child_nodes:
        yield from _iter_nodes(child)

reloaded = Scene.from_file(output_path)
mesh_count = sum(
    1
    for node in _iter_nodes(reloaded.root_node)
    for entity in node.entities
    if isinstance(entity, Mesh)
)
print(f"Round-trip check: {mesh_count} mesh(es) in output")

一般的な問題と対策

出力ファイルは作成されますが、ジオメトリが含まれていません

ソースファイルがメッシュゼロの状態で読み込まれた可能性があります。保存する前にステップ3の検査手順を追加してください。また、ファイル拡張子が実際のフォーマットと一致していることを確認してください。Aspose.3D は拡張子を使用してパーサーを選択します。

glTF 出力にテクスチャが欠落しています

Aspose.3D FOSS は変換時にジオメトリとマテリアル プロパティを引き継ぎます。ソース OBJ が .mtl 内の外部画像ファイルを参照している場合、これらの画像ファイルは .gltf と同時に自動的にコピーされません。保存後、テクスチャ画像を出力ディレクトリに手動でコピーしてください。

STL 出力が内側向きに見える(面法線が反転)

STL は winding‑order のメタデータを持ちません。出力法線が反転している場合は、利用可能であれば StlSaveOptions オプションを設定するか、ロード時に座標系を反転させてください: ObjLoadOptions.flip_coordinate_system = True

ValueError: unsupported format 保存時

出力ファイルの拡張子が .obj.stl.gltf.glb.dae.3mf のいずれかであることを確認してください。Linux では拡張子は大文字小文字を区別します。

非常に大きなファイルは変換が遅くなります

Aspose.3D FOSS はジオメトリをメモリ内で処理します。数百万のポリゴンを含むファイルの場合、十分な RAM を確保してください。現在、ストリーミング書き込み API はありません。


よくある質問 (FAQ)

ディスクに書き込まずにファイルを変換できますか?

はい。scene.open()scene.save() の両方は、ファイルパスに加えてバイナリのファイルライクオブジェクトも受け付けます。ロード時には read() を実装したオブジェクト、保存時には write() を実装したオブジェクトを渡してください:

import io
from aspose.threed import Scene

# Load from an in-memory buffer
data = open('model.obj', 'rb').read()
scene = Scene()
scene.open(io.BytesIO(data))

# Save to an in-memory buffer
buf = io.BytesIO()
scene.save(buf)

FBXは変換のソース形式としてサポートされていますか?

FBX のトークン化は部分的に実装されていますが、パーサーは完全ではありません。FBX 入力は不完全なシーンを生成する可能性があります。信頼できるソース形式として OBJ、STL、glTF、COLLADA、または 3MF を使用してください。

OBJからglTFへの変換でマテリアルは残りますか?

基本的なPhong/Lambertマテリアルプロパティ(拡散色)はSceneモデルを通じて引き継がれ、glTFマテリアルブロックに書き込まれます。glTFマテリアルモデルで表現できない手続き的またはカスタムシェーダーパラメータは破棄されます。

ループで複数のファイルを変換できますか?

はい。各Scene.from_file()呼び出しは独立したオブジェクトを作成するため、パスのリストに対するループは簡単です:

from pathlib import Path
from aspose.threed import Scene
from aspose.threed.formats import StlSaveOptions

source_dir = Path("input")
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

opts = StlSaveOptions()
for obj_file in source_dir.glob("*.obj"):
    scene = Scene.from_file(str(obj_file))
    out_path = output_dir / obj_file.with_suffix(".stl").name
    scene.save(str(out_path), opts)
    print(f"Converted {obj_file.name} -> {out_path.name}")

変換はシーン階層(親/子ノード)を保持しますか?

はい。ノードツリーは、対象フォーマットが許す限り保持されます。STL のようなフォーマットはノード構造のないフラットジオメトリのみを保存し、階層は保存時にフラット化されます。glTF や COLLADA のようなフォーマットは完全な階層を保持します。

 日本語