myara CG blog

CG Design Blog. Thoughts, experiments and experiences.

Fractional Key Frames

非整数キーフレームであってますかね?

アニメーションをリージョンでスケールをかけるとキーフレームが非整数型になってしまいますよね。

それじゃいじりにくいから全キーフレームを選んで「Move Keys Frames to Nearest Frame」実行すれば一番近いフレームに移動されます。

大した手間ではないけど、ワンクリックで全オブジェクトでやりたいなーと思いました。
スクリプトを書くより手動でやった方が早いんだろうけど、これからも同じ作業もあるし、なんか書きたくなりました。

手動でもそんなに手間ではないから、あんまり便利ではないかもしれないけど、もう書いちゃったんだから公開しちゃいます。

スクリプトを書く人には多少役に立つかもしれないし。


DOWNLOAD



/*-----------------------
Get Fractional Key Frames
-------------------------
2012 myara

Select Animated objects and run
It selects objects with fractional keys
If the FIX option is checked, it move their keys to the nearest frame
-------------------------*/

var oPSet = XSIFactory.CreateObject("CustomProperty");
oPSet.name = "Fractional Key Frames ";
oPSet.AddParameter2( "Fix", siBool,0);
oPSet.AddParameter2( "Sel", siBool,1);

var oLayout = oPSet.PPGLayout;
oLayout.AddGroup("",0);
var oItem = oLayout.AddItem( "Fix", "[ Fix ] Move Key to Nearest Frame" );
var oItem = oLayout.AddButton ("Run","R U N");
oItem.SetAttribute( siUICX, 315);
oItem.SetAttribute( siUICY, 50);
oLayout.EndGroup();

oLayout.Logic = Run_OnClicked.toString();

oLayout.Language = "JScript" ;

InspectObj( oPSet,null,null,3 ) ;

function Run_OnClicked()
{
//Turn off log
var prefs = Application.Preferences;
prefs.SetPreferenceValue( "scripting.cmdlog", false );

var oSel = FilterX3D(selection); oSelCount = oSel.Count

if (oSelCount > 0){
var oFracSel = XSIFactory.CreateObject( "XSI.Collection" );
if (!PPG.Fix.value){
for (var i=0; i < oSelCount ; i++){
var GetFrac = GetFractionalKeyFrame(oSel(i))
if( GetFrac != "no"){
logmessage ("["+ GetFrac +"] Fractional Key Frames Found" )
oFracSel.Add ( GetFractionalKeyFrame(oSel(i)) )
}
}
}
else {
for (var i=0; i < oSelCount ; i++){
var GetFrac = GetFractionalKeyFrame(oSel(i),"fix");
if( GetFrac != "no"){
oFracSel.Add ( GetFrac )
}
}
}
if (oFracSel.count>0){
SelectObj(oFracSel)
}
else {
Selection.clear();
logmessage ("No Fractional Key frames found")
}
}
else {
logmessage ("ERROR : Nothing Selected", siWarning)
}

function GetFractionalKeyFrame(oObj, opt)
{
var flag
var oAnimParam = XSIFactory.CreateObject( "XSI.Collection" );
oAnimParam.AddItems ( oObj.NodeAnimatedParameters( siFCurveSource ));

for (var i=0; i < oAnimParam.Count; i++){
var oFc = oAnimParam(i).Source;
for (var j=0, b = oFc.GetNumKeys(); j < b; j++){
if (mRound(oFc.GetKeyFrame(j),10)!= Math.round(oFc.GetKeyFrame(j))){
flag = "yes"
if(opt=="fix"){
oFc.SnapToNearestFrame();
flag = "fixed"
}
}
}
}
if(flag=="yes"){
return(oObj)
}
else if(flag=="fixed"){
LogMessage ("["+oObj + "] Key Frames fixed")
return(oObj)
}
else if (!flag){
return("no")
}
}

function mRound(num,digits){
dig = Math.pow(10,digits)
return Math.round(num * dig)/ dig
}

function FilterX3D(in_Objs){
//----- Get only 3D Objects
var oCol = XSIFactory.CreateObject('XSI.Collection');
oCol.unique = true;

for (var i=0, a=in_Objs.count; i < a; i++) {
if (in_Objs(i).IsClassOf(siX3DObjectID)) {
oCol.Add(in_Objs(i));
}
}
var oExp = oCol.expand();
for (var i=0, a = oExp.count; i < a; i++) {
if (oExp(i).IsClassOf(siX3DObjectID)) {
oCol.Add(oExp(i))
}
}
return oCol;
}
}


ちょっとしたスクリプト解説

取得したフレーム数」を「そのフレーム数を丸めた数値」と比較して、
同じだったら、取得したフレームに小数点が入ってないと思いますよね。

普通はそう思うんですよね。

ところで、前にもJunkiさんのブログで読んだことがある気がするけど、フレーム数を取得する時になぜか小数点が入ってしまうこともあります。例えば50フレームではなく、49.999999... と出てしまいます。

なので、スクリプトで「取得したフレーム数」を「そのフレームを丸めた数字」と比較すると、同じのはずなのに同じじゃないという結果になってしまいます。

こんな時に取得したフレームを少し丸めれば上手く行きます。そのために Jscriptの Math.Round を使って、「mRound」っというfunctionを作っておきました:

function mRound(num,digits)
{
dig = Math.pow(10,digits)
return Math.round(num * dig)/ dig
}


digits = 小数点

これで mRound (取得したフレーム , 小数点) で 取得したフレーム数を直します。

フレームだけじゃなくて、色んなところで変な小数点が入ってしまうからこのFunctionは便利です。



追記

キーフレームやmRoundについては説明不足だったのでもう少し細かく書きます:

例えば、ヌルを作って、キーフレームをいっぱい打ってみましょう。
手動でやっても良いけど、面倒だからスクリプトでやっちゃおう。
この方がヒューマンエラーがないはずですね。

var test = GetPrim("Null", null, null, null);
for (var i=0; i<150; i++){
SaveKey(test + ".kine.local.posx", i, null, null, null, null, null);
}
これでヌルが作られ、posxにフレーム0から150までにキーが打たれました。

次に、キーフレーム を そのキーフレームの丸めた数値と比較してみましょう。

var oObj = selection(0);
var anim = oObj.NodeAnimatedParameters( siFCurveSource );
for (var i=0; i<anim.Count; i++){
var oFc = anim(i).Source;
for (var j=0; j<oFc.GetNumKeys(); j++){
if (oFc.GetKeyFrame(j) != Math.round(oFc.GetKeyFrame(j))){
logmessage(oFc.GetKeyFrame(j))
}
}
}

よし、これで
「キーフレーム」 を 「整数化されたそのキーフレームの数値」 と比べ、
同じ数値じゃなかったら、そのキーフレームをログに残します。

結果は、以下の通り:

// INFO : 31
// INFO : 62
// INFO : 111
// INFO : 123
// INFO : 124
// INFO : 125


でも、ログには小数点が入っていないですね。可笑しいよね。

31 = 31 なので、出るはずがないんでしょう?

試しに、フレームの数値と、丸めた数値を両方をログに残すようにしましょう。

var oObj = selection(0);
var anim = oObj.NodeAnimatedParameters( siFCurveSource );
for (var i=0; i<anim.Count; i++){
var oFc = anim(i).Source;
for (var j=0; j<oFc.GetNumKeys(); j++){
if (oFc.GetKeyFrame(j) != Math.round(oFc.GetKeyFrame(j))){
logmessage(oFc.GetKeyFrame(j) +" = "+ Math.round(oFc.GetKeyFrame(j))

}
}
}



結果はこんなかんじ:

// INFO : 30.999999999999996 = 31
// INFO : 61.99999999999999 = 62
// INFO : 110.99999999999998 = 111
// INFO : 122.99999999999998 = 123
// INFO : 123.99999999999998 = 124
// INFO : 125.00000000000001 = 125


なんで!!

分かりません。知りません。わけわかりません。

分かったのは一つだけ。
このフレームを直しても直らないんです。(直し方が分からないだけかな?)

だから上記のスクリプトに小数点を10桁で丸めて見ました。なんで10桁?単純にヒューマンミスで10桁以降に微妙な数字が入る可能性がほぼ0だと思いますから。

上記のスクリプトは直すスクリプトだけじゃなく、チェックするためのスクリプトです。小数点が入っているキーフレームがあったら、オブジェクトを選択して、ログを残します。

なので、このmRound Functionを使わなかったら、31フレームや62フレームに小数点が入ってるというメッセージが出てしまいます。直しても出てしまいます。