2012年3月5日月曜日

COLLADA (4)シーングラフ3

今回は思考中な部分をそのまま記述したので長文となってしまった。
ということで要点だけ。
<library_nodes>には<instance_node>となる<node>が格納されている(はず)。
<visual_scene>内の<node>と同じく、<node>の詳細も記述されている (はず)。
node id は一意になるため、getElement で最初に取得できる要素は必ず指定した<node>
になる(はず)。
<instance_node>とすべきかどうかはアプリケーション依存ではあるが、シーングラフ上で
複数回出現するノードはもれなく<instance_node>となり、<library_nodes>に格納される(はず)。
またその子、孫・・・ももれなく<library_nodes>に格納される(はず)。
とりあえずこれで様子を見よう。


有象無象のあらゆるノードの取得とカウント。
<instance_node>は展開して数える。
/**
 * <visual_scene>内の特定のノードを検索
 */
domNode* findNode(const daeDatabase* dae_db, daeInt index, const daeString type){
  domNode* dom_node;
  if(const_cast<daeDatabase*>(dae_db)->getElement((daeElement**)&dom_node, index, type, "node") != DAE_OK)
    return NULL;
  daeElement* dae_element = dom_node->getParent();
  if((strcmp(dae_element->getTypeName(), "visual_scene") == 0)
   ||(strcmp(dae_element->getTypeName(), "node") == 0))
    return dom_node;
  return findNode(dae_db, index+1, type);
}

/**
 * <visual_scene>内のノード数を取得
 * <instance_node>は展開する
 */
size_t countNode(const daeDatabase* dae_db, const domNode* dom_node){
  size_t sum = 0;
  size_t node_count = dom_node->getNode_array().getCount();
  for(size_t i = 0; i < node_count; i++){
    sum += countNode(dae_db, dom_node->getNode_array().get(i));
  }
  size_t inode_count = dom_node->getInstance_node_array().getCount();
  for(size_t i = 0; i < inode_count; i++){
    domInstance_node* dom_inst_node = dom_node->getInstance_node_array().get(i);
    domNode* dom_node_target = findNode(dae_db, 0, dom_inst_node->getUrl().fragment().c_str());
    if(dom_node_target){
      sum += countNode(dae_db, dom_node_target) + 1;
    }
  }
  sum += node_count;
  return sum;
}

/**
 * <visual_scene>内のノード数を取得
 * <instance_node>は展開する
 */
size_t countNode(const domVisual_scene* dom_visual_scene){
  daeDatabase* dae_db = const_cast<domVisual_scene*>(dom_visual_scene)->getDAE()->getDatabase();
  size_t sum = 0;
  size_t node_count = dom_visual_scene->getNode_array().getCount();
  for(size_t i = 0; i < node_count; i++){
    sum += countNode(dae_db, dom_visual_scene->getNode_array().get(i));
  }
  sum += node_count;
  return sum;
}

こちらはジオメトリノード版。
/**
 * ジオメトリノードか
 */
bool isGeometryNode(const domNode* dom_node){
  if(dom_node->getInstance_camera_array().getCount())
    return false;
  if(dom_node->getInstance_controller_array().getCount())
    return false;
  if(dom_node->getInstance_light_array().getCount())
    return false;
  const daeElement* dae_element = const_cast<domNode*>(dom_node)->getParent();
  if((strcmp(dae_element->getTypeName(), "visual_scene") != 0)
   &&(strcmp(dae_element->getTypeName(), "node") != 0))
    return false;
  return true;
}

/**
 * <visual_scene>内のジオメトリノードを検索
 */
domNode* findGeometryNode(const daeDatabase* dae_db, daeInt index, const daeString type){
  domNode* dom_node;
  if(const_cast<daeDatabase*>(dae_db)->getElement((daeElement**)&dom_node, index, type, "node") != DAE_OK)
    return NULL;
  if(!isGeometryNode(dom_node))
    return findNode(dae_db, index+1, type);
  return dom_node;
}

/**
 * <visual_scene>内のジオメトリノード数を取得
 * <instance_node>は展開する
 */
size_t countGeometryNode(const daeDatabase* dae_db, const domNode* dom_node){
  size_t sum = 0;
  size_t node_count = dom_node->getNode_array().getCount();
  for(size_t i = 0; i < node_count; i++){
    domNode* dom_node_target = dom_node->getNode_array().get(i);
    if(isGeometryNode(dom_node_target))
      sum += countGeometryNode(dae_db, dom_node->getNode_array().get(i)) + 1;
  }
  size_t inode_count = dom_node->getInstance_node_array().getCount();
  for(size_t i = 0; i < inode_count; i++){
    domInstance_node* dom_inst_node = dom_node->getInstance_node_array().get(i);
    domNode* dom_node_target = findGeometryNode(dae_db, 0, dom_inst_node->getUrl().fragment().c_str());
    if(dom_node_target){
      sum += countGeometryNode(dae_db, dom_node_target) + 1;
    }
  }
  return sum;
}

/**
 * <visual_scene>内のジオメトリノード数を取得
 * <instance_node>は展開する
 */
size_t countGeometryNode(const domVisual_scene* dom_visual_scene){
  daeDatabase* dae_db = const_cast<domVisual_scene*>(dom_visual_scene)->getDAE()->getDatabase();
  size_t sum = 0;
  size_t node_count = dom_visual_scene->getNode_array().getCount();
  for(size_t i = 0; i < node_count; i++){
    domNode* dom_node = dom_visual_scene->getNode_array().get(i);
    if(isGeometryNode(dom_node))
      sum += countGeometryNode(dae_db, dom_node) + 1;
  }
  return sum;
}
厳密にチェックを行う場合、多分子ノードまたは孫ノードと<instance_geometry>を
持っているかまでチェックしないとダメ。
フォーマットが柔軟過ぎて、空の<node>で構成された<visual_scene>も普通に作れちゃう。
ただ、今回は面倒なのでやめ。

適当なサンプルで試している限りは展開後の正味のノード数とノードも取得はできた。
が、今度は展開したノードの扱いが厄介なことに。
上の例ではgetParent()->getType()とすれば、<visual_scene>か<node>は取得できる。
COLLADAでは親ノードも取得できるから便利なのはいいんだが、<instance_node>はあくまで
参照なので、実際のノードはオリジナルのものしか存在しない。
つまり展開する場合、<instance_node>が出現した場合、そこから先はそれの親である
<node>の子、孫としてやらねばならない。
上手いこと伝播しないといけないと。

Scene

+--body
|   |
|   +--leg

+--node

+--body

+--leg

※nodeは<instance_node url="#body"/>を含むケース

[追記]
<node id="LArm" type="NODE">
 <node id="LHand" type="NODE">
  <instance_node url="#Weapon"/>
 </node>
</node>
<node id="RArm" type="NODE">
 <node id="RHand" type="NODE">
  <instance_node url="#Weapon"/>
 </node>
</node>
<node id="Weapon" type="NODE">
 <instance_node url="#ItemA"/>
 <instance_node url="#ItemB"/>
</node>
<node id="ItemA" type="NODE">
</node>
<node id="ItemB" type="NODE">
</node>

シーン上に
左手に武器を握った左腕
右手に武器を握った右腕
道具Aと道具Bで構成される武器
道具A
道具B
が存在するなんとも滑稽なシーン。

これらを展開しid表記にすると
LArm
LHand
LHand_Weapon
LHand_Weapon_Item1
LHand_Weapon_Item2
RArm
RHand
RHand_Weapon
RHand_Weapon_Item1
RHand_Weapon_Item2
Weapon
Weapon_Item1
Weapon_Item2
Item1
Item2
idは一意でなければならないのでこんな感じか。

そもそも<instance_node>が出現する場合、オリジナルのノードも含めて
その分描画するというシチュエーションとなるわけだが、一般的によくあるケースとは言い切れないな。
ただ、ここを無視すると結果に影響がでるわけだから。

[追記その2]
ノードの展開は完了。
一意なidとなることで名前が長くなる分は、ハッシュ値でも持たせておけばいいだろう。
後でシーングラフ上から特定のノードを検索するのもハッシュ値で問題ないはずだ。
また、こういうケースの場合は通常、ファイル(つまり*.dae)に記載してあるノードid(名前)を
元に検索することになるので、<instance_node>については気にしないでいい。
例えばWeaponの色を変更したい場合は、id="Weapon"で検索し、マテリアルバンクとでもいうべきか
Weaponが持っているマテリアルバンクのidからマテリアルを引っ張り、中身を変更すれば、
他Weaponを<instance_node>としているノードにも全て伝播することができる。

余談
シーングラフが肥大化すると検索に掛かる時間も馬鹿にできない。
ということで当初は階層構造の構築にstd::mapを利用することを考えたがどうもメモリ非連続らしい。
あまり意識せずに使ってたようで反省。
ということで急遽実装を自前ハッシュテーブルに変更。
配列サイズはJavaの実装だと極端に大きく感じたので、rubyのを参考に。

[追記その4]
<instance_node>について。
コンセプト
データセットがより大きくなり複雑化するのにともない、単一のコンテナ中で操作するのが難しくなり
ます。そういった複雑さに対処するための1 つの方法は、何らかの条件でデータをいくつか小さなモジ
ュールセットに分割することです。そういったモジュールセットを、ライブラリとして別のリソース中
に保存しておくことができます。

他のlibrariesについて見ていて気づいたが、例えばinstance_geometryなんてその気になれば
<node>以下に直接記述できるはず。
もっともこの場合はCOLLADAが許していないが。
そうすると、instance_geomeryの参照先がlibrary_geometriesであるのなら
instance_nodeの参照先はlibrary_nodesであることになる。
つまり、他のlibrary_***と同じくlibrary_nodes内のnodeはすべて詳細情報があるはず。
そうであるなら、先の例は下記のような出力と考えていい。
<library_nodes>
 <node id="Weapon" type="NODE">
  <instance_node url="#ItemA"/>
  <instance_node url="#ItemB"/>
 </node>
 <node id="ItemA" type="NODE"/>
 <node id="ItemB" type="NODE"/>
</library_nodes>

<node id="LArm" type="NODE">
 <node id="LHand" type="NODE">
  <instance_node url="#Weapon"/>
 </node>
</node>
<node id="RArm" type="NODE">
 <node id="RHand" type="NODE">
  <instance_node url="#Weapon"/>
 </node>
</node>

または

<library_nodes>
 <node id="Weapon" type="NODE">
  <node id="ItemA" type="NODE"/>
  <node id="ItemB" type="NODE"/>
 </node>
</library_nodes>
※おそらく下のパターン

となっていれば、
・左手に武器を握った左腕
・右手に武器を握った右腕
となり、意図しない表示がなくなる。

何らかの条件でデータを
この部分がまさにアプリケーション依存なので実際上記のように出力されるとは限らない可能性もある。
普通に考えればシーングラフ中に複数存在するノードは<instance_node>とし<library_nodes>へ、そうでないものは<visual_scene>へと仕分けするはず。
でなければ<library_nodes>の存在意義が薄いことになる。

といわけで作戦練り直し。

[追記その5]
<instance_node>と<library_nodes>が先の関係の場合、
<library_nodes>
<visual_scene>
<node>
であるかは意識しないでいいかも知れない。
この時は、DBへのクエリも一度で済む。
う~む、実際に<instance_node>を吐き出しているファイルサンプルが欲しいところだな・・・。

0 件のコメント :

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。