UVを数値入力できるツール

こんにちは!Rhinoです。
PS5上で動いているUE5のテックデモ凄かったですね~
皆さんもご覧になられましたか?
久しぶりにワクワクドキドキしてしまいました。

さて、今日は前回作成したウィンドウに機能を追加してみようと思います。
追加するのは選択しているUVの座標の表示と数値入力する機能です。
Maya2017 Update3でUVエディタが新しくなったことで排除された機能なのですが、個人的に便利だと思いますので作ってみました。

# -*- coding: utf-8 -*-
import maya.cmds as cmds
from decimal import Decimal, ROUND_HALF_UP

def getUVs( *args ):
	selection = cmds.ls( sl=True, fl=True )
	uvmaps = cmds.filterExpand( selection, sm=35 )
	if uvmaps != None and len( uvmaps ) > 0:
		uvs = []
		for uvmap in uvmaps:
			uvs.append( cmds.polyEditUV( uvmap, q=True, u=True, v=True ) )
			
		bias = str( cmds.floatField( 'fFld_bias', q=True, v=True ) )
			
		unique_u = list( set( [ Decimal(str(uv[0])).quantize(Decimal(bias), rounding=ROUND_HALF_UP) for uv in uvs ] ) )
		unique_v = list( set( [ Decimal(str(uv[1])).quantize(Decimal(bias), rounding=ROUND_HALF_UP) for uv in uvs ] ) )

		if len( unique_u ) == 1:
			cmds.floatField( 'fFld_UValue', e=True, v=float(unique_u[0]) )
		else:
			cmds.floatField( 'fFld_UValue', e=True, v=0.0 )

		if len( unique_v ) == 1:
			cmds.floatField( 'fFld_VValue', e=True, v=float(unique_v[0]) )
		else:
			cmds.floatField( 'fFld_VValue', e=True, v=0.0 )
			
def enterUValue( *args ):
	selection = cmds.ls( sl=True, fl=True )
	uvmaps = cmds.filterExpand( selection, sm=35 )
	if uvmaps != None and len( uvmaps ) > 0:
		uValue = cmds.floatField( 'fFld_UValue', q=True, v=True )
		cmds.polyEditUV( uvmaps, r=False, u=uValue ) 

def enterVValue( *args ):
	selection = cmds.ls( sl=True, fl=True )
	uvmaps = cmds.filterExpand( selection, sm=35 )
	if uvmaps != None and len( uvmaps ) > 0:
		vValue = cmds.floatField( 'fFld_VValue', q=True, v=True )
		cmds.polyEditUV( uvmaps, r=False, v=vValue ) 

def createGUI( *args ):
	winTitle = 'winUVCoordEditor'
	winTitleName = 'UV Coordinates Editor'
	
	if cmds.window( winTitle, exists=True ):
		cmds.deleteUI( winTitle, window=True )
    
	winUVCoordEditor = cmds.window( winTitle, title=winTitleName )
	
	cmds.columnLayout()
	
	cmds.rowLayout( nc=4 )
	cmds.text( l='UV: ' )
	cmds.floatField( 'fFld_UValue', w=100, pre=4, ec=enterUValue )
	cmds.floatField( 'fFld_VValue', w=100, pre=4, ec=enterVValue )
	cmds.button( l='Get', c=getUVs, w=100 )
	cmds.setParent( '..' )
	
	cmds.rowLayout( nc=2 )
	cmds.text( l='Bias: ' )
	cmds.floatField( 'fFld_bias', w=100, pre=4, v=0.001 )
	cmds.setParent( '..' )
	
	cmds.setParent( '..' )
	
	cmds.showWindow( winUVCoordEditor )

def main():
	createGUI()
	
if __name__ == '__main__':
	main()

解説

数値フィールドやボタンを作成する

まずは前回作成したウィンドウのcreateGUI関数の中に数値を表示したり入力するためのフィールドや数値を取得するためのボタンなどを配置していきます。

	cmds.rowLayout( nc=4 )
	cmds.text( l='UV: ' )
	cmds.floatField( 'fFld_UValue', w=100, pre=4, ec=enterUValue )
	cmds.floatField( 'fFld_VValue', w=100, pre=4, ec=enterVValue )
	cmds.button( l='Get', c=getUVs, w=100 )
	cmds.setParent( '..' )
	
	cmds.rowLayout( nc=2 )
	cmds.text( l='Bias: ' )
	cmds.floatField( 'fFld_bias', w=100, pre=4, v=0.001 )
	cmds.setParent( '..' )
1floatField浮動小数を操作できるフィールドを作成するコマンド
2buttonボタンを作成するコマンド

UVの値を取得する

ボタンを押して関数を呼び出す

Getボタンを押すとgetUVsというUVの値を取得し、floatFieldにその値を表示するという関数が呼び出されます。
呼び出すフラグはc=関数名となります。

cmds.button( l='Get', c=getUVs, w=100 )

選択したUVを取得する

まずは選択しているUVをいつものlsコマンドやfilterExpandコマンドを使って取り出してみましょう。
何も選択していない場合にボタンが押されることもあるかもしれませんので、その場合はif文で排除しています。

selection = cmds.ls( sl=True, fl=True )
uvmaps = cmds.filterExpand( selection, sm=35 )
if uvmaps != None and len( uvmaps ) > 0:

forループでuvmapを1つ1つ取り出してpolyEditUV関数でuvmapのUの値とVの値を一旦リストに格納します。
uvsリストにはこの時点で選択しているすべてのuvmapのUとVの値が格納されています。

uvs = []
for uvmap in uvmaps:
	uvs.append( cmds.polyEditUV( uvmap, q=True, u=True, v=True ) )

UVの値の誤差を考慮する

あとは取得したUVの値を表示するだけなのですが、複数選択した場合のことを想像してみて下さい。
例えば縦一列に選択した場合、同じUの値、またはVの値に限り表示してみたいと考えました。要するに値が1つも重複していなければ、その値を表示すれば良いわけです。

重複をなくすための簡単な方法としては以前にも使ったset関数が使えますね。
但し、1つだけ問題があります。
重複というのは完全に値が同じ場合です。
しかし、選択したUVの値を見てみますと同じ位置にあるように見えて微妙にずれている場合がありますよね。
[0.1875, 0.625], [0.18671901524066925, 0.7490296959877014]
この為、数値の何桁までを使いたいと端数を処理する必要があります。
そこで、今回は Decimal.quantize()メソッドを使いました。

第一引数第二引数
quantize()求めたい桁数を’0.1’や’0.01’のように文字列で指定するroundingに丸めるモードを指定する。
‘ROUND_HALF_UP'(四捨五入)、
‘ROUND_HALF_EVEN’(偶数の丸め)など

round()という丸める為の関数もあるのですが、これだと端数が切り捨てられてしまいます。一般的な四捨五入の方が感覚的に近かったので Decimal.quantize()メソッドを使用しています。

第一引数へ桁を文字列で渡す必要がありますので、floatFieldを作りそこからbiasという変数に渡して取得しています。

cmds.floatField( 'fFld_bias', w=100, pre=4, v=0.001 )

decimalを使用する為にまずインポートします。

from decimal import Decimal, ROUND_HALF_UP

四捨五入した数値をset関数で重複を排除し、list関数でリスト型として変数に格納します。

bias = str( cmds.floatField( 'fFld_bias', q=True, v=True ) )
			
unique_u = list( set( [ Decimal(str(uv[0])).quantize(Decimal(bias), rounding=ROUND_HALF_UP) for uv in uvs ] ) )
unique_v = list( set( [ Decimal(str(uv[1])).quantize(Decimal(bias), rounding=ROUND_HALF_UP) for uv in uvs ] ) )

フィールドに表示する

重複を排除したリストの数を数え、つまりはUVの値が1つだけの場合だったらその数値をフィールドに表示します。
そうでない場合は0(ゼロ)を表示することにしてみました。

フィールドに値を表示する為には次のようにします。

  • 表示したいフィールドを’fFld_UValue’のように名前を文字列で指定する
  • eフラグにTrueを与えて編集モードにする
  • vフラグに表示したい値を与える
if len( unique_u ) == 1:
	cmds.floatField( 'fFld_UValue', e=True, v=float(unique_u[0]) )
else:
	cmds.floatField( 'fFld_UValue', e=True, v=0.0 )

if len( unique_v ) == 1:
	cmds.floatField( 'fFld_VValue', e=True, v=float(unique_v[0]) )
else:
	cmds.floatField( 'fFld_VValue', e=True, v=0.0 )

UVを移動する

フィールドに数値を入力してEnterキーを押した際にUVを移動する機能も追加してみましょう。
floatFieldのecフラグにenterUValueなどの関数名が指定すると、Enterキーを押した際にこの関数が呼び出されます。

cmds.floatField( 'fFld_UValue', w=100, pre=4, ec=enterUValue )
cmds.floatField( 'fFld_VValue', w=100, pre=4, ec=enterVValue )

こちらはフィールドに入力した座標にUVを移動するだけですので、フィールド内の数値を取得し、polyEditUVでU、またはVを移動します。
絶対値で動かしたいのでrフラグにはFalseを入れています。
Vの場合も同様です。

def enterUValue( *args ):
	selection = cmds.ls( sl=True, fl=True )
	uvmaps = cmds.filterExpand( selection, sm=35 )
	if uvmaps != None and len( uvmaps ) > 0:
		uValue = cmds.floatField( 'fFld_UValue', q=True, v=True )
		cmds.polyEditUV( uvmaps, r=False, u=uValue ) 

最後に

突然排除される機能には困りますが、pythonなどを使うと似たような機能を自分で作成することも可能です。
ちょっと長めになってしまいましたが、ぜひ挑戦してみてください。
では、また!

コメント

タイトルとURLをコピーしました