大きい文字で頂点番号表示(Blender Addon)

Blender

はじめに

標準機能の頂点番号の文字サイズが小さいので、文字サイズを変えられるように時前実装
ついで、メニュー階層が深い機能の表面化

ソースコード(ダウンロードはページの一番下)

# -*- 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()

ダウンロード

PgToys_v100.zip

コメント

タイトルとURLをコピーしました