UVを等間隔に並べたい!

こんにちは!Rhinoです。
UVって何度やっても苦手なのですが、皆さんは如何ですか?
今日もそのUVに関して以前書いたスクリプトを解説してみたいと思います。
MayaのUnfold UVを使用してメッシュのトポロジーに合わせてUVを軸ごとに調整できる機能がありますが、それとは異なりUVの幅を等間隔にする方法です。
ゲーム背景の仕事をしていると頻繁にこういうことを行いたくなるので以前作ってみたものになります。

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

def getUVsValue( uvs ):
	result = []
	for uv in uvs:
		buffer = []
		buffer.append( uv )
		buffer.extend( cmds.polyEditUV( uv, q=True ) )
		result.append( buffer )
	return result
	
sel = cmds.ls( sl=True, fl=True )
uvs = cmds.filterExpand( sel, sm=35 )

#U: 1 / V: 2
mode = 1

if uvs != None and len( uvs) >= 3:
	uvValue = getUVsValue( uvs )
	sortedUVs = sorted( uvValue, key=lambda x: x[mode] )
	min = sortedUVs[0][mode]
	max = sortedUVs[-1][mode]
	step = abs( max - min ) / ( len(uvs) - 1 )
	index = 0
	for uv in sortedUVs:
		if mode == 1:
			cmds.polyEditUV( uv[0], r=False, u=( min + (step*index) ) )
		if mode == 2:
			cmds.polyEditUV( uv[0], r=False, v=( min + (step*index) ) )
		index += 1

解説

等間隔に並べるには?

UVを等間隔に並べるということはどういうことでしょうか?
それはUVの間隔の平均値を求めるということです。
UVの間隔が平均値になるように並び替えてあげれば良いと思いませんか?
Uの値について考えてみたいと思います。
まず平均値このようになります。

$$UVの平均値 = UV全体の幅 \div UV同士の間隔の数$$

それぞれの値についてみていきましょう。
UVの全体の幅というのはこのようになります。

$$UV全体の幅 = UVの最大値 – UVの最小値$$

UV同士の間隔の数はこのようになります。

$$UV同士の間隔の数 = 選択したUVの数 – 1$$

UVの値を求める

平均値を求めれば良いということは分かりました。
では求めるには具体的にどうしたら良いでしょうか?
まずはUVの値が必要ですよね。
UVの値を求めるためにgetUVsValueという関数を作りました。

def getUVsValue( uvs ):
	result = []
	for uv in uvs:
		buffer = []
		buffer.append( uv )
		buffer.extend( cmds.polyEditUV( uv, q=True ) )
		result.append( buffer )
	return result

リスト型resultにuvmap、Uの値、Vの値を追加し、まとめて取得できるようにしています。
リストとは他の言語でいつところの配列のようなものですね。
このリストにappendextendという2つの関数で追加していきます。

appendリストの末尾に要素を追加
extendリストの末尾に他のりストなどの要素を結合

まずappendを使って選択しているuvを一時的にbufferというリストに追加します。
次に、extendでbufferにUVの値を結合します。
UVの値はcmds.polyEditUV( uv, q=True )で求めることができますが、この時に返ってくる値がリストの為、extendで要素に展開して結合しているわけです。

UVの平均値を求める

選択しているUVの値は取得できましたので、次は平均値です。
平均値を求めるにはUVの幅の値と選択しているUVの数が必要ですね。
まずはUVの幅の値を求めるために最大値と最小値を求めてみましょう。

	sortedUVs = sorted( uvValue, key=lambda x: x[mode] )

今回はlabmda式というものを使ってUVの値でソートし、ソートされたリストの最初の値を最小値、最後の値を最大値としてみました。
ソート自体はPythonのsorted関数を使っています。ソートされた新しいリストを返してくれる関数になります。
では何をソートするかと言いますと、Uの値やVの値なのですが、これはリストでひとまとめにされていますので、本来はこれらの値をループなどで取り出してsorted関数に渡してあげる必要があります。
でも手間ですよね?
その処理を簡略化できるのがlabmda式になります。
labmda式の中でmodeという変数を使っていますが、この値が1だとUの値、2だとVの値としました。
uvValue変数の中を見て頂くと分かりやすいかと思います。
変数の要素を1つだけ取り出してみました。
このように変数の0番目がuvmapの値、1番目がUの値、2番目がVの値となっています。

[u'pSphere1.map[68]', 0.0, 0.625]

つまりmodeに1を入れるとUの値でソートされた値が返され、2を入れるとVの値でソートされた値が返されるというわけです。
最小値と最大値はソートされたリストのの最初の値と最後の値のはずなので、このようになるでしょう。

	min = sortedUVs[0][mode]
 max = sortedUVs[-1][mode]

最後にこうして得られた値から平均値をstepという変数に入れました。
absというのは絶対値を求める関数で、max – minがマイナスの値を返した場合への対応です。UVの数はlen関数で数えました。

 step = abs( max - min ) / ( len(uvs) - 1 )

求めた値を使ってUVを移動する

欲しい値は一通りそろったかと思いますんで、これらの値を使ってUVを移動させてあげれば処理は完了となります。
それぞれのUVを移動させてあげる必要がありますので、forループで取り出してあげがら、cmds.polyEditUV関数でUとVそれぞれを移動させてあげます。
この際に1つだけ注意するのは最小値を足してあげるということですね。
今回はUの最小値が0(ゼロ)になっているのですが、必ずしも0(ゼロ)とは限りません。最小値を基点として考えて、この値から平均値の値ずつ各UVをずらしているというわけです。

	index = 0
	for uv in sortedUVs:
		if mode == 1:
			cmds.polyEditUV( uv[0], r=False, u=( min + (step*index) ) )
		if mode == 2:
			cmds.polyEditUV( uv[0], r=False, v=( min + (step*index) ) )
		index += 1

modeを1にして実行してあげるとUの幅が均等になったのではないでしょうか?
Vの場合は2にして下さいね。

最後に

如何でしたでしょうか?上手く均等の幅になりましたか?
やっていることは単純なのですが、いざコードで書こうとすると悩みますよね。
今回の正解というわけではなく、もっと簡単な方法もあるかもしれませんので、いろいろと試してみて下さいね。
あと、labmda式やリストなどの解説もいずれやってみたいです。
では、また!

コメント

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