モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

目次目次を開く/閉じる

COLLADA ファイルの扱い方 (.dae file)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2018/10/16
最終更新最終更新
2023/11/17
記事区分記事区分
一般公開

3D アプリケーション間でデータを交換するためのファイルフォーマットの一つに COLLADA (COLLAborative Design Activity) があります。コンピュータグラフィックスのレンダリングに必要な情報およびその他付随する情報を格納できます。COLLADA の仕様にしたがった XML スキーマファイルの拡張子は通常 .dae (digital asset exchange) です。3D アプリケーションの例としては以下のようなものがあります。

COLLADA ファイルをプログラミング言語から扱うためのライブラリには以下のようなものがあります。

以下に Debian9 における FreeCAD および pycollada を利用した、COLLADA ファイル (dae ファイル) の簡単な扱い方を記載します。その際、COLLADA のバージョンは 1.5 とします。

インストール

pycollada

v1.5 対応版をインストールするためには以下のようにします。setuptools の setup.pyを利用します。

git clone https://github.com/pycollada/pycollada.git
cd pycollada/
git checkout collada15spec
sudo python setup.py install

以下のような場所にインストールされます。

ls /usr/local/lib/python2.7/dist-packages/pycollada-0.4.1-py2.7.egg/

ipython
import collada
collada.__path__
collada.__file__

アンインストール方法は以下のようになります。

sudo python setup.py install --record files.txt
cat files.txt | xargs -I{} sudo rm -rf {}

FreeCAD

Debian の場合は apt でインストールできます

sudo apt install freecad

meshlab も同様に apt で提供されています。

sudo apt install meshlab

dae ファイルを読み込むための pycollada 設定

FreeCAD は dae ファイルを操作するために、内部的に pycollada を利用します。

上記手順で既にインストール済みであれば dae ファイルの読み込みおよび書き出しが可能です。

duck_polylist.dae

pycollada サンプルコード

読み込み

duck_triangles.dae を読み込んでみます。おおよそ以下のような構造をしています。

<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
    <asset></asset>
    <library_cameras></library_cameras>
    <library_lights></library_lights>
    <library_images></library_images>
    <library_materials></library_materials>
    <library_effects></library_effects>
    <library_geometries>
        <geometry id="LOD3spShape-lib" name="LOD3spShape">
            <mesh>
                <source id="LOD3spShape-lib-positions" name="position"></source>
                <source id="LOD3spShape-lib-normals" name="normal"></source>
                <source id="LOD3spShape-lib-map1" name="map1"></source>
                <vertices id="LOD3spShape-lib-vertices">
                    <input semantic="POSITION" source="#LOD3spShape-lib-positions"/>
                </vertices>
                <triangles count="4212" material="blinn3SG">
                    <input offset="0" semantic="VERTEX" source="#LOD3spShape-lib-vertices"/>
                    <input offset="1" semantic="NORMAL" source="#LOD3spShape-lib-normals"/>
                    <input offset="2" semantic="TEXCOORD" source="#LOD3spShape-lib-map1" set="0"/>
                    <p></p>
                </triangles>
            </mesh>
        </geometry>
    </library_geometries>
    <library_visual_scenes>
        <visual_scene id="VisualSceneNode" name="untitled"></visual_scene>
    </library_visual_scenes>
    <scene>
        <instance_visual_scene url="#VisualSceneNode"/>
    </scene>
</COLLADA>

バージョンは 1.4.1 であることが確認できます。これを読み込む際に指定します。また、pycollada は内部的に NumPyを利用しています。更に、参照できないオブジェクト ID が指定されていたり、pycollada がサポートしていない機能が存在する場合にエラーではなく無視するように設定しています。

from collada import Collada, DaeBrokenRefError, DaeUnsupportedError, set_collada_version, set_number_dtype
import numpy as np
set_collada_version('1.4.1')
set_number_dtype(np.float64)
mesh = Collada('duck_triangles.dae', ignore=[DaeBrokenRefError, DaeUnsupportedError])

ipython で読み込んだ場合の例です。形状 geometries 情報が一つ格納されている DAE ファイルであることが分かります。

In [2]: mesh
Out[2]: <Collada geometries=1, articulated_systems=0, kinematics_models=0>

形状 geometry は三角形となっています。フェイスとして三角形が利用されている meshということになります。

In [3]: mesh.geometries
Out[3]: [<Geometry id=LOD3spShape-lib, 1 primitives>]

In [6]: mesh.geometries[0].primitives
Out[6]: [<TriangleSet length=4212>]

In [7]: triset = mesh.geometries[0].primitives[0]

情報の取得

ファイル作成日時や作者、単位、軸の向き

mesh.filename #=> 'duck_triangles.dae'
mesh.assetInfo.contributors[0].author
mesh.assetInfo.contributors[0].authoring_tool
mesh.assetInfo.created #=> datetime.datetime(2006, 8, 23, 22, 29, 59, tzinfo=tzutc())
mesh.assetInfo.modified
mesh.assetInfo.unitname #=> 'centimeter'
mesh.assetInfo.unitmeter #=> 0.01
mesh.assetInfo.upaxis #=> 'Y_UP'

library_xxx へのアクセス

mesh.cameras
mesh.lights
mesh.images[0] #=> <CImage id=file2 path=./duckCM.tga>
mesh.materials
mesh.effects
mesh.geometries
mesh.scenes[0] # == mesh.scene

形状 geometries から三角形フェイスの set を取得

mesh.geometries #=> [<Geometry id=LOD3spShape-lib, 1 primitives>]
mesh.geometries[0].primitives #=> [<TriangleSet length=4212>]
triset = mesh.geometries[0].primitives[0]
len(triset) #=> 4212

三角形フェイスの集合は、頂点座標や頂点法線の情報をソースとして持ちます。

triset.sources['VERTEX'] #=> [(0, 'VERTEX', '#LOD3spShape-lib-positions', None, <FloatSource size=2108>)]
triset.sources['NORMAL'] #=> [(1, 'NORMAL', '#LOD3spShape-lib-normals', None, <FloatSource size=2290>)]
len(triset.vertex) #=> 2108
len(triset.normal) #=> 2290

それらソースは一つ以上の三角形フェイスから参照されます。各フェイスが参照する頂点座標や頂点法線の情報を格納したインデックスが以下の二つです。

len(triset) #=> 4212
len(triset.vertex_index) #=> 4212
len(triset.normal_index) #=> 4212

インデックスを用いて、各三角形フェイスが利用する三つの頂点座標、および各頂点の頂点法線ベクトルを取得できます。

triset.vertex[triset.vertex_index][0]
array([[-23.9364,  11.5353,  30.6125],  #=> (x,y,z)
       [-18.7264,  10.108 ,  26.6814],  #=> (x,y,z)
       [-15.6992,  11.4278,  34.2321]]) #=> (x,y,z)

triset.normal[triset.normal_index][0]
array([[-0.192109, -0.934569,  0.299458],  #=> 長さ1
       [-0.06315 , -0.993623,  0.093407],  #=> 長さ1
       [-0.11695 , -0.921313,  0.370816]]) #=> 長さ1

立方体の作成サンプル

立方体の各面は三角形フェイス二つで表現できるため、立方体の表現に必要なフェイス数は 2 x 6 で 12 です。

import collada
import numpy as np

# 空のメッシュを作成
mesh = collada.Collada()

##### mesh.geometries

# 立方体の 8 頂点 (x,y,z) → 8 x 3 = 24
vert_floats = [-50,50,50,50,50,50,-50,-50,50,50,-50,50,-50,50,-50,50,50,-50,-50,-50,-50,50,-50,-50]

# 頂点法線ベクトル (x,y,z) を 8 頂点それぞれに対して向き 3 つ分用意 → 3 x 8 x 3 = 72
normal_floats = [0,0,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1]

# 頂点座標と頂点法線をソースとして登録
vert_src = collada.source.FloatSource("cubeverts-array", np.array(vert_floats), ('X','Y', 'Z'))
normal_src = collada.source.FloatSource("cubenormals-array", np.array(normal_floats), ('X', 'Y', 'Z'))

# id `geometry0`, name `mycube` の geometry をソース情報を含めて新規作成
geom = collada.geometry.Geometry(mesh, "geometry0", "mycube", [vert_src, normal_src])

# 作成した geometry をもとにフェイス情報 triset を新規作成
input_list = collada.source.InputList()
input_list.addInput(0, 'VERTEX', "#cubeverts-array")
input_list.addInput(1, 'NORMAL', "#cubenormals-array")
indices = np.array([0,0,2,1,3,2,0,0,3,2,1,3,0,4,1,5,5,6,0,4,5,6,4,7,6,8,7,9,3,10,6,8,3,10,2,11,0,12,4,13,6,14,0,12,6,14,2,15,3,16,7,17,5,18,3,16,5,18,1,19,5,20,7,21,6,22,5,20,6,22,4,23])
triset = geom.createTriangleSet(indices, input_list, "materialref")

# geometry にフェイス情報 triset を追加
geom.primitives.append(triset)

# mesh に geometry を追加
mesh.geometries.append(geom)

##### mesh.effects, mesh.materials

# 簡単のため Phong の反射モデルを利用するように設定
effect = collada.material.Effect("effect0", [], "phong", diffuse=(1,0,0), specular=(0,1,0))

# id `material0`, name `mymaterial` として material を新規作成
mat = collada.material.Material("material0", "mymaterial", effect)

# mesh に effect と material を追加
mesh.effects.append(effect)
mesh.materials.append(mat)

##### scene の設定

# scene を作成
matnode = collada.scene.MaterialNode("materialref", mat, inputs=[])
geomnode = collada.scene.GeometryNode(geom, [matnode])
node = collada.scene.Node("node0", children=[geomnode])
myscene = collada.scene.Scene("myscene", [node])

# scene リストへの追加および既定値の設定
mesh.scenes.append(myscene)
mesh.scene = myscene

# ファイルへの書き出し
mesh.write('/tmp/test.dae')

作成結果例 test.dae

<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
  <asset>
    <created>2018-10-21T00:20:01.203446</created>
    <modified>2018-10-21T00:20:01.203468</modified>
    <up_axis>Y_UP</up_axis>
  </asset>
  <library_effects>
    <effect name="effect0" id="effect0">
      <profile_COMMON>
        <technique sid="common">
          <phong>
            <emission>
              <color>0 0 0 1</color>
            </emission>
            <ambient>
              <color>0 0 0 1</color>
            </ambient>
            <diffuse>
              <color>1 0 0 1</color>
            </diffuse>
            <specular>
              <color>0 1 0 1</color>
            </specular>
            <shininess>
              <float>0</float>
            </shininess>
            <reflective>
              <color>0 0 0 1</color>
            </reflective>
            <reflectivity>
              <float>0</float>
            </reflectivity>
            <transparent>
              <color>0 0 0 1</color>
            </transparent>
            <transparency>
              <float>1</float>
            </transparency>
          </phong>
        </technique>
        <extra>
          <technique profile="GOOGLEEARTH">
            <double_sided>0</double_sided>
          </technique>
        </extra>
      </profile_COMMON>
    </effect>
  </library_effects>
  <library_geometries>
    <geometry id="geometry0" name="mycube">
      <mesh>
        <source id="cubenormals-array">
          <float_array count="72" id="cubenormals-array-array">0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 0 0 1 0 0 1 0 0 -1 0 0 -1 0 0 -1 0 0 -1 0 -1 0 0 -1 0 0 -1 0 0 -1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 -1 0 0 -1 0 0 -1 0 0 -1</float_array>
          <technique_common>
            <accessor count="24" source="#cubenormals-array-array" stride="3">
              <param type="float" name="X"/>
              <param type="float" name="Y"/>
              <param type="float" name="Z"/>
            </accessor>
          </technique_common>
        </source>
        <source id="cubeverts-array">
          <float_array count="24" id="cubeverts-array-array">-50 50 50 50 50 50 -50 -50 50 50 -50 50 -50 50 -50 50 50 -50 -50 -50 -50 50 -50 -50</float_array>
          <technique_common>
            <accessor count="8" source="#cubeverts-array-array" stride="3">
              <param type="float" name="X"/>
              <param type="float" name="Y"/>
              <param type="float" name="Z"/>
            </accessor>
          </technique_common>
        </source>
        <vertices id="cubeverts-array-vertices">
          <input source="#cubeverts-array" semantic="POSITION"/>
        </vertices>
        <triangles count="12" material="materialref">
          <input source="#cubenormals-array" semantic="NORMAL" offset="1"/>
          <input source="#cubeverts-array-vertices" semantic="VERTEX" offset="0"/>
          <p>0 0 2 1 3 2 0 0 3 2 1 3 0 4 1 5 5 6 0 4 5 6 4 7 6 8 7 9 3 10 6 8 3 10 2 11 0 12 4 13 6 14 0 12 6 14 2 15 3 16 7 17 5 18 3 16 5 18 1 19 5 20 7 21 6 22 5 20 6 22 4 23</p>
        </triangles>
      </mesh>
    </geometry>
  </library_geometries>
  <library_materials>
    <material name="mymaterial" id="material0">
      <instance_effect url="#effect0"/>
    </material>
  </library_materials>
  <library_visual_scenes>
    <visual_scene id="myscene">
      <node id="node0">
        <instance_geometry url="#geometry0">
          <bind_material>
            <technique_common>
              <instance_material symbol="materialref" target="#material0"/>
            </technique_common>
          </bind_material>
        </instance_geometry>
      </node>
    </visual_scene>
  </library_visual_scenes>
  <scene>
    <instance_visual_scene url="#myscene"/>
  </scene>
</COLLADA>
Likeボタン(off)0
詳細設定を開く/閉じる
アカウント プロフィール画像

組み込みエンジニア

記事の執筆者にステッカーを贈る

有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

>>さらに詳しくステッカーを贈る
ステッカーを贈る コンセプト画像

Feedbacks

Feedbacks コンセプト画像

    ログインするとコメントを投稿できます。

    ログインする

    関連記事