日々適当
hibitekitou

なーぶす

xsi |2016-02-11

Softimageのスクリプトでカーブを作ろうとしたんですよ。
スクリプトとしては
NurbsCurveList.AddCurve( ControlPoints, [Knots], [Closed], [Degree], [Parameterization], [NurbsFormat] )
です。
それじゃ、ControlPointsとはなんぞや、Knotsとはなんぞや、Degreeとは?とかなりますわな。

ControlPointsはX,y,z,w 値を指定することになります。xyzは制御点の座標として、wはウェイトでいいのかな。つまりその制御点にどんだけ引き寄せられた線を引くかをここで決めている感じでしょうか。よくわからないので、とりあえずwは1としときます。

Knotsは「ノット値の配列」とあります。Degreeが「NURBS サーフェイスの U 方向の次数」となっていて大ざっぱに何次曲線かって事ですね。直線なら1で3DCGで使うNurbsならデフォルトは3って感じ。で、この次数と制御点の数からKnotsは一意に決まってくるものだそうですが、どうも、制御点 + 1の数でいいようです。で、均等に並べるには均等な配列にします。
それ以外のParameterization(デフォルト値1)とかNurbsFormat(デフォルト値0)はとりあえずデフォルトでいいんで無視します(^^;

というわけで、
制御点を4つ用意し
[ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 2.0, 1.0, -1.0, 0.0, 1.0, 1.0 ]
として、次数を1(直線)とするなら Knotsは 1+4=5個の要素の配列と言うことで
[0,1,2,3,4]
となりましょうか。
結果としてスクリプトはこんな風になります。

oRoot = Application.ActiveProject.ActiveScene.Root 

cp = [ 0.0, 0.0, 0.0, 1.0,\
       1.0, 0.0, 1.0, 1.0,\
       0.0, 0.0, 2.0, 1.0,\
      -1.0, 0.0, 1.0, 1.0 ]
knots = [0,1,2,3,4]

oCrv = oRoot.AddNurbsCurveList()
oCrv.ActivePrimitive.Geometry.AddCurve( cp, knots , True, 1, 0, 0 )

次数をデフォルトの3にするにはoCrv.ActivePrimitive.Geometry.AddCurve( cp, knots , True, 1, 0, 0 )の部分をoCrv.ActivePrimitive.Geometry.AddCurve( cp, knots , True, 3, 0, 0 )にするだけです。

ちなみに、Knotsの値を [ 0,0,1,2,2 ] のようにすると、いびつな形になりますね。

というわけで、最低限これだけ知ってれば曲線はひけるのかなぁ…

コメント ( 0 )|Trackback ( 0 )

昔作ったスクリプトがSIをたたき落とした

xsi |2016-01-04

昔作ったスクリプトがありまして、なにせ昔だからぱっと見ただけだとよく分からないのだけど、とりあえず実行したらPPGを開こうとしてSoftimageごとたたき落とされてる、というような感じになりました。 同じスクリプトを自宅環境で試したら落ちなかったので、環境依存の部分もあるのでしょうが、よくよく確認してみると構文的に正しくない書き方をしている事が原因だったようです。

チェックボックスのあるPPGを作りました。例えばこんなスクリプト。

from win32com.client import constants as c
app = Application

oProp = XSIFactory.CreateObject( "CustomProperty" )
oProp.name = "TEST PPG"

boolcheckbox = oProp.AddParameter2( "boolcheckbox", c.siBool, 1)

oLayout = oProp.PPGLayout

oLayout.AddEnumControl( "boolcheckbox", ["", 0] , "test", c.siControlBoolean )

Err = app.InspectObj(oProp, "", "", c.siModal, False)

if Err == False:
	LogMessage(  boolcheckbox.Value )
else:
	LogMessage("Cancel")
 

問題は、AddEnumControlの部分のリスト部分(["", 0] の所)の書き方にありました。

ここのリストは[ "String", Value, "String", Value ... ] というように文字列のラベルと値の組み合わせを羅列していかなければならないのですけど、これを [ 0, 0 ] って書いていたのですね。チェックボックスだとこのリスト部分ってあんまり意味が無いみたいなのですけど(だから [0,0] にも特に意味が無い?)、環境によってSoftimageごとたたき落とす原因となってしまったようです。

正しく書かないとダメですなぁってことで。

コメント ( 0 )|Trackback ( 0 )

メルカトル図法へ

xsi |2015-07-11

それじゃ、メルカトル図法に行ってみましょう。
メルカトル図法の式はシンプル。Wikipediaによると、地図中央の経度をλ0とし、与えられる経緯度がλ、φである場合、

x = λ - λ0

で、yは sinφ をtanhの逆関数にかけたもの、という事になるそうです。
tanhってなんぞや、となるわけですが、pythonなら大丈夫。mathモジュールを読み込めば、その理屈を知らずとも、tanhを適用すればいいだけで、逆関数もatanhとして用意されています(^^;

import math
app = Application

oObj = app.Selection(0)

oPntArr = oObj.ActivePrimitive.Geometry.Points.PositionArray

newPosArr = [[],[],[]]

lamda0 = 139.741389

for i in range(len(oPntArr[0])):
	x = oPntArr[0][i] - lamda0
	phy = math.radians( -oPntArr[2][i] )
	y = math.degrees( math.atanh( math.sin( math.fabs(phy) ) ) )
	if phy < 0:
		y = -y
	
	newPosArr[0].append(x)
	newPosArr[2].append(-y)
	newPosArr[1].append(0.0)

oObj.ActivePrimitive.Geometry.Points.PositionArray = newPosArr 


理屈は理解せずともw、簡単でいいですね。ただし、極点付近は極端に伸びるので、極点付近は削除の上、実行したほうがよいでしょう。



今回は80度線でぶった切ってみました。
そうすると、実行するとこんな風になります。



重ねてあるのはgoole mapから。低緯度の地域では一致しています。
高緯度の所は、なにせ



こんなふうにメッシュが引き伸ばされている為に、結果として高緯度側のメッシュ分割が粗く、精度が悪くなっております。

でも、対世界地図で活躍してくれそうですな。

コメント ( 0 )|Trackback ( 0 )

正距方位図法に変換

xsi |2015-07-07
地図の中心からの各地点への距離と方向が正しくなるという図法で、日本地図[国土地理院]だと北緯35度39分29秒、東経139度44分29秒という位置が中心(主点)となるようです。

CG上で地球のテクスチャとしてよく使われる正距円筒図法は、経度、緯度をそれぞれx座標、y座標に読み替えたものの為、緯度経度位置の情報(分かりやすいのは標高とか)から作図しやすい反面、既存の地図に重ね合わせるには、図法の変換をせねばなりません。で、その変換をせねばならない用事が生まれましてね。やってみるわけです。

欲しいのは数式なんですけど、正距方位図法で検索してもなかなか該当する物が見つからず、azimuthal equidistant projectionで検索したら

Azimuthal Equidistant Projection [Wolfram MathWorld]

というドンピシャなページが引っかかりました。φとλはそれぞれ緯度、経度に相当する値で、それをx,yに変換したり、x,yから緯度・経度を求める式も載っています。すばらしいです。ということで、これを単純に実装するだけででき上がりました。

緯度・経度が-z座標、x座標になっているメッシュに対して適用します。
import math

app = Application
log = app.Logmessage

oObj = app.Selection(0)

pntPosArr = oObj.ActivePrimitive.Geometry.Points.PositionArray

newPosArr = [[],[],[]]

phi1 = XSIMath.DegreesToRadians(35.658056)
lamda0 =  XSIMath.DegreesToRadians(139.741389)

for i in range(len(pntPosArr[0])):
	phi = XSIMath.DegreesToRadians(-pntPosArr[2][i]) #latitude
	lamda = XSIMath.DegreesToRadians(pntPosArr[0][i]) #longitude
	
	c = math.acos( math.sin(phi1) * math.sin(phi) + math.cos(phi1) * math.cos(phi) * math.cos(lamda - lamda0) )
	
	k = c/math.sin(c)
	
	x = k * math.cos(phi) * math.sin(lamda - lamda0)
	z = k * (math.cos(phi1) * math.sin(phi) - math.sin(phi1) * math.cos(phi) * math.cos( lamda - lamda0 ))
	
	newPosArr[0].append( x )
	newPosArr[2].append( -z )
	newPosArr[1].append( 0.0 )
	
oObj.ActivePrimitive.Geometry.Points.PositionArray = newPosArr




上図の色がついている範囲の板(センターは原点にある)に適用すると



となる。見やすくするために明るさを持ち上げてあります。
形状も、冒頭にリンクを貼った日本周辺にほぼあっているかな、と思いますです、はい。
コメント ( 0 )|Trackback ( 0 )

ポリゴン法線

xsi |2015-01-16

 

Softimageのスクリプトからポリゴンの法線を求めるときはどうすんだ?といういまさらな疑問です。
どうやらFacetCollectionにアクセスすると良いようでございます。そこにNormalArrayというプロパティがあるそうで。

ポリゴンを形成する頂点(Point)の持つ法線の平均でいいやと思って、ポリゴンノード(ActivePrimitive.Geometry.Polygons.Nodes)のNormalプロパティの平均を出そうとしました。これは、ビューポートの法線表示と同じものだから、下図のような平面となっているポリゴンの隣のポリゴンが傾いていると、その接線上の頂点の法線は当然傾いているから、平面となっているポリゴンの頂点の法線の平均をとると、傾いた値が出てきてしまうわけです。

というわけで、困っていたのですが、FacetCollectionのNormalArrayから取得してやれば何とかなりそうでした。
以下のスクリプトは各ノードの法線の値と、FacetCollectionにアクセスするとことで得られる法線の値と、それぞれのポリゴンのポイントとサンプルポイントのインデックスを出してみたものです。

app = Application
log = app.Logmessage

def nodeNomarl( oObj ):
	oPolys = oObj.ActivePrimitive.Geometry.Polygons
	
	vn = XSIMath.CreateVector3()
	
	for oPoly in oPolys:
		oNodes = oPoly.Nodes
		i = 0
		for oNode in oNodes:
			vn = oNode.Normal
			log("Node %d: %f\t%f\t%f"%(oNode.Index, vn.X, vn.Y, vn.Z))

def polygonNormal( oObj ) :
	vn = XSIMath.CreateVector3()
	vy = XSIMath.CreateVector3( 0.0, 1.0, 0.0)
	
	oFacets = oObj.ActivePrimitive.Geometry.Facets
	oNormals = oFacets.NormalArray 
	for i in range(oFacets.Count):
		vn.Set( oNormals[0][i] , oNormals[1][i], oNormals[2][i] )
		log("Facet ID: %d --- %f\t%f\t%f"%( oFacets.IndexArray[i], vn.X , vn.Y, vn.Z ))
		
		log("dot product: %f"%vn.Dot( vy ))
		
		oPnts = oFacets[i].Points 
		oSamples = oFacets[i].Samples
		
		pntsIndices = []
		smpIndices = []
		
		for oPnt in oPnts:
			pntsIndices.append(oPnt.Index)
		for oSmp in oSamples:
			smpIndices.append(oSmp.Index)
		
		log("Point Index: %s"%str(pntsIndices))
		log("Sample Index: %s"%str(smpIndices))

oObj = app.Selection(0)

nodeNomarl( oObj )
polygonNormal( oObj )

この結果が、例えば

# INFO : Node 0: 0.000000 1.000000 0.000000
# INFO : Node 1: 0.000000 1.000000 0.000000
# INFO : Node 5: 0.087156 0.996195 0.000000
# INFO : Node 4: 0.087156 0.996195 0.000000
# INFO : Node 2: 0.173648 0.984808 0.000000
# INFO : Node 3: 0.173648 0.984808 0.000000
# INFO : Node 7: 0.087156 0.996195 0.000000
# INFO : Node 6: 0.087156 0.996195 0.000000
# INFO : Facet ID: 0 --- 0.000000 1.000000 0.000000
# INFO : dot product: 1.000000
# INFO : Point Index: [0, 1, 5, 4]
# INFO : Sample Index: [0, 1, 4, 5]
# INFO : Facet ID: 1 --- 0.173648 0.984808 0.000000
# INFO : dot product: 0.984808
# INFO : Point Index: [3, 2, 4, 5]
# INFO : Sample Index: [2, 3, 6, 7]

というようになります。ちょっと賢くなったような気がします。

コメント ( 0 )|Trackback ( 0 )

XYZファイルをPointCloudに、再び

xsi |2014-12-20

Softimageにてxyzファイルを読み込むにあたり、それが250万行とかある場合に、ICEのString to Arrayノードに座標をぶち込んで、ってのは無理があるようでした。

ということで、Scriptからパーティクルを発生させる方法を考えるわけですけど、例えばMayaなら座標の配列をコマンドに渡してやればパーティクルが作成されるわけですが、それに相当するものを見つけられていません。まぁ、Mayaでその方法を試したら、やっぱり処理が帰ってこなかったんですけどね。たぶん別のやり方があるのだとは思うけど。というわけで、以下の方法を試してみました。

スクリプトでPointCloudのプリミティブオブジェクトを出すことは出来ました。つまり、グリッドとかキューブとか球とかのプリセットから生成されるPointCloudは作成可能という事です。ということで、グリッドのPointCloudを作成し、必要なポイント数に近い数字になるように分割数を設定してやって、で、パーティクルポイントの位置をxyzファイルから読み込んだ位置情報に設定してやる、という手順を踏んでみることにしました。

 

import math
from win32com.client import constants as c

app = Application
oRoot = app.ActiveSceneRoot

xyzFile = "xyz file path" #eg: "c:\file.xyz"

def readXYZFile():
	f = open(xyzFile, 'r')
	str = f.read()    
	f.close()
	lines = str.split('\n')

	posList = [[],[],[]]
	improvisedValueArray = []
	i = 0
	for line in lines:
		
		temp = line.split(' ')
		try:
			posList[0].append(float(temp[0]))
			posList[1].append(float(temp[1]))
			posList[2].append(-float(temp[2]))
			improvisedValueArray.append(True)
		except:
			LogMessage("NOT Params" )
		i += 1
		
	return posList, improvisedValueArray
	
posList, improvisedValueArray = readXYZFile()

pntCnt = len(posList[0])
uvCnt = math.ceil(math.sqrt( pntCnt ))
subCnt = uvCnt**2 - pntCnt
LogMessage("U V:" + str(uvCnt))
posList[0] = posList[0] + [0.0]*int(subCnt)
posList[1] = posList[1] + [0.0]*int(subCnt)
posList[2] = posList[2] + [0.0]*int(subCnt)
improvisedValueArray = improvisedValueArray + [False]*int(subCnt)

oObj = oRoot.AddGeometry("Grid","PointCloud","")

oParams = oObj.Parameters
oParams["subdivu"].Value = oParams["subdivv"].Value = uvCnt - 1
app.FreezeObj(oObj)

oPnts = oObj.ActivePrimitive.Geometry.Points
oPnts.PositionArray = posList

attr = oObj.GetActivePrimitive3().AddICEAttribute( "improvisedValue", c.siICENodeDataBool, c.siICENodeStructureSingle, c.siICENodeContextComponent0D  );
attr.DataArray = improvisedValueArray

グリッドから生成してるから、必要数よりも多くのパーティクルができ上がっており、そこで、improvisedValue っていうICEアトリビュートを設定して、必要のないパーティクルにはFalseを、それ以外はTrueの値を持つようにお尻で設定しています。で、ICE上でその値をみて、Falseの物は削除する、というようにしてやりました。

上図は以前挑戦した時にいつまでたっても読み込みが完了しなかったデータでの結果なんですけど、1分もかからずに読み込みが終了し、上のICE Treeの設定もさくさくできました(動作も軽かった)。xyzファイルの読み込みではないけど、この方法で可能かどうか、適当に1000万個の座標を与えてみたところ、1、2分で結果が返ってきたし、割と現実的な物が出来たかもしれないとか思っているところです。

とか、なんかSoftimage使いの方々がたまにみてくださっているようなので、たまにはSIの話題もといったところでございました。

Softimage感謝祭、楽しかったです。

コメント ( 0 )|Trackback ( 0 )

Softimage 2015 SP1 が出たよー

xsi |2014-12-03
Autodesk® Softimage 2015 Service Pack 1 [Autodesk]

mayaと比較するのもなんだけど、そっちは既にSP5が出ています。しかもSI 2015 SP1における修正された不都合数はわずか9。ちなみにmaya 2015 SP5は30ぐらいリストアップされているようです。これはあれだね、Softimageの完成度の高さを示しているね、と言っていいのかね?

それにしても不思議なのは
Softimage 2015 SP1 は、Softimage 2015 と共存させることはできません。お使いのコンピュータに Softimage 2015 がインストールされている場合は、アンインストールしてから Softimage 2015 SP1 のインストーラを実行してください。
という仕様です。同一バージョンのSP違いの共存が出来ないというのは、他のAutodeskソフト、例えばmayaやmaxではその通りなんだけど、Softimageについては全バージョンが共存出来ていたのが、ここに来て出来ないようになっている。他のソフトウェアとポリシーを合わせてきたようにも見えます。
Softimage 2015ってそこはいじらなくてもいいだろう、って部分を触ってきている例もあるように見受けられる事からや、SP違いを共存させる事が出来なくなったという事から、2015開発当初はまだこれを最終バージョンにするという決定は下っていなかったんじゃないかって想像したりしますが、どうなんでしょうね。

ともあれ、2016年4月までサポートする事になっているのだから、徹底的にバグを潰していただき、あと、HiDPIへの対応やWindows 10への対応あたりもしていっていただけると、サポート終了後も5年ぐらいは使えるんじゃないでしょうか? Autodeskがそれを望まないであろう事は想像しますが…

そいえばダイキンさんがSoftimage感謝祭とやらを開くそうで。とりあえず登録してみたよ。
コメント ( 0 )|Trackback ( 0 )

位置をプロット

xsi |2014-10-29
世界遺産。ユネスコにより採択される遺跡で、そのリストはユネスコのサイトで配布されています。その中にはエクセルファイルもあって、そのエクセルファイルには、緯度・経度が記録されています。
というわけで、それを地図上にプロットするというのは、正距円筒図法に対しては非常に容易だったりします。

まずはエクセルファイルを読むためのモジュールをインストールです。xlrdってモジュールを入れればいいようです。使い方はネット上にいろいろあるんで省略。僕も良くわかってないし。ということで、これを使って、プロットしてやります。

from win32com.client import constants as c
import xlrd

app = Application

xlsfilePath = 'D:\Users\whc-sites-2014.xls' #ダウンロードしたエクセルファイルのパス

xLen = 200.0

book = xlrd.open_workbook( xlsfilePath )
sheet0 = book.sheet_by_index(0)

for col in range( sheet0.ncols ):
	if str( sheet0.cell( 0, col ).value ) == 'longitude':
		colLog = col
		
	elif str( sheet0.cell( 0, col ).value ) == 'latitude':
		colLat = col
		
posList = []
for row in range( 1, sheet0.nrows ):
	LogMessage(sheet0.cell( row, colLog ).value)
	xpos = (xLen * 0.5) * sheet0.cell( row, colLog ).value / 180.0
	zpos = (xLen * 0.5 * 0.5 ) * sheet0.cell( row, colLat ).value / -90.0
	posList.append( xpos )
	posList.append( 0.0 )
	posList.append( zpos )

oPntCloud = app.GetPrim("PointCloud", "", "", "")
oICETrees = app.ApplyOp("ICETree",oPntCloud,c.siNode)
oICETree = oICETrees(0)

oAddPoint = app.AddICENode("$XSI_DSPRESETS\ICENodes\AddPointNode.Preset", oICETree )
app.ConnectICENodes(oICETree.FullName + ".port1", oAddPoint.FullName + ".add")

oStrToArr = app.AddICENode("StringToArray", oICETree)
app.SetValue(oStrToArr.FullName + ".Value_string", str(posList)[1:-1], "")

app.ConnectICENodes(oAddPoint.FullName + ".positions1", oStrToArr.FullName + ".Result")


ここまでで、ICEツリー下図の赤四角で囲まれた範囲が作成されます。ポイントクラウドを作成するわけっすね。



で、その後、ポリゴンのコピーをそのポイント位置に置いていく予定ながら、面積を持ったものをおくと、隣と干渉しまくる密度の濃い地域があるんで、干渉を出来るだけ回避するために、赤四角以外の範囲のICEツリーを手動で組んでいます。

でもって、空のポリゴンメッシュに対してICEツリーで下図を作成するだけ。形状のソースとなるものは、単なる板ポリでございます。



これだけでいいんだもんなぁ。SI、楽だよねぇ。

コメント ( 0 )|Trackback ( 0 )

Parallels 上でのSoftimage 2015の挙動の問題

xsi |2014-05-14
Parallels上でSoftimageをオペレートするというのは過去に散々取り上げてきましたけど、もちろん本格的にモデリングやアニメーション、ライティングの作業をするにはあまりおすすめはしませんけど、起動している環境がMacの時、ちょっと機能を試したいとか、特にICEのツリーの構築を考えたいという時には非常に便利です。
ということで、現在、うちのバーチャルマシンにはSI 2011から2015までの各Softimageが入っております。2011の時代から引き継いだものというわけでは必ずしも無いのですけど、2011年(SI 2012の時代)ぐらいからは軽く引き継いだ環境になっている事と思います。

さて、そんなわけで、今回も嬉々としてあるいは大変な悲しみの気持ちとともにSI 2015をインストールし、ICEのエディタが便利になってるうひょぉとかやっていたわけですが、コンポーネントを触ろうとして問題に気付きます。レクタングルでタグやエッジを選ぶと、そのオブジェクトの全てのポイントやエッジが選ばれてしまう、という問題です。
ログを見ると(球のポイントをタグ付けするためにRectangleのモードで球の任意の範囲を囲ったら)

Application.SelectGeometryComponents("sphere.pnt[*,975]")

となっておりました。*がある事から分かる通り、これでは球のポイント全部がタグ付けされてしまいます。
ということで困ったなぁってTwitterでつぶやいていたら、ある方が素敵に解決方法を教えてくれました。ということで、それをメモっときます。その方法とは setenv.bat に XSI_DISABLE_NEW_PICK=1 という環境変数を書き加える、というものです。その方曰く、2015で選択の反応速度等の改善を施したものの、それを従来の方法に戻すための環境変数ということでした。どうやってこの変数を見つけるんでしょうね(^^)

と言う事で、とりあえずこれまで通りParallels上でSIを触っていけそうです。感謝でございます。
コメント ( 0 )|Trackback ( 0 )

Alt教に入信か

xsi |2014-03-30
将来maya様に移行する時、SI風のオペレーションをmaya様に何とか実装するのか、それともその時に備えて今からmaya様のルールでSIを操作するのか、迷いどころです。後者を選ぶ時、それはAlt教に入信するという事なのです。しかし、ビューポートオペレーション(ドリー、回転)はAltを使っての操作という事は分かるのだけど、その他ショートカットがどうなるのか、さっぱりなのです。
と言うのを思ったのはですね、先日ちょいとParallels上のSoftimageをmaya風のキーマップに設定しててそのまま忘れて今さっき起動したら、maya風の操作がさっぱりで、なんだかいろいろ困ったからなのですよ。



コンポーネントの選択はどうやるのですか?というものですよ。
ていうか、maya様使ってコンポーネントのセレクトツールにする時って右クリックでメニューをだして切り替えている印象があるんだが、ショートカットは特に設定されていないのか?

とか何とか非常に苦労するような気がします。ていうか間違いなく苦労するな。苦労するぐらいなら、maya様じゃなくてhoudiniとかに行きたいな。でもそれは仕事との事情的に許されないだろうな…………………………鬱ですよ。

基本的なところはAlt教に入信しつつ、mayaにもカスタムな設定をしつつSI側にも同じ設定を適用というのをちまちまとやっていくしかないですか。ここいらへん(Suite UsersNotes [ダイキンCOMTEC])を参考にちまちまやりますか。あー、面倒くせ。恨むぞAutodesk。
コメント ( 0 )|Trackback ( 0 )
« 前ページ・