2018年03月

2018年03月29日

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

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

前回のテストの結果、不連続UVマップをペイントした時に微妙にペイントギャップが発生する場合がある事がわかった。

fig01

解決するためには不連続UVマップの境界部分を少しはみ出すようにペイントする必要があるようだ。そこで今回はそれをどうやって実現するのが一番手っ取り早いか考えてみた。

今回のプログラムでは基本的にUVポリゴンを1枚ずつ処理するようになっている。だから連続UVであっても不連続UVであっても処理は変らない。

もしどのポリゴンに対しても外側にはみ出してペイントしたとすると、連続UVマップの部分ではお隣のポリゴンのペイントがはみ出して来てしまって、都合が悪い。だからはみ出し部分をペイントする時にそのピクセルが他のポリゴン内かどうかを判定する必要がある。しかし前回の方法では全てのポリゴンをペイントし終わらないと、すでにペイント済みのポリゴン内のピクセルかどうかを判定出来ない。

そこでまず、ポリゴンをペイントする時にはみ出し部分はペイントする色と場所だけ記録しておいて、ペイントが終わってから改めてはみ出し部分のピクセルについて未ペイントピクセルかどうかチェックしてペイントするようにしたらどうかと検討中だ。

1枚のポリゴンのペイントはX軸のスキャンラインでやっているので一番上のラインではラインの両端の外側のピクセルとラインの上のピクセルを、途中のラインはラインの両端の外側のピクセルを、一番下のラインはラインの両端の外側のピクセルと下側のピクセルをはみ出し塗りピクセルにすればいいね。

位置の記録は前回同様ビット配列を使えばいいわけだけど、よく考えてみたら配列の添字は1から始まっていたので前回のプログラムのままだと(0,0)の位置のピクセルを記録しようとするとエラーになってしまう事に気付いた。アドレスに1足さないとダメだな。

続きはまた次回。

maxまとめページ



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

2018年03月28日

プロシージャルモデリングについて調べてみた その33 modo11.2v2

「ElboPipe」アセットの動作を通してプロシージャルモデリングの作法みたいなものが少しわかってきた。

今回からは「Fusion Damage Trim」を調べてみたい。

fig01

このアセットはメッシュフュージョンを使ってオブジェクト表面をガタガタに削り取るような用途に使うもののようだ。

具体的にはメッシュフュージョンに適した複数のメッシュオブジェクトを組み合わせて内臓のターゲットプレーンに沿って並べるようになっている。

例えば「モデル」レイアウトの「Fusion」サブタブから「Qbicジオメトリ」をクリックして、

fig02

「Fusion Qbic プリセット」パネルから適当なオブジェクトをダブルクリックしてシーンに読み込んで、

fig03

「新規 Fusion」→「選択メッシュで新規 Fusion」で読み込んだオブジェクトから「Fusion Item」を作成して、

fig04

「F6」キーを押して「Presets」パネルを出して「Assets」→「Assembles」→「MeshFusion Assembles」から「Fusion Damage」をダブルクリックしてアセットを読み込む。

fig05

これでアイテムリストは以下のような構成になる。この中から「fDamage UV Targets」を選んで、

fig06

「W」キーで移動ツールを呼び出して下のように移動するとアセット本体が現れる。アセットは色々なメッシュがメッシュ平面に沿って分布していて、個別に移動させたり出来るし、平面を変形させると追従する。

fig07

これらのメッシュと最初に読み込んだオブジェクトの干渉部分をメッシュフュージョンを使って除去する。

まず「fDamage Cluster」にペアレントされた「fDamage」アイテムが16個あるので全て選択して、

fig08

「減算トリム」ボタンをクリックしてこれらのオブジェクトを「減算トリム」アイテムに設定する。

fig09

次に最初に読み込んだ「Q-Barrel1」を追加選択して、

fig10

「トリム」ボタンをクリックする。

fig11

そして「Q-Barrel1」、「Fusion Damage」を非表示にすると、下のように部分的に破壊されたような窪みが確認できる。

fig12

続きはまた次回。

modo10-11ブログ目次



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

2018年03月27日

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

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

前回の方針通りビットマップに塗ったかどうかのチェックを入れて1回のブラシペイント(ストロークではない)で2重にペイントしないようにしてみた。

ペイントしたかどうかは1ビットあれば記録できるのでビット配列を使うことにした。

ビットマップのピクセル数はbitmapX * bitmapYあって、2次元だけどビット配列は1次元なので座標(x,y)のピクセルは一次元のビット配列の(x + bitmapX * y)番目の要素として割り当てた。ビット配列は初期状態では全てfalseで、ピクセルが描画された時に該当する要素にtrueをセットする事でペイント済みと言う事にした。具体的にビットマップに描画しているのは「drawline()」ファンクションなのでそれを下のように書き換えた。これでペイントしていないビットマップしかペイントしなくなる。

fn drawline x1 x2 y theBitmap mb2w bpos = (
  for x = x1 to x2 do (
    if not checkBitmap [x+y*bitmapX] do(
      case BrushShape of (
        1: (
          p = [x,y,1.0] * mb2w
          if distance p bpos <= BrushSize/2 then (
            setPixels theBitmap  [x,y] #(BrushColor)
            checkBitmap [x+y*bitmapX] = true
          )
        )
        2: (
          setPixels theBitmap  [x,y] #(BrushColor)
          checkBitmap [x+y*bitmapX] = true
        )
        3: (
          p = [x,y,1.0] * mb2w
          theFactor = (distance p bpos) / (BrushSize / 2.0)
          if theFactor <= 1.0 do
          (
            theFactor = sin ( 90.0 * theFactor)
            thePixels = getPixels theBitmap [x,y] 1
            if thePixels[1] != undefined do
            (
              thePixels[1] = (thePixels[1] * theFactor) + (BrushColor * (1.0 - theFactor))
              setPixels theBitmap [x,y] thePixels
              checkBitmap [x+y*bitmapX] = true
            )
          )
        )
      )
    )
  )
),

あとはブラシペイントの前にこのビット配列を初期化しておけば1ペイントごとの二重塗り防止チェックになるわけだ。下が全体のリスト。太い文字の部分が主な修正部分だ。

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,

  private
    sanim = undefined,
    fn chckClip p1 p2 w2b direction =
    (
      cplist = #()
      tlist = #()
      hsize = brushsize / 2.0
      bp1 = p1 * w2b
      bp2 = p2 * w2b

      st = 0
      t = 0.0
      case direction of (
        -- +x
        0:  (
            if bp1.x >= hsize then st = 1
            if bp2.x >= hsize then st = st + 2
            if st == 1 or st ==2 then (
              dx = bp2.x - bp1.x
              t = (hsize - bp1.x)/dx
            )
          )
        -- -x
        1:  (
            if bp1.x <= -hsize then st = 1
            if bp2.x <= -hsize then st = st + 2
            if st == 1 or st ==2 then (
              dx = bp2.x - bp1.x
              t = (-hsize - bp1.x)/dx
            )
          )
        -- +y
        2:  (
            if bp1.y >= hsize then st = 1
            if bp2.y >= hsize then st = st + 2
            if st == 1 or st ==2 then ( 
              dy = bp2.y - bp1.y
              t = (hsize - bp1.y)/dy
            )
          )
        -- -y
        3:  (
            if bp1.y <= -hsize then st = 1
            if bp2.y <= -hsize then st = st + 2
            if st == 1 or st ==2 then (
              dy = bp2.y - bp1.y
              t = (-hsize - bp1.y)/dy
            )
          )
        -- +z
        4:  (
            if bp1.z >= hsize then st = 1
            if bp2.z >= hsize then st = st + 2
            if st == 1 or st ==2 then (
              dz = bp2.z - bp1.z
              t = (hsize - bp1.z)/dz
            )
          )
        -- -z
        5:  (
            if bp1.z <= -hsize then st = 1
            if bp2.z <= -hsize then st = st + 2
            if st == 1 or st ==2 then (
              dz = bp2.z - bp1.z
              t = (-hsize - bp1.z)/dz
            )
          )
      )
      return #(st,t)  
    ),

    fn innerpoint p1 p2 t = (
      dx = p2.x - p1.x
      dy = p2.y - p1.y
      dz = p2.z - p1.z
      x = dx * t + p1.x
      y = dy * t + p1.y
      z = dz * t + p1.z
      return [x,y,z]
    ),

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

    fn selectcrossface brushtransform = (
       searchLength = sqrt(maxFaceRadius^2+brushsize^2) * 1.05
      sanim.length = searchLength 
      sanim.height = searchLength 
      sanim.width = searchLength
      centerpivot sanim  
      sanim.transform = brushtransform
      selfaces = getFaceSelection theObj
      return selfaces
    ),

    fn crossFaceMaps &maps theMesh theChannel brushtransform  mt2b =
    (
      invBrushTransform = inverse( brushtransform)
      crossfaces = selectcrossface brushtransform
      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
        mb2g = inverse mg2b
        mb2w = mb2g * mg2w

        clipface &vlist invBrushTransform

        if vlist.count == 0 then continue
        maplist =  #()
        for v in vlist do (
          mp = (meshop.getBaryCoords theMesh faceIndex v) * mg2t
          append maplist mp
        )
        append maps #(maplist , mb2w)
      )
    ),

    fn scanx p1 p2 y = (
      dx = p2[1] - p1[1]
      dy = p2[2] - p1[2]
      return int( dx * (y - p1[2]) / dy + p1[1] )
    ),

    fn drawline x1 x2 y theBitmap mb2w bpos = (
      for x = x1 to x2 do (
        if not checkBitmap [x+y*bitmapX] do(
          case BrushShape of (
            1: (
              p = [x,y,1.0] * mb2w
              if distance p bpos <= BrushSize/2 then (
                setPixels theBitmap  [x,y] #(BrushColor)
                checkBitmap [x+y*bitmapX] = true
              )
            )
            2: (
              setPixels theBitmap  [x,y] #(BrushColor)
              checkBitmap [x+y*bitmapX] = true
            )
            3: (
              p = [x,y,1.0] * mb2w
              theFactor = (distance p bpos) / (BrushSize / 2.0)
              if theFactor <= 1.0 do
              (
                theFactor = sin ( 90.0 * theFactor)
                thePixels = getPixels theBitmap [x,y] 1
                if thePixels[1] != undefined do
                (
                  thePixels[1] = (thePixels[1] * theFactor) + (BrushColor * (1.0 - theFactor))
                  setPixels theBitmap [x,y] thePixels
                  checkBitmap [x+y*bitmapX] = true
                )
              )
            )
          )
        )
      )
    ),

    fn paintFace vface theBitmap mb2w bpos = (
      maxy = miny = vface[1][2]
      top = bottom = 1
      for i = 2 to vface.count do
      (
        if vface[i][2] >= maxy then (
          maxy = vface[i][2]
          bottom = i
        )
        if vface[i][2] <= miny then (
          miny = vface[i][2]
          top = i
        )
      )
      side = #(#(),#())
        
    -- side1 vertex list
      sptr = 1
      vptr = top
      nvptr =  int(mod (vptr) vface.count) + 1
      safty = 1
      while vface[vptr][2] == vface[nvptr][2] do
      (
        vptr = int(mod (vptr) vface.count) + 1
        nvptr =  int(mod (vptr) vface.count) + 1
        safty +=1
        if safty > vface.count then return()
      )
      append side[sptr] vface[vptr]
      safty = 1
      do (
        vptr = int(mod (vptr) vface.count) + 1
        append side[sptr] vface[vptr]
        safty +=1
        if safty > vface.count then return()
      )
      while vface[vptr][2] != maxy
      
    -- side2 vertex list
      sptr = 2
      vptr = bottom
      nvptr =  int(mod (vptr) vface.count) + 1
      safty = 1
      while vface[vptr][2] == vface[nvptr][2] do
      (
        vptr = int(mod (vptr) vface.count) + 1
        nvptr =  int(mod (vptr) vface.count) + 1
        safty +=1
        if safty > vface.count then return()
      )
      
      append side[sptr] vface[vptr]
      safty = 1
      do (
        vptr = int(mod (vptr) vface.count) + 1
        append side[sptr] vface[vptr]
        safty +=1
        if safty > vface.count then return()
      )
      while vface[vptr][2] != miny
    --line scan
      side2ptr = side[2].count
      ix1 = scanx side[1][1] side[1][2] miny
      ix2 = scanx side[2][side2ptr] side[2][side2ptr-1] miny
      drawline ix1 ix2  miny theBitmap mb2w bpos

      for i = 1 to ( side[1].count - 1) do
      (
        for iy = side[1][i][2]+1 to side[1][i+1][2] do
        (
          while iy > side[2][side2ptr - 1][2] do (
            side2ptr -= 1  
          )
          if side2ptr == 1 then side2ptr = 2
          ix1 = scanx side[1][i] side[1][i+1] iy
          ix2 = scanx side[2][side2ptr] side[2][side2ptr-1] iy
          drawline ix1 ix2 iy theBitmap mb2w bpos
        )
      )
    ),

    fn delmodifier obj mtype = (
      local index = -1
      for i = 1 to obj.modifiers.count do (
      if (classof obj.modifiers[i]) == mtype then index = i
      )
      if index != -1 then (
        deletemodifier obj index
      )
    ),
  mt2b,
  public
    fn init = (
      theBitmap = bitmap bitmapX bitmapY color:white filename:temp_bitmap_filename
      if theObj.material == undefined do theObj.material = Standard()
      if theObj.material.diffusemap == undefined do
        theObj.material.diffusemap = bitmapTexture filename:temp_bitmap_filename
      showTextureMap theObj.material true
      
      --select theObj
      if theObj.modifiers[#Vol__Select] == undefined do (
        addModifier theObj (volumeselect())
      )
      if (classof theObj) != Editable_mesh do (
        addModifier theObj  (Turn_to_Mesh ())
      )
      vmod = theObj.modifiers[#Vol__Select]
      vmod.level = 2
      vmod.type = 1
      vmod.volume = 3
      --sanim = vmod.gizmo
      sanim = box()
      vmod.node = sanim
      mt2b=(matrix3 [bitmapX,0,0] [0,-bitmapY,0] [0,bitmapY,1] [0,0,0])
    ),
    fn doPaint = (
      local theMesh = snapshotAsMesh theObj
      local theChannel = 1
      local maps = #()
      crossFaceMaps &maps theMesh theChannel brushtransform mt2b
      checkBitmap = #{}
      for map in maps do (
        vface = #()
        for p in map[1] do (
          append vface [int(p.x * bitmapx), int(bitmapy - p.y * bitmapy)]
        )
        paintFace vface theBitmap map[2] brushtransform.pos
      )
    ),
    fn redraw = (
      theObj.material.diffusemap.bitmap = theBitmap
    ),
    fn saveimage = (
      save theBitmap
      theObj.material.diffusemap.bitmap = theBitmap
    ),
    fn close = (
      delete sanim
      delmodifier theObj Vol__Select
      delmodifier theObj Turn_to_Mesh
    )
)

(
  ParticlePaint_Rollout = undefined
  pt = objectPaint()
  pf = undefined

  fn lerp t1 t2 w = (
    q1 = t1 as quat
    q2 = t2 as quat
    p1 = t1.pos
    p2 = t2.pos
    q = slerp q1 q2 w
    p = ((p2-p1)*w+p1)
    tr = q as matrix3
    tr[4] = p
    return tr
  )

  fn write_bitmap frame = (
    sf = "00" + (frame as string)
    temp_bitmap_filename = (getDir #preview +"/test"+ substring sf (sf.count - 2) (sf.count)  +".tga")
    pt.theBitmap.filename = temp_bitmap_filename
    pt.saveimage()
  )

  fn stroke tmp_file startframe stopframe write_by = (
    particleTMs = #()
    particleScls = #()
    pt.theBitmap = bitmap pt.bitmapX pt.bitmapY color:ParticlePaint_Rollout.blankColor.color
    LTM = matrix3 1
    frame = 0
    while ((not (eof tmp_file)) and (frame < stopframe)) do 
    (
      frame = readvalue tmp_file
      count = readvalue tmp_file
      sliderTime = frame
      FTM = LTM * pt.theObj.transform
      for i =  1 to count do
      (
        id = readvalue tmp_file
        tm = readvalue tmp_file
        scl = readvalue tmp_file

        if particleTMs[id] == undefined then (
          particleTMs[id] = tm
          particleScls[id] = scl
        ) else (          
          t1 = particleTMs[id] * FTM
          t2 = tm
          s1 = particleScls[id]
          s2 = scl
          d = distance t1.pos t2.pos
          if ParticlePaint_Rollout.BrushSizeRef.state == 2 then (
            div = d/((s1+s2) / 2.0 * (1.0 - ParticlePaint_Rollout.BrushOverlap.value))-1
          ) else (
            div = d/(pt.brushsize *  (1.0 - ParticlePaint_Rollout.BrushOverlap.value))-1
          )

          dcount = (div as integer) 
          if dcount < 0 then dcount = 0
          for j = 0 to dcount do (
            if div ==0.0 then w = 0.0 else w = j / div
            pt.brushtransform = lerp t1 t2 w
            if ParticlePaint_Rollout.BrushSizeRef.state == 2 then (
              pt.brushsize = s1 * (1 - w) + s2 * w
            )
            pt.doPaint()
          )
          particleTMs[id] = tm
          particleScls[id] = scl
        )
      )
      LTM = inverse pt.theObj.transform
      if write_by == #frame do write_bitmap frame
    )
    if write_by == #stroke do write_bitmap frame
  )

  fn myseek fp dest= (
    seek fp 0
    seek fp dest
  )

  fn run = (
    pt.BrushShape = ParticlePaint_Rollout.BrushShape.state
    pt.brushsize = ParticlePaint_Rollout.BrushSize.value
    pt.BrushColor = ParticlePaint_Rollout.inkColor.color 
    pt.init()
    pstart = ParticlePaint_Rollout.StartFrame.value
    pstop =ParticlePaint_Rollout.StopFrame.value

    frames =#()

    filename = (getDir #temp +"/particlePaint_temp.txt")
    tmp_file = createfile filename
    for i = pstart to pstop do (
      sliderTime = i
      count = pf.NumParticles()
      frames[i+1] = filePos tmp_file  
      format "%,%\n" i count to:tmp_file
      for j = 1 to count do (
        pf.particleIndex = j
        id = pf.particleID 
        tm = pf.particleTM
        scl = pf.particleScale
        format "%,%,%\n" id tm scl to:tmp_file
      )
    )
    close tmp_file

    tmp_file = openFile filename
    
    eser = showEndResult
    showEndResult = false
    md=undefined
    if pt.theObj.modifiers[#Unwrap_UVW] == undefined then (
      addModifier pt.theObj (Unwrap_UVW ())
      md = pt.theObj.modifiers[#Unwrap_UVW]
      modPanel.setCurrentObject pt.theObj.modifiers[#Unwrap_UVW]
    )
    else  modPanel.setCurrentObject pt.theObj.modifiers[#Vol__Select]

    if ParticlePaint_Rollout.StorokeType.state == 2 then (
      stl = ParticlePaint_Rollout.FalloffFrame.value
      for cf = pstart to pstop do (
        sf = cf - stl
        if sf < pstart do sf = pstart
        myseek tmp_file (frames[sf+1])
        stroke tmp_file sf cf #stroke
      )
    )
    else (
      stroke tmp_file pstart pstop #frame
    )

    modPanel.setCurrentObject pt.theObj.modifiers[1]
    showEndResult = eser
    if md != undefined then (
      deleteModifier pt.theObj md
    )
    pt.close()
    close tmp_file
  )

  fn edgelength e obj =
  (
    verts = (meshop.getVertsUsingEdge obj e) as array
    v1 = meshop.getvert obj verts[1]
    v2 = meshop.getvert obj verts[2]
    d = distance v1 V2
  )

  fn getMaxFaceRadius = (
    --global ParticlePaint_Rollout
    theMesh = snapshotAsMesh pt.theObj
    maxlength = 0.0
    edgecount = theMesh.edges.count
    for i = 1 to edgecount do (
      l = edgelength i theMesh
      if l > maxlength then  maxlength = l
    )
    br = ParticlePaint_Rollout.BrushSize.value
    pt.maxFaceRadius = sqrt((maxlength^2+3*br^2)/3)
  )
  dialogWidth = 200
  dialogHeight =550
  
  --global ParticlePaint_Rollout
  try(destroyDialog ParticlePaint_Rollout)catch()
  
  fn BitmapSaveAs = (
    theSaveName = getSaveFileName types:"BMP (*.bmp)|*.bmp|Targa (*.tga)|*.tga|JPEG (*.jpg)|*.jpg"
    if theSaveName != undefined do
    (
      ParticlePaint_Rollout.BitmapFiles.caption = theSaveName
    )
  )
  
  rcMenu PaintMenu
  (
    -- ここにメニューの内容を定義
    subMenu"File"
    (
      menuItem SaveAs "Save As..."
      menuItem quit_tool "Quit"  
    )

    subMenu"Help"
    (
      menuItem about_tool "About ParticlePaint..." 
    )
    
    on about_tool picked do messagebox "Particle Paint " title:"About..."
    on quit_tool picked do destroyDialog ParticlePaint_Rollout
    on SaveAs picked do (
      print "pick"
      BitmapSaveAs()
    )
  )
  
  fn mesh_filter obj = superclassof obj == GeometryClass and classof obj != TargetObject and obj != PF_Source
  fn pfs_filter obj = classof obj == PF_Source

  rollout ParticlePaint_Rollout "Partilce Paint"
  (
    pickbutton pickMesh "Pick Mesh"  height:30 filter:mesh_filter autodisplay:true
    pickbutton pickPFS "Pick PFSource"  height:30 filter:pfs_filter autodisplay:true
    colorpicker inkColor "BrushColor" height:16 modal:false color:black 
    colorpicker blankColor "BlankColor" height:16 modal:false color:white 
    spinner StartFrame "Start Frame"  type:#integer
    spinner StopFrame "Stop Frame" type:#integer
    radiobuttons StorokeType "Stroke Type" labels:#("constant", "Falloff")
    radiobuttons BrushSizeRef "Brush Size Reference" labels:#("Brush Size","Particle Size")
    spinner BrushSize "Brush Size" type:#integer
    spinner FalloffFrame "Falloff Frame" type:#integer
    
    spinner BrushOverlap "Brush Overlap" type:#float range:[0.0,0.99,0.7]
    
    radiobuttons BrushShape "Brush Shape" labels:#("Circle","Box","Circle Smooth")
    button BitmapFiles "Bitmap Files" width:150 height:16
    button btnMaxFaceRadius  "Get Max Face Radius" width:100 height:30
    spinner spnMaxFaceRadius "Max Face Radius"  type:#float
    spinner spnbitmapX "bitmapX"  type:#integer range:[0,4096,512]
    spinner spnbitmapY "bitmapY"  type:#integer range:[0,4096,512]
    button Painting  "Paint" width:100 height:30
    on ParticlePaint_Rollout open do (
      StartFrame.value = animationRange.start
      StopFrame.value = animationRange.end
      BrushSize.value = 5
      FalloffFrame.value = 5
      FalloffFrame.enabled = false
      btnMaxFaceRadius.enabled = false
      BitmapFiles.caption = (getDir #preview +"/test000.tga")
      Painting.enabled = false
      spnMaxFaceRadius.value = pt.maxFaceRadius
      spnbitmapX.value = pt.bitmapX
      spnbitmapY.value = pt.bitmapY
    )

    on BitmapFiles pressed do (
      BitmapSaveAs()
    )
    on pickMesh picked obj do (
      if pickPFS.object != undefined  then Painting.enabled = true
      btnMaxFaceRadius.enabled = true
      pt.theObj = obj

    )
    on pickPFS picked obj do (
      if pickMesh.object != undefined then Painting.enabled = true
      pf = obj
    )
    
    on StorokeType changed btn do (
      if btn == 1 then FalloffFrame.enabled = false else FalloffFrame.enabled = true
    )

    on Painting pressed do (
      run()
    )
    on btnMaxFaceRadius pressed do (
      getMaxFaceRadius()
      spnMaxFaceRadius.value = pt.maxFaceRadius
    )
    on spnbitmapX changed val do (
      pt.bitmapX = val
    )
    on spnbitmapY changed val do (
      pt.bitmapY = val
    )
  )
  createDialog ParticlePaint_Rollout dialogWidth dialogHeight menu:PaintMenu
)

これでマップ境界で1ピクセルスキップしていたペイントを元に戻しても塗り重ねが出なくなった。

しかし不連続UVマップの境界をよく見ると微妙なペイントギャップが認められる。これは要するにレンダリングする時のビットマップのサンプリング精度によって起こる問題だろうね。通常こういうのを避けるためにUVテクスチャはポリゴンをちょっとはみ出る感じで塗るわけだ。

fig01

続きはまた次回。

maxまとめページ



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

2018年03月26日

プロシージャルモデリングについて調べてみた その32 modo11.2v2

前回に引き続き「ElboPipe」アセットの「elboPipe」アイテムの中を調べてみたい。

「elbo StartCapBev」「elbo EndCapBev」は「ポリゴンベベル」オペレータで、選択ポリゴンを3mmシフトし、生成したポリゴンに「elboMain」マテリアルタグを付けるようになっている。

fig01

どちらのオペレータも2つの選択オペレータがセットされていて、「elboUI」の「Pen Extrude」チャンネルの値によって排他的に有効になるようになっている。「Pen Extrude」チャンネルがONの時は「Select By PenXOP」「Select By PenXOP2」が、OFFの時は「Select by CurvXOP」「Select by CurvXOP2」が有効になる。

下の画像は「Select By PenXOP」「Select By PenXOP2」のプロパティで、なぜか「Select By PenXOP」の「ソースアイテム」は「(none)」になってしまっているけどこれは表示がうまく更新出来ないだけのようで実際には「elbo Pen Xtru」になっているようだ。本当に「(none)」の場合は下の「名称」プロパティは無効になる。

「Select By PenXOP」

fig03

「Select By PenXOP2」

fig04

「elbo Pen Xtru」は「elboPipe」の中で一番最初に出てくる「elbo CylinderProfile」などのパイプの断面を掃引するオペレータで、その「始端キャップ」と「終端キャップ」が選択対象になる。

fig07

同様に「Select by CurvXOP」と「Select by CurvXOP2」はソースアイテムがどちらも「elbo Curve Xtrud」だ。これは「elboUI」の「Pen Extrude」チャンネルがOFFの時に「elbo Pen Xtru」のかわりに断面を掃引するオペレータだった。こちらも片方の「ソースアイテム」は表示がうまく出来ていない。

「Select by CurvXOP」

fig05

「Select by CurvXOP2」

fig06

以上の事から「elbo StartCapBev」と「elbo EndCapBev」は「elboUI」の「Pen Extrude」チャンネルによって切り替わる掃引体の両端を3mmシフトする。その結果、パイプの両端にエッジが1本付け加えられ、サブディビジョンサーフェスで丸みが付いてしまったパイプの両端のエッジをシャープにする役割を果たすようになっている。

fig02

続きはまた次回。

modo10-11ブログ目次



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

2018年03月22日

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

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

前回は実験途中でバグが見つかったので改めて不連続UVマップについて実験をしてみた。

適当にUVマップを切り分けて

fig01

こんな状態でペイントしてみた。

fig02

これが結果。予想通り境界部分に塗り残しの隙間が出来てしまった。

fig03

これを解消するにはポリゴンエッジへのペイントが2度塗りになるかどうか調べて塗るか塗らないか決めるか、繋がってるUVマップを一度に塗るかって事になる。

とりあえず今回は二度塗りになるかどうかを調べるようにしてみようかと思っている。

一番簡単な方法はペイントするビットマップと同じサイズのビットマップを用意して、エッジ部分のピクセルをペイントする時だけそのビットマップにもペイントするようにして、その時にすでにペイントしてあるかどうかを調べる方法だ。エッジにペイントしたかどうかの情報を持たせる方法も考えられるけど、単純な方がいいかな。

続きはまた次回。

maxまとめページ



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