ワークフロー②_スキニング環境
最近作成したBlenderスキニング環境を紹介します。
一言で説明するとLazyWeightToolアドオンを使います。以上。
で終わるくらいにLazyWeightToolを中心としたフローです。
LazyWeightTool
忘却野さんが販売されているウェイトペイント用のアドオンです。¥1,800。
選択頂点に直接指定した値を設定できるのが便利です。他Operatorや選択のサポートも厚く超救われてます。
スキニングで大事なのは知識よりも優秀なツールを持っているかどうかだと思います。
このアドオンは多くのユーザーを救うと思います。
メインフロー
現在はマスクモードとWeight Gradientを中心に組み立てています。
Armature→Meshの順で選択してWeightPaintModeに入る
(Armatureを選択するのはPoseModeも併用するため)頂点マスクモードで頂点を選択する
Weight Gradientを使ってグラデーションを付ける
Normalizeで正規化する
Level(Gain:1.05/0.95)でグラデーションの強さを調整する
Workspace
普通の構成です。
LazyWeightToolを拡げるために3D Viewportを広く取ります。
いくつかポーズを登録しておくとウェイトの確認に便利なためTimelineも。
あとは選択オブジェクトを確認するためのOutlinerと、アクティブな頂点グループを確認するためのProperties Shelfです。
LazyWeightToolへの機能追加
こちらは少しユニークです。
WeightPaintModeで必要な操作があるため、アドオンとしてまとめて追加しました。
機能は以下の3つです。
Reset Pose
すべてのPoseBoneの変形をリセットします。Weight Paint Modeだと1つずつ選択してAlt+RGSしないといけないので若干面倒でした。
Toggle View
Weight Paint Mode時にViewport Shadingをウェイトが塗りやすい設定に変更します。手動で毎回切り替えるのが面倒だったので。
Curve Editor
Draw ToolのCurve Editorです。このカーブはDraw Tool時にしか表示されないので、マスクモード中(Select Tool時)でも操作できるように追加しました。頂点選択→カーブ設定→Weight Gradientを切り替えなしにできて楽です。
コード
だいぶ自分特化なツールですが、必要な方は自由に改造して使ってください。
bl_info = { "name": "ui main plus", "author": "", "version": (0, 0, 1), "blender": (2, 82, 0), "description": "ui main plus", "warning": "", "support": "TESTING", "wiki_url": "", "category": "3D View" } import bpy class LAZYWEIGHT_OT_plus_reset_all_pose(bpy.types.Operator): bl_idname = "lazyweight.plus_reset_all_pose" bl_label = "Lazy Weight Plus " bl_description = "Lazy Weight Plus" bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): return (context.mode == 'PAINT_WEIGHT' and context.active_pose_bone) or (context.mode == 'POSE') def execute(self, context): bpy.ops.pose.select_all(action='SELECT') bpy.ops.pose.transforms_clear() bpy.ops.pose.select_all(action='DESELECT') return {'FINISHED'} class LAZYWEIGHT_OT_plus_toggle_view(bpy.types.Operator): bl_idname = "lazyweight.toggle_view" bl_label = "Lazy Weight Plus " bl_description = "Lazy Weight Plus" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): if context.mode == 'PAINT_WEIGHT': context.space_data.shading.type = 'WIREFRAME' context.scene.tool_settings.vertex_group_user = 'ACTIVE' context.space_data.overlay.show_wpaint_contours = True context.space_data.shading.show_xray = True context.space_data.shading.xray_alpha = 0.9 context.space_data.shading.xray_alpha_wireframe = 0.95 else: context.space_data.shading.type = 'SOLID' context.space_data.shading.light = 'STUDIO' context.space_data.shading.show_xray = False return {'FINISHED'} class LAZYWEIGHT_PT_plus_main(bpy.types.Panel): bl_label = "Lazy Weight Tool+" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Item" def draw(self, context): workspace = context.workspace layout = self.layout row = layout.row(align=True) row.operator(LAZYWEIGHT_OT_plus_reset_all_pose.bl_idname, text="Reset Pose") row.operator(LAZYWEIGHT_OT_plus_toggle_view.bl_idname, text="Toggle View") layout.use_property_split = True layout.use_property_decorate = False class LAZYWEIGHT_PT_plus_curve(bpy.types.Panel): bl_label = "Falloff" bl_parent_id = "LAZYWEIGHT_PT_plus_main" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = "Item" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(self, context): return context.mode == 'PAINT_WEIGHT' def draw(self, context): layout = self.layout settings = context.tool_settings.weight_paint brush = settings.brush if brush is None: return col = layout.column(align=True) row = col.row(align=True) row.prop(brush, "curve_preset", text="") if brush.curve_preset == 'CUSTOM': layout.template_curve_mapping(brush, "curve", brush=True) col = layout.column(align=True) row = col.row(align=True) row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' col.separator() row = col.row(align=True) row.use_property_split = True row.use_property_decorate = False row.prop(brush, "falloff_shape", expand=True) classes = ( LAZYWEIGHT_OT_plus_reset_all_pose, LAZYWEIGHT_OT_plus_toggle_view, LAZYWEIGHT_PT_plus_main, LAZYWEIGHT_PT_plus_curve, ) def register(): for i in classes: bpy.utils.register_class(i) def unregister(): del bpy.types.Scene.mywrkspc for i in classes: bpy.utils.unregister_class(i)
次回もスキニング関連の予定です。