ゲーム作りが大好きな人のブログ

ゲームを作るのが大好きな人のブログ。UE4とBlender、MAYA(LT)、3DCoatを使用しています!

【Blender】【Python】指定アーマチュアで頂点ウェイトを塗りつぶすアドオン

Blenderのバージョンは2.81aで検証しています
※ あまりに疲れているので日本語がおかしい可能性があります
※ 既存でこの機能があったらごめんなさい。

f:id:toofu0:20200112223631g:plain
作りました。
 
 

このアドオンで出来る事

指定アーマチュアで選択頂点(複数可)を1.0で上書きする。
指定アーマチュア以外のウェイトは削除する 
 
 

経緯

Blenderでビックリしたのが下の状況でして……
f:id:toofu0:20200112222719p:plain
1.0のWeightが複数ある!!!
コレどういった挙動するんだろう、と。
一応理想的なアーマチュアの動き(ちゃんときれいに曲がる)をしていたのですが
ゲームで書き出した時に果たして正常な動作をするのか疑問に思いまして……

f:id:toofu0:20200112222759p:plain
この状況を発生させるには
上の画像のAssign機能を使うと簡単に発生します。
MAYAでも同様の機能をよく使っていたので焦りました。
「ドロー」を使用した場合は上書き扱いとなり発生しません。

個人的には最初は上の階層から下の階層の骨にかけて
段階的にウェイト1.0をかけていきたい(最初は複数頂点で制御したくない)ので
この仕様がキツイなと思いました。

例)
一番最初に全頂点をRootで1.0にして、次に腰の頂点を選択 → 腰の骨を選択して1.0にする……といった感じです。

 

手順

  1. 骨を選択します
  2. 複数の頂点を選択します
  3. ボタンを押すと選択骨に1.0のウェイトを選択頂点に設定します。

ぶっちゃけこの記事TOPのGIF画像がそれです。
 
 

コード

情報が足りなくて四苦八苦しました。
その分コードが多い……
あと途中段階の不要コードが混じってます。

import bpy
import bmesh
from bpy.props import *

bl_info = {
    "name": "Set Fixed Vertex Weight",
    "author": "hisamu",
    "version": (1, 1),
    "blender": (2, 80, 0),
    "location": "",
    "description": "Set Fixed Vertex Weight",
    "warning": "",
    "support": "TESTING",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Armature"
}

# myClass
class cVertexIndexAndWeight:
    def __init__(self):
        self.index = 0
        self.weight = 0.0

# 
class SetFixedVertexWeight_OT_Operator(bpy.types.Operator):
  bl_idname = "copy.button"
  bl_label = "Set Fixed Vertex Weight"
  bl_options = {'REGISTER', 'UNDO'}
  
  #--- properties ---#
  m_WeightValue: FloatProperty(default=1.0, options = {'HIDDEN'})
  
  # execute button
  def execute(self, context):
    print("start")

    # create variable
    meshObject = bpy.context.edit_object
    bm=bmesh.from_edit_mesh(meshObject.data)
    selectVertexGroupIndex =  meshObject.vertex_groups.active_index
    mulRate = 1.0 - self.m_WeightValue
    selectIndexes = []
    vertexDic = {}
    
    # 
    bpy.ops.object.mode_set(mode = 'EDIT')
    bpy.context.edit_object.update_from_editmode()

    # get select vertex array
    for vIndex , vertex in enumerate(bm.verts):
        if vertex.select:
            selectIndexes.append(vIndex)

    # 
    bpy.ops.object.mode_set(mode = 'OBJECT')
    meshObject.vertex_groups[selectVertexGroupIndex].remove(selectIndexes)
    bpy.ops.object.mode_set(mode = 'EDIT')

    # get delete vertex group
    for vIndex , vertex in enumerate(meshObject.data.vertices):
        if vIndex in selectIndexes:
            for _group in vertex.groups:
                # new
                if not _group.group in vertexDic.keys():
                    vertexDic[_group.group] = []
                
                # calc value
                weightValue = _group.weight * mulRate
                
                # 
                if _group.group == selectVertexGroupIndex:
                    weightValue = self.m_WeightValue
                
                if weightValue > 0:
                    classValue = cVertexIndexAndWeight()
                    # setup
                    classValue.index = vIndex
                    classValue.weight = weightValue
                    # set
                    vertexDic[_group.group].append(classValue)
    
    # set fixed parameter            
    if not selectVertexGroupIndex in vertexDic.keys():
        vertexDic[selectVertexGroupIndex] = []
        for vIndex in selectIndexes:
            classValue = cVertexIndexAndWeight()
            classValue.index = vIndex
            classValue.weight = self.m_WeightValue                      
            vertexDic[selectVertexGroupIndex].append(classValue) 
            
    
    bpy.ops.object.mode_set(mode = 'OBJECT')

    # delete select vertex
    for _group in meshObject.vertex_groups:
        _group.remove(selectIndexes)

    # apply value
    for gIndex , _group in enumerate(meshObject.vertex_groups):
        if gIndex in vertexDic.keys():
            for dic in vertexDic[gIndex]:
                indexes = []
                indexes.append(dic.index)
                print(str(dic.index) + ' : ' + str(dic.weight))
                _group.add(indexes,dic.weight,'REPLACE')    
                
    # revert mode
    bpy.ops.object.mode_set(mode = 'EDIT')
    
    # normalize
    bpy.ops.object.vertex_group_normalize()
    
    return{'FINISHED'}

# panel
class SetFixedVertexWeight_PT_Panel(bpy.types.Panel):
  bl_label = "[Addon] Set Fixed Vertex Weight"
  bl_space_type = "VIEW_3D"
  bl_region_type = "UI"
  bl_category = "Item"
  
  def draw(self, context):
    layout = self.layout
    
    layout.prop(context.scene, "weightValue")
    
    op_prop = layout.operator(SetFixedVertexWeight_OT_Operator.bl_idname, text = "Execute") # ボタンを作成
    op_prop.m_WeightValue = context.scene.weightValue


# register or unregister classes
classes = (
  SetFixedVertexWeight_PT_Panel,
  SetFixedVertexWeight_OT_Operator
)

def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    
    bpy.types.Scene.weightValue = FloatProperty(default=1.0,min=0, max=1.0)

def unregister():
    del bpy.types.Scene.weightValue
    for cls in classes:
        bpy.utils.unregister_class(cls)


if __name__ == "__main__":
    register()

雑感

既に設定されているウェイトデータを残しつつ割り当てる等
もう少し高度な事もできるんですが
自分がここまでしか要らないと思ったので
ここで終わりました。
何かしらバグとかがあったらまた更新していきたいと思います。