ChatGPTを使って、複数のスクリプトが同梱されたBlenderのアドオンパッケージを作ってみました。
新規マテリアルにノードを設定してオブジェクトに割り当てると、三角形の数によってオブジェクトに自動で名前をつけるの処理を、一つのボタンで同時に行います。
スクリプト
今回は3つのスクリプトを作りました。Blender 3.5と互換性があります。
__init__.py
# -*- coding: utf-8 -*-
bl_info = {
"name": "Simple Tool",
"author": "Your Name",
"version": (1, 0),
"blender": (3, 5, 0),
"location": "View3D > Sidebar > Simple Tool",
"description": "Simple tool for input and action",
"category": "3D View",
}
import bpy
from . import rename_utils
from . import material_setup
class SimpleOperator(bpy.types.Operator):
bl_idname = "object.simple_operator"
bl_label = "Apply Name"
def execute(self, context):
rename_utils.rename_by_triangle_count(context.scene.my_string_prop)
material_setup.setup_material(context.scene.my_string_prop)
return {'FINISHED'}
class SimpleToolPanel(bpy.types.Panel):
bl_label = "Simple Tool"
bl_idname = "OBJECT_PT_simple_tool"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Simple Tool"
def draw(self, context):
layout = self.layout
row = layout.row()
row.prop(context.scene, "my_string_prop")
row = layout.row()
row.operator("object.simple_operator")
def register():
bpy.utils.register_class(SimpleToolPanel)
bpy.utils.register_class(SimpleOperator)
bpy.types.Scene.my_string_prop = bpy.props.StringProperty(name = "New Name")
def unregister():
bpy.utils.unregister_class(SimpleToolPanel)
bpy.utils.unregister_class(SimpleOperator)
del bpy.types.Scene.my_string_prop
material_setup.py
import bpy
def initialize_material_nodes_and_textures(material):
material_name = material.name
# マテリアルのノードを作成
material.use_nodes = True
material.node_tree.nodes.clear()
nodes = material.node_tree.nodes
links = material.node_tree.links
# デフォルトのマテリアルアウトプットノード
material_output = nodes.new('ShaderNodeOutputMaterial')
material_output.location = (300, 25)
# Principled BSDFノード
principled_bsdf = nodes.new('ShaderNodeBsdfPrincipled')
principled_bsdf.location = (0, 0)
links.new(principled_bsdf.outputs['BSDF'], material_output.inputs['Surface'])
# Normal Mapノード
normal_map = nodes.new('ShaderNodeNormalMap')
normal_map.location = (-250, -730)
links.new(normal_map.outputs['Normal'], principled_bsdf.inputs['Normal'])
map_size = bpy.context.scene["my_enum_prop"]
# Image Textureノード(Albedo)
image_albedo = nodes.new('ShaderNodeTexImage')
image_albedo.location = (-400, 100)
image_albedo.image = bpy.data.images.new(name=material_name + '_albedo', width=map_size, height=map_size)
image_albedo.image.colorspace_settings.name = 'sRGB'
image_albedo.image.use_fake_user = True
links.new(image_albedo.outputs['Color'], principled_bsdf.inputs['Base Color'])
# Image Textureノード(Metallic)
image_metallic = nodes.new('ShaderNodeTexImage')
image_metallic.location = (-400, -170)
image_metallic.image = bpy.data.images.new(name=material_name + '_metallic', width=map_size, height=map_size)
image_metallic.image.colorspace_settings.name = 'Non-Color'
image_metallic.image.use_fake_user = True
links.new(image_metallic.outputs['Color'], principled_bsdf.inputs['Metallic'])
# Image Textureノード(Roughness)
image_roughness = nodes.new('ShaderNodeTexImage')
image_roughness.location = (-400, -440)
image_roughness.image = bpy.data.images.new(name=material_name + '_roughness', width=map_size, height=map_size)
image_roughness.image.colorspace_settings.name = 'Non-Color'
image_roughness.image.use_fake_user = True
links.new(image_roughness.outputs['Color'], principled_bsdf.inputs['Roughness'])
# Image Textureノード(Normal)
image_normal = nodes.new('ShaderNodeTexImage')
image_normal.location = (-600, -760)
image_normal.image = bpy.data.images.new(name=material_name + '_normal', width=map_size, height=map_size)
image_normal.image.colorspace_settings.name = 'Non-Color'
image_normal.image.use_fake_user = True
links.new(image_normal.outputs['Color'], normal_map.inputs['Color'])
for n in nodes:
n.select = False
def setup_material(material_name):
# 現在のシーンとテキストフィールドの値を取得
scene = bpy.context.scene
# オブジェクトが選択されているか確認
selected_objects = bpy.context.selected_objects
if not selected_objects:
raise Exception("No objects selected.")
# マテリアルを作成
material = bpy.data.materials.get(material_name)
if material is None :
material = bpy.data.materials.new(name=material_name)
initialize_material_nodes_and_textures(material)
material.use_fake_user = True
# 選択された全てのオブジェクトをループ
for obj in bpy.context.selected_objects:
# オブジェクトをアクティブに設定
bpy.context.view_layer.objects.active = obj
# マテリアルがある場合のみ処理を行う
if obj.type == 'MESH' and obj.material_slots:
# 全てのマテリアルのuse_fake_userをTrueに設定
for slot in obj.material_slots:
if slot.material is not None: # マテリアルスロットが空でないことを確認
slot.material.use_fake_user = True
# 全てのマテリアルスロットを削除
for _ in range(len(obj.material_slots)):
bpy.ops.object.material_slot_remove()
# マテリアルを選択されたオブジェクトに割り当て
for obj in selected_objects:
if obj.type == 'MESH':
obj.data.materials.append(material)
rename_utils.py
import bpy
def calculate_triangle_count(obj):
if obj.type != 'MESH':
return 0
mesh = obj.data
mesh.calc_loop_triangles()
return len(mesh.loop_triangles)
def rename_by_triangle_count(new_name):
selected_objects = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
if not selected_objects:
print("No object selected.")
else:
object_triangle_counts = {obj: calculate_triangle_count(obj) for obj in selected_objects}
sorted_objects = sorted(object_triangle_counts, key=object_triangle_counts.get, reverse=True)
for i, obj in enumerate(sorted_objects):
obj.name = new_name + f"_LOD{i}"
インストール方法
まず「simple_tool」というフォルダを作り、上の3つのスクリプトをそれぞれメモ帳などにコピペして、名前を付けてこのフォルダに保存します。拡張子は.pyに変更します。
このフォルダをzipファイルに圧縮します。
Blenderを開き、Edit > Preferences… を開きます。
Add-onsの「Install…」ボタンをクリックします。
上のzipファイルを選び、「Install Add-ons」ボタンを押します。
アドオンがインストールされるので、一覧でチェックを入れて有効化します。
その横の三角アイコンでアドオンの情報が見れます。アドオンを削除するときは「Remove」ボタンを押します。
使い方
有効化すると、3Dビューポートのサイドバーに新しいタブとカスタムパネルが表示されます。パネルにはテキストフィールドとボタンが一つずつあります。
シーンに適当にCube、Cylinder、Suzanneの3つのメッシュオブジェクトを追加しました。
この3つをすべて選択し、テキストフィールドに名前を入力して、その下のボタンを押します。
すると、オブジェクトの名前が変更されました。
三角形の数の多いSuzanne、Cylinder、Cubeの順になっています。
また、テキストフィールドと同じ名前のマテリアルが作られ、3つのオブジェクトに割当てられています。
Image Textureノードには同名の新しいテクスチャが設定されていて、ノードも指定したとおりに接続されています。
これで、複数スクリプトを同梱したアドオンを作ることができました。
ChatGPT
今回も、ChatGPT(GPT-4)にスクリプトを作ってもらいました。前の記事のチャットに続けて、名前を変更するスクリプトの全体と、ボタンを押したときにその処理をしたい旨を伝えました。
すると、他のスクリプトのメソッドを使う方法や、アドオンには__init__.pyを含めることなどを教えてくれました。
通常、Pythonでは他のPythonファイル(.py)をモジュールとしてインポートすることで再利用します。rename_by_triangle_count.py ファイルが同じディレクトリにある場合、その内容を import ステートメントでインポートすることが可能です。
以下の例では、rename_by_triangle_count.py ファイルから calculate_triangle_count 関数と rename_objects 関数をインポートします。これらの関数は、ChangeNamesOperator の execute メソッドから呼び出すことが可能となります。
# -*- coding: utf-8 -*-
import bpy
from .rename_by_triangle_count import calculate_triangle_count, rename_objects # Assuming these functions are defined in rename_by_triangle_count.py
# ...
class ChangeNamesOperator(bpy.types.Operator):
bl_idname = "object.change_names"
bl_label = "Change Object Names"
def execute(self, context):
selected_objects = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
if not selected_objects:
print("No object selected.")
else:
rename_objects(selected_objects, context.scene.simple_tool.my_string)
return {'FINISHED'}
# ...
パッケージは、特定のディレクトリに配置された一連の .py ファイルで、そのディレクトリには __init__.py ファイルが含まれている必要があります。
具体的なディレクトリ構成や作成手順も聞きました。
simple_tool/
├── __init__.py # メインのアドオンスクリプト
└── rename_utils.py # 三角形の数に基づいてオブジェクトの名前を変更するユーティリティ
具体的には以下のような手順となります:
simple_tool ディレクトリを右クリックし、「Send to」 > 「Compressed (zipped) folder」を選択します(Windowsの場合)。 生成されたZIPファイルを適当な名前にリネームします(例えば simple_tool.zip)。 Blenderを開き、「Edit」 > 「Preferences」を選択します。 「Add-ons」タブをクリックし、「Install…」ボタンをクリックします。 ダイアログボックスが開くので、生成したZIPファイル(simple_tool.zip)を選択し、「Install Add-on」ボタンをクリックします。
これにより、アドオンがインストールされ、Blenderの「Preferences」メニューの「Add-ons」セクションに表示されるようになります。
その後、マテリアルを設定するスクリプトも含めて、全体のスクリプトを出力してもらいました。自分で修正したスクリプトを送ると修正してくれます。
ChatGPTを使うことで、素早くアドオンパッケージが作れました。