2019年05月

2019年05月30日

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

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

今回は前回書き換えた「crossFaceMaps()」のコードについて動作を確認しておきたい。

fn crossFaceMaps theMesh theChannel mt2b =
(
  invBrushTransform = inverse( brushtransform)
  crossfaces = selectcrossface()
  maps = #()
  bareas = #()

  for faceIndex in crossfaces do (
    vlist = #()

    sufFace = getFace theMesh  faceIndex
    append vlist (getVert theMesh sufFace.x)
    append vlist (getVert theMesh sufFace.y)
    append vlist (getVert theMesh sufFace.z)
    mg2w=(matrix3 [vlist[1].x,vlist[1].y,vlist[1].z] [vlist[2].x,vlist[2].y,vlist[2].z] [vlist[3].x,vlist[3].y,vlist[3].z] [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])

    mg2b = mg2t * mt2b
    mw2b = (inverse mg2w) * mg2b
    mb2w = inverse mw2b
    mt2w = (inverse mg2t) * mg2w
    
    for i = 1 to 3 do (
      if borders[faceIndex][i] then (
        j = i + 1
        if j == 4 then j = 1
        barea = #()
        append barea vlist[i]
        append barea vlist[j]
        offpoint[texFace[j]][3] = 1.0
        append barea (offpoint[texFace[j]] * mt2w)
        offpoint[texFace[i]][3] = 1.0
        append barea (offpoint[texFace[i]] * mt2w)

        clipface &barea invBrushTransform 

        if barea.count != 0 then (
          bmaplist =  #()
          for v in barea do (
            append bmaplist (v * mw2b)
          )
          append bareas #(bmaplist,mb2w)
        )
      )
    )
    
    clipface &vlist invBrushTransform 

    if vlist.count != 0 then (
      maplist =  #()

      for v in vlist do (
        mp = v * mw2b
        append maplist mp
      )

      append maps #(maplist , mb2w)
    )
  )
),

まず「invBrushTransform」にワールド座標系からブラシ立方体のローカル座標系に変換するマトリクスが格納される。

次に「crossfaces」に「selectcrossface()」でブラシ立方体と干渉する面のリストを取得する。

ブラシ立方体でトリムされたUVマップを格納する「maps」とその輪郭を広げた部分をブラシ立方体でトリムしたUVマップを格納する「bareas」を初期化する。

fn crossFaceMaps theMesh theChannel mt2b =
(
  invBrushTransform = inverse( brushtransform)
  crossfaces = selectcrossface()
  maps = #()
  bareas = #()

「crossface」から1枚ずつ面を取り出して「vlist」にその頂点座標(ワールド座標)をセットして、「mg2w」に重心座標からワールド座標に変換するマトリクスをセットする。

for faceIndex in crossfaces do (
  vlist = #()

  sufFace = getFace theMesh  faceIndex
  append vlist (getVert theMesh sufFace.x)
  append vlist (getVert theMesh sufFace.y)
  append vlist (getVert theMesh sufFace.z)
  mg2w=(matrix3 [vlist[1].x,vlist[1].y,vlist[1].z] [vlist[2].x,vlist[2].y,vlist[2].z] [vlist[3].x,vlist[3].y,vlist[3].z] [0,0,0])

「texFace」に「faceIndex」で指定した面のUVマップでの頂点番号をPoint3で取得し、そこからUV座標で3頂点の座標を得て、「mg2t」に重心座標からテクスチャ座標に変換するマトリクスをセットする。

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])

「mg2b」に重心座標からビットマップ座標に変換するマトリクスを、「mw2b」にワールド座標からビットマップへ変換するマトリクスを、「mb2w」にその逆を、「mt2w」にUV座標からワールド座標に変換するマトリクスをセットする。

mg2b = mg2t * mt2b
mw2b = (inverse mg2w) * mg2b
mb2w = inverse mw2b
mt2w = (inverse mg2t) * mg2w

「faceIndex」で示す面の3辺について「borders」でその辺が輪郭線かどうかを調べて、輪郭線の場合はその両端の点と、その点を膨らませた頂点2つを「barea」にセットして、エッジを膨らませた部分の四角形の頂点リストを生成する。膨らませた頂点については「offpoint」に格納されているけど、UV座標系なので「mt2w」をかけてワールド座標系に変換している。「mt2w」はUVW座標のW軸が1になるのが前提になっているので「offpoint」のW座標に1をセットしている。これで「barea」にワールド座標系の四角形のデータが格納されるので、これを「clipface()」に渡してブラシ立方体でトリムする。その結果面が残ったら、それを「mw2b」をかけてビットマップ座標系に変換して「mb2w」と共に「bareas」配列に格納する。

for i = 1 to 3 do (
  if borders[faceIndex][i] then (
    j = i + 1
    if j == 4 then j = 1
    barea = #()
    append barea vlist[i]
    append barea vlist[j]
    offpoint[texFace[j]][3] = 1.0
    append barea (offpoint[texFace[j]] * mt2w)
    offpoint[texFace[i]][3] = 1.0
    append barea (offpoint[texFace[i]] * mt2w)

    clipface &barea invBrushTransform 

    if barea.count != 0 then (
      bmaplist =  #()
      for v in barea do (
        append bmaplist (v * mw2b)
      )
      append bareas #(bmaplist,mb2w)
    )
  )
)

同様に「faceIndex」で示す面を「clipface()」でトリムして、それをビットマップ座標に変換して「maplist」に収めて、それを「mb2w」と共に「maps」に格納する。

clipface &vlist invBrushTransform 

if vlist.count != 0 then (
  maplist =  #()

  for v in vlist do (
    mp = v * mw2b
    append maplist mp
  )

  append maps #(maplist , mb2w)
)

これで「maps」にペイントする多角形が、「bareas」に輪郭からはみ出し部分の多角形がビットマップ座標で格納される。

続きはまた次回。来週は更新をお休みします。

maxまとめページ



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

2019年05月29日

メッシュオペレータでいろいろやってみた その39 modo13

今回もmodo13で追加されたオペレータを調べてみたい。

今回は「Edge Relax」オペレータ。

fig 1

これはベベルで丸められたコーナーをエッジレベルで調整するオペレータのようだ。

このオペレータは下の画像のようにベベルで丸めたコーナーとそれに続く両側のエッジに適用することが想定されているようで、このオペレータによってコーナーの曲率や大きさ、その隣接エッジへの影響をコントロール出来るようになっている。

fig 2

「プロパティ」は以下の通り。

fig 3

「オフセット」はベベルで丸められたカーブの両端をそれに接続するエッジに沿って移動させる距離を設定するパラメータ。

例えば下の画像のエッジを選択してこのオペレータを追加すると、「インデックスで選択」オペレータが追加されてこれらのエッジだけがオペレータの効果対象になる。

fig 4

この状態で「オフセット」パラメータを増減させるとその値の距離だけ左右のエッジ方向にカーブ両端の頂点が移動する。

fig 5

「張力」は数値を大きくするとベベルで丸まった部分が中央に絞られる。下の画像は「張力」がどう作用するかを試してみたものだ。100%で影響が無くなり、0%で曲線が直線になっている。

fig 7

「深度」は曲線の端点を結んだ直線から曲線上の点までの垂直距離にかかる倍率。下の画像はその効果を比較してみたもの。100%で効果が無くなり、0%で直線に、−100%で反転する。

fig 8

続きはまた次回。

modo10-13ブログ目次



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

2019年05月28日

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

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

前回「boeders」に各三角面の3つのエッジがUVマップの輪郭線かどうかのデータを格納するようにした。

今回はそのデータを使ってようやくはみ出し部分の処理をしていくわけだけど、プライベートメンバの初期化位置をちょっと変更した。そして新たに「bareas」メンバを追加した。これに輪郭を広げた部分をブラシ立方体でトリムした多角形のUVマップを格納する。従来の「maps」と分けたのははみ出し塗りを本体の塗りから分離して、あとから塗ることではみ出し部分が本体のペイントを上塗りするのを防ぐことが出来るからだ。

struct objectPaint (
  public
    maxFaceRadius = 40.0,
    brushsize=10,
    BrushShape = 2,
    BrushColor = black,
    theObj,
    brushtransform,
    bitmapX = 512,
    bitmapY = 512,
    temp_bitmap_filename = (getDir #preview +"/microPaint_temp2.tga"),
    theBitmap,
    checkBitmap,
    overpaint = 0.01,

  private
    sanim,
    tmpm,
    maps,
    muw,
    cmuw = false,
    offpoint,
    borders,
    bareas,

「clipface()」は元に戻した。

fn clipface &vlist w2b = ( 
  for i = 0 to 5 do (
    vtmp = #()
    for j = 1 to (vlist.count - 1) do (
      st = chckClip vlist[j] vlist[j+1] w2b i
      if st[1] == 0 then (
        append vtmp vlist[j]
      ) else if st[1] == 2 then (
        append vtmp vlist[j] 
        append vtmp (innerpoint vlist[j] vlist[j+1] st[2])
      ) else if st[1] == 1 then (
        append vtmp (innerpoint vlist[j] vlist[j+1] st[2])
      )
    )
    if vlist.count == 0 then exit
    st = chckClip vlist[vlist.count] vlist[1] w2b i
    if st[1] == 0 then (
        append vtmp vlist[vlist.count]
    ) else if st[1] == 2 then (
      append vtmp vlist[vlist.count]
      append vtmp (innerpoint vlist[vlist.count] vlist[1] st[2])
    ) else if st[1] == 1 then (
      append vtmp (innerpoint vlist[vlist.count] vlist[1] st[2])
    )
    vlist = vtmp
  )
),

「get_offpoints()」にプライベートメンバの初期化を移した。

fn get_offpoints = (
  offpoint = #()
  borders = #()
  useEdges = #()
  islands = #()

  muw = theObj.modifiers[#unwrap_uvw]

「crossFaceMaps()」を書き換えて輪郭エッジとそれを膨らませたエッジの間の四角い領域を「barea」として生成して、それを「clipface()」を使ってブラシ立方体にトリムさせてはみ出し部分の塗り領域を生成して「bareas」に追加するようにした。その際、ビットマップ座標からワールド座標に変換するマトリクスを一緒につけることであるピクセルのブラシ中心からの距離などが算出できるようにした。

fn crossFaceMaps theMesh theChannel mt2b =
(
  invBrushTransform = inverse( brushtransform)
  crossfaces = selectcrossface()
  maps = #()
  bareas = #()

  for faceIndex in crossfaces do (
    vlist = #()

    sufFace = getFace theMesh  faceIndex
    append vlist (getVert theMesh sufFace.x)
    append vlist (getVert theMesh sufFace.y)
    append vlist (getVert theMesh sufFace.z)
    mg2w=(matrix3 [vlist[1].x,vlist[1].y,vlist[1].z] [vlist[2].x,vlist[2].y,vlist[2].z] [vlist[3].x,vlist[3].y,vlist[3].z] [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])

    mg2b = mg2t * mt2b
    mw2b = (inverse mg2w) * mg2b
    mb2w = inverse mw2b
    mt2w = (inverse mg2t) * mg2w
    
    for i = 1 to 3 do (
      if borders[faceIndex][i] then (
        j = i + 1
        if j == 4 then j = 1
        barea = #()
        append barea vlist[i]
        append barea vlist[j]
        offpoint[texFace[j]][3] = 1.0
        append barea (offpoint[texFace[j]] * mt2w)
        offpoint[texFace[i]][3] = 1.0
        append barea (offpoint[texFace[i]] * mt2w)

        clipface &barea invBrushTransform 

        if barea.count != 0 then (
          bmaplist =  #()
          for v in barea do (
            append bmaplist (v * mw2b)
          )
          append bareas #(bmaplist,mb2w)
        )
      )
    )
    
    clipface &vlist invBrushTransform 

    if vlist.count != 0 then (
      maplist =  #()

      for v in vlist do (
        mp = v * mw2b
        append maplist mp
      )

      append maps #(maplist , mb2w)
    )
  )
),

続きはまた次回。

maxまとめページ



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

2019年05月27日

メッシュオペレータでいろいろやってみた その38 modo13

今回も前回に引き続き「Surface Constraint」オペレータについて調べてみたい。

fig 1

「両面」オプションはONにするとサーフェスの表裏に関係なくコンストレイントできるようになる。OFFの場合はサーフェスの裏側は無視され、コンストレイント方向に裏側を向いたサーフェスがあると、その後ろに表を向いたサーフェスがあってもコンストレイントされない。

例えば下の画像のシーンは、コンストレイントの対象になるメッシュアイテム内に向きが反対の2枚の面が配置してあって、球体に「Surface Constraint」オペレータが設定されていて、「方向」モードで「Y」軸、「反転」ONになっていて、黄色い矢印方向にコンストレイントサーフェスを見つけるモードになっている。ところがY軸マイナス方向のレイが最初にぶつかるのは裏を向いた面なので、球体はコンストレイントできるサーフェスが無いものとして処理される。

fig 2

裏を向いた面だけちょっと移動させて球体の一部から表を向いた面が見えるようにしてみると、下の画像のように裏面で遮られていない部分はコンストレイントされる事がわかる。

fig 3

これは裏面が球体の上にあっても同じで、「方向」モードの場合は無限遠から来るレイが最初にぶつかる面が裏か表かが問題になるわけだ。

fig 4

だからコンストレイントされるメッシュ頂点からレイが飛ぶ「ハーフレイ」モードでは裏面が球体の上にある場合はレイは裏面にはぶつからず、下の表面にぶつかるので球体はコンストレイントされる。

fig 5

「両面」オプションがONの時はこの表裏の判定が無くなるので、面が裏であっても表であってもレイが最初にぶつかった面にメッシュ頂点がコンストレイントされて貼り付けられる。

「サーフェス法線を頂点法線マップへと保存」オプションを使うと、レイが当ったサーフェスの法線をコンストレイント対象のメッシュ頂点にコピー出来る。

例えば下のシーンで立方体の各面は鏡面のマテリアルが貼ってある。

fig 6

このままレンダリングすると赤い球体が立方体の面に映る。

fig 7

ここで立方体に「Surface Constraint」オペレータを設定して、下の画像の4つの頂点を

fig 8

「Select By Index」でコンストレイントの範囲を限定する。

fig 9

このように設定すると、

fig 10

選択頂点が左の面のサーフェスにコンストレイントされて下のようになる。

fig 11

ここで「強さ」を0%にすると、

fig 12

シーンは元の状態に戻る。

fig 13

ここで「サーフェス法線を頂点法線マップへと保存」をONにすると、

fig 14

左の面の法線がコピーされて面の法線が左を向き、面に左側の青い球体が映る。

fig 15

続きはまた次回。

modo10-13ブログ目次



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

2019年05月23日

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

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

UVマップの輪郭頂点だけ記録しても面が三角形なので面のどの辺が輪郭線なのか不明になってしまう問題は、新たに「borders」配列を作って、そこに面の番号に対して3辺の状態をあらわすビット配列で記録することにした。

struct objectPaint (
  public
    maxFaceRadius = 40.0,
    brushsize=10,
    BrushShape = 2,
    BrushColor = black,
    theObj,
    brushtransform,
    bitmapX = 512,
    bitmapY = 512,
    temp_bitmap_filename = (getDir #preview +"/microPaint_temp2.tga"),
    theBitmap,
    checkBitmap,
    overpaint = 0.01,

  private
    sanim = undefined,
    tmpm = undefined,
    maps = undefined,
    muw=undefined,
    cmuw = false,
    offpoint = #(),
    borders = #(),

そして「borders」は「get_offpoints」の中で生成した。

fn get_offpoints = (
  useEdges = #()
  theObj = $
  islands = #()

  muw = theObj.modifiers[#unwrap_uvw]
  muw.setTVSubObjectMode 3
  n = muw.numberPolygons()
  allpolys = #{1..n}
  do (
  sseed = (allpolys as array)[1]
  muw.selectFaces #{sseed}
  a = muw.getSelectedFaces()
  do (
      b = a
      muw.expandSelection() 
      a = muw.getSelectedFaces()
  ) while not (a-b).isempty
  append islands (a as array)
  allpolys -= a
  ) while not allpolys.isempty
  muw.selectFaces #{}

  theMesh = snapshotAsMesh theObj
  theChannel = 1
  edges = #{}

  for island in islands do (
      for faceIndex in island do(
          texFace = (meshop.getMapFace theMesh theChannel faceIndex)
          
          border = #{}
          if is_unce texFace[1] texFace[2] $ theChannel useEdges then (
              edges += #{texFace[1],texFace[2]}
              border[1] = true
          )
          if is_unce texFace[2] texFace[3] $ theChannel useEdges then (
              edges += #{texFace[2],texFace[3]}
              border[2] = true
          )
          if is_unce texFace[3] texFace[1] $ theChannel useEdges then (
              edges += #{texFace[3],texFace[1]}
              border[3] = true
          )
          borders[faceIndex] = border
      )
  )

これで面番号からどのエッジがUVメッシュの輪郭線になっているかわかるようになった。

面をペイントする時にはみ出し部分が接続している面の部分とテクスチャが連続するにはビットマップ座標からワールド座標に変換するマトリクス(mb2w)が同じであればいいので、それさえ守ればはみ出し部分と元の面はバラバラに処理しても構わないはずだ。

そこで面をブラシ立方体でトリムする「crossFaceMaps()」では面のトリムはそのままにして、はみ出し部分の四角形だけ別に生成して、トリムして、その多角形に面と同じ(mb2w)を付け加えた値を配列として生成して、すべての面のペイントが終わってから同じルーチンではみ出し部分も処理させることではみ出し部分がはみ出していない部分を上書きしないようにする事が出来そうだ。

続きはまた次回。

maxまとめページ



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