2017年12月

2017年12月21日

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

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

まず「パーティクルシステム」の「PFソース」から「パーティクルビュー」を開けて「mParticles フロー」をビューにドロップして、シェイプなどを変える。

fig01

さらに「mP コリジョン」を追加する。下の「mPシェイプ」はボックスになってるけど「球」に変更しないとね。

fig02

シーンに球を1つ追加して、「モディファイヤ」パネルから「PFlow コリジョンシェイプ」を加える。そしてこのシェイプを上で追加した「mP コリジョン」に登録する。

fig03

そして「PFlow コリジョンシェイプ」のモディファイヤパネルで「アクティブ化」をONにする。

これでパーティクルは球体に衝突しながら落ちるようになる。

ペイントするオブジェクトは今のところ「PFlow コリジョンシェイプ」が追加されているとうまくいかないので、同じ球をもう一つ追加して、

fig04

そっちには「UVWアンラップ」を追加する。

fig05

プログラムを起動して、「UVWアンラップ」を追加した球と、「PFソース」をピックボックスで追加する。

fig06

「UVWアンラップ」を追加した球をちょっとだけ大きくして、元の球と位置合わせをする。

fig07

これでシーン設定が出来たので「Paint」ボタンを押すとペイントが開始する。

fig08

終わると「previews」フォルダにビットマップファイルが生成される。

fig09

まだファイル名や出力フォルダは変更出来なかったり、いろいろヤラナキャならないことはあるけど、一応動作は確認できた。

今年はここまでにしておきます。

新年は1月9日から始めようと思います。

みなさん良いお年をお迎え下さい。

maxまとめページ



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

2017年12月20日

UVポリゴンの裏表の片側だけ選択する その5 modo11.0v3

プログラムはとりあえず完成したので中身について書いておきたい。

「uv2v3()」ファンクションはUVマップのUV2値に3軸目の座標値0を付け加えて「Vector3」オブジェクトを作るものだ。「Vector3」にしておくと外積とか使えるからね。引数「uv」はUV値を持つ「tupple」か「list」だ。

def uv2v3(uv):
   return modo.Vector3(uv[0],uv[1],0)

このファンクションを利用しているのが「whitchSide()」ファンクションで、与えられたポリゴンオブジェクトとテクスチャマップオブジェクトからそのポリゴンが裏か表か判定する。ポリゴン「p」から頂点オブジェクトのリストを「verts」に取得して、その全ての頂点に対してリストの前後の頂点と組み合わせてベクトル「v1」「v2」を生成して、2つのベクトルの外積をすべて加算することでこのポリゴンが裏表どっちが優性な面なのかを法線ベクトルの大きさから判定する。垂直方向の成分はW方向に出るので外積の合計から出た「uvw」の3軸目である2番目の要素が0以上かどうかで裏表を識別する。

def whitchSide(p,m):
  verts = p.vertices
  if len(verts)<3 :
    return False
  uvw = modo.Vector3()
  for i in range(len(verts)):
    pls = (i+1) % len(verts)
    mns = (len(verts) + i - 1) % len(verts)
    v1 = uv2v3(verts[mns].getUVs(m)) - uv2v3(verts[i].getUVs(m))
    v2 = uv2v3(verts[pls].getUVs(m)) - uv2v3(verts[i].getUVs(m))
    uvw += v1.cross(v2)
  if uvw[2] >=0 :
    return False
  else:
    return True 

これを使うのが下のメインルーチン。

scene = modo.Scene()
selectedMeshes = scene.selectedByType('mesh')
for mesh in selectedMeshes:
  polys = mesh.geometry.polygons
  uvmaps = mesh.geometry.vmaps.uvMaps
  lx.eval('query layerservice layer.name ? %s' % mesh.index)
  selectedvmaps = lx.evalN('query layerservice vmaps ? selected')
  uvname = ""
  for i in selectedvmaps:
    if lx.eval('query layerservice vmap.type ? %s' % i) == 'texture':
      uvname = lx.eval('query layerservice vmap.name ? %s' % i)
  map = None
  for m in uvmaps:
    if m.name == uvname:
      map = m

  if map != None :
    for p in polys:
      if whitchSide(p,map):
        p.select()

まずシーンオブジェクトから選択されているメッシュを取得する。

scene = modo.Scene()
selectedMeshes = scene.selectedByType('mesh')

選択されたメッシュアイテムをひとつずつ取り出して繰り返し処理をする。まずメッシュアイテムからポリゴンリストとUVマップリストを取得する。

for mesh in selectedMeshes:
  polys = mesh.geometry.polygons
  uvmaps = mesh.geometry.vmaps.uvMaps

今回は残念ながら選択されたUVマップの取得の方法が調べ切れなかったので従来方式のクエリで選択されている頂点マップの番号を調べて、その中からタイプが「texture」のUVマップの名前を調べて、先に取得したUVマップのリストから同じ名前のものを探して変数「map」にセットするようになっている。

  lx.eval('query layerservice layer.name ? %s' % mesh.index)
  selectedvmaps = lx.evalN('query layerservice vmaps ? selected')
  uvname = ""
  for i in selectedvmaps:
    if lx.eval('query layerservice vmap.type ? %s' % i) == 'texture':
      uvname = lx.eval('query layerservice vmap.name ? %s' % i)
  map = None
  for m in uvmaps:
    if m.name == uvname:
      map = m

マップが見つかったらポリゴン全てに対して「whitchSide」でそのポリゴンのUVマップの裏表を調べてtrueを返すものだけ選択する。

  if map != None :
    for p in polys:
      if whitchSide(p,map):
        p.select()

以上がプログラムの流れだ。

それではまた次回。

modo10-11ブログ目次



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

2017年12月19日

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

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

前回作ったダイアログをプログラムに組み込んだ。まだファイル名の指定とか出来てないのと、「maxFaceRadius」の値が固定になっていてペイントする相手によっては効率が悪くなったりペイント出来なかったりする事になってしまう。マテリアルも「standard」で固定だ。

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
      bbox = nodeGetBoundingBox theObj theObj.transform
      cp = ((bbox[1] + bbox[2])/2) * theObj.transform
      sx = abs(bbox[1][1] - bbox[2][1])
      sy = abs(bbox[1][2] - bbox[2][2])
      sz = abs(bbox[1][3] - bbox[2][3])
      sm = scaleMatrix [searchLength / sx,searchLength/sy, searchLength/sz]
      sanim.transform = sm * brushtransform * inverse(theObj.transform)
      sanim.pos = (-(cp * inverse(theObj.transform)) *sm * brushtransform) * inverse(theObj.transform)
      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 = 0
      sanim = vmod.gizmo
      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 = (
      delmodifier theObj Vol__Select
      delmodifier theObj Turn_to_Mesh
    )
)

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
    format "3:frame,count = %,%\n" frame count
    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.maxFaceRadius =20.0
  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

  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
  )
  pt.close()
  close tmp_file
)

(
  dialogWidth = 200
  dialogHeight = 400
  
  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 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
      BitmapFiles.caption = (getDir #preview +"/test000.tga")
      Painting.enabled = false
    )

    on BitmapFiles pressed do (
      BitmapSaveAs()
    )
    on pickMesh picked obj do (
      if pickPFS.object != undefined then Painting.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()
    )
  )
  createDialog ParticlePaint_Rollout dialogWidth dialogHeight menu:PaintMenu
)

このプログラムはまだmax2018に対応してないけど、マテリアルの部分をいじればたぶん動くようになるかな。

次回は簡単なシーンで使ってこのプログラムを試してみたい。

maxまとめページ



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

2017年12月18日

UVポリゴンの裏表の片側だけ選択する その4 modo11.0v3

前回ポリゴンの裏表の判定が頂点リストの順番で出来そうなのがわかったのでそれをもとにUVマップの裏表判定プログラムを作ってみた。

import modo
import lx

def uv2v3(uv):
  return modo.Vector3(uv[0],uv[1],0)

def whitchSide(p,m):
  verts = p.vertices
  if len(verts)<3 :
    return False
  uvw = modo.Vector3()
  for i in range(len(verts)):
    pls = (i+1) % len(verts)
    mns = (len(verts) + i - 1) % len(verts)
    v1 = uv2v3(verts[mns].getUVs(m)) - uv2v3(verts[i].getUVs(m))
    v2 = uv2v3(verts[pls].getUVs(m)) - uv2v3(verts[i].getUVs(m))
    uvw += v1.cross(v2)
  if uvw[2] >=0 :
    return False
  else:
    return True

scene = modo.Scene()
selectedMeshes = scene.selectedByType('mesh')
for mesh in selectedMeshes:
  polys = mesh.geometry.polygons
  uvmaps = mesh.geometry.vmaps.uvMaps
  lx.eval('query layerservice layer.name ? %s' % mesh.index)
  selectedvmaps = lx.evalN('query layerservice vmaps ? selected')
  uvname = ""
  for i in selectedvmaps:
    if lx.eval('query layerservice vmap.type ? %s' % i) == 'texture':
      uvname = lx.eval('query layerservice vmap.name ? %s' % i)
  map = None
  for m in uvmaps:
    if m.name == uvname:
      map = m

  if map != None :
    for p in polys:
      if whitchSide(p,map):
        p.select()

使い方は「ポリゴン選択」モードで、処理したいメッシュレイヤとテクスチャレイヤを選択して実行する。

fig01

これでこっち側が表面のUVポリゴンだけが選択される。

fig02

「切り離し」オプションをONにして移動させれば分離できる。

fig03

3Dで見るとこんな感じ。

fig04

「UVビュー投影」ツールの「隠面消去」オプションをONにして作ったUVマップと比較してみると、微妙な差が出た。

fig06

下が「隠面消去」オプションを使ったもの。

fig05

続きはまた次回。

modo10-11ブログ目次



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

2017年12月14日

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

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

前回のダイアログのテンプレートにコントロールを追加してみた。下がそのプログラム。まだ本体とは接続していないから使えないけどね。

(
  dialogWidth = 200
  dialogHeight = 400
  
  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 Painting  "Paint" width:100 height:30
    
    on ParticlePaint_Rollout open do (
      StartFrame.value = animationRange.start
      StopFrame.value = animationRange.end
      BrushSize.value = 10
      FalloffFrame.enabled = false
      BitmapFiles.caption = (getDir #preview +"/test.tga")
      Painting.enabled = false
    )

    on BitmapFiles pressed do (
      BitmapSaveAs()
    )
  )
  createDialog ParticlePaint_Rollout dialogWidth dialogHeight menu:PaintMenu
)

実行すると下のダイアログが出る。

fig01

ペイントするオブジェクトとPFソースは「pickbutton」コントロールで取得する。

ブラシの色とキャンバスの地の色は「colorpicker」コントロールで取得する。

処理の開始・終了フレーム、ブラシサイズ、ストロークのフォールオフフレームは「spinner」コントロールで取得する。

ストロークを減衰させるかどうかとブラシの形状は「radiobuttons」コントロールで取得する。

ビットマップの出力先のファイルの指定は「File」メニューの「Save As」か、「button」コントロールから取得して、結果を「button」コントロールの「caption」に設定してボタンに表示するようにした。

そして処理の開始を「button」コントロールから行うようにした。

コントロールの初期値の設定は「ParticlePaint_Rollout」ロールアウトの「on ParticlePaint_Rollout open do」に書いて、ロールアウトが開かれた時に実行されるようにした。例えば処理の開始・終了はシーンの開始と終了のフレームをセットするようにした。また、処理を開始するボタンはパラメータが入力されていないので無効な状態で始まるようにした。「Falloff Frame」も同様で、「Stroke Type」で「Falloff」が選ばれた時だけ有効にしたい。

「pickbutton」コントロールはフィルターを作って余計なものがピックされないようにしてある。「メッシュ」の方はカーソルをあわせたオブジェクトの親クラスが「GeometryClass」で「TargetObject」でも「PF_Source」でもないものに、「PFソース」の方は「PF_Source」のみに反応するようになっている。「メッシュ」の方はいろんなタイプのオブジェクトに対応しなくちゃならないのに対して「PFソース」の方は1種類でいいからこうなった。「メッシュ」の方はチュートリアルの設定を書き換えたものだ。

 fn mesh_filter obj = superclassof obj == GeometryClass and classof obj != TargetObject and obj != PF_Source
 fn pfs_filter obj = classof obj == PF_Source

続きはまた次回。

maxまとめページ



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