2017年03月

2017年03月31日

PSVR 頭を締め付けられて辛いので対策してみた

ファームウエア4.5のお陰でとうとうPSVRでも3DBDの鑑賞が可能になった。

3DTVの新規開発が終了してしまった現在、今のテレビが壊れたら3DBDの鑑賞はPSVRに頼るしかない。

しかし構造上頭を締め付ける形で固定する装置なので、長時間装着していると結構な負担がある。自分の頭の形が悪いせいか酷い頭痛に悩まされていた。

そこで何が悪いのかよく調べてみた結果、ストラップを後ろに引っ張るとヘッドパッドの左右の部分が狭められて丁度コメカミ付近を圧迫するのが原因だと気付いた。もちろんこうなっているのには理由があるんだろうけど、自分の頭の形は設計時に想定していたものの中では悪い方に含まれちゃうんだろうね。

fig01

原因がわかったので下のようにパッドの左右の部分を針金で縛ってみた。こうすれば後ろから引っ張られても外側のブームの強度である程度締め付けを緩和してくれるはずだ。

fig02

そしてこれがその結果。最初の画像と比べると、ヘッドパッドの両端の締め付けがある程度抑えられているのがわかる。

fig03

で、実際装着してみると随分快適で、2時間の映画を鑑賞しても頭痛に悩まされることは無くなった。

来月はローグワンが出るから3DBDで買っちゃおうかな。



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)Goods | その他

2017年03月30日

modo10.2v1のスクリプトについて調べてみた その71

引き続き「TD SDK」の「modo.mathutils」モジュールの「Matrix3」について調べてみたい。

「Matrix3」クラスでは「len()」ファンクションが下のようにオーバーライドされていて、3X3の行列の行数が出力されるようになっている。

 def __len__(self):
     """

     :returns int: The size of the Matrix
     """
     return len(self.m)

Matrix3は必ず3なんだから定数3を返してもいいような気もするけど「Matrix3」を継承した「Matrix4」でもこのメソッドが使われているので行列が入っている「self.m」を調べて出力しないとダメなわけだね。

from modo.mathutils import *
m3 = Matrix3()
m4 = Matrix4()

print "len(Matrix3) = %s" % len(m3)
print "len(Matrix4) = %s" % len(m4)

# Result: 
len(Matrix3) = 3
len(Matrix4) = 4

添字付きで要素へアクセスすることをサポートする「_getitem__()」と「__setitem__()」は以下のように引数「index」で指定される行の要素を呼び出したり書き換えたりする。

 def __getitem__(self, index):
     """
     :param index: Row-index
     :type index: Int
     :returns list: A row from the Matrix
     """
     return self.m[index]

 def __setitem__(self, index, value):
     """
     :param index: Row-index
     :type index: Int
     """
     for i in range(len(value)):
         self.m[index][i] = value[i]
     # self.m[index] = value

「_getitem__()」、「__setitem__()」はプログラムの中では「[ ]」演算子の形で現れる。添字付き変数の値を参照する時は「_getitem__()」が呼び出され、値を代入する時は「_setitem__()」が呼び出され、「[ ]」内の値が引数「index」に、代入される値が引数「value」に渡されて処理される。

from modo.mathutils import *

m3 = Matrix3([[1,2,3],[4,5,6],[7,8,9]])

print "m3 : %s\n" % m3
print "m3[0] : %s" % m3[0]
print "m3[1] : %s" % m3[1]
print "m3[2] : %s\n" % m3[2]

m3[1] = [9,9,9]

print "m3[1] = [9,9,9]\n"
print "m3 : %s" % m3

# Result: 
m3 : Matrix3([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

m3[0] : [1, 2, 3]
m3[1] : [4, 5, 6]
m3[2] : [7, 8, 9]

m3[1] = [9,9,9]

m3 : Matrix3([[1, 2, 3], [9, 9, 9], [7, 8, 9]])

「==」演算子は以下のようにオーバーライドされていて、リストやタプル、「Matrix3」、「Matrix4」と比較でき、行数が等しく、かつ互いの要素の値の差が0.0000000001以下なら等しいと見なすようだ。

 def __eq__(self, other):
     """Test for equality against other Matrix

     :param other:
     :return:
     """
     if isinstance(other, (list, tuple)):
         other = self.__class__(other)
     if isinstance(other, (Matrix3, Matrix4)):

         equal = False

         if self.size != other.size:
             return equal

         for column in range(self.size):
             for i, j in zip(self.m[column], other.m[column]):
                 if abs(i - j) > 0.0000000001:
                     return False
         return True
     else:
         raise TypeError

続きはまた次回。

modo10ブログ目次



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | CG

2017年03月29日

ビットマップペイントツールのチュートリアルをやってみた その37 3dsmax 2017

引き続きMAXScriptマニュアルに載っている「チュートリアル-ビットマップペイントツールを9つの簡単なステップで作成する」 の続きを考えて見たい。

今回はビットマップに投影した四角形の中を塗りつぶすプログラムについて考えてみたい。

下の画像はマス目がビットマップのピクセルで、黒い線が塗りつぶす四角形だ。この四角形の中を塗りつぶすには下のように塗りつぶす四角形をピクセル単位で横にスライスして、そのスライスの線と四角形のエッジの交点2つの間のピクセルを塗りつぶして行けばいい。

fig01

だからまずは四角形からスライスすべき左右のエッジを取り出してこないとならない。

エッジが水平の場合、そのエッジと水平のスライス線との交点は無いので無視していい。例えば下の左の図形のように水平の戦が2本あるなら左右のエッジは1本ずつで、エッジを構成する頂点の数は左右共に2点だけ。パターンとしては水平のエッジが0、1、2本の場合が考えられて、左右のエッジのポイント数は下のようになる。

fig02

このほかにも形状は考えられるけど、今回のプログラムから生成される四角形ではこの範囲に収まるはずなので、これ以上は考えないでいいだろう。

とりあえず作ってみたのが下のプログラムだ。

まず、以前にテストのために作った「surfaceRect()」で変数「p0、p1、p2、p3」にUVマップ上の四角形の頂点を得ていて、それをビットマップ座標に直して「vrect」配列に格納している。

 surfaceRect BrushSize.value &p0 &p1 &p2 &p3
 vrect = #()
 append vrect [p0.x * bitmapx_1, bitmapy_1 - p0.y * bitmapy_1]
 append vrect [p1.x * bitmapx_1, bitmapy_1 - p1.y * bitmapy_1]
 append vrect [p2.x * bitmapx_1, bitmapy_1 - p2.y * bitmapy_1]
 append vrect [p3.x * bitmapx_1, bitmapy_1 - p3.y * bitmapy_1]

 maxy = miny = vrect[1][2]
 top = bottom = 1
 for i = 2 to 4 do
 (
    if vrect[i][2] >= maxy then (
       maxy = vrect[i][2]
       bottom = i
    )
    if vrect[i][2] <= miny then (
        miny = vrect[i][2]
        top = i
    )
 )
 side = #(#(),#())
 sptr = 1
 append side[sptr] vrect[top]
 nptr = top + 1
 if nptr > 4 then nptr = 1	
 if vrect[top][2] > vrect[nptr][2] then dir = -1 else dir = 1
 
 for i = 0 to 3 do (
    ptr = top + i
    if ptr >4 then ptr = ptr - 4
    nptr = ptr + 1
    if nptr >4 then nptr = nptr - 4
    if vrect[ptr][2] ==  vrect[nptr][2] then continue 
    if (dir == 1 and vrect[ptr][2] > vrect[nptr][2]) or (dir == -1 and vrect[ptr][2] < vrect[nptr][2]) then (
        sptr = sptr + 1
        if sptr == 1 then append side[sptr] vrect[ptr]
        else if sptr == 2 then insertitem vrect[ptr] side[sptr] 1 
        if dir == -1 then dir = 1 else dir = -1
    )
    if sptr == 1 then append side[sptr] vrect[nptr]
    else if sptr == 2 then insertitem vrect[nptr] side[sptr] 1 
 )

次のfor文のループで、「maxy」「miny」にこの四角形のビットマップ座標値の最大値、最小値を、「top」「bottom」にY座標が最小値になる頂点の番号、最大値になる頂点の番号が入る。

 maxy = miny = vrect[1][2]
 top = bottom = 1
 for i = 2 to 4 do
 (
    if vrect[i][2] >= maxy then (
       maxy = vrect[i][2]
       bottom = i
    )
    if vrect[i][2] <= miny then (
        miny = vrect[i][2]
        top = i
    )
 )

最後に四角形の頂点を左右のサイドに分ける。それを格納するのが「side」配列で、プログラムが現在処理しているサイドは「sptr」の値で示され、サイドは1と2になる。まずは1番目のサイドはtopが示す頂点番号から始める。そこでtop番目の頂点を「side」配列に格納してからスタートする。「nptr」はtopの次の頂点番号で、頂点番号は1〜4なのでtopが4なら次は5じゃなくて1に戻るように調整してある。topと次の頂点のY座標値を比較して、Y軸値が減ってたら「dir」を−1、増えてたら+1にセットして、変数「dir」に現在処理してるエッジがY軸に対して増加方向なのか減少方向なのかを調べておく。この傾向が逆になったら折り返し地点だからエッジを切り替えることになる。

 side = #(#(),#())
 sptr = 1
 append side[sptr] vrect[top]
 nptr = top + 1
 if nptr > 4 then nptr = 1	
 if vrect[top][2] > vrect[nptr][2] then dir = -1 else dir = 1

調べるエッジは変数「ptr」と「nptr」が示すポイント番号の間にあるものとして、どちらも1〜4の間の数値を示し、「nptr」は「ptr」より1つ増加方向にずらした番号のポイントを示す。2つのポイントのY軸が等しければエッジは水平だから無視していいエッジ。「dir」が1で、「ptr」から「nptr」へのY軸値が減る傾向か、「dir」が−1でY軸値が増加傾向なら「ptr」がエッジの折り返し点になるので、「sptr」を1増加させてサイドをチェンジし、2番目のサイドなら「ptr」が示す折り返し点のポイントを「side」配列に追加し、「dir」の方向を反転する。そして最後に「side」配列の現在のサイドに「nptr」が示す番号のポイントを格納する。配列に格納する時にそのまま追加だと頂点の並びがY軸値で逆方向になっちゃうので、そうならないように片方は後ろに追加する「append」で処理して、もう片方は「insertitem」を使って先頭に挿入している。

 for i = 0 to 3 do (
    ptr = top + i
    if ptr >4 then ptr = ptr - 4
    nptr = ptr + 1
    if nptr >4 then nptr = nptr - 4
    if vrect[ptr][2] ==  vrect[nptr][2] then continue 
    if (dir == 1 and vrect[ptr][2] > vrect[nptr][2]) or (dir == -1 and vrect[ptr][2] < vrect[nptr][2]) then (
        sptr = sptr + 1
        if sptr == 1 then append side[sptr] vrect[ptr]
        else if sptr == 2 then insertitem vrect[ptr] side[sptr] 1 
        if dir == -1 then dir = 1 else dir = -1
    )
    if sptr == 1 then append side[sptr] vrect[nptr]
    else if sptr == 2 then insertitem vrect[nptr] side[sptr] 1 
 )

以上で「side」配列に左右のエッジを構成する頂点が接続順に格納される。

続きはまた次回。

maxまとめページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)3ds Max | CG

2017年03月28日

modo10.2v1のスクリプトについて調べてみた その70

引き続き「TD SDK」について調べてみたい。

今回は「modo.mathutils」モジュールについて調べてみたい。

まずは「Matrix3」クラス。これは3X3の行列を扱うクラスだ。初期化メソッド「__init__()」は以下のようになっていて、引数として「なし」、「lx.object.Unknown」、「lx.object.Matrix」、「Matrix4」、「Matrix3」、「tuple」、「list」がとれるようになっている。

 class Matrix3(object):
    """Matrix3 class """

    def __init__(self, other=None):
        """Initializes a Matrix object

        :param other: Copies from other Matrix3 (optional)
        :type other: Matrix3
        returns: Matrix3
        """

        self.m = []
        self.size = 3

        self.setIdentity()
        if other:
            # TODO: look into removing redundant conversion
            if isinstance(other, lx.object.Unknown):
                inmat = lx.object.Matrix(other)
                self.set(inmat.Get3())
            if isinstance(other, lx.object.Matrix):
                self.set(other.Get3())
            if isinstance(other, Matrix4):
                for row in range(3):
                    for column in range(3):
                        self.m[row][column] = other[row][column]
            else:
                self.set(other)
                
    @staticmethod
    def _getIdentity(size=None):

        mat = []
        size = 3 if size is None else size

        for row in range(size):
            r = [0.0 for i in range(size)]
            r[row] = 1.0
            mat.append(r)
        return mat
               
    def set(self, other, transpose=True):
        """Copy values from other Matrix

        :param Matrix other: Source Matrix
        """
        if isinstance(other, Matrix3):
            self.m = [list(row[:]) for row in other.m]
        elif isinstance(other, (tuple, list)):
            # Input is a 3x3 matrix
            if len(other[0]) == 3:
                self.m = [list(row[:]) for row in other]
            # Input is a 4x4 matrix
            else:
                self.m = [list(row[:]) for row in other]

引数なしでインスタンスを作った場合は3X3の単位行列が生成される。

from modo.mathutils import *

m = Matrix3()
print m

# Result: 
Matrix3([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])

| 1 0 0 |
| 0 1 0 |
| 0 0 1 |

ここで使われている「_getIdentity(n)」はクラスメソッドでnXnの単位行列が生成できる。引数なしなら3X3の単位行列が生成される。

from modo.mathutils import *

print Matrix3._getIdentity(5)

# Result: 
Matrix3([[1.0, 0.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0, 1.0]])

「lx.object.Unknown」はCOMの基本インターフェースで渡ってくる場合で「value」値がmodoから渡される時かな。渡されたオブジェクトは「lx.object.Matrix」インターフェースを持っているものとしてラッピングされて、「Get3()」メソッドを使って3X3の値が取り出されて「self.m」に2次元のリストとしてセットされる。

「Matrix4」の時は3X3の部分だけがコピーされて残りの1行は捨てられる。

四則演算のオーバーライドは「*」乗算のみのようで、しかもMatrix3どうしの乗算のみがサポートされているようだ。下のプログラムを見るとなんでmatrixTmpとmatrixResultを作ったかよくわからないけど、この乗算でオペランドのマトリクスが変化する事は無いようだ。

 def __mul__(self, other):
     """
     Multiply with other matrix
        
     :param other: Matrix to multiply with
     :type other: Matrix3
        
     """
     matrixTmp, matrixResult = self.__class__(), self.__class__()

     for i in range(self.size):
         for j in range(self.size):
             matrixTmp.m[i][j] = 0.0

     for i in range(self.size):
         for j in range(self.size):
             for k in range(self.size):
                 matrixTmp.m[i][j] += self.m[i][k] * other.m[k][j]

     for i in range(self.size):
         for j in range(self.size):
             matrixResult.m[i][j] = matrixTmp.m[i][j]

     return matrixResult

このモジュールには「Vector3」クラスと言う3次元ベクトルを扱うクラスもあるけど、これを変換する行列は「Matrix4」クラスの方で行うようで、「Matrix3」で変換したい場合は一度「Matrix4」に直してから行う必要があるようだ。

from modo.mathutils import *
m = Matrix3([[1.0,4.0,1.0],[4.0,3.0,6.0],[1.0,2.0,1.0]])
m4 = Matrix4(m)
v = Vector3([1,2,3])
v.mulByMatrixAsPoint(m4)
print v

# Result: 
Vector3(12.000000, 16.000000, 16.000000)

つづきはまた次回。

modo10ブログ目次



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)modo | CG

2017年03月27日

ビットマップペイントツールのチュートリアルをやってみた その36 3dsmax 2017

引き続きMAXScriptマニュアルに載っている「チュートリアル-ビットマップペイントツールを9つの簡単なステップで作成する」 の続きを考えて見たい。

今回はビットマップ座標系からワールド座標系への変換マトリクスを生成してみた。

まずビットマップ座標系は下の画像のように左上が原点で、今回のプログラムでは512X512ピクセルだ。

fig01

そしてUVマップは左下が原点でサイズは1X1だ。

fig02

ビットマップ座標からUVマップ座標への変換行列は以下のようになる。

fig03

UVマップ座標を三角形の重心座標に変換する行列は、重心座標値の合計が1になる事を使って、

fig05

fig04

となる。ここで得られた重心座標に三角ポリゴン各頂点のワールド座標値をかけて足してやれば、対応するワールド座標値が出る。

fig06

これらの変換行列を繋ぎ合わせれば、ビットマップ座標(Bx,By)をワールド座標(Wx,Wy,Wz)に変換する行列が得られる。
fig07

以下がその変換行列を求めるファンクションだ。

	fn getB2WMatrix = (
		mb2t = matrix3 [ 1.0/bitmapx_1,0,0][0,-1.0/ bitmapy_1, 0][0,1,1][0,0,0]
		texFace = meshop.getMapFace theMesh theChannel faceIndex
		tv1= meshop.getMapVert theMesh theChannel texFace.x
		tv2= meshop.getMapVert theMesh theChannel texFace.y
		tv3= meshop.getMapVert theMesh theChannel texFace.z
		mg2t=(matrix3 [tv1.x,tv1.y,1] [tv2.x,tv2.y,1] [tv3.x,tv3.y,1] [0,0,0])
		theMesh = theObj.mesh
		sufFace = getFace theMesh faceIndex
		sv1 = (getVert theMesh sufFace.x)
		sv2 = (getVert theMesh sufFace.y)
		sv3 = (getVert theMesh sufFace.z)
		mg2w=(matrix3 sv1 sv2 sv3 [0,0,0])  * theObj.transform
		return mb2t*(inverse mg2t) * mg2w
	)

続きはまた次回。

maxまとめページ



take_z_ultima at 11:30|この記事のURLComments(0)TrackBack(0)3ds Max | CG
Archives