2018年02月21日

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

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

前回までのところで、「elbo Pen Xtru」と「elbo Curve Xtrud」が「elboUI」の「Pen Extrude」で切り替えが出来て、ONなら「elbo Pen Xtru」が使われてパイプのコーナーにジョイント部が生成され、OFFなら「elbo Curve Xtrud」が使われて、コーナーのジョイント部は生成されず、パイプを構成するセグメントが等間隔になる事がわかった。

fig09

「elbo Pen Xtru」は「スイープエフェクタ」の「有効」「ポリゴンを反転」「始端キャップ」「終端キャップ」がリンクされていて、

fig04

「ペンジェネレータ」については特にリンクはされていない。

fig05

リンクを辿ると「elboUI」に繋がっていて、「ポリゴンを反転」が「Extrude Profile Flip」、「始端キャップ」が「Extrude Cap Start」、「終端キャップ」が「Extrude Cap End」に接続されている。

fig01

「Pen Extrude」がONの時に「Extrude Profile Flip」を切り替えると下のようにパイプのポリゴンが反転するのが確認できる。

fig10

同様に「Pen Extrude」がONの時に「Extrude Cap Start/End」を切り替えると下のようにパイプの両端を閉じるか開放するかを個別に切り替えられる事が確認できる。

fig11

「elbo Curve Xtrud」は「スイープエフェクタ」の「有効」、「ポリゴンを反転」、

fig06

「パスセグメントジェネレータ」の「ステップ」がリンクされていて、

fig07

「パスジェネレータ」にはリンクは無い。

fig08

リンク先を見ると「ポリゴンを反転」はこれも「elboUI」の「Extrude Profile Flip」に、「ステップ」は「elboUI」の「Curve Extrude Steps」に繋がっている。

fig02

「Pen Extrude」をOFFにして「Curve Extrude Steps」の値を変更すると、

fig03

パイプのセグメント数が変化するのが確認出来る。

fig12

続きはまた次回。

modo10-11ブログ目次



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

2018年02月20日

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

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

今のプログラムではブラシサイズが使われているのは「Get Max Face Radius」の部分だけで、ペイント時にその値のかわりにパーティクルのサイズが使われていた。

そこでまずそれを修正して、パーティクルサイズを使うのか、それともパネルの指定値を使うのか切り替えられるようにしてみた。

「Brush Size Reference」を「Brush Size」にすると「Brush Size」で指定したブラシのサイズ固定でペイントされて、「Particle Size」にすると「Brush Size」は無視されてパーティクルのサイズでペイントされる。

fig01

プログラムは以下。

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,

  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 BrushShape == 1 then (
          p = [x,y,1.0] * mb2w
          if distance p bpos <= BrushSize/2 then setPixels theBitmap  [x,y] #(BrushColor)
        )
        else if BrushShape == 2 then setPixels theBitmap  [x,y] #(BrushColor)
      )
    ),

    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
      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 (
          brushOverlaid = 0.3
          t1 = particleTMs[id] * FTM
          t2 = tm
          s1 = particleScls[id]
          s2 = scl
          d = distance t1.pos t2.pos
          div = d/(pt.brushsize * brushOverlaid)-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]

    -- ##############################

    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 = 500
  
  --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
    radiobuttons BrushShape "Brush Shape" labels:#("Circle","Box")
    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
)

続きはまた次回。

maxまとめページ



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

2018年02月19日

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

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

fig02

前回は「elbo Pen Xtru」と「elbo Curve Xtrud」が「elboUI」の「Pen Extrude」チャンネルによって排他的に切り替わるようになっていると言うところまで調べた。

この2つのオペレータはどちらも断面形状をパスに沿って掃引するもので、機能はかなり似通っている。「elbo Pen Xtru」は「Pen Extrude」で、「elbo Curve Xtrud」は「Curve Extrude」のことで、2つの大きな違いは「Pen Extrude」がパスの頂点位置に輪郭エッジを配置するのに対して、「Curve Extrude」はパスの頂点に係わらず等間隔に輪郭エッジを配置する。

例えば下のポリラインに対して2つのオペレータを適用して四角形を掃引してみると、

fig03

「Pen Extrude」では下のようにポリラインの各頂点の位置に輪郭エッジが入るのに対して、

fig04

「Curve Extrude」では下のように輪郭エッジが等間隔に並んでいる。

fig05

この間隔は、パスの長さを「Curve Extrude」の「Path Segment Generator」のステップで割った値になる。

fig06

ここで、2つのオペレータで参照される「elboPath」は元になる「elbo 7 PointLine」のポリラインのコーナーを丸めた下のような形状で、丸めた部分には頂点が集中している状態だ。

fig07

ここに「elbo Curve Xtrud」が適用されたらこのポリライン上を等分割する輪郭線で構成されるパイプになり、

fig02

「Pen Extrude」が適用されると、ポリラインの頂点位置に輪郭線が生成されるように掃引される。

fig01

続きはまた次回。

modo10-11ブログ目次



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

2018年02月15日

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

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

以前に考えた「PFlowコリジョンジェイプ(WSM)」モディファイヤで「衝突シェイプを視覚化」がONになっていたり、さらに「最終結果を表示」がONになっていたりした時にプログラムがエラーになる事を回避するためにモディファイヤスタックのカレントオブジェクトを切り替えたりする動作を組み込んでみた。下がそのプログラム。書き換えたのは「run()」ファンクションの部分だ。

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,

  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 BrushShape == 1 then (
          p = [x,y,1.0] * mb2w
          if distance p bpos <= BrushSize/2 then setPixels theBitmap  [x,y] #(BrushColor)
        )
        else if BrushShape == 2 then setPixels theBitmap  [x,y] #(BrushColor)
      )
    ),

    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
      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 (
          brushOverlaid = 0.3
          t1 = particleTMs[id] * FTM
          t2 = tm
          s1 = particleScls[id]
          s2 = scl
          d = distance t1.pos t2.pos
          div = d/(pt.brushsize * brushOverlaid)-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
            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]

    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 = 500
  
  --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")
    spinner BrushSize "Size" type:#integer
    spinner FalloffFrame "Falloff Frame" type:#integer
    radiobuttons BrushShape "Brush Shape" labels:#("Circle","Box")
    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
)

試しに前回動作確認のために作ったシーンで、「衝突シェイプを視覚化」をONのままプログラムを実行してみたけど問題なく動作するようになった。

続きはまた次回。

maxまとめページ



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

2018年02月14日

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

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

fig02

「elbo CustomProfile MM」は「メッシュを統合」オペレータで、他のメッシュアイテムを取り込む時に使うオペレータのようだ。

「elbo CylinderProfile」をOFFにして「elbo CustomProfile MM」オペレータを有効にして、オペレータを展開すると「ソース」チャンネルがあって、「ソースを追加」をクリックすると、

fig01

「オペレーションを追加」パネルが出て、ここからパイプの断面形状として使いたい「メッシュ」アイテムを指定してやると、

fig02

メッシュアイテム内にある形状を断面にしたパイプ形状が生成される。下の画像は「メッシュ」アイテムに四角形ポリゴンを1枚配置して「ソース」に指定したものだ。

fig03

「メッシュ」アイテムに入れる形状はポリゴンメッシュやポリライン、カーブなども可能だけど、立体的に閉じたメッシュ形状(例えば球体など)は掃引方向が定まらないためかパイプが生成されないようだ。

また、アイテムのサビディビジョン設定は継承される。下の画像は「elbo CustomProfile MM」で参照したポリゴンをサブディビジョンサーフェスに切り替えてみたものだ。パイプもサブディビジョンサーフェスに切り替わるのがわかる。

fig05

パイプの中心が断面形状のどこになるかは自動的に決まるようで、形状を「メッシュ」アイテムのどこに置いても形状の中央に中心が揃えられる。

「elbo CylinderProfile」、「elbo N-SideProfile」、「elbo CustomProfile MM」で生成あるいは読み込まれたパイプの断面形状は、その上の「elbo Pen Xtru」または「elbo Curve Xtrud」によって掃引される。

fig06

これら2つのオペレータはどちらも「スイープエフェクタ」オペレータで、内部のツールパイプの組み合わせから「elbo Pen Xtru」は「Pen Extrude」、「elbo Curve Xtrud」は「Curve Extrude」になっている。

fig10

そしてこれらのオペレータは下のように「elboUI」の「Pen Extrude」チャンネルから「Extrude Type Conditional」ノードを経て、それぞれの「有効」チャンネルに接続されている。

fig07

そして「Extrude Type Conditional」ノードは下のように設定されていて、「elboUI」の「Pen Extrude」チャンネルの値が1ならば「結果」チャンネルに0、「出力」チャンネルにTrueが、それ以外の値なら「結果」チャンネルに1、「出力チャンネル」にFalseが出力される。

fig08

よって、「elboUI」の「Pen Extrude」チャンネルがONになっている時は「elbo Pen Xtru」が有効になり、「elbo Curve Xtrud」は無効になり、「elboUI」の「Pen Extrude」チャンネルがOFFになっている時は「elbo Pen Xtru」が無効になり、「elbo Curve Xtrud」が有効になる。

fig09

続きはまた次回。

modo10-11ブログ目次



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