2018年07月

2018年07月31日

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

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

前回の方針をもとにペイントする矩形の4頂点を生成するように「paintFace()」を書き換えてみた。

まず、矩形の方じゃなくて本来ペイントするための多角形の両サイドについてそれぞれの2番目の頂点のX座標値を比較して、side[1]の方が小さくなるように、大きい場合に両エッジを入れ替えるようにした。これでスライスのたびに切片のX座標値を比較する必要が無くなった。

fn paintFace vface theBitmap mb2w bpos = (
  corner_n = #()
  corner_y = #()

       :

  if side.count>=2 then (
    if side[1][2][1] > side[2][side[2].count-1][1] then (
      tmps = side[1]
      side[1] = side[2]
      side[2] = tmps
    )
    s1 = s2 = 0  
    if side[1][1][2] > side[1][side[1].count][2] then (
      s1 = 2
      s2 = 1
    ) else (
      s1 = 1
      s2 = 2
    )
    
    side2ptr = side[s2].count
    for i = 1 to ( side[s1].count - 1) do
    (
      for iy = side[s1][i][2]+1 to side[s1][i+1][2] do
      (
        while iy > side[s2][side2ptr - 1][2] do (
          side2ptr -= 1  
        )
        if side2ptr == 1 then side2ptr = 2
        ix1 = scanx side[s1][i] side[s1][i+1] iy
        ix2 = scanx side[s2][side2ptr] side[s2][side2ptr-1] iy
        drawline ix1 ix2 iy theBitmap mb2w bpos #none
      )
    )

次に「cap」と「side」それぞれについてエッジの始点側の3番目の要素がtrueの時はそのエッジが不連続UV境界なので、処理するようにして、その中で「overpaint」ぶんの幅を持つ矩形領域を生成した。

「cap」側の水平なエッジでは「side[1]」の真ん中あたりのY座標値と比較して大きいか小さいかで上側のエッジなのか下側のエッジなのかを判定して矩形を生成する側を決めている。「side」の方は1がX軸小さい側になるようにしたのでそのエッジに対する法線ベクトルのX軸要素がマイナスになる側を「side[1]」に割り当てるように法線の方向を調整した。

    for ed in cap do (
      for p = 1 to (ed.count - 1) do (
        if ed[p][3] == true then (
            x1 = ed[1][1]
            x2 = ed[2][1]
            y1 = ed[1][2]
            y2 = ed[2][2]
            x4 = ed[1][1]
            x3 = ed[2][1]
          if ed[1][2] < side[1][side[1].count / 2][2] then (
            y4 = ed[1][2] - overpaint
            y3 = ed[2][2] - overpaint
          ) else (
            y4 = ed[1][2] + overpaint
            y3 = ed[2][2] + overpaint
          )
        )

             :

      )
    )
    for ed in side do (
      for p = 1 to (ed.count - 1) do (
        if ed[p][3] == true then (
          x1 = ed[p][1]
          x2 = ed[p+1][1]
          y1 = ed[p][2]
          y2 = ed[p+1][2]
          l = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
          nx =  - (y2-y1) / l * overpaint
          ny =  (x2-x1) / l * overpaint
          if nx < 0 then (
            nx = -nx
            ny = -ny
          )  
          if  s == 1 then (
            x3 = x2 - nx
            y3 = y2 - ny
            x4 = x1 - nx
            y4 = y1 - ny
          ) else (
            x3 = x2 + nx
            y3 = y2 + ny
            x4 = x1 + nx
            y4 = y1 + ny
          )

              :

        )
      )
    )
  )
),

続きはまた次回。

maxまとめページ



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

2018年07月30日

パーティクル系のアセンブリプリセットについて調べてみた その12 modo12

引き続き「presets」パネルの「Particles」にあるアセンブリを調べてみたい。

「Random Size」と「Random Velocity」は「Random Rotation」と基本的なところは同じで、適用するのがパーティクルのサイズか速度かってだけだ。

fig03

下の画像が「Random Size」の中身だ。やはり「Random」ノードの「シード」にパーティクルのIDを入力する事で各パーティクルに個別の乱数が割り当てられるようにしている。そしてその値はパーティクルの「サイズ」チャンネルに入力されるからパーティクルサイズがランダムな大きさに変るわけだ。

fig01

「Random Velocity」も同様の方法で乱数を生成しているけど、このアセンブリではパーティクルの移動方向は変化させたく無いので、パーティクルから「速度」チャンネルを読み込んで、その速度ベクトルの長さを「Set Length」ノードを使って、乱数値になるように修正して、再びパーティクルの「速度」チャンネルに戻している。

fig02

続きはまた次回。

modo10-11ブログ目次



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

2018年07月26日

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

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

さてとりあえずエッジのパターンを広げてペイントするテストプログラムが出来たので、これを「ParticlePaint」に組み込む事になる。主となるのは「overpaint()」の部分で、まずは塗り潰す矩形部分の生成から置き換えを考えてみたい。

fn overpaint = (
  x1 = toX.value
  y1 = toY.value
  paintWidth = spWidth.value
    
  -- create rectangle points
  x2 = -x1
  y2 = -y1
  x1 += bitmapX/2
  x2 += bitmapX/2
  y1 = bitmapY/2 - y1
  y2 = bitmapY/2 - y2
  
  l = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
  nx =  - (y2-y1) / l * paintWidth
  ny =  (x2-x1) / l * paintWidth
  
  x3 = x2 + nx
  y3 = y2 + ny
  x4 = x1 + nx
  y4 = y1 + ny
  
  -- bounding box (arx[1],ary[1])-(arx[4],ary[4])
  
  arx = sort #(x1,x2,x3,x4)
  ary = sort #(y1,y2,y3,y4)
  
  
  det=(x2-x1)*y4+(-x4+x1)*y2+(x4-x2)*y1
  m11 = (y4-y1)/det
  m12 = -(y2-y1)/det
  m21 = -(x4-x1)/det
  m22 = (x2-x1)/det
  m31 = -(x1*y4-x4*y1)/det
  m32 = (x1*y2-x2*y1)/det
  
  -- paint bounding box
  for y = ary[1] to ary[4] do (
    for x = arx[1] to arx[4] do (
      tx = m11 * (x-0.5) + m21 * (y-0.5) + m31
      ty = m12 * (x-0.5) + m22 * (y-0.5) + m32
      if tx >=0 and tx <=1 and ty >=0 and ty <= 1 then (
        c = colOnEdge x y x1 y1 x2 y2
        setPixels theCanvasBitmap  [x,y] #(c)
      )
    )
  )
)

エッジは水平方向は「cap」、斜め・垂直方向は「side」に分けてあるので、あとはエッジをまたいで外側がどっちかさえわかればいいわけだ。その判定は「cap」ならば1つの頂点のY座標値を他の「side」の2番目あたりの頂点のY座標値と比較して大きいか小さいかで多角形の上側のフタなのか下側のフタなのかわかる。「side」についてもエッジをスキャンラインでスライスした時にエッジとスキャンラインの交点のX座標値を比較して大小を出しているので、このあたりから情報を取得すればエッジのどちらが外側か判定できそうだ。

fn paintFace vface theBitmap mb2w bpos = (
  corner_n = #()
  corner_y = #()
  n = vface.count
  dlt1 = delty vface[n] vface[1]
  for i = 1 to n do
  (
    nexti = int(mod i n ) + 1
    dlt2 = delty vface[i] vface[nexti]
    chng = dlt1 * dlt2
    if chng <= 0 and (dlt1 !=0 or dlt2 !=0)then (
      append corner_n i
      append corner_y vface[i].y
    )
    dlt1 = dlt2
  )
  append corner_n corner_n[1]  
  append corner_y corner_y[1]
      
  side = #()
  cap = #()
  for i = 1 to (corner_n.count-1) do (
    border = #()
    ptr = corner_n[i]
    s = 0
    do (
      append border vface[ptr]
      ptr = int(mod ptr n ) + 1
      s = s + 1
      if s > 100 then exit
    ) while (ptr != corner_n[i + 1])
    append border vface[ptr]
    if (corner_y[i] - corner_y[i+1]) == 0 then (
      append cap border
    ) else (    
      append side border
    )
  )
  if side.count>=2 then (
    s1 = s2 = 0  
    if side[1][1][2] > side[1][side[1].count][2] then (
      s1 = 2
      s2 = 1
    ) else (
      s1 = 1
      s2 = 2
    )
    side2ptr = side[s2].count
    for i = 1 to ( side[s1].count - 1) do
    (
      for iy = side[s1][i][2]+1 to side[s1][i+1][2] do
      (
        while iy > side[s2][side2ptr - 1][2] do (
          side2ptr -= 1  
        )
        if side2ptr == 1 then side2ptr = 2
        ix1 = scanx side[s1][i] side[s1][i+1] iy
        ix2 = scanx side[s2][side2ptr] side[s2][side2ptr-1] iy
        if ix1>ix2 then (
          swp = ix1
          ix1 = ix2
          ix2 = swp
        )
        drawline ix1 ix2 iy theBitmap mb2w bpos #none
      )
    )
  )
),

当然エッジを広げてペイントするかどうかはエッジが不連続UV境界かどうかを調べてって事になり、それはエッジの1番目の頂点の3番目の値を調べればわかるようになっているわけだ。

続きはまた次回。

maxまとめページ



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

2018年07月25日

パーティクル系のアセンブリプリセットについて調べてみた その11 modo12

引き続き「presets」パネルの「Particles」にある「Random Rotation」アセンブリを調べてみたい。

シーンに「Radial Emitter」と立方体を追加して、立方体はちょっと小さくスケーリングしておく。

fig02

さらに「Replicator」を追加して、

fig01

パーティクルに立方体を下のように割り当てる。

fig03

これでシミュレーションすると下のように立方体は同じ角度でパーティクルに割り当てられる。

fig04

ここでスケマティックビューに「Particle Simulation」アイテムと「Random Rotation」ノードを追加して下のように接続して、シミュレーション計算すると、

fig05

このように立方体の方向がランダムになる。ただしこの方向は時間が経過しても変化しない。それはパーティクルの角度がパーティクルのIDから生成される「シード」値によって決まるためで、時間が経過してもIDは変化しないから角度が変化しないわけだ。

fig06

それを検証するために下のように「Seed」に「Time」ノードから「フレーム」を入力して、「Seed」の値が時間経過と共に変化するようにしてみた。

fig07

シミュレーションを再計算してみると、立方体の方向は1フレームごとにランダムに変化するのが確認できた。

続きはまた次回。

modo10-11ブログ目次



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

2018年07月24日

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

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

前回のプログラムについて追加部分「overpaint()」について書いておくよ。

まずダイアログのコントロールから3つの値を取得する。「paintWidth」がエッジを広げる幅になる。

  fn overpaint = (
    x1 = toX.value
    y1 = toY.value
    paintWidth = spWidth.value

次にダイアログから得たX、Y値からペイントする長方形の底辺にあたるエッジの線分の両端の座標値(x1 , y1)、(x2 , y2)を生成する。これはビットマップの中心を原点(0,0)、右をX軸正、上をY軸正とした座標値からビットマップの左上を原点とした下方向をY軸正としたビットマップ座標に変換する。

    -- create rectangle points
    x2 = -x1
    y2 = -y1
    x1 += bitmapX/2
    x2 += bitmapX/2
    y1 = bitmapY/2 - y1
    y2 = bitmapY/2 - y2

この2つの座標からエッジのベクトルを作り、そのベクトルに直交するベクトルを生成して、それを自分の長さで割って単位ベクトルにして、それに「paintWidth」をかけて長方形の幅方向のベクトル(nx,ny)を生成した。

    l = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))
    nx =  - (y2-y1) / l * paintWidth
    ny =  (x2-x1) / l * paintWidth

エッジの両端の頂点にこの垂直ベクトルを加算して、長方形の反対側のエッジの両端の座標値(x3 , y3),(x4 , y4)を得る。これでペイントする長方形の4頂点の座標が生成された。

    x3 = x2 + nx
    y3 = y2 + ny
    x4 = x1 + nx
    y4 = y1 + ny    

これらの4つの頂点のX座標値、Y座標値を配列に入れて小さい順に並べ替えた。これで配列の1番目と4番目に各要素の最大最小値が入るので、(arx[1] , ary[1]) - (arx[4] , ary[4])がバウンディングボックスの範囲になる。

    -- bounding box (arx[1],ary[1])-(arx[4],ary[4])
    
    arx = sort #(x1,x2,x3,x4)
    ary = sort #(y1,y2,y3,y4)

長方形の内外を判定する変換行列を生成する。

    -- invertMatrix
    det=(x2-x1)*y4+(-x4+x1)*y2+(x4-x2)*y1
    m11 = (y4-y1)/det
    m12 = -(y2-y1)/det
    m21 = -(x4-x1)/det
    m22 = (x2-x1)/det
    m31 = -(x1*y4-x4*y1)/det
    m32 = (x1*y2-x2*y1)/det

バウンディングボックスの端から端まで全てのピクセルに対して変換行列を適用して、長方形の内部かどうかを判定し、内側だったらそのピクセルに対応するエッジ上の位置の色を取得「colOnEdge()」して、そのピクセルを塗り潰す。

    -- paint bounding box
    for y = ary[1] to ary[4] do (
      for x = arx[1] to arx[4] do (
        tx = m11 * (x-0.5) + m21 * (y-0.5) + m31
        ty = m12 * (x-0.5) + m22 * (y-0.5) + m32
        if tx >=0 and tx <=1 and ty >=0 and ty <= 1 then (
          c = colOnEdge x y x1 y1 x2 y2
          setPixels theCanvasBitmap  [x,y] #(c)
        )
      )
    )
  )

続きはまた次回。

maxまとめページ



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