はじめに
標準機能の頂点番号の文字サイズが小さいので、文字サイズを変えられるように時前実装
ついで、メニュー階層が深い機能の表面化
ソースコード(ダウンロードはページの一番下)
# -*- coding: utf-8 -*-
import bpy
from bpy.props import (
BoolProperty,
IntProperty,
FloatVectorProperty,
)
import bmesh
import blf
from bpy_extras import view3d_utils
# Addon情報
bl_info = {
"name": "PgToys",
"author": "YdlProg",
"version": (1, 0),
"blender": (4, 2, 0),
"location": "View3D > Sidebar > MyMenu",
"description": "Python開発でよく使う機能",
"warning": "",
"doc_url": "https://ydlprog.ddns.net/2024/09/29/%e5%a4%a7%e3%81%8d%e3%81%84%e6%96%87%e5%ad%97%e3%81%a7%e9%a0%82%e7%82%b9%e7%95%aa%e5%8f%b7%e8%a1%a8%e7%a4%bablender-addon/",
"category": "Object",
}
# パネル情報クラス
class PgToys_Property(bpy.types.PropertyGroup):
# 頂点番号表示
show_vert_no: BoolProperty(default=False)
# 文字サイズ
font_size: IntProperty(default=16, min=10, max=30)
# 文字色
font_color: FloatVectorProperty(
default=(1.0, 1.0, 1.0), min=0.0, max=1.0, subtype="COLOR"
)
# 描画ユーティリティクラス
class DrawUtil:
__handle = None
# 2D描画コールバック登録
@staticmethod
def handle_add():
DrawUtil.__handle = bpy.types.SpaceView3D.draw_handler_add(
DrawUtil.draw_callback, (), "WINDOW", "POST_PIXEL"
)
# 2D描画コールバック削除
@staticmethod
def handle_remove():
if DrawUtil.__handle is not None:
bpy.types.SpaceView3D.draw_handler_remove(DrawUtil.__handle, "WINDOW")
DrawUtil.__handle = None
# 2D描画コールバック
@staticmethod
def draw_callback():
PgToys = bpy.context.scene.PgToysProperty
# 編集モード以外なら終了
if bpy.context.mode != "EDIT_MESH":
return
# フォント設定
ui_scale = bpy.context.preferences.system.ui_scale
font_id = 0
blf.size(font_id, PgToys.font_size * ui_scale)
blf.color(
0, PgToys.font_color[0], PgToys.font_color[1], PgToys.font_color[2], 1.0
)
# アウトライン
blf.shadow(0, 6, 0.0, 0.0, 0.0, 1.0)
# 頂点、辺、面選択モードか
is_vert_mode, is_edge_mode, is_face_mode = (
bpy.context.tool_settings.mesh_select_mode[:]
)
# 頂点番号表示
if PgToys.show_vert_no:
# 現在のモードのオブジェクトリスト
sel_obj = bpy.context.objects_in_mode
for obj in sel_obj:
# メッシュ以外は無視
if obj and obj.type != "MESH":
continue
# 編集中のメッシュを取得
mesh = obj.data
bm = bmesh.from_edit_mesh(obj.data)
# 選択中の頂点リスト
if is_vert_mode:
selected_verts = [v for v in bm.verts if v.select]
for v in selected_verts:
DrawUtil.draw_vector_position(
font_id, obj.matrix_world @ v.co, str(v.index)
)
# 選択中の辺リスト
if is_edge_mode:
selected_edges = [e for e in bm.edges if e.select]
for e in selected_edges:
v0 = bm.verts[e.verts[0].index].co
v1 = bm.verts[e.verts[1].index].co
v = v0 + (v1 - v0) / 2
DrawUtil.draw_vector_position(
font_id, obj.matrix_world @ v, str(e.index)
)
# 選択中の面リスト
if is_face_mode:
selected_faces = [f for f in bm.faces if f.select]
for f in selected_faces:
v = f.calc_center_bounds()
DrawUtil.draw_vector_position(
font_id, obj.matrix_world @ v, str(f.index)
)
# Vector位置に文字列描画
@staticmethod
def draw_vector_position(font_id: int, pos, text: str):
# 2Dに投影変換
co_2d = view3d_utils.location_3d_to_region_2d(
bpy.context.region,
bpy.context.space_data.region_3d,
pos,
)
# 番号描画
if co_2d:
w, h = blf.dimensions(0, text)
blf.enable(0, blf.SHADOW)
blf.position(font_id, co_2d.x - w / 2, co_2d.y - h / 2, 0)
blf.draw(font_id, text)
blf.disable(0, blf.SHADOW)
# パネルクラス
class VIEW3D_PT_PgToys(bpy.types.Panel):
bl_label = "PgToys"
bl_idname = "VIEW3D_PT_pg_toys"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "PgToys"
def draw(self, context):
layout = self.layout
scene = context.scene
PgToys = context.scene.PgToysProperty
box = layout.box()
# ポリゴン情報(チェックボックス)
box.prop(
context.space_data.overlay, "show_stats",
text="オブジェクト情報",
icon="INFO"
)
# 頂点法線表示(チェックボックス)
box.prop(
context.space_data.overlay, "show_vertex_normals",
text="頂点法線表示",
icon="NORMALS_VERTEX"
)
box.prop(
context.space_data.overlay, "show_face_normals",
text="面法線表示",
icon="NORMALS_FACE"
)
box = layout.box()
# 頂点番号表示(チェックボックス)
box.prop(PgToys, "show_vert_no", text="頂点番号表示")
# ラベルと値の表示を分離
box.use_property_split = True
# 分離時に出るデコレータ?領域を表示するか
box.use_property_decorate = False
# 文字サイズ
box.prop(PgToys, "font_size", text="サイズ")
# 文字色
box.prop(PgToys, "font_color", text="色")
# Blenderに登録するクラス
classes = [
VIEW3D_PT_PgToys,
PgToys_Property,
]
# Addon有効化
def register():
# クラス登録
for c in classes:
bpy.utils.register_class(c)
# プロパティグループの登録
bpy.types.Scene.PgToysProperty = bpy.props.PointerProperty(type=PgToys_Property)
DrawUtil.handle_add()
# Addon無効化
def unregister():
DrawUtil.handle_remove()
# クラス削除
for c in classes:
bpy.utils.unregister_class(c)
# プロパティグループの削除
del bpy.types.Scene.PgToysProperty
# メイン処理
if __name__ == "__main__":
register()
コメント