![]() | Cube.001 やら Cylinder.002 など元の名前のまま残っているメッシュデータを、オブジェクトの名前に変更します。 |
![]() | 2つ以上のオブジェクトで共用されているメッシュデータの場合は、確認用ポップアップが出てくるので、そこで再度押すと実行されます。 |
# Blender内部のデータ構造にアクセスするために必要 import bpy import mathutils # プラグインに関する情報 bl_info = { "name" : "DataRenamer", # プラグイン名 "author" : "Q@StudioPotpourri", # 作者 "version" : (0,4), # プラグインのバージョン "blender" : (2, 7, 7), # プラグインが動作するBlenderのバージョン "location" : "Properties > Object > DataRenamer", # Blender内部でのプラグインの位置づけ "description" : "Rename mesh", # プラグインの説明 "warning" : "", "wiki_url" : "", # プラグインの説明が存在するWikiページのURL "tracker_url" : "", # Blender Developer OrgのスレッドURL "category" : "Object" # プラグインのカテゴリ名 } class RenamerForce(bpy.types.Operator): """RenamerForce""" bl_idname = "object.renamerforce" bl_label = "RenameForce" bl_options = {'UNDO'} def execute(self, context): object = context.object name = object.name object.data.name = name return {'FINISHED'} class Renamer(bpy.types.Operator): """Renamer""" bl_idname = "object.renamer" bl_label = "DataRenamer" bl_options = {'REGISTER','UNDO'} def invoke(self, context, event): object = context.object name = object.name if (object.name == object.data.name): return {'CANCELLED'} if (object.data.users == 1): return self.execute(context) else: return context.window_manager.invoke_props_popup(self, event) return {'CANCELLED'} def execute(self, context): object = context.object name = object.name; if (object.data.users == 1): object.data.name = name; return {'FINISHED'} def draw(self, context): layout = self.layout object = context.object data = context.object.data if (object.name == data.name): return col = layout.column() col.label(text="Data \"" + object.data.name + "\" has " + str(object.data.users) + " users.") col.operator("object.renamerforce", text="Rename it to \"" + object.name + "\" even so") class OBJECT_PT_DataRenamer(bpy.types.Panel): bl_label = "DataRenamer" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "object" @classmethod def poll(cls, context): return (context.object.type == 'MESH' or context.object.type == 'LAMP' or context.object.type == 'CAMERA' or context.object.type == "CURVE") def draw(self, context): layout = self.layout object = context.object data = context.object.data l_row = layout.row(); l_row.label(text=object.name, icon='OBJECT_DATA') if (object.type == 'MESH'): l_row.label(text=data.name, icon='MESH_DATA') if (object.type == 'CAMERA'): l_row.label(text=data.name, icon='CAMERA_DATA') if (object.type == 'CURVE'): l_row.label(text=data.name, icon='CURVE_DATA') if (object.type == 'LAMP'): if (data.type == 'POINT'): l_row.label(text=data.name, icon='LAMP_POINT') if (data.type == 'SUN'): l_row.label(text=data.name, icon='LAMP_SUN') if (data.type == 'SPOT'): l_row.label(text=data.name, icon='LAMP_SPOT') if (data.type == 'HEMI'): l_row.label(text=data.name, icon='LAMP_HEMI') if (data.type == 'AREA'): l_row.label(text=data.name, icon='LAMP_AREA') if (object.name == data.name and data.users == 1): l_row.label(text="users=" + str(data.users), icon='FILE_TICK') else: l_row.label(text="users=" + str(data.users), icon='DOT') if (object.name != data.name): layout.operator("object.renamer", text="Rename Single User Data") def register(): bpy.utils.register_module(__name__) def unregister(): bpy.utils.unregister_module(__name__) if __name__ == "__main__": register() |
![]() | 選択されている面をランダムに選択解除しながら Extrude を繰り返し、このような構造を作ります。 |
![]() | メッシュ分割された平面に適応すれば、このように簡易的な都市のような構造になります。 |
![]() | 3D View の左側、メッシュツール内に登録されます。繰り返し回数や、一ステップごとの選択解除の確率を設定します。 Type: の選択肢は、上のような形状ではNormal方向へ伸ばすタイプ、下の街のような構造では Z directional を選びます。 (街のような構造の場合Normalでも見た目は一緒なのですが、ブロック境界のポリゴンに無駄が生じます) |
# Blender内部のデータ構造にアクセスするために必要 import bpy import mathutils # Tool Self でプロパティ変更 from bpy.props import * # プラグインに関する情報 bl_info = { "name" : "Random Extrude", # プラグイン名 "author" : "Q@StudioPotpourri", # 作者 "version" : (0,3), # プラグインのバージョン "blender" : (2, 7, 7), # プラグインが動作するBlenderのバージョン "location" : "Mesh > Random Extrude", # Blender内部でのプラグインの位置づけ "description" : "Repeat random deselect and extrude", # プラグインの説明 "warning" : "", "wiki_url" : "", # プラグインの説明が存在するWikiページのURL "tracker_url" : "", # Blender Developer OrgのスレッドURL "category" : "Mesh" # プラグインのカテゴリ名 } # メニュー class CRandomExtrude(bpy.types.Operator): bl_idname = "mesh.random_extrude" # ID名 bl_label = "Random Extrude" # メニューに表示される文字列 bl_description = "Random Extrude" # メニューに表示される説明文 bl_options = {'REGISTER', 'UNDO'} # ツールシェルフへ表示させる値 repeat = IntProperty( name = "Repeat", description = "repeat ...", default = 3, min = 0, max = 99) deselectFirst = IntProperty( name = "First", description = "Deselect Percent ...", default = 0, subtype = 'PERCENTAGE', min = 0, max = 100) deselect = IntProperty( name = "", description = "Deselect Percent ...", default = 40, subtype = 'PERCENTAGE', min = 0, max = 100) seed = IntProperty( name = "Random seed", description = "Random Seed ...", default = 3, min = 0) height = FloatProperty( name = "Step height", description = "lineSkip ...", default = 1, min = 0) Types = [("0", "Normal", "Normal"), ("1", "Z directional", "Z directional")] NormalTypes = EnumProperty(name="Type", description ="Type", items=Types) def draw(self, context): layout = self.layout; layout.prop(self, "repeat") layout.label("Deselect Percent:") row = layout.row() row.prop(self, "deselectFirst") row.prop(self, "deselect") layout.prop(self, "seed") layout.prop(self, "height") layout.prop(self, "NormalTypes") layout.prop(self, "normal") # 実際にプラグインが処理を実施する処理 def execute(self, context): for num in range(0, self.repeat): deselect = self.deselect if num == 0: deselect = self.deselectFirst bpy.ops.mesh.select_random(percent=deselect, seed=self.seed, action='DESELECT') if int(self.NormalTypes) == 1: bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate={"value":(0, 0, self.height)}) else: bpy.ops.mesh.extrude_faces_move(TRANSFORM_OT_shrink_fatten={"value":-self.height}) return {'FINISHED'} # 成功した場合はFINISHEDを返す # メニューを登録する関数 def menu_func(self, context): self.layout.label("Random Extrude:") self.layout.operator(CRandomExtrude.bl_idname) # 登録したいクラスの「bl_idname」を指定 # プラグインをインストールしたときの処理 def register(): bpy.utils.register_module(__name__) bpy.types.VIEW3D_PT_tools_meshedit.append(menu_func) # プラグインをアンインストールしたときの処理 def unregister(): bpy.utils.unregister_module(__name__) bpy.types.VIEW3D_PT_tools_meshedit.remove(menu_func) # メイン関数 if __name__ == "__main__": register() |
![]() | 内部的に行っていることとしては、選択されている頂点を分離して Mirror モディファイアをかけて 元と合成するということになります。 選択の状態を維持したり、既にモディファイアがかかっている場合などでも機能するように幾つかの工夫があります。 一旦分離して合成するという内部の操作上、一続きのつながったメッシュの一部だけを Mirror するときには、形状が分離します。一体化させたいときには Remove Double などで重複した頂点を除去するといった操作が必要になります。 |
![]() | 3D View の左側、メッシュツール内に登録されます。Mirrorをかける軸を設定できます。 |
# Blender内部のデータ構造にアクセスするために必要 import bpy import mathutils # Tool Self でプロパティ変更 from bpy.props import * # プラグインに関する情報 bl_info = { "name" : "Mirror Selected Vertices",# プラグイン名 "author" : "Q@SturioPotpourri", # 作者 "version" : (0,8), # プラグインのバージョン "blender" : (2, 7, 8), # プラグインが動作するBlenderのバージョン "location" : "Mesh > Mirror Selected", # Blender内部でのプラグインの位置づけ "description" : "Mirror Selected Vertices", # プラグインの説明 "warning" : "", "wiki_url" : "", # プラグインの説明が存在するWikiページのURL "tracker_url" : "", # Blender Developer OrgのスレッドURL "category" : "Mesh" # プラグインのカテゴリ名 } def diff(first, second): second = set(second) return [item for item in first if item not in second] # メニュー class CMirrorSelectedVertices(bpy.types.Operator): bl_idname = "mesh.mirror_selected_vertices" # ID名 bl_label = "Mirror Selected" # メニューに表示される文字列 bl_description = "Mirror Selected" # メニューに表示される説明文 bl_options = {'REGISTER', 'UNDO'} Types = [("0", "X", "X axis mirror"), ("1", "Y", "Y axis mirror"), ("2", "Z", "Z axis mirror")] AxisTypes = EnumProperty(name="Axis", description ="Axis", items=Types) def draw(self, context): layout = self.layout; layout.prop(self, "AxisTypes") # 実際にプラグインが処理を実施する処理 def execute(self, context): obj = context.object; mesh = obj.data bpy.ops.object.mode_set(mode='OBJECT') # Active のみ Selected に & 現状復帰用 selectedObj = [o for o in bpy.data.objects if o.select] for o in selectedObj: if (o != obj): o.select = False # 頂点数チェック & 選択されたもの復帰用 selected = [v.index for v in mesh.vertices if v.select] selectedNum = len(selected) selectedFace = [f.index for f in mesh.polygons if f.select] selectedNumFace = len(selectedFace) selectedEdge = [e.index for e in mesh.edges if e.select] selectedNumEdge = len(selectedEdge) if (selectedNum == 0): print("Selected") bpy.ops.object.mode_set(mode='EDIT') return {'CANCELLED'} # モディファイアの順番調整用 modifierlist = obj.modifiers[:] modifiershow = [mod.show_viewport for mod in modifierlist] for mod in modifierlist: mod.show_viewport = False meshlist = bpy.data.objects[:] bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.separate(); bpy.ops.object.mode_set(mode='OBJECT') meshlist2 = bpy.data.objects[:] differenceB = diff(meshlist2, meshlist) separated = differenceB[0] separatedMesh = separated.data bpy.context.scene.objects.active = separated modnum = len(modifierlist) modifierlist1 = separated.modifiers[:] bpy.ops.object.modifier_add(type="MIRROR") modifierlist2 = separated.modifiers[:] differenceC = diff(modifierlist2, modifierlist1); mirrorName = differenceC[0].name for index in range (0, modnum): bpy.ops.object.modifier_move_up(modifier=mirrorName) type = int(self.AxisTypes) if (type == 0): separated.modifiers[mirrorName].use_x = True separated.modifiers[mirrorName].use_y = False separated.modifiers[mirrorName].use_z = False if (type == 1): separated.modifiers[mirrorName].use_x = False separated.modifiers[mirrorName].use_y = True separated.modifiers[mirrorName].use_z = False if (type == 2): separated.modifiers[mirrorName].use_x = False separated.modifiers[mirrorName].use_y = False separated.modifiers[mirrorName].use_z = True separated.modifiers[mirrorName].use_mirror_merge = False bpy.ops.object.modifier_apply(modifier=mirrorName) bpy.context.scene.objects.active = obj bpy.ops.object.join() bpy.data.meshes.remove(separatedMesh) for vindex in range(0, selectedNum*2): mesh.vertices[vindex].select = True for eindex in range(0, selectedNumEdge*2): mesh.edges[eindex].select = True for findex in range(0, selectedNumFace*2): mesh.polygons[findex].select = True #print(selectedNum, selectedNumFace, selectedNumEdge) for (mod, show) in zip(modifierlist, modifiershow): mod.show_viewport = show for o in selectedObj: if (o != obj): o.select = True bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} # メニューを登録する関数 def menu_func(self, context): self.layout.operator(CMirrorSelectedVertices.bl_idname) # 登録したいクラスの「bl_idname」を指定 # プラグインをインストールしたときの処理 def register(): bpy.utils.register_module(__name__) bpy.types.VIEW3D_PT_tools_meshedit.append(menu_func) # プラグインをアンインストールしたときの処理 def unregister(): bpy.utils.unregister_module(__name__) bpy.types.VIEW3D_PT_tools_meshedit.remove(menu_func) # メイン関数 if __name__ == "__main__": register() |
![]() | Set placeholders ボタンを押すと、レンダリングをしたくないフレームの出力ファイル名に相当する空のファイル(Placeholder)が作成されます。 レンダリング設定で Overwrite(上書き) が無効になっていれば、ファイルのあるフレームのレンダリングが省略されます。 Camera visibility モードでは、アクティブなカメラがレンダリングから隠れている状態のフレームでレンダリングを省略するようにします。 Marked frames モードでは、マーカーの打たれたフレーム以外のレンダリングを省略します。 Remove placeholders は、レンダリング終了後に邪魔な Placeholder を削除します。 Remove all output は、すべての出力ファイルを削除します。設定上、出力ファイルの上書きができないので、再レンダリングを行なう際に必要になります。 Fill placeholders は、placeholder のままで残っているファイルを、直前の空ではない画像ファイルのコピーに置き換えます。これにより、動きが全くないフレームのレンダリングを省略した後、省略したフレームを直前の有効フレームの画像に置き換えることができます。 |
![]() | 仕組み上、Overwrite(上書き)を無効にする必要があります。上書きが無効になっているときに、右上にチェック印が付くようになっています。 |
# Blender内部のデータ構造にアクセスするために必要 import bpy import os import shutil from pathlib import Path # プラグインに関する情報 bl_info = { "name" : "TweakPlaceHolder", # プラグイン名 "author" : "Q@StudioPotpourri", # 作者 "version" : (0,4), # プラグインのバージョン "blender" : (2, 7, 9), # プラグインが動作するBlenderのバージョン "location" : "Properties > Render > Object Scripter", # Blender内部でのプラグインの位置づけ "description" : "Set or remove placeholders.", # プラグインの説明 "warning" : "", "wiki_url" : "", # プラグインの説明が存在するWikiページのURL "tracker_url" : "", # Blender Developer OrgのスレッドURL "category" : "Render" # プラグインのカテゴリ名 } class TweakPlaceHolderModeProperties(bpy.types.PropertyGroup): Types = [("0", "Camera visibility", "Render for frames active camera is not hide_render"), ("1", "Marked frames", "Render for frames that marked")] mode = bpy.props.EnumProperty(name="TweakMode", description="Mode", items=Types, default="0") def GetExtension(scene, type): if scene.render.use_file_extension == False: return '' if (type == 'PNG'): return '.png' if (type == 'JPEG'): return '.jpg' if (type == 'IRIS'): return '.rgb' if (type == 'JPEG2000'): return '.jp2' if (type == 'TARGA' or type == 'TARGA_RAW'): return '.tga' # 1 = place holder を置く def CheckRender(scene, hide_render, frame): if int(scene.tweakplaceholderprops.mode) == 0: if hide_render == None: return 0 if hide_render.evaluate(frame) == 1: return 1 if int(scene.tweakplaceholderprops.mode) == 1: for markers in scene.timeline_markers: if (markers.frame == frame): return 0 return 1 return 0 class SetPlaceHolder(bpy.types.Operator): """SetPlaceHolder""" bl_idname = "render.setplaceholder" bl_label = "SetPlaceHolder" bl_options = {'UNDO'} def execute(self, context): scene = context.scene if not hasattr(scene, "camera"): print ("not active camera") return {'CANCELLED'} if not hasattr(scene.camera, "animation_data"): print ("not animation_data") return {'CANCELLED'} if not hasattr(scene.camera.animation_data, "action"): print ("not action") return {'CANCELLED'} if hasattr(scene.camera.animation_data.action, "fcurves"): fc = scene.camera.animation_data.action.fcurves hide_render = fc.find('hide_render', index=0) else: print("not fcurves") return {'CANCELLED'} filepath = scene.render.filepath fileformat = scene.render.image_settings.file_format ext = GetExtension(scene, fileformat) ini = scene.frame_start fin = scene.frame_end for f in range(ini,fin+1): if CheckRender(scene, hide_render, f) == 1: fullpath = filepath + "%04d" % f + ext Path(fullpath).touch() return {'FINISHED'} class RemovePlaceHolder(bpy.types.Operator): """RemovePlaceHolder""" bl_idname = "render.removeplaceholder" bl_label = "RemovePlaceHolder" bl_options = {'UNDO'} def execute(self, context): scene = context.scene filepath = scene.render.filepath fileformat = scene.render.image_settings.file_format ext = GetExtension(scene, fileformat) ini = scene.frame_start fin = scene.frame_end for f in range(ini,fin+1): fullpath = filepath + "%04d" % f + ext if (os.path.isfile(fullpath)): if (os.path.getsize(fullpath) == 0): os.remove(fullpath) return {'FINISHED'} class RemoveAllOutputForce(bpy.types.Operator): """RemoveAllOutputForce""" bl_idname = "render.removealloutputforce" bl_label = "RemoveAllOutputForce" bl_options = {'UNDO'} def execute(self, context): scene = context.scene filepath = scene.render.filepath fileformat = scene.render.image_settings.file_format ext = GetExtension(scene, fileformat) ini = scene.frame_start fin = scene.frame_end for f in range(ini,fin+1): fullpath = filepath + "%04d" % f + ext if (os.path.isfile(fullpath)): os.remove(fullpath) return {'FINISHED'} class RemoveAllOutput(bpy.types.Operator): """RemoveAllOutput""" bl_idname = "render.removealloutput" bl_label = "RemoveAllOutput" bl_options = {'REGISTER','UNDO'} def invoke(self, context, event): return context.window_manager.invoke_props_popup(self, event) def execute(self, context): return {'FINISHED'} def draw(self, context): layout = self.layout; layout.operator("render.removealloutputforce", text="Remove files?") class FillPlaceHolder(bpy.types.Operator): """FillPlaceHolders""" bl_idname = "render.fillplaceholders" bl_label = "FillPlaceHolders" bl_options = {'UNDO'} def execute(self, context): scene = context.scene filepath = scene.render.filepath fileformat = scene.render.image_settings.file_format ext = GetExtension(scene, fileformat) ini = scene.frame_start fin = scene.frame_end for f in range(ini,fin+1): fullpathPre = filepath + "%04d" % (f-1) + ext fullpath = filepath + "%04d" % f + ext if (os.path.isfile(fullpath) and os.path.isfile(fullpathPre)): if (os.path.getsize(fullpath) == 0 and os.path.getsize(fullpathPre) != 0): shutil.copyfile(fullpathPre, fullpath) return {'FINISHED'} class RENDER_PT_TweakPlaceHolder(bpy.types.Panel): bl_label = "TweakPlaceHolder" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "render" @classmethod def poll(cls, context): return True def draw(self, context): layout = self.layout; row = layout.row(); row.prop(context.scene.tweakplaceholderprops, "mode", text=''); if (context.scene.render.use_overwrite == False): row.label(icon = 'FILE_TICK', ) else: row.label("Overwrite", icon = 'X',) layout.operator("render.setplaceholder", text="Set placeholders") row = layout.row() row.operator("render.removeplaceholder", text="Remove placeholders") row.operator("render.removealloutput", text="Reomve all output") layout.operator("render.fillplaceholders", text="Fill placeholders") def register(): bpy.utils.register_module(__name__) bpy.types.Scene.tweakplaceholderprops = bpy.props.PointerProperty(type=TweakPlaceHolderModeProperties) def unregister(): bpy.utils.unregister_module(__name__) del bpy.types.Scene.tweakplaceholderprops if __name__ == "__main__": register() |