前回「Vol.5:MAXScriptから見たアニメーションデータ」では、 Autodesk 3ds Max(以下、3ds Max) 内部でのアニメーションデータの扱われ方と、MAXScript からどのようにアニメーションデータにアクセスするのかを学習しました。今回はさらに一歩進んでパイプライン構築に必要な機能を紹介します。

Vol.6:アニメーションの分離と差し替え

今回は、MAXScriptを用いてアニメーションデータの分離と差し替え方法を解説していきます

アニメーションデータの分離

前回(Vol.5:MAXScriptから見たアニメーションデータ)説明した通り、3ds Max 内ではアニメーションデータはコントローラとして扱われ、モデルデータとは別の扱いになります。これを上手に利用すると、アニメーションデータとモデルを分けて管理することが可能です。

では、簡単なデータを見て、確かめてみることにしましょう。

まずは前回同様、円運動をする Box を作成します。



 b = Box length:1 width:1 height:1
 for i=0 to 100 do (
 	addNewKey b.pos.controller.x_position.controller i
 	b.pos.controller.x_position.controller.keys[i+1].value =cos(360.0/100*i)
 	addNewKey b.pos.controller.z_position.controller i
 	b.pos.controller.z_position.controller.keys[i+1].value = sin(360.0/100*i)
 )

さらに球も作成します。



 Sphere radius:0.5

Box と Sphere のオブジェクトを作成

Box と球を作成した

タイムスライダを動かしてみてください。Box は円運動をし、球は原点に留まっています。ここで、Sphere001 のコントローラとして Box001 のコントローラを指定します。



 $Sphere001.controller = $Box001.controller

むむっ! 何も起きませんね。

しかし、トラックビューでアニメーションデータを確認してみてください。Sphere001 にもアニメーションデータが入っています。実は、スクリプトからコントローラを代入しただけではビューポートには反映されないのです。

そのため、このような場合は notifyDepends を使用します。



 notifyDependents  $Sphere001.controller

これは、指定したオブジェクトに関連するものに、更新情報を送信するコマンドです。

このようにして別のオブジェクトに代入したコントローラは、インスタンスと同じような振舞いをします。試しに Sphere のアニメーションデータを編集してみてください。 Box も連動してアニメーションが変化します。

では、 Box と Sphere のコントローラを別々に扱いたい場合はどうしたら良いのでしょう?

これは単純に copy を使うだけです。



 $Sphere001.controller = copy $Box001.controller

copy を使用すると、コントーラを複製して Sphere のコントローラとして割り当ててくれます。

この違いが少し判り辛いのでまとめてみます。最初の状態は下図のようになっています。Box と Sphere 別々にコントローラが付いていて、別々のアニメーションをしているとしましょう。
四角と球のオブジェクトを作成
代入を行うと下図のようになります。B は参照されなくなり、Box も Sphere も同じコントローラ/アニメーションを参照するようになります。この時、アニメーション A を変更するとそれに併せて Box も Sphere も動きが変わります。
四角と球のオブジェクトを作成
copy して代入する場合はこのようになります。
四角と球のオブジェクトを作成
アニメーションもコントローラもコピーされているので、A を変更しても Sphere にしか影響を及ぼしません。この挙動は、オブジェクトのコピーとインスタンスの違いと同じです。

結局、コントローラもオブジェクトと同様な扱いをすることができることが判りますね。

[[SplitPage]]

では、先ほどの円運動をする Box のスクリプトが、長くてとても読み辛かったので、変数を使って書きかえてみましょう。



 b = Box length:1 width:1 height:1
 for i=0 to 100 do (
 	addNewKey b.pos.controller.x_position.controller i
 	b.pos.controller.x_position.controller.keys[i+1].value =cos(360.0/100*i)
 	addNewKey b.pos.controller.z_position.controller i
 	b.pos.controller.z_position.controller.keys[i+1].value = sin(360.0/100*i)
 )

これを、次のように書き直します。



 b = Box length:1 width:1 height:1

 xCtrl = b.pos.controller.x_position.controller
 yCtrl = b.pos.controller.z_position.controller

 for i=0 to 100 do (
 	addNewKey xCtrl i
 	xCtrl.keys[i+1].value =cos(360.0/100*i)
 	addNewKey yCtrl i
 	yCtrl.keys[i+1].value = sin(360.0/100*i)
 )


一気にスッキリしました!

アニメーションの差し替えを行う

制作現場では、モデルデータを更新する度、既にレイアウトの終わっているシーンデータのモデルを差し替えるという作業が、頻繁に起こります。特にキャラクタを扱う作品では必ず起こる上、数やデータ量も多くなりがちで時間がかかります。

このように、何度も繰り返し発生する面倒臭い作業は、ツール化するのにもってこいの題材です。とは言っても、キャラクタの差し替えを行う仕組みを、全てここで解説するにはボリュームがありすぎるので、今回は、そのエッセンスだけを紹介します。

先ほどの章では、あるオブジェクトから別のオブジェクトにコントローラを代入するだけで、アニメーションを移し替えることができました。このテクニックを使って、アニメーションデータをそのままでモデルデータに差し替えてみましょう。

01 元データの準備

キャラクタの差し替えシステムを作成するためにはリグを作成する必要がありますが、今回は簡単化のため、ただの Box を使用します。

Box を作成

まずは Box を作成する

また、差し替え用のモデルも作成しておきます。

リグサンプル

アニメーションを移し替えるモデルも作成

もちろん実際の制作ではきちんとコントローラを設けたり、階層を作る必要はありますが、今回はこの Box にアニメーションを付けてみます。

02 新規シーンの作成とモデルの読みこみ/アニメーション作成

次に、アニメーションを行うために新規シーンを作成してモデルデータを合成で読み込みます。

この時、モデルデータを1体、読み込んだら、そのモデルにプレフィックスを追加してください。こうすることで複数のモデルを読みこんだときにも名前の衝突が起こらなくなります。

今回は2体のモデルデータを読み込み、それぞれに model001model002 というプレフィックスを追加しました。ちなみに、プレフィックスと名前の間は ':' で区切っています。

こうしておけば、モデル間でオブジェクト名が被ったときでも、勝手に名前が変更されることはありません。

プレフィックスを追加

モデルにプレフィックスを追加し、名前の衝突を避ける

このシーンにアニメーションを付けていきましょう。

[[SplitPage]]

03 モデルデータの差し替え

シーンの準備ができたので、モデルデータの差し替えを行います。データの差し替え手順は以下の通りです。

STEP 1:新しいモデルを合成する

STEP 2:コントローラを差し替える

STEP 3:古いモデルを削除する

STEP 4:新しいモデルにプレフィックスを付ける

これをコードに落としたものがこちらです。



 fn getPrefix node = (
  	return (filterString node.Name ":")[1]
 )
 
 fn importAsset = (
 	fname = get3ds MaxOpenFileName()
 	if fname == undefined do (
 		return false
 	)
 	
 	merge3ds MaxFile fname #select #useMergedMtlDups
 	
 	return selection[1]
 )
 
 with animate off (
 	disableSceneRedraw()
 	
 	srcNode = selection[1]
 	
 	--新しいモデルを合成する
 	newNode = importAsset()
 	
 	--ノードのPrefixを取得する
 	prfx = getPrefix srcNode
 	
 	--アニメーションデータを転送する
 	if srcNode.pos.isAnimated == true then (
 		newNode.pos.controller = srcNode.pos.controller
 	) else (
 		in coordsys parent newNode.pos = srcNode.pos
 	)
 		
 	if srcNode.rotation.isAnimated == true then (
 		newNode.rotation.controller = srcNode.rotation.controller
 	) else (
 		in coordsys parent newNode.rotation = srcNode.rotation
 	)
 		
 	notifyDependents  newNode.controller
 	
 	--元のモデルを削除する
 	delete srcNode
 	
 	--新しいモデルにPrefixをつける
 	newNode.Name = prfx + ":" + newNode.Name
 	
 	enableSceneRedraw()
 )

このツールの動作結果をムービーにしました。見ての通り、モデルを差し替えてもきちんとアニメーションが再現できています。

 

いかがでしたか? この他にも、3ds Max のアニメーション周りは非常に複雑で、知らないと行き詰ってしまう事柄が多数存在します。そこで次回は、今回作成したツールを元に、スクリプトからアニメーションデータを扱うための注意事項を説明していきましょう。

TEXT_痴山紘史(JCGS)
映像制作のためのパイプライン構築をはじめ、技術提供を行なっていくエンジニア集団、「JCGS(日本CGサービス)」 の代表取締役......というのは表の顔で、実態は飲み会とCG関連の技術が好きなただのCGオタク。
個人サイト 「PHILO式」