MODO801

MODO801が発表されたかと思ったら、早速英語版が先行して販売されました。
ローカライズが終わったら日本語版が手に入る、という701の時と同じ形式だそうなので、アップグレードしました。

 

今回の目玉は、ノードベースシェーダ、Mari、Nukeとの連携でしょうか。
リアルタイムモデルでの恩恵は余りありません。
UV機能の向上とベイク機能の向上くらいでしょうか。

 

今回は見送ったほうが良かったかもしれない。とちょっと思っております。
ただ、調べたところ、801で吐き出されたFBXでは頂点カラーの情報を頂点フェイスで吐き出している。
しかし、吐き出されるのはカラー情報だけでインデックス情報がないのでMayaでそのまま読み込むとおかしくなる。

 

カラーの配列から順番にインデックスを生成すればいいだけなので、素直に吐き出してくれればいいのに、、
とMODOに文句をいうか、読み込むときにその順番でインデックスを生成すればいいのに、、とMayaに文句をいうか。
Colladaではきちんと再現出来た。でも選択セットが吐き出されない、、
うーん、惜しい。

 

カラーマネージメントがデフォルトで入ったのはありがたい。
レンダリングのガンマを変更すると、読み込んでいるイメージのガンマも変更してくれるわけではないのかな?
まだその辺りは分からない。

Maya Script Python – DAGノード名

結局作ってしまった、、
必要性に駆られ、MayaからMODOにハードエッジの情報を持ってゆくために、ひとつのエッジに含まれる2つの頂点インデックス
をリスト化し、MODOで読み込んでエッジ選択。

 

ただ、あまりにも急ごしらえのため公開はしません。
sqlite3でやりとりすることを考えておりましたが、今すぐに必要だったため、開発にちょっと時間がかかると思い、pickleで済ませました。
で、その時今まで散々MayaのDAGノードの名前に悩まされていたことを思い出しました。
最早バグといってもいいような問題ですが、Mayaは名前の重複があり得る。
選択したDAGノードを複製すると、そのノードの名前はユニーク名が割り当てられるにも関わらず、下層のノードは同じ名前になる。
なので、フルパスでノード名を吐き出すときのファイル名にしてしまうと「|」記号が入ってしまう。ファイル名にその記号が使えないので、
エラーとなる。

 

データベースであれば、変数の一つとして扱われるので問題ない。
もちろんファイル名でもノードの名前ではなく、シーン名やプロジェクト名にすれば問題ない。
でもデータベースのほうが、色々と便利だ。

 

全く困ったものだ。
恐らく今更変えられないのだろう。古いデータがダメになる可能性がある。

 

それはそうと、Maya2015ではようやく読み込まれている画像が更新されたら自動でリロードする機能が付いたそうですね。
ただ、プリファレンスで設定しないと作動しない。というのはいかがなものだろうか?いちいち切らなければならない理由が見つからない。
まぁ、文句はこの辺までに。

 

SubstancePainter、まだベータ版ではありますが、凄い進化ですねぇ。
自宅の古いマシンでは重いのですが、、
フォトショップでいうところの「ベタ塗りレイヤー」にSubstanceが読み込めるのが面白い。
ダイナミックアトリビュートを作っておけば、パラメータも調整できる。マスクをペイントしたり、マスクにビットマップを読み込めたりと、素晴らしい。
一番気になっていたのはUV境界でのペイントですが、MODOはもちろん、3Dcoatに比べても精度が高いように見える。
ただしスムーズブラシがないのが、難点。
X軸でのシンメトリー機能が追加されたらどうなるのだろうか?
通常のブラシはともかく、パーティクルはさすがに無理なんじゃないかなぁ。と、考えております。
正式版ではパーティクルの設定ももっと細かくできるのだろうか?使っていると、ちょっと物足りなさを感じてしまう。

Maya Script – MayaとMODOでのデータのやり取り

MayaとMODOでデータをやりとりするときに、どのフォーマットが最も良いか?
候補としては、obj、fbx、dae、abc。が挙げられるかと思います。

 

objは殆どのツールで読み込めるので、手堅いですが色々と制限があるので最終手段。といった感じでしょうか。
fbx、こちらも最早殆どのツールで読み込むことができるので、お手軽且つ機能的で使用頻度は高いです。
dae、まだ対応していないツールがあるので中々手を出すことがありません。
abc、使ってみた感じ、映像ではシームレスにツールを行き来できるフォーマットとして使用されておりますが、ゲームでは色々と足りていないように感じます。

 

結論で言うと、自分はfbxを多用します。
大抵はそれだけで問題はありません。
で、そのfbx。最近気がついたのですが、選択セット(Mayaではセレクションセット)を保存することができる。
何が言いたいかというと、今までMODOとMayaのやりとりで一番困っていたのはハードエッジの情報でした。
MODOではポリゴン単位でそれを設定する必要があるため、思ったようにハードエッジを設定することが難しく、そのためMayaに持ってゆく時は、
UVを1つ追加し、ハードエッジに設定したいエッジに合わせて展開したUVを持つ、などということをしておりました。

 

しかし、選択セットが保存できれば、その問題が解決できる。と、試しに使ってみました。
MODOでは2013のバージョンのfbxを使って出力すると、選択セットが保存できます。
Mayaに持ってゆくときちんとセットとして情報があることがアウトライナで確認でき、選択することが可能です。

 

ただ、ゼロからMayaで作ったものをMODOに持ってゆくと、、
エッジのインデックスが合っていない、、
そもそもMODOにはエッジにインデックスという概念が多分ありません。
2つの頂点のインデックスを指定することでエッジの選択が行われている。
恐らくMayaは独自の方法でエッジのインデックスを作っているのでしょう。他のツールで作ったfbxデータであれば、保存されている選択セットとエッジインデックスは一致する。
自社のファイルフォーマットなのに、どういうことだろうか?いい加減にして欲しい、、

 

選択セットが保存できると分かってスクリプトを作ってみましたが、結局役立たず。
対処するとなると、fbxを作るときはアスキーで固定し、出力後にスクリプトで編集するようにする。というのと、
ローカルにデータベースを作成し、ファイル名と対応するエッジの頂点インデックスを保存しておき、MODO側でそれにアクセスして適応。という感じだろうか。
前者であれば、アスキー固定になってしまうが、後者であればそういった制限はない。うーん、そこまでする?
と、面倒臭くなり、止まりました。

 

それと、もう一つ。
頂点カラーエディタを作っていたので、MayaとMODOで頂点カラーのやりとりもテストしてみました。
アスキーで保存すると良く分かるのですが、Mayaで保存されたデータは頂点カラーをフェイス頂点で持っているのに対して、MODOでは頂点単位で持っている。
MODOでは裏ワザ的な方法でしかフェイス頂点にカラーを適用できない。
うーん、もどかしい。結局中途半端なことしかできない。ということでした。

 

それはそうと、DF-TALKさんでMayaコマンド、Pymel、OpenMaya、MayaAPI2.0、C++での速度比較の記事が書かれている。
あー、やっぱりAPI2.0ってまだ中途半端なんだぁ、と分かってガッカリ?ホッとした?そんな感じです。
しかしC++よりも早い、というのは驚きでした。どういうことなのだろうか。スクリプトで組んでも結局最後はコンパイラー言語で実行されるのだろうから、
あり得なさそうだけど、ありえるのですねぇ。しかし、デコーダそんな機能があるなんて、知りませんでした、、Python奥深いなぁ。

Maya Script Python – カラークラス

何気なく気が向いたので、頂点カラーエディタをapi2.0で作り始めました。
とりあえずの実装は1時間ほどで終わり、数万ポリゴン程度であれば、リアルタイムでの更新も重さとしては問題なさそうです。
ただ、更新がきちんとされない場合がある、、
apiで編集した結果をビューポートに再現するときは、MFnMesh関数セットのupdateSurface()を使うようですが、
なんだか思うようにならない。
全ての変更を適用後、一度だけメソッドを実行すれば良い。というような事が書かれているかと思うが、
人によってはポリゴン単位で実行している。

 

うーん、良く分からない。
プラグイン化すれば済む話なのだろうか。

 

それはそうと、カラーを編集する際に、apiではMColorクラスをインスタンス作成時に値を入れて作成したものを使いますが、
MColorクラスは大した機能を持っていない。(多分)
PymelのColorクラスでは色々な機能が存在するので、値の編集には便利そうだ。
しかも継承している元のクラスがVectorなので、そっちのメソッドも使うことができる。
例えばこんな感じ。

import maya.api.OpenMaya as om2
import pymel.core as pm

pColor = pm.datatypes.Color([0.5,0.5,0.5])
mColor = om2.MColor([0.5,0.5,0.5])
pColorB = pm.datatypes.Color.black

print "{0:-^30}".format("MColor")
print mColor
print mColor * 2
print mColor.r,mColor.g,mColor.b,mColor.a
print "{0:-^30}".format("pm.datatypes.Color")
print pColor
print pColor * 2
print pColor.hsva
print pColor.gamma(2.0)
print pColor.normal()
print pColorB

上記を実行すると、
————MColor————
(0.5, 0.5, 0.5, 1)
(1, 1, 1, 1)
0.5 0.5 0.5 1.0
——pm.datatypes.Color——
[0.5, 0.5, 0.5, 1.0]
[1.0, 1.0, 1.0, 2.0]
(0.0, 0.0, 0.5, 1.0)
[0.25, 0.25, 0.25, 1.0]
[0.577350258827, 0.577350258827, 0.577350258827, 1.0]
[0.0, 0.0, 0.0, 1.0]
と表示される。
Pymelの方では、hsvに変換したり、ガンマ補正をかけたり、ベクトルとしてノーマルを求めたりすることができる。
ただ、*2などの掛け算をするとアルファの値にまで影響が及ぶのがいただけない。

Mayaのオンラインドキュメント

少し前にMayaの2015が発表されました。
2014からあるモデリングツールというのはNEXを作っている会社を買収して作成されたものなのですねぇ。
2015で更なる進化を遂げているようですが、日頃MODOを使っていると、今更そんな機能を大々的に宣伝しているのか、とちょっと呆れてしまいます。
でもグイグイと確実に追い込んできているので、今後どうなるのか分かりません。
そもそも、XSIも無くしたことだし、MAX一本のみに絞るのではないか?そんな事も考えてしまいます。
まぁ、どうでもいいや。

 

2015のオンラインドキュメントを見つけたので、リンクしておきます。
まずはMayaコマンド
次にPymel
そしてAPI2.0

 

API2.0、随分とスッキリして見やすくなりました。
ただ、2014のドキュメントは翻訳機能が付いていたので、その点でちょっと便利でした。
こちら

 

ドキュメントを見ていると、知らないコマンドが沢山ある。
Pymel、さすがに便利そうな関数やクラスが沢山あります。

 
それはそうと、MODOも801がアナウンスされています。
予想よりも早い、、懐の都合が、、
既存ユーザーには50%オフだそうですが、Foundryと提携してから元の値段が上がっているから、今までどおりなのだろうか。
楽しみのような、恐ろしいような。

Maya Script Python – ファイルエクスプローラ(仮)

import pymel.util.path as pmp
import pymel.core as pm
import maya.cmds as cmds
import maya.OpenMaya as om
import maya.OpenMayaUI as OpenMayaUI
from PySide import QtGui, QtCore
import shiboken

class TestQtWindow(QtGui.QMainWindow):
	def __init__(self,parent=None):
		super(TestQtWindow,self).__init__(parent)
		self.setWindowTitle('Maya Explorer')
		self.resize( 600, 800 )
		
		menuBar = self.menuBar()
		menuBar.addMenu( "memu" )
		
		widget = QtGui.QWidget()
		self.setCentralWidget(widget)
		
		mainLayout = QtGui.QVBoxLayout()
		
		widget.setLayout(mainLayout)
		
		self.line = QtGui.QLineEdit(self)
		self.line.editingFinished.connect(self.put_text)
		
		self.fileBrowserWidget = QtGui.QWidget(self)
		
		self.dirmodel = QtGui.QFileSystemModel()
		self.dirmodel.setRootPath("C:/")
		
		self.folder_view = QtGui.QTreeView(parent=self)
		self.folder_view.setModel(self.dirmodel)
		self.folder_view.clicked[QtCore.QModelIndex].connect(self.clicked) 
		self.folder_view.doubleClicked[QtCore.QModelIndex].connect(self.doubleClicked) 
		
		self.folder_view.setColumnWidth(0,250)
		
		mainLayout.addWidget(self.line,1,0)
		mainLayout.addWidget(self.folder_view)
	
	def clicked(self,index):
		index = self.folder_view.currentIndex()
		dir_path = pmp(self.dirmodel.filePath(index))
		self.line.setText(dir_path.normpath())
	
	def doubleClicked(self,index):
		index = self.folder_view.currentIndex()
		path = pmp(self.dirmodel.filePath(index))
		self.open_scene_file(path)
	
	def put_text(self):
		path = self.line.text()
		index = self.dirmodel.index(path)
		self.folder_view.setCurrentIndex(index)

	def open_scene_file(self,path):
		fbxPlug = "fbxmaya"
		types = {".ma":"mayaAscii",".mb":"mayaBinary",".fbx":"FBX",".obj":"OBJ"}
		options = {".ma":"v=0",".mb":"v=0",".fbx":"fbx",".dae":"dae",".obj":"obj"}
		if path.ext.lower() not in types.keys():
			return
		sceneName = pm.sceneName()
		fileType = types[path.ext.lower()]
		option = options[path.ext.lower()]
		scenePath = path.replace("\\","/")
		prjPath = path.parent.normpath().rsplit("\\scenes",1)[0]
		io = om.MFileIO()
		if not sceneName:
			#pm.openFile(scenePath,f=1,op=option,typ=fileType)
			#cmds.file(scenePath,f=1,op=option,typ=fileType,o=1)
			io.open(scenePath,fileType,1)
			self.add_rectnt_file(scenePath,fileType)
			self.set_project(prjPath.replace("\\","/"))
		else:
			flag = 1
			while flag:
				result = pm.confirmDialog(t="File Open",m="New Scene Open or Import Scene?",b=["New Scene","Import Scene","Cansel"],db="New Scene",cb="Cansel",ds="Cansel")
				if result == "Cansel":
					return
				else:
					flag = 0
			if result == "New Scene":
				#pm.openFile(scenePath,f=1,op=option,typ=fileType)
				#cmds.file(scenePath,f=1,op=option,typ=fileType,o=1)
				io.open(scenePath,fileType,1)
				self.add_rectnt_file(scenePath,fileType)
				self.set_project(prjPath.replace("\\","/"))
			elif result == "Import Scene":
				pm.loadPlugin("{0:}.mll".format(fbxPlug),qt=1)
				if fbxPlug not in pm.pluginInfo(q=1,ls=1):
					#pm.system.displayWarning("{0:} Plugin in not loaded".format(fbxPlug))
					#om.MGlobal.displayWarning("{0:} Plugin in not loaded".format(fbxPlug))
					#pm.system.displayError("{0:} Plugin in not loaded".format(fbxPlug))
					om.MGlobal.displayError("{0:} Plugin in not loaded".format(fbxPlug))
					return
				#pm.importFile(scenePath,type=fileType,ra=1,ns=path.name.replace(".","_"),op=option,lrd="all")
				#cmds.file(scenePath,i=1,type=fileType,ra=1,ns=path.name.replace(".","_"),op=option,lrd="all")
				io.importFile(scenePath,fileType,1,str(path.name.replace(".","_")))
		[cmds.setAttr(x+'.ftn',cmds.getAttr(x+'.ftn'),type='string') for x in cmds.ls(typ='file',type='mentalrayTexture')]

	def set_project(self,project):
		pm.Workspace.open(project)
		maxSize = pm.optionVar["RecentProjectsMaxSize"]
		[pm.optionVar(rfa=["RecentProjectsList",i]) for i,x in enumerate(pm.optionVar["RecentProjectsList"]) if project == x]
		pm.optionVar(sva=["RecentProjectsList",project ])
		prjList = pm.optionVar["RecentProjectsList"]
		if len(prjList) > maxSize:
			[pm.optionVar(rfa=["RecentProjectsList",0]) for i,x in enumerate(prjList) if i >= maxSize]

	def add_rectnt_file(self,filePath,fileType):
		maxSize = pm.optionVar["RecentFilesMaxSize"]
		[[pm.optionVar(rfa=["RecentFilesList",i]),pm.optionVar(rfa=["RecentFilesTypeList",i])] for x in enumerate(pm.optionVar["RecentFilesList"]) if filePath == x]
		pm.optionVar(sva=["RecentFilesList",filePath])
		pm.optionVar(sva=["RecentFilesTypeList",fileType])
		fileList = pm.optionVar["RecentFilesList"]
		if len(fileList) > maxSize:
			[[pm.optionVar(rfa=["RecentFilesList",0]),pm.optionVar(rfa=["RecentFilesTypeList",0])] for i,x in enumerate(fileList) if i >= maxSize]


ptr = OpenMayaUI.MQtUtil.mainWindow()
widget = shiboken.wrapInstance(long(ptr),QtGui.QWidget)

testWin = TestQtWindow(widget)
testWin.show()

Mayaで使えるウィンドウズエクスプローラの様なものです。
Pysideが入っていないと使えません。
2014からデフォルトで入っていたかと思うのですが、バージョンが違う場合はインストールが必要かと思います。

 

このスクリプト、随分と前に書きました。
PysideのQFileSystemModel()クラスが凄く、Mayaの外で更新されたファイルやディレクトリの情報も勝手に取ってくれます。
なので、このエクスプローラも自動で更新されます。
MayaのUI機能だけでこういったものを作ると、必ずリロードボタンをつけていました。出ないと更新が取得できない。
ただ、始めに表示された時のドライブリストが納得行かない。順番が綺麗にならない、、
調べてみると、どうやら現状のPysideのバグだ、という話がありました。
で、ちょっとげんなりして開発を止めていました。
更には、ウィンドウズエクスプローラのように、左にツリービュー、右にディレクトリやファイルのリストを表示したかったのですが、
やり方が分からなかったので、これも理由に開発を止めていました。

 

なので、プロトタイプです。
ただ、機能としては実用性はあるので、プロトタイプはプロトタイプとして公開しておきます。

 

スクリプトを実行すると、ウィンドウズエクスプローラの様なウィンドウが表示されます。
使い方はだいたい同じですが、ツリービューにファイルも表示されます。
ファイルの拡張子が、「.ma」「.mb」「.fbx」「.dae」「.obj」であればシーンに読み込むことができます。
Maya上でシーンが開かれていない場合は、そのまま開きます。
すでにシーンが開いている場合は、新たに読み込むか、シーンにインポートするかを選べます。

 

このスクリプトを使ってファイルを開くと、
プロジェクトのセットを自動で行います。
最近使ったファイル、最近使ったプロジェクトに登録も行います。
開いたシーンファイル内のテクスチャもリロードします。

 

なので、このスクリプトでシーンを開くと、その他の余計な操作が不要になります。
というか、デフォルトでそれくらいの事はできて欲しい、、

 

で、備忘録ついでに、
ファイルの読み込み、エラーやワーニングの表示に、
Pymel、Mayaコマンド、OpenMayaの三種類で同じ事を実現するためにそれらをコメントアウトして記載しておきました。
デフォルトで使用されているのはOpenMayaです。
ただ、MayaコマンドでPymelやOpenMayaの様なエラーやワーニングの出し方を忘れました。
errorコマンドだと違う結果になる。まぁ、必要ないからいいや。ということで。

Maya Script Python – 選択オブジェクトのエクスポート

開いているシーンで選択しているオブジェクトを手早くエクスポートしたい。
そんな時のスクリプトです。
メッシュノードを選択し、実行します。
開いているシーンが存在するフォルダに出力されます。
シーンを開いていないとダメです。

import maya.OpenMaya as om
import pymel.core as pm
import pymel.util.path as pmp

def fbx_dae_export(eType = "fbx"):
	pluginName = "fbxmaya"
	fileTypes = {"fbx":"FBX export","dae":"DAE_FBX export"}
	pm.loadPlugin("{0:}.mll".format(pluginName),qt=1)
	if pluginName not in pm.pluginInfo(q=1,ls=1):
		pm.system.displayWarning("{0:} plugin is not loaded".format(pluginName))
		return
	sName = pmp(pm.sceneName())
	if not sName or not sName.exists():
		return
	meshs = pm.ls(sl=1,o=1)
	if meshs:
		exMeshs = [x for x in meshs if type(x)==pm.nt.Mesh or type(x)==pm.nt.Transform]
		if exMeshs:
			exPath = sName.parent
			name = exMeshs[0].name()
			io = om.MFileIO()
			io.exportSelected(exPath.joinpath(name).replace("\\","/") + "." + eType,fileTypes[eType])

fbx_dae_export()

最後の実行部分「fbx_dae_export()」のところに「fbx_dae_export(“dae”)」とするとcolladaで出力できます。

 

因みに、出力の設定はしません。
なので、予め設定が必要です。
MODOとMayaでやりとりする場合、fbxであれば単位系はデフォルトの「自動」で大丈夫です。colladaの場合は「メートル」に変更すれば、大丈夫です。
MODO側では「ゲーム単位系」の「1.0」にすれば大きさを揃えることができます。

 

しかしAPI2.0にはファイル関係のクラスが見当たらない。
やはり2.0はサブ的なものなのだろうか。

Python – Pythonのモジュール群

もはや最近は完全な備忘録。
Pysthonのモジュールを調べているとこんなページを発見。

 

その名の通り、Pythonの追加モジュールが沢山ある。
その中でたまたま知ったのが、OpenCV。
Cのライブラリで、画像認識関係のものが入っているらしい。
画像に含まれる、人の顔である部分の検出などは面白そう。ニューラルネットワークなんてのもある。何に使うのだろう、楽しそうだ。

 

最新の科学研究では、開発コストの低さからPythonが選ばれることが多いそうだ。
Cythonを使えば実行コストも抑えられるので、良いらしい。

 

とにかく欲しいライブラリが一つのところで探せるのが助かる。

アニメーションが親切に解説されております

レンダリング、ライティングの基本が分かります

図版が見やすい美術解剖書です