ON-BLOG

CGのこと、あれこれ書いてます。

続・スクリプト書いてみようかと

前回までで、選択したものの値などを外部に保存することまでできました。
今回は引き続きコードを書いていきたいと思います。

次は、まず前回までで、できたものを整理します。
始めに現状だと一個のオブジェクトしか対応していないので
それを対応する為
配列を用意して、その配列に対して処理を行うようにします。

こんな感じで整理してみました。
※まだまだ改良の余地がありますが
 シェイプの件などもあるので一旦これで

# -*- coding: utf-8 -*-
import maya.cmds as cmds
import codecs
import os.path

oSel = cmds.ls(sl=True,l=True,type='transform')
Select_Value = []
ImportText = "import maya.cmds as cmds"
Select_Value.append(ImportText)
for i in oSel:
    #name
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    for att in list:
        Tmpsel = cmds.ls(sl=True,type='transform')[0] 
        gettype = cmds.getAttr(Tmpsel + "." + att,type=True)
        if gettype == "bool":
            getValue = cmds.getAttr(Tmpsel + "." + att)
        else:
            getValue = cmds.getAttr(Tmpsel + "." + att)
        AttrStr =  str(i) + "." + str(att)
        Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)"
        Select_Value.append("cmds.setAttr" + Value)
        
ScenePath = cmds.internalVar(uad = True) + 'SavePose'
if not os.path.exists(ScenePath): 
	os.makedirs(ScenePath)

TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8")
for val in Select_Value:
    TextOpen.write(val)
    TextOpen.write("\r\n")
TextOpen.close()


複数選択してから実行すると、以下の画像みたいになるはずです。
f:id:tommy_on:20160906155833p:plain
変更点は配列「Select_Value」を追加し、値を追加したものを
外部へ出力する際に、Forで繰り返し出力しただけです。


これである程度まとまりができたので、テストを行います。
まずはすべてのコントローラーを選択して、実行してみます。
すると‥

f:id:tommy_on:20160906160504p:plain

はい。エラーになります。
エラー文を読みと
「# エラー: line 1: ValueError: file line 15: 名前と一致するオブジェクトがありません: ALL.blendOrient1 # 」
はい。
これは15行目でエラーになってることが分かります。

gettype = cmds.getAttr(Tmpsel + "." + att,type=True)

もっと詳しくみていくと、エラーが発生するコントローラーがあります。
右手のUPVですね。
f:id:tommy_on:20160906161849p:plain
書いてる通りアトリビュートの「blendOrient1」で問題が出ていることが分かります。
が、ここで改めてエラー文を見ると
「名前と一致するオブジェクトがありません: ALL.blendOrient1 # 」

とあります。
ただ、Allには ALL.blendOrient1はなく、エラーが出ているのは右手のUPVです。

このコトから、スクリプトの記載順番や
構造に問題があるんじゃないか?と想定します。

そう!簡単なミスです。
15行目のコマンドがエラーしているということは
その記述が間違っていなかったら
原因はそのコマンドに渡している
引数ないし、値に問題あるんですね。

ここでいうと、「Tmpsel 」か「att」に絞られます。
経験者なら、読んだ瞬間間違いに気づいていると思いますが
私は10分は悩んでしまいましたw
原因はTmpsel の中身です。
ここでTmpsel = cmds.ls(sl=True)[0] と
一番始めに選択しているものを再度取得した為
Allにはブレンドがないのに!
とエラーで弾かれてたわけです。
正しくは以下の中身になります。

# -*- coding: utf-8 -*-
import maya.cmds as cmds
import codecs
import os.path

oSel = cmds.ls(sl=True,l=True,type='transform')
Select_Value = []
print oSel
ImportText = "import maya.cmds as cmds"
Select_Value.append(ImportText)
for i in oSel:
    #name
    print i
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    print list
    for att in list:
        gettype = cmds.getAttr(i + "." + att,type=True)
        if gettype == "bool":
            getValue = cmds.getAttr(i + "." + att)
        else:
            getValue = cmds.getAttr(i + "." + att)
        AttrStr =  str(i) + "." + str(att)
        Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)"
        Select_Value.append("cmds.setAttr" + Value)
        
ScenePath = cmds.internalVar(uad = True) + 'SavePose'
if not os.path.exists(ScenePath): 
	os.makedirs(ScenePath)

TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8")
for val in Select_Value:
    TextOpen.write(val)
    TextOpen.write("\r\n")
TextOpen.close()

はい実行するとこんな感じ。
f:id:tommy_on:20160906162612p:plain

無事通りました。
再度テストを込めて、保存したものを別フレームで実行してみます。
ただ、今は読み込みの機能をつけていないので
データを開いてスクリプトエディタに
直接ペーストして実行します。

f:id:tommy_on:20160906162821p:plain
この状態で実行すると

f:id:tommy_on:20160906162845p:plain
こうなりました。

という点から、とりあえず問題ないように思います。
※本当はいっぱいありますが…


では、続けて読み込みのコマンドを用意してみます。
読み込みはすでにPythonコードが用意されているので
やり方は複数あります。
1.execfileを使ってスクリプトを実行
2.Openで開いてから読み込むで実行
3.execで読み込んで実行

以上のパターンがありました。
※本当はもっとあると思います。
今回のスクリプトで、一番相性がいいのが
どれかと考えていたのですが
結局エラー処理も同時にしたいので
一行ずつ読み込むようにします。
簡単に用意しました。こんな感じ。

import maya.cmds as cmds
import codecs
import os.path
import sys

script_code = []
script_err = []
ScenePath = cmds.internalVar(uad = True) + 'SavePose\SavePose.py'
for line in open(ScenePath, 'r').xreadlines():
   line = line.rstrip()
   script_code.append(line)
   
for i in script_code:
    try:
        exec(i)
    except:
        print i+"は存在していない為、実行できませんでした。"
        script_err.append(i)

はい。これで読み込みまでいけたはずです。
ただ、細かいエラー処理などはまだ組み込んでないです。
それらは、UI実装時に合わせて行います。

とまた一旦ここらで切ります。
次回以降はコマンドを関数化し、UIを実装してみます。

では…

スクリプト書いてみようかと

超久しぶりにブログ書きます。
というのも色々本当にありまして…


さておき
では今回は割とガッツリスクリプトを書いてみようかと思います。

はじめに仕様面を決めてみようかと思います。

お題:ポーズセーブスクリプト
制作理由:MAYAデフォルトでポーズだけ保存したり、外部に残しておける方法がないため。
実装方法:今回は選択した物と、その子ども以下の階層のSRTとアトリビュートの値を保存する。
     シェイプや、追加アトリビュートについては現状は無しで、完成後追加で実装できればする。
     できればカレントのシェリフにも追加したい。

ポーズ保存と実行なので、以下の注意点がある。
1.選択したオブジェクトはフルパスし、重複するオブジェクトはエラーで出力
2.実行する際は、階層の持ち方に注意する。
3.外部に保存するコマンドとそうではない、イテレーションが早いコマンドも必要。パスはドキュメントで
4.外部保存の形式はなんでもいいが、中身はPythonで実装。
5.できれば、複数フレームの一斉書き出しがほしい。



プロトタイプとして、まずは値の取得から行う。

# -*- coding: utf-8 -*-
import maya.cmds as cmds

oSel = cmds.ls(sl=True,l=True, type='transform')

for i in oSel:
  #name
  print i
  #Rotate
  ro = cmds.xform(i,q=True,ro=True)
  #Trans
  tr = cmds.xform(i,q=True,t=True)
  #Vis
  vis = cmds.getAttr(i+".visibility")
  #Scale
  sc = cmds.xform(i,q=True,s=True)
  print sc

これで値自体は取れた。
ただ、ロックしている値まで取ってきているので、処理的に問題にもなりそう。
ということで、ロック情報を取得するか、除外する方法を考える。
SDK等みていると、「listAttr」というコマンドが使えそう。
このコマンドのフラグにて、
unlocked(u) boolean create
ロックが解除されているアトリビュートのみがリストされます。

f:id:tommy_on:20160905233516p:plain


このフラグがそのまま使えそうですね。
しかもありがたい事に、keyableや、channelBoxなどもありますね。
ではこれを用いて、再度値の取得を書いてみました。

# -*- coding: utf-8 -*-
import maya.cmds as cmds
oSel = cmds.ls(sl=True,l=True,type='transform')
print oSel
for i in oSel:
    #name
    print i
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    print list

で結果。

[u'visibility', u'translateX', u'translateY', u'translateZ', u'rotateX', u'rotateY', u'rotateZ', u'scaleX', u'scaleY', u'scaleZ']

あれ…値が。。。
そうなんです。このままだと値が取れないので、ここからさらにGetAttrをかまして値を取ってみます。

# -*- coding: utf-8 -*-
import maya.cmds as cmds
oSel = cmds.ls(sl=True,l=True,type='transform')
print oSel
for i in oSel:
    #name
    print i
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    print list
    for att in list:
        Tmpsel = cmds.ls(sl=True)[0] 
        getValue = cmds.getAttr(Tmpsel + "." + att)
        print getValue

結果こんな感じ。
[-1.4327737,100.82367,-26.672975]

うん。取れました。


あとこれは余談ですが、個人的にアトリビュートのタイプは分けて処理したほうがいいので、以下の感じで
一回アトリビュートのタイプで篩をかけています。
※大きく意味はないのですが、一応。

# -*- coding: utf-8 -*-
import maya.cmds as cmds
oSel = cmds.ls(sl=True,l=True,type='transform')
print oSel
for i in oSel:
    #name
    print i
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    print list
    for att in list:
        Tmpsel = cmds.ls(sl=True,type='transform')[0] 
        gettype = cmds.getAttr(Tmpsel + "." + att,type=True)
        if gettype == "bool":
            BoolValue = cmds.getAttr(Tmpsel + "." + att)
            print BoolValue
        else:
            getValue = cmds.getAttr(Tmpsel + "." + att)
            print getValue


では、以上で値が取れたきた事を確認しました。
続いては、この情報を選択名とくっつけて、テキスト保存し拡張子を変更しましょう。
まずは、値を実行できるように、「SetAttr」としてまとめていきます。
現状取得できているのは、
「i」で選択物の名前
「att」でアトリビュート
「getValue」でその値
が取得できています。
ここまで取得できてると、setattr用に簡易にまとめれますね。
こんな感じでまとめました。

# -*- coding: utf-8 -*-
import maya.cmds as cmds
oSel = cmds.ls(sl=True,l=True,type='transform')
print oSel
for i in oSel:
    #name
    print i
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    print list
    for att in list:
        Tmpsel = cmds.ls(sl=True,type='transform')[0] 
        gettype = cmds.getAttr(Tmpsel + "." + att,type=True)
        if gettype == "bool":
            getValue = cmds.getAttr(Tmpsel + "." + att)
            print getValue
        else:
            getValue = cmds.getAttr(Tmpsel + "." + att)
            print getValue
        AttrStr =  str(i) + "." + str(att)
        Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)"
        SetValue = "cmds.setAttr" + Value
        #exec(SetValue)

こんな感じで値をまとめれました。
実行したらこんな感じに↓

cmds.setAttr('|Monster_Rig|ALL|COG|Stomach|Chest|Neck|Head.rotateX',-11.0883595763 , clamp = True)
cmds.setAttr('|Monster_Rig|ALL|COG|Stomach|Chest|Neck|Head.rotateY',0.0 , clamp = True)
cmds.setAttr('|Monster_Rig|ALL|COG|Stomach|Chest|Neck|Head.rotateZ',0.0 , clamp = True)

あとはこれをテキストに保存します。
保存の仕方はGoogle先生なんかでよくでてくる感じでまとました。↓
またその際に、保存するディレクトリーを作成し、配置させてます。
ディレクトリーがない場合は、作成する方向で

# -*- coding: utf-8 -*-
import maya.cmds as cmds
import codecs
import os.path

oSel = cmds.ls(sl=True,l=True,type='transform')
for i in oSel:
    #name
    list = cmds.listAttr(i, unlocked = True , visible = True , keyable = True, connectable = True, scalar = True, write = True, hd = True)
    for att in list:
        Tmpsel = cmds.ls(sl=True,type='transform')[0] 
        gettype = cmds.getAttr(Tmpsel + "." + att,type=True)
        if gettype == "bool":
            getValue = cmds.getAttr(Tmpsel + "." + att)
        else:
            getValue = cmds.getAttr(Tmpsel + "." + att)
        AttrStr =  str(i) + "." + str(att)
        Value = "(¥'" + str(AttrStr) + "¥'," + str(getValue) + " , clamp = True)"
        SetValue = "cmds.setAttr" + Value
        #exec(SetValue)

ScenePath = cmds.internalVar(uad = True) + 'SavePose'
ImportText = "import maya.cmds as cmds"
if not os.path.exists(ScenePath): 
	os.makedirs(ScenePath)

TextOpen = codecs.open(ScenePath+"\SavePose.py","w","utf-8")
TextOpen.write(ImportText)
TextOpen.write("\r\n")
TextOpen.write(SetValue)
TextOpen.write("\r\n")
TextOpen.close()



"""
使わないけど、拡張子変更する方法も記載。
file_path = ScenePath+"\SavePose.txt" 
basefile, ext = os.path.splitext(file_path)
basefile, ext = os.path.splitext( os.path.basename(file_path) )
print ScenePath + "\\" + basefile + ".py"
os.rename(file_path, ScenePath + "\\" + basefile + ".py")
"""


すげぇ汚いっすね。
今後スクラッチかけますわ‥
とりあえず、リグの頭を選んで実行すると、以下の画像みたくなります。
※拡張子がテキストになってますが、間違いです。。。
正式には、「.py」になってます。
f:id:tommy_on:20160906140906p:plain

はい。
これでテキスト出力までできました。
次はこれらを複数できるようにカスタムし、実行文とUIをやっていきます。

が、長くなったので一旦ここで切ります。

大体ここまでで2時間ぐらいでしょうか‥


それでは‥

フェースを表示トグルするツール

久しぶりに更新します。

とある事で必要に駆られたので作りました。
※WEBにもっといいやつありますし、あたらしいエクステンションには標準で搭載されてますが…


ポリゴン表示をトグルです。


一応ルールがあります。
1.初めてフェースを非表示にする時は、オブジェクトのフェース(消したい所)を選んで実行して下さい。
2.実行後は、フェース選択モードからオブジェクトモードに戻して下さい。
3.1回実行した後は、アウトライナーに【Poly_Hide_Set】という名のSetができるので、それを選んでスクリプトを実行して下さい。
 それで非表示だった箇所が表示されます。
4.もう1回【Poly_Hide_Set】という名のSetを選んで実行すると、再度非表示になります。
 これを繰り返してお使い下さい。
5.提出、納品時は【Poly_Hide_Set】という名のSetを選んで【Ctrl】キーを押してスクリプトを実行して下さい。
 これで、setsと、ヒストリーが削除され、綺麗になります。

あと、連打実行はNGで、【Poly_Hide_Set】という名のSetの名前は絶対に変えないで下さい。
というより、名前が変わった際は、手動で消して下さい。

詳しくは動画にて。

SelPoly_Hide from tommy_on on Vimeo.


以下コード

# -*- coding: utf-8 -*-
import maya.cmds as cmds
import maya.mel as mel	

def SelPoly_Hide():
	#選択をリストに格納
	oSel = cmds.ls(sl=True)
	#フラグ立て
	Check = 0
	#選択されていなかったら
	if oSel == []:
		return
	#キーボードステートをチェック
	mods = cmds.getModifiers()
	#コントロール押していたら、関数に飛んで終了
	if (mods & 4) > 0:
		if oSel[0] == "Poly_Hide_Set":
			Toggle(oSel)
			Clean()
			return
	#選択がセットで且つ、コントロール押していなかったら。
	if oSel[0] == "Poly_Hide_Set":
		Toggle(oSel)
		return
	#フェースを選択
	oSelFace = cmds.filterExpand(sm=34)
	#フェースじゃなかったら
	if oSelFace == None:
		return
	#フェースからオブジェクトを取得
	const =  oSelFace[0].rpartition(".")
	#ヒストリーを取得
	oList = cmds.listHistory(const[0])
	for i in oList:
		#穴開けるヒストリーがあったら、フラグを立てる
		if i == "Hide_Poly":
			Check = 1  
	#フラグが立っていたら
	if Check == 1:
		cmds.polyHole(assignHole=0)
	#なかったら
	else:
		mel.eval('ToggleHoleFaces;')
		FaceSet = cmds.sets(n="Poly_Hide_Set")
		cmds.polyHole(assignHole=1)
		cmds.rename("polyHoleFace1","Hide_Poly")
	
#単純に表示を切り替えているだけ
def Toggle(oSel):
	cmds.select(hierarchy=True)
	mel.eval('ToggleHoleFaces;')
	cmds.select(cl=True)

#ヒストリとセットを削除
def Clean():
	cmds.delete("Hide_Poly")
	cmds.delete("Poly_Hide_Set")



SelPoly_Hide()

【覚書】maya python入門 其の十三

今回はmaya.cmdsを使ってXMLの簡単な例を書いてみたいと思います。

SIではJSを使えば、結構簡単に出力、読み込みができましたが
Pythonでやるのが、未経験だったのでメモに。

今回は、シーンのディスプレイレイヤーを取得し
それをXMLとして出力してみます。
早速コード

# -*- coding:utf-8 -*-
import maya.cmds as cmds
import xml.dom.minidom as xml
import codecs

imp_XML = xml.getDOMImplementation()
doc = imp_XML.createDocument(None, 'SceneLayers', None)
top_element = doc.documentElement
Disp_Layers = cmds.ls(typ ="displayLayer")
for i in Disp_Layers:
    Member = cmds.editDisplayLayerMembers( i, query=True )
    Layer = doc.createElement('Layer')
    Layer_attr = doc.createAttribute("layer_name")
    Layer_value = doc.createTextNode(str(Member))
    Layer.appendChild(Layer_value)
    Layer_attr.value = str(i)
    Layer.setAttributeNode(Layer_attr)
    top_element.appendChild(Layer)
print(doc.toxml('UTF-8'))

# ファイルに保存
f = codecs.open('sample.xml', 'w', 'utf-8')
doc.writexml(writer=f, encoding='UTF-8', newl='\n', addindent='\t')
f.close()

f:id:tommy_on:20151006000640p:plain
上記のコードを実行するとmayaのデフォルトフォルダ
C:\Program Files\Autodesk\Mayaバージョン\bin
XMLが保存されます。
※必要に応じてディレクトリーは変えてください。


そうすると、こんな感じで出力されています。
f:id:tommy_on:20151006000434p:plain


出力はこんな感じ。
細かいコマンドについては、他を参照いただければと思います。
超カンタンです。


では、次は簡単な解析を

# -*- coding:utf-8 -*-
from xml.dom import minidom
# ----- DOM生成 -----
# DOM 実装を取得


doc = minidom.parse('c:\\Program Files\\Autodesk\\Maya2016\\bin\\sample.xml')


elements = doc.getElementsByTagName("Layer")

for i, element in enumerate(elements) :
    print(element.childNodes[0].data )

XMLファイルを取得して、【Layer】以下のタグを取得しているだけ。
ここからMAYAで色んな処理を、噛ますと色々できるかと思います。

私は今回MAYA←→SIでレイヤー構造がうまくいかないので
作成しておりました。
細かい仕様は書かないですが、レイヤー以外にも
マテリアルや、モーションにも使えそうですね。


以上です。

argsってなんですのん?

会社の人に説明するときに、単純に【引数の事】でアーギュメントの略だよ。
と伝えても、うん?
って感じだったので、コードと共に説明した事をメモ。

def test_code(*args):
    print args[0]
    print args[1]

test_args1 = "code1"
test_args2 = "code2"
test_code(test_args1,test_args2)

結果

>>> 
code1
code2
>>> 


これも

def test_code(*args):
    print args[0]
    print args[1]
    print args

test_code("test1","test2")

結果

>>> 
test1
test2
('test1', 'test2')
>>> 

こうすると、理解されたので、そのままメモしときます。
要は、【*Args】としておけば
関数の引数をタプルで指定したまま使用できる感じです。

キーワード付きの場合は、【kwargs】で代用できます。

def keyward_args(**kwargs):
	for key, value in kwargs.iteritems():
		print key , value

keyward_args(test1="test1_code",test2="test2_code",test3="test3_code")

結果

>>> 
test1 test1_code
test3 test3_code
test2 test2_code
>>> 

こんな感じです。
この辺りが微妙にややこしいのがハードル高くしてますよね。



あ、あとこれは私感なんですが、これ記述しておけば
体外の引数が受け取ってくれるような気がしますね。

以上です。

【覚書】maya python入門 其の十二

今回もWEBで学んだことを書いていきます。(確かWEBだったはず…)


内容は頂点にモデルを配置するといった、簡単なモノです。
ここから、ランダムにしたり、計算によって配置したりできますね。
いきなりコードを…

import maya.cmds as cmds
if cmds.window("RandomWindow" , exists=True ):
	cmds.deleteUI("RandomWindow")
	
RandomWindow = cmds.window( "RandomWindow" , t="Ramdom Set Script" , w=300 , h=300 )
cmds.columnLayout( adj=True )
cmds.separator( h=10 )
cmds.text( "Ramdom_Vertex" )
cmds.separator( h=10 )
cmds.button( l="Ramdom_Tree" , h=50 , c="ramGenerator()")
cmds.separator(h=10)

cmds.showWindow(RandomWindow)


def ramGenerator():
	oSel = cmds.ls(selection = True , fl = True)
	for i in oSel:
		vertexPos = cmds.xform(i, q=True , ws=True , t=True )
		GetTree = tree()
		cmds.move(vertexPos[0],vertexPos[1],vertexPos[2],GetTree)
		
		
def tree():
	Tree_Trunk = cmds.polyCylinder( r=0.3 , h=6 , sx=8 , n="Green_Trunk")
	cmds.move( 0 , 6/2.4 , 0 , Tree_Trunk )
	Tree_Leaves = cmds.polySphere( r=3 , sx=8 , sy=8 , n="Green_Leaves")
	cmds.move( 0 , 5 , 0 , Tree_Leaves )
	cmds.scale( 0.5 , 1.25 , 0.5 ,Tree_Leaves )
	cmds.select( Tree_Trunk , Tree_Leaves )
	Marge_Tree = cmds.polyUnite( ch=False )
	cmds.rename("Tree#")

使い方は実行するだけですね。
頂点を選んで
f:id:tommy_on:20150817110301j:plain
ボタンを押すだけ。
f:id:tommy_on:20150817110456j:plain

正直めちゃくちゃ簡単なんで、内容は省きます。

と今回は手抜き…


以上です。

【覚書】maya python入門 其の十一

かなり番外編ですが、11…





とある理由でデータが壊れる事があったので、MAYAで自動バックを作成する
ツールを作りました。



このスクリプトはセーブ実行時にそのデータを指定フォルダに
コピーし、日時で細かく管理、バックアップするツールです。
誤ってセーブしてしまい、一個前に戻りたい…
とかいう時に使用して下さい。
個人的に、あまりナンバリングをシーンにつけるのが
好きじゃないので作成しています。
私はナンバリング派だから。
という人はスルーして下さい。


下記に記載している、コマンドを「AutoBackUP.py」として保存し

C:\Users\ユーザー名\Documents\maya\バージョン\ja_JP\prefs\scripts
英語の場合は
C:\Users\ユーザー名\Documents\maya\バージョン\prefs\scripts
に配置して下さい。

んでMAYA起動。
その後このスクリプトをセーブのショートカットに割り当てます。
手順は以下の通り。
まずはホットキーに新規マッピングします。
マッピングは以下の所から変更できます。
f:id:tommy_on:20150807170317j:plain

続けて
ホットキーエディタで、コマンドを作成します。
「カテゴリー」から今回は「File」を選択します。
f:id:tommy_on:20150807170448j:plain

さらに右下の「新規」を押して
下部にあるコマンドに以下のコマンドを入力してください。

import AutoBackUP
AutoBackUP.Auto()

f:id:tommy_on:20150807170257j:plain

入力が完了したら、ホットキーを割り当てます。
右上にある、「新しいホットキーの割り当て」
にて、ご自身の好きなコマンドを割り当ててください。
私はショートカットのセーブ時に必ず行いたいので
「Ctrl+S」にしてます。
なので、既存のセーブシーンの上書きです。
※普通のセーブシーンがホットキーから外れます。
後は下にある、「割り当て」を実行して下さい。
以上で完了です。

実行してみると、こんな感じになります。
これが実行前。
f:id:tommy_on:20150807170357j:plain

んで実行すると
f:id:tommy_on:20150807170352j:plain

こんな感じでプロジェクトフォルダのシーンフォルダ内に「Ver」と命名されたフォルダができます。
この中にバックアップデータができていきます。
f:id:tommy_on:20150807170339j:plain

画像にも書いてますが、セーブ事にできるので容量には気をつけて下さい。

あと、これは改善予定ではあるんですが、バージョン違いでシーンを開いた時は
このスクリプトがうまく適用できない事があるかと思います。
その時は、一度メニューのセーブを行なってから実行してみてください。
これで大概いけるはずです。

以上です。



以下コード

import maya.cmds as cmds
import os.path
import shutil
import datetime
 
def BackUPP():
    day = datetime.datetime.now()
    Time = day.strftime("%m-%d_%Hh%Mm%Ss")
 
    ScenePath = cmds.file( query=True, lastTempFile=True).rpartition( "/" )[0] 
    Scene_Name = cmds.file( query=True, sn=True)
    Scene_Name_Only = cmds.file( query=True, sn=True , shn=True)
 
    Path = ScenePath + "/ver/"
    if not os.path.exists(Path): 
        os.makedirs(Path)
 
    Rename = str(Path)+str(Scene_Name_Only)+"_"+str(Time)+".ma"
    shutil.copyfile(Scene_Name, Rename)
 
 
def Auto():
    cmds.file( save=True)
    BackUPP()