COLLADA ファイルの扱い方 (.dae file)
[履歴] [最終更新] (2018/10/21 23:22:49)
1
作品
409
技術情報
最近の投稿
ここは
趣味の電子工作を楽しむ人のためのハードウェア情報共有サイト

技術情報や作品の投稿機能、リアルタイム遠隔操作 API をご利用いただけます。
新着作品

概要

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

Uploaded Image

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 です。

Uploaded Image

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>
関連ページ
    概要 ロボットアプリケーションの開発環境の一つ OpenRAVE (Open Robotics Automation Virtual Environment) の環境を構築するための手順を記載します。ここでは特に Debian9 を利用します。 インストール 依存パッケージ ビルドツール関連 sudo apt install git build-essential cmake