本日は Blender の技術調査枠です。
Blender2.9で利用可能なアドオンスクリプトをpythonで作ります。
ポリゴン数指定のリダクションを行うアドオン
アドオンを追加すると[3Dビュー]のサイドバーから以下の設定・処理を実行できます。
①.削減後の総ポリゴン(三角面)数
ポリゴン数削減後の総ポリゴン(三角面)数を指定します。
②.1メッシュ辺りの最低ポリゴン数
1メッシュ辺りの最低ポリゴン数を指定します。
③.ポリゴン数削減の実行
全メッシュを対象に指定のポリゴン数までの削減を行います。
実行例
[ビューポートオーバーレイ]から[統計]にチェックを入れて[統計]情報を表示しておくことをお奨めします。
[統計]情報には現在表示中の総ポリゴン数が表示されます。
[3Dビューのシェーディング]を[ワイヤーフレーム]に変更しておくことをお奨めします。
リダクションによるポリゴン数の変化が分かり易くなります。
[削減後の総ポリゴン数(三角面)]の値を変更して指定します。
[1メッシュ辺りの最低ポリゴン数]の値を変更して指定します。
元からポリゴン数が少なく、リダクションすると変形してしまうオブジェクトを保護できます。
指定が完了したら[ポリゴン数の削減]をクリックします。
1メッシュ辺りの最低ポリゴン数を維持しながら、全体が指定のポリゴン数になるようリダクションが実行されます。
なお、最低ポリゴン数の制限により指定のポリゴン数まで削減できない場合は可能な最小のポリゴン数までリダクションします。
ポリゴン数指定のリダクションを行うアドオンを追加します。
・Addon_decimate_mesh.py
ADDON_TITLE = "Mesh Decimate"
ADDON_COMMONNAME = "holomon_decimate_mesh"
ADDON_OPERATOR_IDNAME = "holomon.decimate_mesh"
bl_info = {
"name": ADDON_TITLE + " Addon by HoloMon",
"author": "HoloMon",
"version": (1, 0),
"blender": (2, 90, 0),
"support": "TESTING",
"category": "3D View",
"location": "View3D > Sidebar > HoloMon",
"description": ADDON_TITLE + "Addon",
"location": "",
"warning": "",
"doc_url": "",
}
import bpy
import math
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import PointerProperty, IntProperty
class HOLOMON_PT_holomon_decimate_mesh(Panel):
bl_label = "ポリゴン数の削減"
bl_idname = "HOLOMON_PT_" + ADDON_COMMONNAME
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_options = set()
bl_order = 2
bl_category = "HMToolkit"
def draw(self, context):
draw_layout = self.layout
line_row = draw_layout.row()
line_row.prop(context.scene.holomon_decimate_mesh, "prop_targettrianglecount")
line_row = draw_layout.row()
line_row.prop(context.scene.holomon_decimate_mesh, "prop_mintrianglecount")
line_row = draw_layout.row()
line_row.operator(ADDON_OPERATOR_IDNAME)
class HOLOMON_OT_holomon_decimate_mesh(Operator):
bl_idname = ADDON_OPERATOR_IDNAME
bl_label = "ポリゴン数削減の実行"
bl_description = "全メッシュを対象に指定のポリゴン数までの削減を行います"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
targettrianglecount = context.scene.holomon_decimate_mesh.prop_targettrianglecount
if targettrianglecount <= 0:
self.report({'ERROR'}, "削減後の総ポリゴン数は 1 以上を設定して下さい")
return {'CANCELLED'}
mintrianglecount = context.scene.holomon_decimate_mesh.prop_mintrianglecount
operator_result = apply_decimate_calculate(
arg_targettrianglecount=targettrianglecount,
arg_mintrianglecount=mintrianglecount
)
if operator_result == False:
self.report({'ERROR'}, "実行に失敗しました")
return {'CANCELLED'}
return {'FINISHED'}
class HOLOMON_PROP_holomon_decimate_mesh(PropertyGroup):
prop_targettrianglecount: IntProperty(
name = "削減後の総ポリゴン(三角面)数",
default=0,
description = "削減後の総ポリゴン(三角面)数を指定します",
)
prop_mintrianglecount: IntProperty(
name = "1メッシュ辺りの最低ポリゴン数",
default=0,
description = "1メッシュ辺りの最低ポリゴン数を指定します",
)
regist_classes = (
HOLOMON_PT_holomon_decimate_mesh,
HOLOMON_OT_holomon_decimate_mesh,
HOLOMON_PROP_holomon_decimate_mesh,
)
def register():
for regist_cls in regist_classes:
bpy.utils.register_class(regist_cls)
bpy.types.Scene.holomon_decimate_mesh = \
PointerProperty(type=HOLOMON_PROP_holomon_decimate_mesh)
def unregister():
del bpy.types.Scene.holomon_decimate_mesh
for regist_cls in regist_classes:
bpy.utils.unregister_class(regist_cls)
def apply_decimate_calculate(arg_targettrianglecount:int=10000, arg_mintrianglecount:int=0) -> float:
"""全メッシュを対象に指定のポリゴン数まで削減する
Keyword Arguments:
arg_targettrianglecount {int} -- 削減後の指定ポリゴン数 (default: {10000})
arg_mintrianglecount {int} -- オブジェクトの最低ポリゴン数 (default: {0})
Returns:
float -- 削減比率
"""
current_trianglecount = count_alltriangles_mesh()
target_ratio = 1.0
if current_trianglecount > arg_targettrianglecount:
target_ratio = arg_targettrianglecount / current_trianglecount
forecast_trianglecount = current_trianglecount
while forecast_trianglecount > arg_targettrianglecount:
continue_flg = False
forecast_trianglecount = 0
for obj in bpy.context.scene.objects:
obj_ratio = target_ratio
obj_trianglecount = count_triangles_mesh(obj)
if obj_trianglecount < arg_mintrianglecount:
obj_ratio = 1.0
elif (obj_trianglecount * target_ratio) < arg_mintrianglecount:
obj_ratio = arg_mintrianglecount / obj_trianglecount
else:
continue_flg = True
after_trianglecount = obj_trianglecount * obj_ratio
forecast_trianglecount += math.floor(after_trianglecount)
if continue_flg == False:
break
if forecast_trianglecount > arg_targettrianglecount:
over_ratio = arg_targettrianglecount / forecast_trianglecount
target_ratio *= over_ratio
for obj in bpy.context.scene.objects:
obj_ratio = target_ratio
obj_trianglecount = count_triangles_mesh(obj)
if obj_trianglecount < arg_mintrianglecount:
continue
if (obj_trianglecount * target_ratio) < arg_mintrianglecount:
obj_ratio = arg_mintrianglecount / obj_trianglecount
apply_decimate_mesh(arg_targetobject=obj, arg_decimateratio=obj_ratio)
return True
def apply_decimate_mesh(arg_targetobject:bpy.types.Object, arg_decimateratio:float=1.0) -> bool:
"""対象オブジェクトを指定の比率でポリゴン削減する
Keyword Arguments:
arg_targetobject {bpy.types.Object} -- 対象オブジェクト
arg_decimateratio {float} -- 削減比率 (default: {1.0})
Returns:
Bool -- 実行正否
"""
if arg_targetobject.type != 'MESH':
return False
bpy.context.view_layer.objects.active = arg_targetobject
bpy.ops.object.modifier_add(type='DECIMATE')
decimate_modifier = arg_targetobject.modifiers[-1]
decimate_modifier.ratio = arg_decimateratio
bpy.ops.object.modifier_apply(modifier=decimate_modifier.name)
return True
def count_alltriangles_mesh() -> int:
"""全メッシュの総三角面数を取得する
Keyword Arguments:
Returns:
int -- 三角面数(取得失敗時:0)
"""
triangles_count = 0
for obj in bpy.data.objects:
triangles_count += count_triangles_mesh(obj)
return triangles_count
def count_triangles_mesh(arg_object:bpy.types.Object) -> int:
"""指定メッシュの三角面数を取得する
Keyword Arguments:
arg_objectname {bpy.types.Object} -- 対象オブジェクト
Returns:
int -- 三角面数(取得失敗時:0)
"""
if arg_object.type != 'MESH':
return 0
msh = arg_object.data
msh.calc_loop_triangles()
triangles_count = len(msh.loop_triangles)
return triangles_count
if __name__ == "__main__":
register()