diff options
author | lijenstina <lijenstina@gmail.com> | 2017-05-24 19:31:25 +0300 |
---|---|---|
committer | lijenstina <lijenstina@gmail.com> | 2017-05-24 19:31:25 +0300 |
commit | 331f2982d714f51cb6ded56b3f6b10ba004ff62f (patch) | |
tree | b74d92ae548eb9f54980901b84f08c91aa1119ff | |
parent | 97bf32a8b84b7a9ef40b9406802f293e0280814f (diff) |
Add Advanced Objects: Bump to ver. 0.1.3, Cleanup, Fixes
Bump version to 0.1.3
Cleanup - some style and UI modifications
Improved Tooltips, add missing descriptions
Menu and Panel Listing in the preferences
Move scene props to init for proper reg - unreg
Translate comments into English
Fix several bugs with scripts:
- Drop to ground: add missing poll for Drop all
- Cubester: fix sound generation, missing checks for data
move properties to __init__ together with the update functions
- Make Struts: add a try block allows the global undo to be
restored, fix division by zero error, add missing poll
- rope_alpha: fix several crashes with hardcoded names on re-runs
make the similation work for new scenes
- Circle array: fix (flawed attempt) check for allowed types
- Copy2: add check for properties passed, error handling, division by 0
on non mesh copy objects, add a missing poll
- Unfold Transition: fix polls for active objects, division by 0
add a warning about selecting an another armature
- oscurart_constellation: add a try block, move the prop to init
- easy_lattice: simplify enumproperty, polling
- Laplacian Lightning: prints are optional, API change to obj.raycast,
UI refactor, move winmgr props to scene properties
- add_light_template - try block, fix crash with constraints
and no camera in the scene
26 files changed, 3340 insertions, 2561 deletions
diff --git a/add_advanced_objects/__init__.py b/add_advanced_objects/__init__.py index 3c15d1b4..cad3c077 100644 --- a/add_advanced_objects/__init__.py +++ b/add_advanced_objects/__init__.py @@ -20,19 +20,18 @@ # meta-androcto, Bill Currie, Jorge Hernandez - Melenedez Jacob Morris, Oscurart # # Rebellion, Antonis Karvelas, Eleanor Howick, lijenstina, Daniel Schalla, Domlysz # # Unnikrishnan(kodemax), Florian Meyer, Omar ahmed, Brian Hinton (Nichod), liero # -# Dannyboy, Mano-Wii, Kursad Karatas, teldredge +# Atom, Dannyboy, Mano-Wii, Kursad Karatas, teldredge, Phil Cote # bl_info = { "name": "Add Advanced Objects", "author": "Meta Androcto,", - "version": (0, 1, 1), + "version": (0, 1, 3), "blender": (2, 78, 0), "location": "View3D > Add ", "description": "Add Object & Camera extras", "warning": "", "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6" "/Py/Scripts", - "tracker_url": "", "category": "Object"} if "bpy" in locals(): @@ -64,46 +63,54 @@ if "bpy" in locals(): importlib.reload(oscurart_chain_maker) else: - from . import ( - add_light_template, - scene_objects_bi, - scene_objects_cycles, - scene_texture_render, - trilighting, - pixelate_3d, - object_add_chain, - oscurart_chain_maker, - drop_to_ground, - circle_array, - unfold_transition, - copy2, - make_struts, - random_box_structure, - cubester, - rope_alpha, - add_mesh_aggregate, - object_mangle_tools, - arrange_on_curve, - object_laplace_lightning, - mesh_easylattice - ) - from .delaunay_voronoi import ( - DelaunayVoronoi, - delaunayVoronoiBlender, - oscurart_constellation - ) + from . import add_light_template + from . import scene_objects_bi + from . import scene_objects_cycles + from . import scene_texture_render + from . import trilighting + from . import pixelate_3d + from . import object_add_chain + from . import oscurart_chain_maker + from . import drop_to_ground + from . import circle_array + from . import unfold_transition + from . import copy2 + from . import make_struts + from . import random_box_structure + from . import cubester + from . import rope_alpha + from . import add_mesh_aggregate + from . import object_mangle_tools + from . import arrange_on_curve + from . import object_laplace_lightning + from . import mesh_easylattice + + from .delaunay_voronoi import DelaunayVoronoi + from .delaunay_voronoi import delaunayVoronoiBlender + from .delaunay_voronoi import oscurart_constellation import bpy from bpy.types import ( Menu, AddonPreferences, + PropertyGroup, + ) +from bpy.props import ( + BoolProperty, + BoolVectorProperty, + EnumProperty, + FloatProperty, + FloatVectorProperty, + IntProperty, + StringProperty, + PointerProperty, ) +# Define the "Scenes" menu class INFO_MT_scene_elements_add(Menu): - # Define the "scenes" menu bl_idname = "INFO_MT_scene_elements" - bl_label = "Test scenes" + bl_label = "Test Scenes" def draw(self, context): layout = self.layout @@ -116,8 +123,8 @@ class INFO_MT_scene_elements_add(Menu): text="Scene_Textures_Cycles") +# Define the "Lights" menu class INFO_MT_mesh_lamps_add(Menu): - # Define the "lights" menu bl_idname = "INFO_MT_scene_lamps" bl_label = "Lighting Sets" @@ -130,8 +137,8 @@ class INFO_MT_mesh_lamps_add(Menu): text="Add Tri Lighting") +# Define the "Chains" menu class INFO_MT_mesh_chain_add(Menu): - # Define the "Chains" menu bl_idname = "INFO_MT_mesh_chain" bl_label = "Chains" @@ -142,47 +149,45 @@ class INFO_MT_mesh_chain_add(Menu): layout.operator("mesh.primitive_oscurart_chain_add", icon="LINKED") +# Define the "Array" Menu class INFO_MT_array_mods_add(Menu): - # Define the "array" menu bl_idname = "INFO_MT_array_mods" bl_label = "Array Mods" def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - self.layout.menu("INFO_MT_mesh_chain", icon="LINKED") + + layout.menu("INFO_MT_mesh_chain", icon="LINKED") + layout.operator("objects.circle_array_operator", - text="Circle Array", icon='MOD_ARRAY') + text="Circle Array", icon="MOD_ARRAY") layout.operator("object.agregate_mesh", - text="Aggregate Mesh", icon='MOD_ARRAY') - obj = context.object - if obj.type in ['MESH',]: - layout.operator("mesh.copy2", - text="Copy To Vert/Edge", icon='MOD_ARRAY') - + text="Aggregate Mesh", icon="MOD_ARRAY") + layout.operator("mesh.copy2", + text="Copy To Vert/Edge", icon="MOD_ARRAY") +# Define the "Blocks" Menu class INFO_MT_quick_blocks_add(Menu): - # Define the "Blocks" menu bl_idname = "INFO_MT_quick_tools" bl_label = "Block Tools" def draw(self, context): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator('object.pixelate', icon='MESH_GRID') - obj = context.object - if obj.type in ['MESH',]: - layout.operator("mesh.generate_struts", - text="Struts", icon='GRID') - layout.operator("object.easy_lattice", - text="Easy Lattice", icon='MOD_LATTICE') - layout.operator("object.make_structure", - text="Random Boxes", icon='SEQ_SEQUENCER') + layout.operator("object.pixelate", icon="MESH_GRID") + layout.operator("mesh.generate_struts", + text="Struts", icon="GRID") + layout.operator("object.make_structure", + text="Random Boxes", icon="SEQ_SEQUENCER") + layout.operator("object.easy_lattice", + text="Easy Lattice", icon="MOD_LATTICE") + +# Define the "Phsysics Tools" Menu class INFO_MT_Physics_tools_add(Menu): - # Define the "mesh objects" menu bl_idname = "INFO_MT_physics_tools" bl_label = "Physics Tools" @@ -190,7 +195,7 @@ class INFO_MT_Physics_tools_add(Menu): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' layout.operator("object.drop_on_active", - text="Drop To Ground") + text="Drop To Ground", icon="SORTSIZE") layout.operator("ball.rope", text="Wrecking Ball", icon='PHYSICS') layout.operator("clot.rope", @@ -211,22 +216,551 @@ def menu(self, context): # Addons Preferences -class AddonPreferences(AddonPreferences): +class AdvancedObjPreferences(AddonPreferences): bl_idname = __name__ + show_menu_list = BoolProperty( + name="Menu List", + description="Show/Hide the Add Menu items", + default=False + ) + show_panel_list = BoolProperty( + name="Panels List", + description="Show/Hide the Panel items", + default=False + ) + def draw(self, context): layout = self.layout - layout.label(text="----Add Menu Advanced----") - layout.label(text="Quick Tools:") - layout.label(text="Drop, Pixelate & Wrecking Ball") - layout.label(text="Array Mods:") - layout.label(text="Circle Array, Chains, Vert to Edge, Aggregate") + + icon_1 = "TRIA_RIGHT" if not self.show_menu_list else "TRIA_DOWN" + box = layout.box() + box.prop(self, "show_menu_list", emboss=False, icon=icon_1) + + if self.show_menu_list: + box.label(text="Items located in the Add Menu (default shortcut Ctrl + A):", + icon="LAYER_USED") + box.label(text="Test Scenes:", icon="LAYER_ACTIVE") + box.label(text="Scene Objects BI, Scene Objects Cycles, Scene Textures Cycles", + icon="LAYER_USED") + box.label(text="Lighting Sets:", icon="LAYER_ACTIVE") + box.label(text="Add Light Template, Add Tri Lighting", icon="LAYER_USED") + box.label(text="Array Mods:", icon="LAYER_ACTIVE") + box.label(text="Circle Array, Chains submenu, Copy Vert/Edge and Aggregate Mesh", + icon="LAYER_ACTIVE") + box.label(text="Chains Submenu - Add Chain, Chain to Bones", + icon="LAYER_ACTIVE") + box.label(text="Block Tools:", icon="LAYER_ACTIVE") + box.label(text="Pixelate Object, Struts, Random Boxes, Easy Lattice", + icon="LAYER_USED") + box.label(text="Physics Tools:", icon="LAYER_ACTIVE") + box.label(text="Drop to Ground, Wrecking Ball and Cloth Rope", icon="LAYER_USED") + + icon_2 = "TRIA_RIGHT" if not self.show_panel_list else "TRIA_DOWN" + box = layout.box() + box.prop(self, "show_panel_list", emboss=False, icon=icon_2) + + if self.show_panel_list: + box.label(text="Panels located in 3D View Tools Region > Create", + icon="LAYER_ACTIVE") + box.label(text="Drop to Ground", icon="LAYER_USED") + box.label(text="Unfold Transition", icon="LAYER_USED") + box.label(text="CubeSter", icon="LAYER_USED") + box.label(text="Mangle tools", icon="LAYER_USED") + box.label(text="Laplacian Lighting", icon="LAYER_USED") + box.label(text="Delaunay Voronoi", icon="LAYER_USED") + box.label(text="Duplicate on Curve (Shown if an Active Curve Object is it the 3D View)", + icon="LAYER_USED") + + +# Cubester update functions +def find_audio_length(self, context): + adv_obj = context.scene.advanced_objects + audio_file = adv_obj.cubester_audio_path + length = 0 + + if audio_file != "": + # confirm that strip hasn't been loaded yet + get_sequence = getattr(context.scene.sequence_editor, "sequences_all", []) + for strip in get_sequence: + if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file: + length = strip.frame_final_duration + + if length == 0: + area = context.area + old_type = area.type + area.type = "SEQUENCE_EDITOR" + try: + bpy.ops.sequencer.sound_strip_add(filepath=audio_file) + adv_obj.cubester_check_audio = True + except Exception as e: + print("\n[Add Advanced Objects]\n Function: " + "find_audio_length\n {}\n".format(e)) + adv_obj.cubester_check_audio = False + pass + + area.type = old_type + + # find audio file + for strip in context.scene.sequence_editor.sequences_all: + if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file: + adv_obj.cubester_check_audio = True + length = strip.frame_final_duration + + adv_obj.cubester_audio_file_length = length + + +# load image if possible +def adjust_selected_image(self, context): + scene = context.scene.advanced_objects + try: + image = bpy.data.images.load(scene.cubester_load_image) + scene.cubester_image = image.name + except Exception as e: + print("\n[Add Advanced Objects]\n Function: " + "adjust_selected_image\n {}\n".format(e)) + + +# load color image if possible +def adjust_selected_color_image(self, context): + scene = context.scene.advanced_objects + try: + image = bpy.data.images.load(scene.cubester_load_color_image) + scene.cubester_color_image = image.name + except Exception as e: + print("\nAdd Advanced Objects]\n Function: " + "adjust_selected_color_image\n {}\n".format(e)) + + +class AdvancedObjProperties(PropertyGroup): + # cubester + # main properties + cubester_check_audio = BoolProperty( + name="", + default=False + ) + cubester_audio_image = EnumProperty( + name="Input Type", + items=(("image", "Image", + "Use an Image as input for generating Geometry", "IMAGE_COL", 0), + ("audio", "Audio", + "Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1)) + ) + cubester_audio_file_length = IntProperty( + default=0 + ) + # audio + cubester_audio_path = StringProperty( + default="", + name="Audio File", + subtype="FILE_PATH", + update=find_audio_length + ) + cubester_audio_min_freq = IntProperty( + name="Minimum Frequency", + min=20, max=100000, + default=20 + ) + cubester_audio_max_freq = IntProperty( + name="Maximum Frequency", + min=21, max=999999, + default=5000 + ) + cubester_audio_offset_type = EnumProperty( + name="Offset Type", + items=(("freq", "Frequency Offset", ""), + ("frame", "Frame Offset", "")), + description="Type of offset per row of mesh" + ) + cubester_audio_frame_offset = IntProperty( + name="Frame Offset", + min=0, max=10, + default=2 + ) + cubester_audio_block_layout = EnumProperty( + name="Block Layout", + items=(("rectangle", "Rectangular", ""), + ("radial", "Radial", "")) + ) + cubester_audio_width_blocks = IntProperty( + name="Width Block Count", + min=1, max=10000, + default=5 + ) + cubester_audio_length_blocks = IntProperty( + name="Length Block Count", + min=1, max=10000, + default=50 + ) + # image + cubester_load_type = EnumProperty( + name="Image Input Type", + items=(("single", "Single Image", ""), + ("multiple", "Image Sequence", "")) + ) + cubester_image = StringProperty( + default="", + name="" + ) + cubester_load_image = StringProperty( + default="", + name="Load Image", + subtype="FILE_PATH", + update=adjust_selected_image + ) + cubester_skip_images = IntProperty( + name="Image Step", + min=1, max=30, + default=1, + description="Step from image to image by this number" + ) + cubester_max_images = IntProperty( + name="Max Number Of Images", + min=2, max=1000, + default=10, + description="Maximum number of images to be used" + ) + cubester_frame_step = IntProperty( + name="Frame Step Size", + min=1, max=10, + default=4, + description="The number of frames each picture is used" + ) + cubester_skip_pixels = IntProperty( + name="Skip # Pixels", + min=0, max=256, + default=64, + description="Skip this number of pixels before placing the next" + ) + cubester_mesh_style = EnumProperty( + name="Mesh Type", + items=(("blocks", "Blocks", ""), + ("plane", "Plane", "")), + description="Compose mesh of multiple blocks or of a single plane" + ) + cubester_block_style = EnumProperty( + name="Block Style", + items=(("size", "Vary Size", ""), + ("position", "Vary Position", "")), + description="Vary Z-size of block, or vary Z-position" + ) + cubester_height_scale = FloatProperty( + name="Height Scale", + subtype="DISTANCE", + min=0.1, max=2, + default=0.2 + ) + cubester_invert = BoolProperty( + name="Invert Height", + default=False + ) + # general adjustments + cubester_size_per_hundred_pixels = FloatProperty( + name="Size Per 100 Blocks/Points", + subtype="DISTANCE", + min=0.001, max=5, + default=1 + ) + # material based stuff + cubester_materials = EnumProperty( + name="Material", + items=(("vertex", "Vertex Colors", ""), + ("image", "Image", "")), + description="Color with vertex colors, or uv unwrap and use an image" + ) + cubester_use_image_color = BoolProperty( + name="Use Original Image Colors'?", + default=True, + description="Use original image colors, or replace with an another one" + ) + cubester_color_image = StringProperty( + default="", + name="" + ) + cubester_load_color_image = StringProperty( + default="", + name="Load Color Image", + subtype="FILE_PATH", + update=adjust_selected_color_image + ) + cubester_vertex_colors = {} + # advanced + cubester_advanced = BoolProperty( + name="Advanced Options", + default=False + ) + cubester_random_weights = BoolProperty( + name="Random Weights", + default=False + ) + cubester_weight_r = FloatProperty( + name="Red", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) + cubester_weight_g = FloatProperty( + name="Green", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) + cubester_weight_b = FloatProperty( + name="Blue", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) + cubester_weight_a = FloatProperty( + name="Alpha", + subtype="FACTOR", + min=0.01, max=1.0, + default=0.25 + ) + + # pixelate_3d properties + pixelate_3d_size = FloatProperty( + name="Size", + min=.05, max=5, + default=.25, + description="Size of the cube / grid" + ) + pixelate_3d_gap = IntProperty( + name="Gap", + min=0, max=90, + default=10, + subtype='PERCENTAGE', + description="Separation - percent of size" + ) + pixelate_3d_smooth = FloatProperty( + name="Smooth", + min=0, max=1, + default=.0, + description="Smooth factor when subdividing mesh" + ) + # arrange_on_curve + arrange_c_use_selected = BoolProperty( + name="Use Selected", + description="Use the selected objects to duplicate", + default=True, + ) + arrange_c_obj_arranjar = StringProperty( + name="" + ) + arrange_c_select_type = EnumProperty( + name="Type", + description="Select object or group", + items=[ + ('O', "Object", "Make duplicates of a specific object"), + ('G', "Group", "Make duplicates of the objects in a group"), + ], + default='O', + ) + # object_laplace_lighting props + ORIGIN = FloatVectorProperty( + name="Origin charge" + ) + GROUNDZ = IntProperty( + name="Ground Z coordinate" + ) + HORDER = IntProperty( + name="Secondary paths orders", + default=1 + ) + # object_laplace_lighting UI props + TSTEPS = IntProperty( + name="Iterations", + default=350, + description="Number of cells to create\n" + "Will end early if hits ground plane or cloud" + ) + GSCALE = FloatProperty( + name="Grid unit size", + default=0.12, + description="scale of cells, .25 = 4 cells per blenderUnit" + ) + BIGVAR = FloatProperty( + name="Straightness", + default=6.3, + description="Straightness/branchiness of bolt, \n" + "<2 is mush, >12 is staight line, 6.3 is good" + ) + GROUNDBOOL = BoolProperty( + name="Use Ground object", + description="Use ground plane or not", + default=True + ) + GROUNDC = IntProperty( + name="Ground charge", + default=-250, + description="Charge of the ground plane" + ) + CLOUDBOOL = BoolProperty( + name="Use Cloud object", + default=False, + description="Use cloud object - attracts and terminates like ground but\n" + "any obj instead of z plane\n" + "Can slow down loop if obj is large, overrides ground" + ) + CLOUDC = IntProperty( + name="Cloud charge", + default=-1, + description="Charge of a cell in cloud object\n" + "(so total charge also depends on obj size)" + ) + VMMESH = BoolProperty( + name="Multi mesh", + default=True, + description="Output to multi-meshes for different materials on main/sec/side branches" + ) + VSMESH = BoolProperty( + name="Single mesh", + default=False, + description="Output to single mesh for using build modifier and particles for effects" + ) + VCUBE = BoolProperty( + name="Cubes", + default=False, + description="CTRL-J after run to JOIN\n" + "Outputs a bunch of cube objects, mostly for testing" + ) + VVOX = BoolProperty( + name="Voxel (experimental)", + default=False, + description="Output to a voxel file to bpy.data.filepath\FSLGvoxels.raw\n" + "(doesn't work well right now)" + ) + IBOOL = BoolProperty( + name="Use Insulator object", + default=False, + description="Use insulator mesh object to prevent growth of bolt in areas" + ) + OOB = StringProperty( + name="Select", + default="", + description="Origin of bolt, can be an Empty\n" + "if object is a mesh will use all verts as charges") + GOB = StringProperty( + name="Select", + default="", + description="Object to use as ground plane, uses z coord only" + ) + COB = StringProperty( + name="Select", + default="", + description="Object to use as cloud, best to use a cube" + ) + IOB = StringProperty( + name="Select", + default="", + description="Object to use as insulator, 'voxelized'\n" + "before generating bolt (can be slow)" + ) + # object_mangle_tools properties + mangle_constraint_vector = BoolVectorProperty( + name="Mangle Constraint", + default=(True, True, True), + subtype='XYZ', + description="Constrains Mangle Direction" + ) + mangle_random_magnitude = IntProperty( + name="Mangle Severity", + default=5, + min=1, max=30, + description="Severity of mangling" + ) + mangle_name = StringProperty( + name="Shape Key Name", + default="mangle", + description="Name given for mangled shape keys" + ) + # unfold_transition properties + unfold_arm_name = StringProperty( + default="" + ) + unfold_modo = EnumProperty( + name="", + items=[("cursor", "3D Cursor", "Use the Distance to 3D Cursor"), + ("weight", "Weight Map", "Use a Painted Weight map"), + ("index", "Mesh Indices", "Use Faces and Vertices index")], + description="How to Sort Bones for animation", default="cursor" + ) + unfold_flip = BoolProperty( + name="Flipping Faces", + default=False, + description="Rotate faces around the Center and skip Scaling - " + "keep checked for both operators" + ) + unfold_fold_duration = IntProperty( + name="Total Time", + min=5, soft_min=25, + max=10000, soft_max=2500, + default=200, + description="Total animation length" + ) + unfold_sca_time = IntProperty( + name="Scale Time", + min=1, + max=5000, soft_max=500, + default=10, + description="Faces scaling time" + ) + unfold_rot_time = IntProperty( + name="Rotation Time", + min=1, soft_min=5, + max=5000, soft_max=500, + default=15, + description="Faces rotation time" + ) + unfold_rot_max = IntProperty( + name="Angle", + min=-180, + max=180, + default=135, + description="Faces rotation angle" + ) + unfold_fold_noise = IntProperty( + name="Noise", + min=0, + max=500, soft_max=50, + default=0, + description="Offset some faces animation" + ) + unfold_bounce = FloatProperty( + name="Bounce", + min=0, + max=10, soft_max=2.5, + default=0, + description="Add some bounce to rotation" + ) + unfold_from_point = BoolProperty( + name="Point", + default=False, + description="Scale faces from a Point instead of from an Edge" + ) + unfold_wiggle_rot = BoolProperty( + name="Wiggle", + default=False, + description="Use all Axis + Random Rotation instead of X Aligned" + ) + # oscurart_constellation + constellation_limit = FloatProperty( + name="Inital Threshold", + description="Edges will be created only if the distance\n" + "between vertices is smaller than this value\n" + "This is a starting value on Operator Invoke", + default=2, + min=0 + ) def register(): - object_mangle_tools.register() - arrange_on_curve.register() bpy.utils.register_module(__name__) + + bpy.types.Scene.advanced_objects = PointerProperty( + type=AdvancedObjProperties + ) + # Add "Extras" menu to the "Add" menu bpy.types.INFO_MT_add.append(menu) try: @@ -236,8 +770,6 @@ def register(): def unregister(): - object_mangle_tools.unregister() - arrange_on_curve.unregister() # Remove "Extras" menu from the "Add" menu. bpy.types.INFO_MT_add.remove(menu) try: @@ -246,6 +778,11 @@ def unregister(): pass bpy.utils.unregister_module(__name__) + del bpy.types.Scene.advanced_objects + + # cleanup Easy Lattice Scene Property if it was created + if hasattr(bpy.types.Scene, "activelatticeobject"): + del bpy.types.Scene.activelatticeobject if __name__ == "__main__": diff --git a/add_advanced_objects/add_light_template.py b/add_advanced_objects/add_light_template.py index 312b2d9e..9e2c139f 100644 --- a/add_advanced_objects/add_light_template.py +++ b/add_advanced_objects/add_light_template.py @@ -6,120 +6,129 @@ from bpy.props import BoolProperty def add_lamps(self, context): - try: - if self.bKeyLight: - keyLight = bpy.data.lamps.new(name="Key_Light", type="SPOT") - ob = bpy.data.objects.new("Key_Light", keyLight) - constraint = ob.constraints.new(type='COPY_LOCATION') - constraint.use_offset = True - constraint.owner_space = 'LOCAL' - constraint.target = self.camera - constraint = ob.constraints.new(type='TRACK_TO') - constraint.target = self.target - constraint.track_axis = 'TRACK_NEGATIVE_Z' - constraint.up_axis = 'UP_X' - constraint.owner_space = 'LOCAL' - bpy.context.scene.objects.link(ob) - ob.rotation_euler[2] = -0.785398 - - if self.bFillLight: - fillLight = bpy.data.lamps.new(name="Fill_Light", type="SPOT") - ob = bpy.data.objects.new("Fill_Light", fillLight) - constraint = ob.constraints.new(type='COPY_LOCATION') - constraint.use_offset = True - constraint.owner_space = 'LOCAL' - constraint.target = self.camera - constraint = ob.constraints.new(type='TRACK_TO') - constraint.target = self.target - constraint.track_axis = 'TRACK_NEGATIVE_Z' - constraint.up_axis = 'UP_X' - constraint.owner_space = 'LOCAL' - bpy.context.scene.objects.link(ob) - ob.rotation_euler[2] = 0.785398 - ob.data.energy = 0.3 - - if self.bBackLight: - backLight = bpy.data.lamps.new(name="Back_Light", type="SPOT") - ob = bpy.data.objects.new("Back_Light", backLight) - constraint = ob.constraints.new(type='COPY_LOCATION') - constraint.use_offset = True - constraint.owner_space = 'LOCAL' - constraint.target = self.camera - constraint = ob.constraints.new(type='TRACK_TO') - constraint.target = self.target - constraint.track_axis = 'TRACK_NEGATIVE_Z' - constraint.up_axis = 'UP_X' - constraint.owner_space = 'LOCAL' - bpy.context.scene.objects.link(ob) - ob.rotation_euler[2] = 3.14159 - ob.data.energy = 0.2 - - if self.camera_constraint: - constraint = self.camera.constraints.new(type='TRACK_TO') - constraint.target = self.target - constraint.track_axis = 'TRACK_NEGATIVE_Z' - constraint.up_axis = 'UP_Y' - - except Exception as e: - self.report({'WARNING'}, - "Some operations could not be performed (See Console for more info)") - - print("\n[object.add_light_template]\nError: {}".format(e)) + + if self.bKeyLight: + keyLight = bpy.data.lamps.new(name="Key_Light", type="SPOT") + ob = bpy.data.objects.new("Key_Light", keyLight) + constraint = ob.constraints.new(type='COPY_LOCATION') + constraint.use_offset = True + constraint.owner_space = 'LOCAL' + constraint.target = self.camera + constraint = ob.constraints.new(type='TRACK_TO') + constraint.target = self.target + constraint.track_axis = 'TRACK_NEGATIVE_Z' + constraint.up_axis = 'UP_X' + constraint.owner_space = 'LOCAL' + bpy.context.scene.objects.link(ob) + ob.rotation_euler[2] = -0.785398 + + if self.bFillLight: + fillLight = bpy.data.lamps.new(name="Fill_Light", type="SPOT") + ob = bpy.data.objects.new("Fill_Light", fillLight) + constraint = ob.constraints.new(type='COPY_LOCATION') + constraint.use_offset = True + constraint.owner_space = 'LOCAL' + constraint.target = self.camera + constraint = ob.constraints.new(type='TRACK_TO') + constraint.target = self.target + constraint.track_axis = 'TRACK_NEGATIVE_Z' + constraint.up_axis = 'UP_X' + constraint.owner_space = 'LOCAL' + bpy.context.scene.objects.link(ob) + ob.rotation_euler[2] = 0.785398 + ob.data.energy = 0.3 + + if self.bBackLight: + backLight = bpy.data.lamps.new(name="Back_Light", type="SPOT") + ob = bpy.data.objects.new("Back_Light", backLight) + constraint = ob.constraints.new(type='COPY_LOCATION') + constraint.use_offset = True + constraint.owner_space = 'LOCAL' + constraint.target = self.camera + constraint = ob.constraints.new(type='TRACK_TO') + constraint.target = self.target + constraint.track_axis = 'TRACK_NEGATIVE_Z' + constraint.up_axis = 'UP_X' + constraint.owner_space = 'LOCAL' + bpy.context.scene.objects.link(ob) + ob.rotation_euler[2] = 3.14159 + ob.data.energy = 0.2 + + if self.camera_constraint and self.camera is not None and \ + self.camera.type == "CAMERA": + + constraint = self.camera.constraints.new(type='TRACK_TO') + constraint.target = self.target + constraint.track_axis = 'TRACK_NEGATIVE_Z' + constraint.up_axis = 'UP_Y' class OBJECT_OT_add_light_template(Operator): bl_idname = "object.add_light_template" bl_label = "Add Light Template" - bl_description = "Add Key, Fill & Back Lights" + bl_description = ("Add Key, Fill and Back Lights to the Scene\n" + "Needs an existing Active Object") bl_options = {'REGISTER', 'UNDO'} camera = None target = None bKeyLight = BoolProperty( - name="Key Light", - default=True - ) + name="Key Light", + description="Enable Key Light in the Scene", + default=True + ) bFillLight = BoolProperty( - name="Fill Light", - default=True - ) + name="Fill Light", + description="Enable Fill Light in the Scene", + default=True + ) bBackLight = BoolProperty( - name="Back Light", - default=True - ) + name="Back Light", + description="Enable Back Light in the Scene", + default=True + ) camera_constraint = BoolProperty( - name="Camera Constraint", - default=False - ) + name="Camera Constraint", + description="Add a Constraint to the Camera Object", + default=False + ) @classmethod def poll(cls, context): return context.active_object is not None def execute(self, context): - objects = context.selected_objects - - if len(objects) == 2: - for ob in objects: - if ob.type == 'CAMERA': - self.camera = ob + try: + objects = context.selected_objects + + if len(objects) == 2: + for ob in objects: + if ob.type == 'CAMERA': + self.camera = ob + else: + self.target = ob + elif len(objects) == 1: + if objects[0].type == 'CAMERA': + self.camera = objects[0] + bpy.ops.object.empty_add() + self.target = context.active_object else: - self.target = ob - elif len(objects) == 1: - if objects[0].type == 'CAMERA': - self.camera = objects[0] + self.camera = context.scene.camera + self.target = context.active_object + elif len(objects) == 0: bpy.ops.object.empty_add() self.target = context.active_object - else: self.camera = context.scene.camera - self.target = context.active_object - elif len(objects) == 0: - bpy.ops.object.empty_add() - self.target = context.active_object - self.camera = context.scene.camera - add_lamps(self, context) + add_lamps(self, context) + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "object.add_light_template\nError: {}".format(e)) return {'FINISHED'} diff --git a/add_advanced_objects/add_mesh_aggregate.py b/add_advanced_objects/add_mesh_aggregate.py index e2ac5f07..6072cb9c 100644 --- a/add_advanced_objects/add_mesh_aggregate.py +++ b/add_advanced_objects/add_mesh_aggregate.py @@ -72,107 +72,108 @@ class OBJECT_OT_agregate_mesh(Operator): bl_options = {'REGISTER', 'UNDO', 'PRESET'} volX = FloatProperty( - name="Volume X", - min=0.1, max=25, - default=3, - description="The cloud around cursor" - ) + name="Volume X", + min=0.1, max=25, + default=3, + description="The cloud around cursor" + ) volY = FloatProperty( - name="Volume Y", - min=0.1, max=25, - default=3, - description="The cloud around cursor" - ) + name="Volume Y", + min=0.1, max=25, + default=3, + description="The cloud around cursor" + ) volZ = FloatProperty( - name="Volume Z", - min=0.1, max=25, - default=3, - description="The cloud around cursor" - ) + name="Volume Z", + min=0.1, max=25, + default=3, + description="The cloud around cursor" + ) baseSca = FloatProperty( - name="Scale", - min=0.01, max=5, - default=.25, - description="Particle Scale" - ) + name="Scale", + min=0.01, max=5, + default=.25, + description="Particle Scale" + ) varSca = FloatProperty( - name="Var", - min=0, max=1, - default=0, - description="Particle Scale Variation" - ) + name="Var", + min=0, max=1, + default=0, + description="Particle Scale Variation" + ) rotX = FloatProperty( - name="Rot Var X", - min=0, max=2, - default=0, - description="X Rotation Variation" - ) + name="Rot Var X", + min=0, max=2, + default=0, + description="X Rotation Variation" + ) rotY = FloatProperty( - name="Rot Var Y", - min=0, max=2, - default=0, - description="Y Rotation Variation" - ) + name="Rot Var Y", + min=0, max=2, + default=0, + description="Y Rotation Variation" + ) rotZ = FloatProperty( - name="Rot Var Z", - min=0, max=2, - default=1, - description="Z Rotation Variation" - ) + name="Rot Var Z", + min=0, max=2, + default=1, + description="Z Rotation Variation" + ) rSeed = IntProperty( - name="Random seed", - min=0, max=999999, - default=1, - description="Seed to feed random values" - ) + name="Random seed", + min=0, max=999999, + default=1, + description="Seed to feed random values" + ) numP = IntProperty( - name="Number", - min=1, - max=9999, soft_max=500, - default=50, - description="Number of particles" - ) + name="Number", + min=1, + max=9999, soft_max=500, + default=50, + description="Number of particles" + ) nor = BoolProperty( - name="Normal Oriented", - default=False, - description="Align Z axis with Faces normals" - ) + name="Normal Oriented", + default=False, + description="Align Z axis with Faces normals" + ) cent = BoolProperty( - name="Use Face Center", - default=False, - description="Center on Faces" - ) + name="Use Face Center", + default=False, + description="Center on Faces" + ) track = BoolProperty( - name="Cursor Follows", - default=False, - description="Cursor moves as structure grows / more compact results" - ) + name="Cursor Follows", + default=False, + description="Cursor moves as structure grows / more compact results" + ) anim = BoolProperty( - name="Animatable", - default=False, - description="Sort faces so you can regrow with Build Modifier, materials are lost" - ) - - refresh = bpy.props.BoolProperty( - name="Update", - default=False - ) - auto_refresh = bpy.props.BoolProperty( - name="Auto", - description="Auto update spline", - default=False - ) + name="Animatable", + default=False, + description="Sort faces so you can regrow with Build Modifier, materials are lost" + ) + refresh = BoolProperty( + name="Update", + default=False + ) + auto_refresh = BoolProperty( + name="Auto", + description="Auto update spline", + default=False + ) def draw(self, context): layout = self.layout col = layout.column(align=True) row = col.row(align=True) + if self.auto_refresh is False: self.refresh = False elif self.auto_refresh is True: self.refresh = True - row.prop(self, 'auto_refresh', toggle=True, icon='AUTO') - row.prop(self, 'refresh', toggle=True, icon='FILE_REFRESH') + + row.prop(self, "auto_refresh", toggle=True, icon="AUTO") + row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH") col = layout.column(align=True) col.separator() @@ -194,18 +195,20 @@ class OBJECT_OT_agregate_mesh(Operator): col = layout.column(align=True) col.prop(self, "rSeed", slider=False) + col.prop(self, "numP") - col = layout.column(align=True) - col.prop(self, "nor") - col.prop(self, "cent") - col.prop(self, "track") - col.prop(self, "anim") + row = layout.row(align=True) + row.prop(self, "nor") + row.prop(self, "cent") - col.prop(self, 'numP') + row = layout.row(align=True) + row.prop(self, "track") + row.prop(self, "anim") @classmethod def poll(cls, context): - return(len(bpy.context.selected_objects) > 1 and bpy.context.object.type == 'MESH') + return (len(bpy.context.selected_objects) > 1 and + bpy.context.object.type == 'MESH') def invoke(self, context, event): self.refresh = True @@ -227,8 +230,10 @@ class OBJECT_OT_agregate_mesh(Operator): (0, 0, 0, 1)) ) if obj.matrix_world != mat: - self.report({'WARNING'}, "Apply transformations to Active Object first!") + self.report({'WARNING'}, + "Please, Apply transformations to Active Object first") return{'FINISHED'} + par = [o for o in bpy.context.selected_objects if o.type == 'MESH' and o != obj] if not par: return{'FINISHED'} @@ -296,9 +301,8 @@ class OBJECT_OT_agregate_mesh(Operator): bme.to_mesh(obj.data) remover(True) - - newobj.data.user_clear() - bpy.data.meshes.remove(newobj.data) + # Note: foo.user_clear() is deprecated use do_unlink=True instead + bpy.data.meshes.remove(newobj.data, do_unlink=True) else: scn.objects.active = obj @@ -318,8 +322,6 @@ class OBJECT_OT_agregate_mesh(Operator): if self.auto_refresh is False: self.refresh = False - #elif self.auto_refresh is True: - # self.refresh = True return{'FINISHED'} @@ -333,4 +335,4 @@ def unregister(): if __name__ == '__main__': - register()
\ No newline at end of file + register() diff --git a/add_advanced_objects/arrange_on_curve.py b/add_advanced_objects/arrange_on_curve.py index 9894de12..14017480 100644 --- a/add_advanced_objects/arrange_on_curve.py +++ b/add_advanced_objects/arrange_on_curve.py @@ -9,10 +9,12 @@ bl_info = { "description": "Arrange objects along a curve", "warning": "Select curve", "wiki_url": "", - "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", "category": "3D View" } +# Note: scene properties are moved into __init__ +# search for patterns advanced_objects and adv_obj + import bpy import mathutils from bpy.types import ( @@ -20,11 +22,9 @@ from bpy.types import ( Panel, ) from bpy.props import ( - BoolProperty, EnumProperty, FloatProperty, IntProperty, - StringProperty, ) FLT_MIN = 0.004 @@ -44,82 +44,90 @@ class PanelDupliCurve(Panel): def draw(self, context): layout = self.layout - layout.prop(context.scene, "use_selected") - if not context.scene.use_selected: - layout.prop(context.scene, "select_type", expand=True) - if context.scene.select_type == 'O': + adv_obj = context.scene.advanced_objects + + layout.prop(adv_obj, "arrange_c_use_selected") + + if not adv_obj.arrange_c_use_selected: + layout.prop(adv_obj, "arrange_c_select_type", expand=True) + if adv_obj.arrange_c_select_type == 'O': layout.column(align=True).prop_search( - context.scene, "objeto_arranjar", - bpy.data, "objects" - ) - elif context.scene.select_type == 'G': + adv_obj, "arrange_c_obj_arranjar", + bpy.data, "objects" + ) + elif adv_obj.arrange_c_select_type == 'G': layout.column(align=True).prop_search( - context.scene, "objeto_arranjar", - bpy.data, "groups" - ) + adv_obj, "arrange_c_obj_arranjar", + bpy.data, "groups" + ) if context.object.type == 'CURVE': layout.operator("object.arranjar_numa_curva", text="Arrange Objects") class DupliCurve(Operator): bl_idname = "object.arranjar_numa_curva" - bl_label = "Arrange Objects" + bl_label = "Arrange Objects along a Curve" + bl_description = "Arange chosen / selected objects along the Active Curve" bl_options = {'REGISTER', 'UNDO'} use_distance = EnumProperty( - items=[ - ("D", "Distance", "Objects are arranged depending on the distance", 0), - ("Q", "Quantity", "Objects are arranged depending on the quantity", 1), - ("R", "Range", "Objects are arranged uniformly between the corners", 2) - ] - ) + name="Arrangement", + items=[ + ("D", "Distance", "Objects are arranged depending on the distance", 0), + ("Q", "Quantity", "Objects are arranged depending on the quantity", 1), + ("R", "Range", "Objects are arranged uniformly between the corners", 2) + ] + ) distance = FloatProperty( - name="Distance", - description="Distancia entre objetos", - default=1.0, - min=FLT_MIN, - soft_min=0.1, - unit='LENGTH', - ) + name="Distance", + description="Distance between Objects", + default=1.0, + min=FLT_MIN, + soft_min=0.1, + unit='LENGTH', + ) object_qt = IntProperty( - name="Quantity", - description="Object amount.", - default=2, - min=0, - ) + name="Quantity", + description="Object amount", + default=2, + min=0, + ) scale = FloatProperty( - name="Scale", - description="Object Scale", - default=1.0, - min=FLT_MIN, - unit='LENGTH', + name="Scale", + description="Object Scale", + default=1.0, + min=FLT_MIN, + unit='LENGTH', ) Yaw = FloatProperty( - default=0.0, - name="X", - unit='ROTATION' - ) + name="X", + description="Rotate around the X axis (Yaw)", + default=0.0, + unit='ROTATION' + ) Pitch = FloatProperty( - default=0.0, - name="Y", - unit='ROTATION' - ) + default=0.0, + description="Rotate around the Y axis (Pitch)", + name="Y", + unit='ROTATION' + ) Roll = FloatProperty( - default=0.0, - name="Z", - unit='ROTATION' - ) + default=0.0, + description="Rotate around the Z axis (Roll)", + name="Z", + unit='ROTATION' + ) max_angle = FloatProperty( - default=1.57079, - max=3.141592, - name="Angle", - unit='ROTATION' - ) + default=1.57079, + max=3.141592, + name="Angle", + unit='ROTATION' + ) offset = FloatProperty( - default=0.0, - name="offset", - unit='LENGTH' - ) + default=0.0, + name="Offset", + unit='LENGTH' + ) @classmethod def poll(cls, context): @@ -191,7 +199,8 @@ class DupliCurve(Operator): tmp_Gpoints.append(tuple(sp2)) sp2 = [lp] except Exception as e: - print(e) + print("\n[Add Advanced Objects]\nOperator: " + "object.arranjar_numa_curva\nError: {}".format(e)) pass sp2.append(p) v1 = v2 @@ -216,21 +225,25 @@ class DupliCurve(Operator): curve = context.active_object Gpoints, lengs = self.Glpoints(curve) + adv_obj = context.scene.advanced_objects - if context.scene.use_selected: + if adv_obj.arrange_c_use_selected: G_Objeto = context.selected_objects G_Objeto.remove(curve) + if not G_Objeto: return {'CANCELLED'} - elif context.scene.select_type == 'O': - G_Objeto = bpy.data.objects[context.scene.objeto_arranjar], - elif context.scene.select_type == 'G': - G_Objeto = bpy.data.groups[context.scene.objeto_arranjar].objects + + elif adv_obj.arrange_c_select_type == 'O': + G_Objeto = bpy.data.objects[adv_obj.arrange_c_obj_arranjar], + elif adv_obj.arrange_c_select_type == 'G': + G_Objeto = bpy.data.groups[adv_obj.arrange_c_obj_arranjar].objects + yawMatrix = mathutils.Matrix.Rotation(self.Yaw, 4, 'X') pitchMatrix = mathutils.Matrix.Rotation(self.Pitch, 4, 'Y') rollMatrix = mathutils.Matrix.Rotation(self.Roll, 4, 'Z') - max_angle = self.max_angle # is this used? + max_angle = self.max_angle # max_angle is called in Glpoints if self.use_distance == "D": dist = self.distance @@ -331,31 +344,11 @@ class DupliCurve(Operator): def register(): bpy.utils.register_class(PanelDupliCurve) bpy.utils.register_class(DupliCurve) - bpy.types.Scene.use_selected = BoolProperty( - name='Use Selected', - description='Use the selected objects to duplicate', - default=True, - ) - bpy.types.Scene.objeto_arranjar = StringProperty( - name="" - ) - bpy.types.Scene.select_type = EnumProperty( - name="Type", - description="Select object or group", - items=[ - ('O', "OBJECT", "make duplicates of a specific object"), - ('G', "GROUP", "make duplicates of the objects in a group"), - ], - default='O', - ) def unregister(): bpy.utils.unregister_class(PanelDupliCurve) bpy.utils.unregister_class(DupliCurve) - del bpy.types.Scene.objeto_arranjar - del bpy.types.Scene.use_selected - del bpy.types.Scene.select_type if __name__ == "__main__": diff --git a/add_advanced_objects/circle_array.py b/add_advanced_objects/circle_array.py index 170ea7a6..af5a6a0a 100644 --- a/add_advanced_objects/circle_array.py +++ b/add_advanced_objects/circle_array.py @@ -5,14 +5,13 @@ bl_info = { "name": "Circle Array", "author": "Antonis Karvelas", - "version": (1, 0), + "version": (1, 0, 1), "blender": (2, 6, 7), "location": "View3D > Object > Circle_Array", "description": "Uses an existing array and creates an empty, " "rotates it properly and makes a Circle Array", "warning": "", "wiki_url": "", - "tracker_url": "", "category": "Mesh" } @@ -26,7 +25,10 @@ class Circle_Array(Operator): bl_label = "Circle Array" bl_idname = "objects.circle_array_operator" bl_description = ("Creates an Array Modifier with offset empty object\n" - "Works with Mesh, Curve, Text & Surface") + "Works with Mesh, Curve, Text and Surface\n" + "Use an object with an existing Array modifier\n" + "or rotate the newly created Empty with the name pattern\n" + "EMPTY_C_Array_ if the Array doesn't exist (angle: 360/Count)") @classmethod def poll(cls, context): @@ -53,27 +55,52 @@ class Circle_Array(Operator): return None def execute(self, context): + is_allowed = True try: allowed_obj = ['MESH', 'CURVE', 'SURFACE', 'FONT'] - if context.active_object.type not in allowed_obj: + for obj in context.selected_objects: + if obj.type not in allowed_obj: + is_allowed = False + break + + if not is_allowed: self.report( {"WARNING"}, - "Operation Cancelled. The active object is not of " - "Mesh, Curve, Surface or Font type" + "The Active/Selected objects are not of " + "Mesh, Curve, Surface or Font type. Operation Cancelled" ) return {'CANCELLED'} default_name = self.check_empty_name(context) or "EMPTY_C_Array" bpy.ops.object.modifier_add(type='ARRAY') - if len(bpy.context.selected_objects) == 2: - list = bpy.context.selected_objects - active = list[0] + if len(context.selected_objects) == 2: + selected = context.selected_objects + lists = [obj for obj in selected if obj != context.active_object] + active = lists[0] + # check if the list object has a modifier + check_mod = None + for mod in active.modifiers[:]: + if mod.type == "ARRAY": + check_mod = mod + break + + if check_mod: + check_mod.use_object_offset = True + check_mod.use_relative_offset = False + else: + # fallback + bpy.context.scene.objects.active = active + bpy.ops.object.modifier_add(type='ARRAY') + active.modifiers[0].use_object_offset = True + active.modifiers[0].use_relative_offset = False + active.modifiers[0].use_object_offset = True active.modifiers[0].use_relative_offset = False active.select = False - bpy.context.scene.objects.active = list[0] + bpy.context.scene.objects.active = context.active_object bpy.ops.view3d.snap_cursor_to_selected() + if active.modifiers[0].offset_object is None: bpy.ops.object.add(type='EMPTY') empty_name = bpy.context.active_object @@ -98,6 +125,7 @@ class Circle_Array(Operator): active.modifiers[0].use_object_offset = True active.modifiers[0].use_relative_offset = False bpy.ops.view3d.snap_cursor_to_selected() + if active.modifiers[0].offset_object is None: bpy.ops.object.add(type='EMPTY') empty_name = bpy.context.active_object @@ -116,6 +144,7 @@ class Circle_Array(Operator): active.select = True return {'FINISHED'} + except Exception as e: self.report({'WARNING'}, "Circle Array operator could not be executed (See the console for more info)") @@ -125,18 +154,12 @@ class Circle_Array(Operator): # Register -def circle_array_menu(self, context): - self.layout.operator(Circle_Array.bl_idname, text="Circle_Array") - - def register(): bpy.utils.register_class(Circle_Array) - bpy.types.VIEW3D_MT_object.append(circle_array_menu) def unregister(): bpy.utils.unregister_class(Circle_Array) - bpy.types.VIEW3D_MT_object.remove(circle_array_menu) if __name__ == "__main__": diff --git a/add_advanced_objects/copy2.py b/add_advanced_objects/copy2.py index 3afdef22..489f6dee 100644 --- a/add_advanced_objects/copy2.py +++ b/add_advanced_objects/copy2.py @@ -1,4 +1,4 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** +# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,12 +15,12 @@ # or write to the Free Software Foundation, Inc., 51 Franklin Street, # Fifth Floor, Boston, MA 02110-1301, USA. # -# ***** END GPL LICENSE BLOCK ***** +# ##### END GPL LICENSE BLOCK ##### bl_info = { - "name": "Copy2 vertices, edges or faces", + "name": "Copy2 Vertices, Edges or Faces", "author": "Eleanor Howick (elfnor.com)", - "version": (0, 1), + "version": (0, 1, 1), "blender": (2, 71, 0), "location": "3D View > Object > Copy 2", "description": "Copy one object to the selected vertices, edges or faces of another object", @@ -29,13 +29,23 @@ bl_info = { } import bpy - -from mathutils import Vector, Matrix - - -class Copy2(bpy.types.Operator): +from bpy.types import Operator +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + ) +from mathutils import ( + Vector, + Matrix, + ) + + +class Copy2(Operator): bl_idname = "mesh.copy2" bl_label = "Copy 2" + bl_description = ("Copy Vertices, Edges or Faces to the Selected object\n" + "Needs an existing Active Mesh Object") bl_options = {"REGISTER", "UNDO"} obj_list = None @@ -45,84 +55,126 @@ class Copy2(bpy.types.Operator): def sec_axes_list_cb(self, context): if self.priaxes == 'X': - sec_list = [('Y', 'Y', 'Y'), ('Z', 'Z', 'Z')] + sec_list = [('Y', "Y", "Secondary axis Y"), + ('Z', "Z", "Secondary axis Z")] if self.priaxes == 'Y': - sec_list = [('X', 'X', 'X'), ('Z', 'Z', 'Z')] + sec_list = [('X', "X", "Secondary axis X"), + ('Z', "Z", "Secondary axis Z")] if self.priaxes == 'Z': - sec_list = [('X', 'X', 'X'), ('Y', 'Y', 'Y')] + sec_list = [('X', "X", "Secondary axis X"), + ('Y', "Y", "Secondary axis Y")] return sec_list - copytype = bpy.props.EnumProperty(items=(('V', '', 'paste to vertices', 'VERTEXSEL', 0), - ('E', '', 'paste to edges', 'EDGESEL', 1), - ('F', '', 'paste to faces', 'FACESEL', 2)), - description='where to paste to') - - copyfromobject = bpy.props.EnumProperty(items=obj_list_cb, name='Copy from:') - - priaxes = bpy.props.EnumProperty(items=(('X', 'X', 'along X'), - ('Y', 'Y', 'along Y'), - ('Z', 'Z', 'along Z')), - ) - - edgescale = bpy.props.BoolProperty(name='Scale to fill edge?') - - secaxes = bpy.props.EnumProperty(items=sec_axes_list_cb, name='Secondary Axis') - - scale = bpy.props.FloatProperty(default=1.0, min=0.0, name='Scale') + copytype = EnumProperty( + items=(('V', "Vertex", + "Paste the Copied Geometry to Vertices of the Active Object", 'VERTEXSEL', 0), + ('E', "Edge", + "Paste the Copied Geometry to Edges of the Active Object", 'EDGESEL', 1), + ('F', "Face", + "Paste the Copied Geometry to Faces of the Active Object", 'FACESEL', 2)), + ) + copyfromobject = EnumProperty( + name="Copy from", + description="Copy an Object from the list", + items=obj_list_cb + ) + priaxes = EnumProperty( + description="Primary axes used for Copied Object orientation", + items=(('X', "X", "Along X"), + ('Y', "Y", "Along Y"), + ('Z', "Z", "Along Z")), + ) + edgescale = BoolProperty( + name="Scale to fill edge", + default=False + ) + secaxes = EnumProperty( + name="Secondary Axis", + description="Secondary axis used for Copied Object orientation", + items=sec_axes_list_cb + ) + scale = FloatProperty( + name="Scale", + default=1.0, + min=0.0, + ) + + @classmethod + def poll(cls, context): + obj = context.active_object + return obj and obj.type == "MESH" def draw(self, context): layout = self.layout - layout.prop(self, 'copyfromobject') + layout.prop(self, "copyfromobject") layout.label("to:") - layout.prop(self, 'copytype', expand=True) - layout.label("primary axis:") - layout.prop(self, 'priaxes', expand=True) - layout.label("secondary axis:") - layout.prop(self, 'secaxes', expand=True) - if self.copytype == 'E': - layout.prop(self, 'edgescale') + layout.prop(self, "copytype", expand=True) + layout.label("Primary axis:") + layout.prop(self, "priaxes", expand=True) + layout.label("Secondary axis:") + layout.prop(self, "secaxes", expand=True) + if self.copytype == "E": + layout.prop(self, "edgescale") if self.edgescale: - layout.prop(self, 'scale') + layout.prop(self, "scale") return def execute(self, context): copytoobject = context.active_object.name axes = self.priaxes + self.secaxes - copy_list = copy_to_from(context.scene, - bpy.data.objects[copytoobject], - bpy.data.objects[self.copyfromobject], - self.copytype, - axes, - self.edgescale, - self.scale) + + # check if there is a problem with the strings related to some chars + copy_to_object = bpy.data.objects[copytoobject] if \ + copytoobject in bpy.data.objects else None + + copy_from_object = bpy.data.objects[self.copyfromobject] if \ + self.copyfromobject in bpy.data.objects else None + + if copy_to_object is None or copy_from_object is None: + self.report({"WARNING"}, + "There was a problem with retrieving Object data. Operation Cancelled") + return {"CANCELLED"} + try: + copy_to_from( + context.scene, + copy_to_object, + copy_from_object, + self.copytype, + axes, + self.edgescale, + self.scale + ) + except Exception as e: + self.report({"WARNING"}, + "Copy2 could not be completed (Check the Console for more info)") + print("\n[Add Advanced Objects]\nOperator: mesh.copy2\n{}\n".format(e)) + + return {"CANCELLED"} + return {"FINISHED"} def invoke(self, context, event): Copy2.obj_list = [(obj.name, obj.name, obj.name) for obj in bpy.data.objects] - return {"FINISHED"} -# end Copy2 class - -#--------------------------------------------------------------------------------------- - - -def add_to_menu(self, context): - self.layout.operator(Copy2.bl_idname) - return + return {"FINISHED"} -#----------------------------------------------------------------- def copy_to_from(scene, to_obj, from_obj, copymode, axes, edgescale, scale): if copymode == 'V': - copy_list = vertex_copy(scene, to_obj, from_obj, axes) + vertex_copy(scene, to_obj, from_obj, axes) + if copymode == 'E': - copy_list = edge_copy(scene, to_obj, from_obj, axes, edgescale, scale) + # don't pass edgescalling to object types that cannot be scaled + if from_obj.type in ["CAMERA", "LAMP", "EMPTY", "ARMATURE", "SPEAKER", "META"]: + edgescale = False + edge_copy(scene, to_obj, from_obj, axes, edgescale, scale) + if copymode == 'F': - copy_list = face_copy(scene, to_obj, from_obj, axes) - return copy_list + face_copy(scene, to_obj, from_obj, axes) + axes_dict = {'XY': (1, 2, 0), 'XZ': (2, 1, 0), @@ -133,9 +185,9 @@ axes_dict = {'XY': (1, 2, 0), def copyto(scene, source_obj, pos, xdir, zdir, axes, scale=None): - """ - copy the source_obj to pos, so its primary axis points in zdir and its - secondary axis points in xdir + """ + copy the source_obj to pos, so its primary axis points in zdir and its + secondary axis points in xdir """ copy_obj = source_obj.copy() scene.objects.link(copy_obj) @@ -161,7 +213,7 @@ def copyto(scene, source_obj, pos, xdir, zdir, axes, scale=None): copy_obj.location = pos # scale object - if scale != None: + if scale is not None: copy_obj.scale = scale return copy_obj @@ -171,8 +223,9 @@ def vertex_copy(scene, obj, source_obj, axes): # vertex select mode sel_verts = [] copy_list = [] + for v in obj.data.vertices: - if v.select == True: + if v.select is True: sel_verts.append(v) # make a set for each vertex. The set contains all the connected vertices @@ -198,20 +251,22 @@ def vertex_copy(scene, obj, source_obj, axes): copy = copyto(scene, source_obj, pos, xdir, zdir, axes) copy_list.append(copy) + # select all copied objects for copy in copy_list: copy.select = True obj.select = False - return copy_list def edge_copy(scene, obj, source_obj, axes, es, scale): # edge select mode sel_edges = [] copy_list = [] + for e in obj.data.edges: - if e.select == True: + if e.select is True: sel_edges.append(e) + for e in sel_edges: # pos is average of two edge vertexs v0 = obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed() @@ -222,10 +277,10 @@ def edge_copy(scene, obj, source_obj, axes, es, scale): xlen = xdir.magnitude xdir = xdir.normalized() # project each edge vertex normal onto plane normal to xdir - vn0 = (obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed() - + obj.data.vertices[e.vertices[0]].normal) - v0 - vn1 = (obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed() - + obj.data.vertices[e.vertices[1]].normal) - v1 + vn0 = (obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed() + + obj.data.vertices[e.vertices[0]].normal) - v0 + vn1 = (obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed() + + obj.data.vertices[e.vertices[1]].normal) - v1 vn0p = vn0 - vn0.dot(xdir) * xdir vn1p = vn1 - vn1.dot(xdir) * xdir # the mean of the two projected normals is the zdir @@ -239,20 +294,22 @@ def edge_copy(scene, obj, source_obj, axes, es, scale): copy = copyto(scene, source_obj, pos, xdir, zdir, axes, scale=escale) copy_list.append(copy) + # select all copied objects for copy in copy_list: copy.select = True obj.select = False - return copy_list def face_copy(scene, obj, source_obj, axes): # face select mode sel_faces = [] copy_list = [] + for f in obj.data.polygons: - if f.select == True: + if f.select is True: sel_faces.append(f) + for f in sel_faces: fco = f.center * obj.matrix_world.transposed() # get first vertex corner of transformed object @@ -263,25 +320,19 @@ def face_copy(scene, obj, source_obj, axes): copy = copyto(scene, source_obj, fco, vco - fco, fn, axes) copy_list.append(copy) + # select all copied objects for copy in copy_list: copy.select = True obj.select = False - return copy_list - -#------------------------------------------------------------------- def register(): - - bpy.utils.register_module(__name__) - bpy.types.VIEW3D_MT_object.append(add_to_menu) + bpy.utils.register_class(Copy2) def unregister(): - - bpy.types.VIEW3D_MT_object.remove(add_to_menu) - bpy.utils.unregister_module(__name__) + bpy.utils.unregister_class(Copy2) if __name__ == "__main__": diff --git a/add_advanced_objects/cubester.py b/add_advanced_objects/cubester.py index 83053ad1..1a516bd0 100644 --- a/add_advanced_objects/cubester.py +++ b/add_advanced_objects/cubester.py @@ -19,10 +19,13 @@ # Original Author = Jacob Morris # URL = blendingjacob.blogspot.com +# Note: scene properties are moved into __init__ together with the 3 update functions +# for properties search for the name patterns adv_obj and advanced_objects + bl_info = { "name": "CubeSter", "author": "Jacob Morris", - "version": (0, 7), + "version": (0, 7, 1), "blender": (2, 78, 0), "location": "View 3D > Toolbar > CubeSter", "description": "Takes image, image sequence, or audio file and converts it " @@ -32,13 +35,6 @@ bl_info = { import bpy import bmesh -from bpy.props import ( - BoolProperty, - IntProperty, - FloatProperty, - StringProperty, - EnumProperty, - ) from bpy.types import ( Operator, Panel, @@ -53,29 +49,9 @@ from os import ( ) -# load image if possible -def adjust_selected_image(self, context): - scene = context.scene - try: - image = bpy.data.images.load(scene.cubester_load_image) - scene.cubester_image = image.name - except RuntimeError: - self.report({"ERROR"}, "CubeSter: '{}' could not be loaded".format(scene.cubester_load_image)) - - -# load color image if possible -def adjust_selected_color_image(self, context): - scene = context.scene - try: - image = bpy.data.images.load(scene.cubester_load_color_image) - scene.cubester_color_image = image.name - except RuntimeError: - self.report({"ERROR"}, "CubeSter: '{}' could not be loaded".format(scene.cubester_load_color_image)) - - -# crate block at center position x, y with block width 2 * hx and 2 * hy and height of h +# create block at center position x, y with block width 2 * hx and 2 * hy and height of h def create_block(x, y, hw, h, verts: list, faces: list): - if bpy.context.scene.cubester_block_style == "size": + if bpy.context.scene.advanced_objects.cubester_block_style == "size": z = 0.0 else: z = h @@ -134,12 +110,20 @@ def create_f_curves(mesh, frames, frame_step_size, style): # create material with given name, apply to object def create_material(scene, ob, name): mat = bpy.data.materials.new("CubeSter_" + name) + adv_obj = scene.advanced_objects + image = None # image - if not scene.cubester_use_image_color and scene.cubester_color_image in bpy.data.images: - image = bpy.data.images[scene.cubester_color_image] + if not adv_obj.cubester_use_image_color and adv_obj.cubester_color_image in bpy.data.images: + try: + image = bpy.data.images[adv_obj.cubester_color_image] + except: + pass else: - image = bpy.data.images[scene.cubester_image] + try: + image = bpy.data.images[adv_obj.cubester_image] + except: + pass if scene.render.engine == "CYCLES": mat.use_nodes = True @@ -150,16 +134,17 @@ def create_material(scene, ob, name): att.location = (-200, 300) att = nodes.new("ShaderNodeTexImage") - att.image = image + if image: + att.image = image - if scene.cubester_load_type == "multiple": + if adv_obj.cubester_load_type == "multiple": att.image.source = "SEQUENCE" att.location = (-200, 700) att = nodes.new("ShaderNodeTexCoord") att.location = (-450, 600) - if scene.cubester_materials == "image": + if adv_obj.cubester_materials == "image": mat.node_tree.links.new( nodes["Image Texture"].outputs[0], nodes["Diffuse BSDF"].inputs[0] @@ -174,9 +159,10 @@ def create_material(scene, ob, name): nodes["Diffuse BSDF"].inputs[0] ) else: - if scene.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER": + if adv_obj.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER": tex = bpy.data.textures.new("CubeSter_" + name, "IMAGE") - tex.image = image + if image: + tex.image = image slot = mat.texture_slots.add() slot.texture = tex else: @@ -187,10 +173,11 @@ def create_material(scene, ob, name): # generate mesh from audio def create_mesh_from_audio(self, scene, verts, faces): - audio_filepath = scene.cubester_audio_path - width = scene.cubester_audio_width_blocks - length = scene.cubester_audio_length_blocks - size_per_hundred = scene.cubester_size_per_hundred_pixels + adv_obj = scene.advanced_objects + audio_filepath = adv_obj.cubester_audio_path + width = adv_obj.cubester_audio_width_blocks + length = adv_obj.cubester_audio_length_blocks + size_per_hundred = adv_obj.cubester_size_per_hundred_pixels size = size_per_hundred / 100 @@ -213,8 +200,8 @@ def create_mesh_from_audio(self, scene, verts, faces): ob.select = True # inital vertex colors - if scene.cubester_materials == "image": - picture = bpy.data.images[scene.cubester_color_image] + if adv_obj.cubester_materials == "image" and adv_obj.cubester_color_image != "": + picture = bpy.data.images[adv_obj.cubester_color_image] pixels = list(picture.pixels) vert_colors = [] @@ -228,22 +215,25 @@ def create_mesh_from_audio(self, scene, verts, faces): vert_colors += [(r, g, b) for i in range(24)] bpy.ops.mesh.vertex_color_add() + i = 0 + vert_colors_size = len(vert_colors) for c in ob.data.vertex_colors[0].data: - c.color = vert_colors[i] - i += 1 + if i < vert_colors_size: + c.color = vert_colors[i] + i += 1 # image sequence handling - if scene.cubester_load_type == "multiple": + if adv_obj.cubester_load_type == "multiple": images = find_sequence_images(self, bpy.context) frames_vert_colors = [] - max_images = scene.cubester_max_images + 1 if \ - len(images[0]) > scene.cubester_max_images else len(images[0]) + max_images = adv_obj.cubester_max_images + 1 if \ + len(images[0]) > adv_obj.cubester_max_images else len(images[0]) # goes through and for each image for each block finds new height - for image_index in range(0, max_images, scene.cubester_skip_images): + for image_index in range(0, max_images, adv_obj.cubester_skip_images): filepath = images[0][image_index] name = images[1][image_index] picture = fetch_image(self, name, filepath) @@ -258,9 +248,10 @@ def create_mesh_from_audio(self, scene, verts, faces): frames_vert_colors.append(frame_colors) - scene.cubester_vertex_colors[ob.name] = {"type": "vertex", "frames": frames_vert_colors, - "frame_skip": scene.cubester_frame_step, - "total_images": max_images} + adv_obj.cubester_vertex_colors[ob.name] = \ + {"type": "vertex", "frames": frames_vert_colors, + "frame_skip": adv_obj.cubester_frame_step, + "total_images": max_images} # either add material or create if ("CubeSter_" + "Vertex") in bpy.data.materials: @@ -285,14 +276,14 @@ def create_mesh_from_audio(self, scene, verts, faces): for i in fcurves: i.select = False - max_images = scene.cubester_audio_max_freq - min_freq = scene.cubester_audio_min_freq - freq_frame = scene.cubester_audio_offset_type + max_images = adv_obj.cubester_audio_max_freq + min_freq = adv_obj.cubester_audio_min_freq + freq_frame = adv_obj.cubester_audio_offset_type freq_step = (max_images - min_freq) / length freq_sub_step = freq_step / width - frame_step = scene.cubester_audio_frame_offset + frame_step = adv_obj.cubester_audio_frame_offset # animate each block with a portion of the frequency for c in range(length): @@ -314,8 +305,10 @@ def create_mesh_from_audio(self, scene, verts, faces): for i in range(index, index + 4): curve = i * 3 + 2 # fcurve location fcurves[curve].select = True - - bpy.ops.graph.sound_bake(filepath=bpy.path.abspath(audio_filepath), low=l, high=h) + try: + bpy.ops.graph.sound_bake(filepath=bpy.path.abspath(audio_filepath), low=l, high=h) + except: + pass # deselect curves for i in range(index, index + 4): @@ -328,7 +321,7 @@ def create_mesh_from_audio(self, scene, verts, faces): create_uv_map(bpy.context, width, length) # if radial apply needed modifiers - if scene.cubester_audio_block_layout == "radial": + if adv_obj.cubester_audio_block_layout == "radial": # add bezier curve of correct width bpy.ops.curve.primitive_bezier_circle_add() curve = bpy.context.object @@ -358,14 +351,15 @@ def create_mesh_from_audio(self, scene, verts, faces): # generate mesh from image(s) def create_mesh_from_image(self, scene, verts, faces): context = bpy.context - picture = bpy.data.images[scene.cubester_image] + adv_obj = scene.advanced_objects + picture = bpy.data.images[adv_obj.cubester_image] pixels = list(picture.pixels) - x_pixels = picture.size[0] / (scene.cubester_skip_pixels + 1) - y_pixels = picture.size[1] / (scene.cubester_skip_pixels + 1) + x_pixels = picture.size[0] / (adv_obj.cubester_skip_pixels + 1) + y_pixels = picture.size[1] / (adv_obj.cubester_skip_pixels + 1) - width = x_pixels / 100 * scene.cubester_size_per_hundred_pixels - height = y_pixels / 100 * scene.cubester_size_per_hundred_pixels + width = x_pixels / 100 * adv_obj.cubester_size_per_hundred_pixels + height = y_pixels / 100 * adv_obj.cubester_size_per_hundred_pixels step = width / x_pixels half_width = step / 2 @@ -376,18 +370,18 @@ def create_mesh_from_image(self, scene, verts, faces): weights = [uniform(0.0, 1.0) for i in range(4)] # random weights rows = 0 - # go through each row of pixels stepping by scene.cubester_skip_pixels + 1 - for row in range(0, picture.size[1], scene.cubester_skip_pixels + 1): + # go through each row of pixels stepping by adv_obj.cubester_skip_pixels + 1 + for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1): rows += 1 x = -width / 2 + half_width # reset to left edge of mesh # go through each column, step by appropriate amount - for column in range(0, picture.size[0] * 4, 4 + scene.cubester_skip_pixels * 4): + for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4): r, g, b, a = get_pixel_values(picture, pixels, row, column) h = find_point_height(r, g, b, a, scene) # if not transparent if h != -1: - if scene.cubester_mesh_style == "blocks": + if adv_obj.cubester_mesh_style == "blocks": create_block(x, y, half_width, h, verts, faces) vert_colors += [(r, g, b) for i in range(24)] else: @@ -399,11 +393,11 @@ def create_mesh_from_image(self, scene, verts, faces): # if plane not blocks, then remove last 4 items from vertex_colors # as the faces have already wrapped around - if scene.cubester_mesh_style == "plane": + if adv_obj.cubester_mesh_style == "plane": del vert_colors[len(vert_colors) - 4:len(vert_colors)] # create faces if plane based and not block based - if scene.cubester_mesh_style == "plane": + if adv_obj.cubester_mesh_style == "plane": off = int(len(verts) / rows) for r in range(rows - 1): for c in range(off - 1): @@ -417,20 +411,21 @@ def create_mesh_from_image(self, scene, verts, faces): ob.select = True # uv unwrap - if scene.cubester_mesh_style == "blocks": + if adv_obj.cubester_mesh_style == "blocks": create_uv_map(context, rows, int(len(faces) / 6 / rows)) else: create_uv_map(context, rows - 1, int(len(faces) / (rows - 1))) # material # determine name and if already created - if scene.cubester_materials == "vertex": # vertex color + if adv_obj.cubester_materials == "vertex": # vertex color image_name = "Vertex" - elif not scene.cubester_use_image_color and scene.cubester_color_image in bpy.data.images and \ - scene.cubester_materials == "image": # replaced image - image_name = scene.cubester_color_image + elif not adv_obj.cubester_use_image_color and \ + adv_obj.cubester_color_image in bpy.data.images and \ + adv_obj.cubester_materials == "image": # replaced image + image_name = adv_obj.cubester_color_image else: # normal image - image_name = scene.cubester_image + image_name = adv_obj.cubester_image # either add material or create if ("CubeSter_" + image_name) in bpy.data.materials: @@ -449,15 +444,15 @@ def create_mesh_from_image(self, scene, verts, faces): frames = [] # image sequence handling - if scene.cubester_load_type == "multiple": + if adv_obj.cubester_load_type == "multiple": images = find_sequence_images(self, context) frames_vert_colors = [] - max_images = scene.cubester_max_images + 1 if \ - len(images[0]) > scene.cubester_max_images else len(images[0]) + max_images = adv_obj.cubester_max_images + 1 if \ + len(images[0]) > adv_obj.cubester_max_images else len(images[0]) # goes through and for each image for each block finds new height - for image_index in range(0, max_images, scene.cubester_skip_images): + for image_index in range(0, max_images, adv_obj.cubester_skip_images): filepath = images[0][image_index] name = images[1][image_index] picture = fetch_image(self, name, filepath) @@ -466,45 +461,50 @@ def create_mesh_from_image(self, scene, verts, faces): frame_heights = [] frame_colors = [] - for row in range(0, picture.size[1], scene.cubester_skip_pixels + 1): - for column in range(0, picture.size[0] * 4, 4 + scene.cubester_skip_pixels * 4): + for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1): + for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4): r, g, b, a = get_pixel_values(picture, pixels, row, column) h = find_point_height(r, g, b, a, scene) if h != -1: frame_heights.append(h) - if scene.cubester_mesh_style == "blocks": + if adv_obj.cubester_mesh_style == "blocks": frame_colors += [(r, g, b) for i in range(24)] else: frame_colors += [(r, g, b) for i in range(4)] - if scene.cubester_mesh_style == "plane": + if adv_obj.cubester_mesh_style == "plane": del vert_colors[len(vert_colors) - 4:len(vert_colors)] frames.append(frame_heights) frames_vert_colors.append(frame_colors) # determine what data to use - if scene.cubester_materials == "vertex" or scene.render.engine == "BLENDER_ENGINE": - scene.cubester_vertex_colors[ob.name] = { - "type": "vertex", "frames": frames_vert_colors, - "frame_skip": scene.cubester_frame_step, - "total_images": max_images - } + if adv_obj.cubester_materials == "vertex" or scene.render.engine == "BLENDER_ENGINE": + adv_obj.cubester_vertex_colors[ob.name] = { + "type": "vertex", "frames": frames_vert_colors, + "frame_skip": adv_obj.cubester_frame_step, + "total_images": max_images + } else: - scene.cubester_vertex_colors[ob.name] = { - "type": "image", "frame_skip": scene.cubester_frame_step, - "total_images": max_images - } + adv_obj.cubester_vertex_colors[ob.name] = { + "type": "image", "frame_skip": scene.cubester_frame_step, + "total_images": max_images + } att = get_image_node(ob.data.materials[0]) - att.image_user.frame_duration = len(frames) * scene.cubester_frame_step + att.image_user.frame_duration = len(frames) * adv_obj.cubester_frame_step # animate mesh - create_f_curves(mesh, frames, scene.cubester_frame_step, scene.cubester_mesh_style) + create_f_curves( + mesh, frames, + adv_obj.cubester_frame_step, + adv_obj.cubester_mesh_style + ) # generate uv map for object def create_uv_map(context, rows, columns): + adv_obj = context.scene.advanced_objects mesh = context.object.data mesh.uv_textures.new("cubester") bm = bmesh.new() @@ -521,7 +521,7 @@ def create_uv_map(context, rows, columns): count = columns - 1 # hold current count to compare to if need to go to next row # if blocks - if context.scene.cubester_mesh_style == "blocks": + if adv_obj.cubester_mesh_style == "blocks": for fa in range(int(len(bm.faces) / 6)): for i in range(6): pos = (fa * 6) + i @@ -555,33 +555,6 @@ def create_uv_map(context, rows, columns): bm.to_mesh(mesh) -# returns length in frames -def find_audio_length(self, context): - audio_file = context.scene.cubester_audio_path - length = 0 - - if audio_file != "": - # confirm that strip hasn't been loaded yet - for strip in context.scene.sequence_editor.sequences_all: - if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file: - length = strip.frame_final_duration - - if length == 0: - area = context.area - old_type = area.type - area.type = "SEQUENCE_EDITOR" - - bpy.ops.sequencer.sound_strip_add(filepath=audio_file) - area.type = old_type - - # find audio file - for strip in context.scene.sequence_editor.sequences_all: - if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file: - length = strip.frame_final_duration - - context.scene.cubester_audio_file_length = str(length) - - # if already loaded return image, else load and return def fetch_image(self, name, load_path): if name in bpy.data.images: @@ -597,19 +570,20 @@ def fetch_image(self, name, load_path): # find height for point def find_point_height(r, g, b, a, scene): + adv_obj = scene.advanced_objects if a: # if not completely transparent normalize = 1 # channel weighting - if not scene.cubester_advanced: + if not adv_obj.cubester_advanced: composed = 0.25 * r + 0.25 * g + 0.25 * b + 0.25 * a else: # user defined weighting - if not scene.cubester_random_weights: - composed = scene.cubester_weight_r * r + scene.cubester_weight_g * g + \ - scene.cubester_weight_b * b + scene.cubester_weight_a * a - total = scene.cubester_weight_r + scene.cubester_weight_g + scene.cubester_weight_b + \ - scene.cubester_weight_a + if not adv_obj.cubester_random_weights: + composed = adv_obj.cubester_weight_r * r + adv_obj.cubester_weight_g * g + \ + adv_obj.cubester_weight_b * b + adv_obj.cubester_weight_a * a + total = adv_obj.cubester_weight_r + adv_obj.cubester_weight_g + adv_obj.cubester_weight_b + \ + adv_obj.cubester_weight_a normalize = 1 / total # random weighting @@ -619,10 +593,10 @@ def find_point_height(r, g, b, a, scene): total = weights[0] + weights[1] + weights[2] + weights[3] normalize = 1 / total - if scene.cubester_invert: - h = (1 - composed) * scene.cubester_height_scale * normalize + if adv_obj.cubester_invert: + h = (1 - composed) * adv_obj.cubester_height_scale * normalize else: - h = composed * scene.cubester_height_scale * normalize + h = composed * adv_obj.cubester_height_scale * normalize return h else: @@ -634,8 +608,8 @@ def find_sequence_images(self, context): scene = context.scene images = [[], []] - if scene.cubester_image in bpy.data.images: - image = bpy.data.images[scene.cubester_image] + if scene.advanced_objects.cubester_image in bpy.data.images: + image = bpy.data.images[scene.advanced_objects.cubester_image] main = image.name.split(".")[0] # first part of name to check against other files @@ -685,15 +659,16 @@ def get_pixel_values(picture, pixels, row, column): # frame change handler for materials def material_frame_handler(scene): frame = scene.frame_current + adv_obj = scene.advanced_objects - keys = list(scene.cubester_vertex_colors.keys()) + keys = list(adv_obj.cubester_vertex_colors.keys()) # get keys and see if object is still in scene for i in keys: # if object is in scene then update information if i in bpy.data.objects: ob = bpy.data.objects[i] - data = scene.cubester_vertex_colors[ob.name] + data = adv_obj.advanced_objects.cubester_vertex_colors[ob.name] skip_frames = data["frame_skip"] # update materials using vertex colors @@ -716,183 +691,7 @@ def material_frame_handler(scene): # if the object is no longer in the scene then delete then entry else: - del scene.cubester_vertex_colors[i] - - -# main properties -bpy.types.Scene.cubester_audio_image = EnumProperty( - name="Input Type", - items=(("image", "Image", ""), - ("audio", "Audio", "")) - ) -bpy.types.Scene.cubester_audio_file_length = StringProperty( - default="" - ) -# audio -bpy.types.Scene.cubester_audio_path = StringProperty( - default="", - name="Audio File", - subtype="FILE_PATH", - update=find_audio_length - ) -bpy.types.Scene.cubester_audio_min_freq = IntProperty( - name="Minimum Frequency", - min=20, max=100000, - default=20 - ) -bpy.types.Scene.cubester_audio_max_freq = IntProperty( - name="Maximum Frequency", - min=21, max=999999, - default=5000 - ) -bpy.types.Scene.cubester_audio_offset_type = EnumProperty( - name="Offset Type", - items=(("freq", "Frequency Offset", ""), - ("frame", "Frame Offset", "")), - description="Type of offset per row of mesh" - ) -bpy.types.Scene.cubester_audio_frame_offset = IntProperty( - name="Frame Offset", - min=0, max=10, - default=2 - ) -bpy.types.Scene.cubester_audio_block_layout = EnumProperty( - name="Block Layout", - items=(("rectangle", "Rectangular", ""), - ("radial", "Radial", "")) - ) -bpy.types.Scene.cubester_audio_width_blocks = IntProperty( - name="Width Block Count", - min=1, max=10000, - default=5 - ) -bpy.types.Scene.cubester_audio_length_blocks = IntProperty( - name="Length Block Count", - min=1, max=10000, - default=50 - ) -# image -bpy.types.Scene.cubester_load_type = EnumProperty( - name="Image Input Type", - items=(("single", "Single Image", ""), - ("multiple", "Image Sequence", "")) - ) -bpy.types.Scene.cubester_image = StringProperty( - default="", - name="" - ) -bpy.types.Scene.cubester_load_image = StringProperty( - default="", - name="Load Image", - subtype="FILE_PATH", - update=adjust_selected_image - ) -bpy.types.Scene.cubester_skip_images = IntProperty( - name="Image Step", - min=1, max=30, - default=1, - description="Step from image to image by this number" - ) -bpy.types.Scene.cubester_max_images = IntProperty( - name="Max Number Of Images", - min=2, max=1000, - default=10, - description="Maximum number of images to be used" - ) -bpy.types.Scene.cubester_frame_step = IntProperty( - name="Frame Step Size", - min=1, max=10, - default=4, - description="The number of frames each picture is used" - ) -bpy.types.Scene.cubester_skip_pixels = IntProperty( - name="Skip # Pixels", - min=0, max=256, - default=64, - description="Skip this number of pixels before placing the next" - ) -bpy.types.Scene.cubester_mesh_style = EnumProperty( - name="Mesh Type", - items=(("blocks", "Blocks", ""), - ("plane", "Plane", "")), - description="Compose mesh of multiple blocks or of a single plane" - ) -bpy.types.Scene.cubester_block_style = EnumProperty( - name="Block Style", - items=(("size", "Vary Size", ""), - ("position", "Vary Position", "")), - description="Vary Z-size of block, or vary Z-position" - ) -bpy.types.Scene.cubester_height_scale = FloatProperty( - name="Height Scale", - subtype="DISTANCE", - min=0.1, max=2, - default=0.2 - ) -bpy.types.Scene.cubester_invert = BoolProperty( - name="Invert Height?", - default=False - ) -# general adjustments -bpy.types.Scene.cubester_size_per_hundred_pixels = FloatProperty( - name="Size Per 100 Blocks/Points", - subtype="DISTANCE", - min=0.001, max=5, - default=1 - ) -# material based stuff -bpy.types.Scene.cubester_materials = EnumProperty( - name="Material", - items=(("vertex", "Vertex Colors", ""), - ("image", "Image", "")), - description="Color with vertex colors, or uv unwrap and use an image" - ) -bpy.types.Scene.cubester_use_image_color = BoolProperty( - name="Use Original Image Colors'?", - default=True, - description="Use original image colors, or replace with other" - ) -bpy.types.Scene.cubester_color_image = StringProperty( - default="", name="" - ) -bpy.types.Scene.cubester_load_color_image = StringProperty( - default="", - name="Load Color Image", - subtype="FILE_PATH", - update=adjust_selected_color_image - ) -bpy.types.Scene.cubester_vertex_colors = {} -# advanced -bpy.types.Scene.cubester_advanced = BoolProperty( - name="Advanced Options?" - ) -bpy.types.Scene.cubester_random_weights = BoolProperty( - name="Random Weights?" - ) -bpy.types.Scene.cubester_weight_r = FloatProperty( - name="Red", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) -bpy.types.Scene.cubester_weight_g = FloatProperty( - name="Green", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) -bpy.types.Scene.cubester_weight_b = FloatProperty( - name="Blue", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) -bpy.types.Scene.cubester_weight_a = FloatProperty( - name="Alpha", - subtype="FACTOR", - min=0.01, max=1.0, - default=0.25 - ) + del adv_obj.advanced_objects.cubester_vertex_colors[i] class CubeSterPanel(Panel): @@ -907,127 +706,123 @@ class CubeSterPanel(Panel): def draw(self, context): layout = self.layout.box() scene = bpy.context.scene + adv_obj = scene.advanced_objects images_found = 0 rows = 0 columns = 0 - layout.prop(scene, "cubester_audio_image", icon="IMAGE_COL") - layout.separator() + layout.prop(adv_obj, "cubester_audio_image") - if scene.cubester_audio_image == "image": + if adv_obj.cubester_audio_image == "image": box = layout.box() - box.prop(scene, "cubester_load_type") + box.prop(adv_obj, "cubester_load_type") box.label("Image To Convert:") - box.prop_search(scene, "cubester_image", bpy.data, "images") - box.prop(scene, "cubester_load_image") - layout.separator() + box.prop_search(adv_obj, "cubester_image", bpy.data, "images") + box.prop(adv_obj, "cubester_load_image") # find number of approriate images if sequence - if scene.cubester_load_type == "multiple": + if adv_obj.cubester_load_type == "multiple": box = layout.box() # display number of images found there images = find_sequence_images(self, context) - images_found = len(images[0]) if len(images[0]) <= scene.cubester_max_images \ - else scene.cubester_max_images + images_found = len(images[0]) if len(images[0]) <= adv_obj.cubester_max_images \ + else adv_obj.cubester_max_images if len(images[0]): box.label(str(len(images[0])) + " Images Found", icon="PACKAGE") - box.prop(scene, "cubester_max_images") - box.prop(scene, "cubester_skip_images") - box.prop(scene, "cubester_frame_step") - - layout.separator() + box.prop(adv_obj, "cubester_max_images") + box.prop(adv_obj, "cubester_skip_images") + box.prop(adv_obj, "cubester_frame_step") box = layout.box() - box.prop(scene, "cubester_skip_pixels") - box.prop(scene, "cubester_size_per_hundred_pixels") - box.prop(scene, "cubester_height_scale") - box.prop(scene, "cubester_invert", icon="FILE_REFRESH") + col = box.column(align=True) + col.prop(adv_obj, "cubester_skip_pixels") + col.prop(adv_obj, "cubester_size_per_hundred_pixels") + col.prop(adv_obj, "cubester_height_scale") + box.prop(adv_obj, "cubester_invert", icon="FILE_REFRESH") - layout.separator() box = layout.box() - box.prop(scene, "cubester_mesh_style", icon="MESH_GRID") + box.prop(adv_obj, "cubester_mesh_style", icon="MESH_GRID") - if scene.cubester_mesh_style == "blocks": - box.prop(scene, "cubester_block_style") - - # audio file + if adv_obj.cubester_mesh_style == "blocks": + box.prop(adv_obj, "cubester_block_style") else: - layout.prop(scene, "cubester_audio_path") - layout.separator() + # audio file + layout.prop(adv_obj, "cubester_audio_path") + box = layout.box() + col = box.column(align=True) + col.prop(adv_obj, "cubester_audio_min_freq") + col.prop(adv_obj, "cubester_audio_max_freq") - box.prop(scene, "cubester_audio_min_freq") - box.prop(scene, "cubester_audio_max_freq") box.separator() - box.prop(scene, "cubester_audio_offset_type") + box.prop(adv_obj, "cubester_audio_offset_type") - if scene.cubester_audio_offset_type == "frame": - box.prop(scene, "cubester_audio_frame_offset") + if adv_obj.cubester_audio_offset_type == "frame": + box.prop(adv_obj, "cubester_audio_frame_offset") + box.prop(adv_obj, "cubester_audio_block_layout") box.separator() - box.prop(scene, "cubester_audio_block_layout") - box.prop(scene, "cubester_audio_width_blocks") - box.prop(scene, "cubester_audio_length_blocks") + col = box.column(align=True) + col.prop(adv_obj, "cubester_audio_width_blocks") + col.prop(adv_obj, "cubester_audio_length_blocks") - rows = scene.cubester_audio_width_blocks - columns = scene.cubester_audio_length_blocks + rows = adv_obj.cubester_audio_width_blocks + columns = adv_obj.cubester_audio_length_blocks - box.prop(scene, "cubester_size_per_hundred_pixels") + col.prop(adv_obj, "cubester_size_per_hundred_pixels") # materials - layout.separator() box = layout.box() + box.prop(adv_obj, "cubester_materials", icon="MATERIAL") - box.prop(scene, "cubester_materials", icon="MATERIAL") - - if scene.cubester_materials == "image": - box.prop(scene, "cubester_load_type") + if adv_obj.cubester_materials == "image": + box.prop(adv_obj, "cubester_load_type") # find number of approriate images if sequence - if scene.cubester_load_type == "multiple": + if adv_obj.cubester_load_type == "multiple": # display number of images found there images = find_sequence_images(self, context) - images_found = len(images[0]) if len(images[0]) <= scene.cubester_max_images \ - else scene.cubester_max_images + images_found = len(images[0]) if len(images[0]) <= adv_obj.cubester_max_images \ + else adv_obj.cubester_max_images if len(images[0]): box.label(str(len(images[0])) + " Images Found", icon="PACKAGE") - box.prop(scene, "cubester_max_images") - box.prop(scene, "cubester_skip_images") - box.prop(scene, "cubester_frame_step") + box.prop(adv_obj, "cubester_max_images") + box.prop(adv_obj, "cubester_skip_images") + box.prop(adv_obj, "cubester_frame_step") box.separator() - if scene.cubester_audio_image == "image": - box.prop(scene, "cubester_use_image_color", icon="COLOR") + if adv_obj.cubester_audio_image == "image": + box.prop(adv_obj, "cubester_use_image_color", icon="COLOR") - if not scene.cubester_use_image_color or scene.cubester_audio_image == "audio": + if not adv_obj.cubester_use_image_color or adv_obj.cubester_audio_image == "audio": box.label("Image To Use For Colors:") - box.prop_search(scene, "cubester_color_image", bpy.data, "images") - box.prop(scene, "cubester_load_color_image") + box.prop_search(adv_obj, "cubester_color_image", bpy.data, "images") + box.prop(adv_obj, "cubester_load_color_image") - if scene.cubester_image in bpy.data.images: - rows = int(bpy.data.images[scene.cubester_image].size[1] / (scene.cubester_skip_pixels + 1)) - columns = int(bpy.data.images[scene.cubester_image].size[0] / (scene.cubester_skip_pixels + 1)) + if adv_obj.cubester_image in bpy.data.images: + rows = int(bpy.data.images[adv_obj.cubester_image].size[1] / + (adv_obj.cubester_skip_pixels + 1)) + columns = int(bpy.data.images[adv_obj.cubester_image].size[0] / + (adv_obj.cubester_skip_pixels + 1)) - layout.separator() box = layout.box() - if scene.cubester_mesh_style == "blocks": + if adv_obj.cubester_mesh_style == "blocks": box.label("Approximate Cube Count: " + str(rows * columns)) - box.label("Expected # Verts/Faces: " + str(rows * columns * 8) + " / " + str(rows * columns * 6)) + box.label("Expected Verts/Faces: " + str(rows * columns * 8) + " / " + str(rows * columns * 6)) else: box.label("Approximate Point Count: " + str(rows * columns)) - box.label("Expected # Verts/Faces: " + str(rows * columns) + " / " + str(rows * (columns - 1))) + box.label("Expected Verts/Faces: " + str(rows * columns) + " / " + str(rows * (columns - 1))) # blocks and plane generation time values - if scene.cubester_mesh_style == "blocks": + if adv_obj.cubester_mesh_style == "blocks": slope = 0.0000876958 intercept = 0.02501 block_infl, frame_infl, intercept2 = 0.0025934, 0.38507, -0.5840189 - else: slope = 0.000017753 intercept = 0.04201 @@ -1035,21 +830,22 @@ class CubeSterPanel(Panel): # if creating image based mesh points = rows * columns - if scene.cubester_audio_image == "image": - if scene.cubester_load_type == "single": + if adv_obj.cubester_audio_image == "image": + if adv_obj.cubester_load_type == "single": time = rows * columns * slope + intercept # approximate time count for mesh else: time = (points * slope) + intercept + (points * block_infl) + \ - (images_found / scene.cubester_skip_images * frame_infl) + intercept2 + (images_found / adv_obj.cubester_skip_images * frame_infl) + intercept2 - box.label("Images To Be Used: " + str(int(images_found / scene.cubester_skip_images))) - - # audio based mesh + box.label("Images To Be Used: " + str(int(images_found / adv_obj.cubester_skip_images))) else: - box.label("Audio Track Length: " + scene.cubester_audio_file_length + " frames") + # audio based mesh + box.label("Audio Track Length: " + str(adv_obj.cubester_audio_file_length) + " frames") block_infl, frame_infl, intercept = 0.0948, 0.0687566, -25.85985 - time = (points * block_infl) + (int(scene.cubester_audio_file_length) * frame_infl) + intercept + time = (points * block_infl) + (adv_obj.cubester_audio_file_length * frame_infl) + intercept + if time < 0.0: # usually no audio loaded + time = 0.0 time_mod = "s" if time > 60: # convert to minutes if needed @@ -1060,29 +856,31 @@ class CubeSterPanel(Panel): box.label("Expected Time: " + str(time) + " " + time_mod) # advanced - if scene.cubester_audio_image == "image": - layout.separator() + if adv_obj.cubester_audio_image == "image": + icon_1 = "TRIA_DOWN" if adv_obj.cubester_advanced else "TRIA_RIGHT" + # layout.separator() box = layout.box() - box.prop(scene, "cubester_advanced", icon="TRIA_DOWN") - if bpy.context.scene.cubester_advanced: - box.prop(scene, "cubester_random_weights", icon="RNDCURVE") - box.separator() + box.prop(adv_obj, "cubester_advanced", icon=icon_1) + + if adv_obj.cubester_advanced: + box.prop(adv_obj, "cubester_random_weights", icon="RNDCURVE") - if not bpy.context.scene.cubester_random_weights: + if not adv_obj.cubester_random_weights: box.label("RGBA Channel Weights", icon="COLOR") - box.prop(scene, "cubester_weight_r") - box.prop(scene, "cubester_weight_g") - box.prop(scene, "cubester_weight_b") - box.prop(scene, "cubester_weight_a") + col = box.column(align=True) + col.prop(adv_obj, "cubester_weight_r") + col.prop(adv_obj, "cubester_weight_g") + col.prop(adv_obj, "cubester_weight_b") + col.prop(adv_obj, "cubester_weight_a") # generate mesh - layout.separator() layout.operator("mesh.cubester", icon="OBJECT_DATA") class CubeSter(Operator): bl_idname = "mesh.cubester" bl_label = "Generate Mesh" + bl_description = "Generate a mesh from an Image or Sound File" bl_options = {"REGISTER", "UNDO"} def execute(self, context): @@ -1090,18 +888,31 @@ class CubeSter(Operator): start = timeit.default_timer() scene = bpy.context.scene + adv_obj = scene.advanced_objects - if scene.cubester_audio_image == "image": - create_mesh_from_image(self, scene, verts, faces) - frames = find_sequence_images(self, context) - created = len(frames[0]) + if adv_obj.cubester_audio_image == "image": + if adv_obj.cubester_image != "": + create_mesh_from_image(self, scene, verts, faces) + frames = find_sequence_images(self, context) + created = len(frames[0]) + else: + self.report({'WARNING'}, + "Please add an Image for Object generation. Operation Cancelled") + return {"CANCELLED"} else: - create_mesh_from_audio(self, scene, verts, faces) - created = int(scene.cubester_audio_file_length) + if (adv_obj.cubester_audio_path != "" and + path.isfile(adv_obj.cubester_audio_path) and adv_obj.cubester_check_audio is True): + + create_mesh_from_audio(self, scene, verts, faces) + created = adv_obj.cubester_audio_file_length + else: + self.report({'WARNING'}, + "Please add an Sound File for Object generation. Operation Cancelled") + return {"CANCELLED"} stop = timeit.default_timer() - if scene.cubester_mesh_style == "blocks" or scene.cubester_audio_image == "audio": + if adv_obj.cubester_mesh_style == "blocks" or adv_obj.cubester_audio_image == "audio": self.report({"INFO"}, "CubeSter: {} blocks and {} frame(s) " "in {}s".format(str(int(len(verts) / 8)), diff --git a/add_advanced_objects/delaunay_voronoi/DelaunayVoronoi.py b/add_advanced_objects/delaunay_voronoi/DelaunayVoronoi.py index 18d7f38f..dcce7f68 100644 --- a/add_advanced_objects/delaunay_voronoi/DelaunayVoronoi.py +++ b/add_advanced_objects/delaunay_voronoi/DelaunayVoronoi.py @@ -61,13 +61,11 @@ import bpy import math -import sys -import getopt + +# Globals TOLERANCE = 1e-9 BIG_FLOAT = 1e38 -# TODO : Licence, prints, imports, autorship - class Context(object): @@ -295,14 +293,14 @@ class Context(object): def outTriple(self, s1, s2, s3): self.triangles.append((s1.sitenum, s2.sitenum, s3.sitenum)) - if(self.debug): + if (self.debug): print("circle through left=%d right=%d bottom=%d" % (s1.sitenum, s2.sitenum, s3.sitenum)) - elif(self.triangulate and self.doPrint): + elif (self.triangulate and self.doPrint): print("%d %d %d" % (s1.sitenum, s2.sitenum, s3.sitenum)) def outBisector(self, edge): self.lines.append((edge.a, edge.b, edge.c)) - if(self.debug): + if (self.debug): print("line(%d) %gx+%gy=%g, bisecting %d %d" % (edge.edgenum, edge.a, edge.b, edge.c, edge.reg[0].sitenum, edge.reg[1].sitenum) @@ -328,8 +326,8 @@ class Context(object): self.edges.append((edge.edgenum, sitenumL, sitenumR)) - if(not self.triangulate): - if(self.doPrint): + if (not self.triangulate): + if (self.doPrint): print("e %d" % edge.edgenum) print(" %d " % sitenumL) print("%d" % sitenumR) diff --git a/add_advanced_objects/delaunay_voronoi/__init__.py b/add_advanced_objects/delaunay_voronoi/__init__.py index 1d210a2a..c32eb374 100644 --- a/add_advanced_objects/delaunay_voronoi/__init__.py +++ b/add_advanced_objects/delaunay_voronoi/__init__.py @@ -21,14 +21,13 @@ bl_info = { "name": "Delaunay Voronoi", "description": "Points cloud Delaunay triangulation in 2.5D " - "(suitable for terrain modelling) or Voronoi diagram in 2D", + "(suitable for terrain modelling) or Voronoi diagram in 2D", "author": "Domlysz, Oscurart", "version": (1, 3), "blender": (2, 7, 0), "location": "View3D > Tools > GIS", "warning": "", "wiki_url": "https://github.com/domlysz/BlenderGIS/wiki", - "tracker_url": "", "category": "" } diff --git a/add_advanced_objects/delaunay_voronoi/delaunayVoronoiBlender.py b/add_advanced_objects/delaunay_voronoi/delaunayVoronoiBlender.py index e937e7a1..707c45b4 100644 --- a/add_advanced_objects/delaunay_voronoi/delaunayVoronoiBlender.py +++ b/add_advanced_objects/delaunay_voronoi/delaunayVoronoiBlender.py @@ -2,9 +2,9 @@ import bpy from .DelaunayVoronoi import ( - computeVoronoiDiagram, - computeDelaunayTriangulation, - ) + computeVoronoiDiagram, + computeDelaunayTriangulation, + ) from bpy.types import ( Operator, Panel, @@ -12,6 +12,17 @@ from bpy.types import ( from bpy.props import EnumProperty +# Globals +# set to True to enable debug_prints +DEBUG = False + + +def debug_prints(text=""): + global DEBUG + if DEBUG and text: + print(text) + + class Point: def __init__(self, x, y, z): self.x, self.y, self.z = x, y, z @@ -56,45 +67,56 @@ class ToolsPanelDelaunay(Panel): def draw(self, context): layout = self.layout - layout.label('Constellation') - self.layout.operator("delaunay.triangulation") - self.layout.operator("voronoi.tesselation") - layout.label('Constellation') - layout.operator("mesh.constellation", text="Cross Section") + adv_obj = context.scene.advanced_objects + + box = layout.box() + col = box.column(align=True) + col.label("Point Cloud:") + col.operator("delaunay.triangulation") + col.operator("voronoi.tesselation") + + box = layout.box() + col = box.column(align=True) + col.label("Constellation:") + col.operator("mesh.constellation", text="Cross Section") + col.prop(adv_obj, "constellation_limit") class OBJECT_OT_TriangulateButton(Operator): bl_idname = "delaunay.triangulation" bl_label = "Triangulation" - bl_description = "Terrain points cloud Delaunay triangulation in 2.5D" - bl_options = {"UNDO"} + bl_description = ("Terrain points cloud Delaunay triangulation in 2.5D\n" + "Needs an existing Active Mesh Object") + bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - # Get selected obj - objs = bpy.context.selected_objects - if len(objs) == 0 or len(objs) > 1: - self.report({'INFO'}, "Selection is empty or too much object selected") - return {'FINISHED'} + @classmethod + def poll(cls, context): + obj = context.active_object + return (obj is not None and obj.type == "MESH") - obj = objs[0] - if obj.type != 'MESH': - self.report({'INFO'}, "Selection isn't a mesh") - return {'FINISHED'} + def execute(self, context): + # move the check into the poll + obj = context.active_object # Get points coodinates r = obj.rotation_euler s = obj.scale mesh = obj.data vertsPts = [vertex.co for vertex in mesh.vertices] + # Remove duplicate verts = [[vert.x, vert.y, vert.z] for vert in vertsPts] nDupli, nZcolinear = unique(verts) nVerts = len(verts) - print(str(nDupli) + " duplicates points ignored") - print(str(nZcolinear) + " z colinear points excluded") + + debug_prints(text=str(nDupli) + " duplicate points ignored") + debug_prints(str(nZcolinear) + " z colinear points excluded") + if nVerts < 3: - self.report({'ERROR'}, "Not enough points") - return {'FINISHED'} + self.report({"WARNING"}, + "Not enough points to continue. Operation Cancelled") + + return {"CANCELLED"} # Check colinear xValues = [pt[0] for pt in verts] @@ -105,59 +127,63 @@ class OBJECT_OT_TriangulateButton(Operator): return {'FINISHED'} # Triangulate - print("Triangulate " + str(nVerts) + " points...") + debug_prints(text="Triangulate " + str(nVerts) + " points...") + vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts] triangles = computeDelaunayTriangulation(vertsPts) # reverse point order --> if all triangles are specified anticlockwise then all faces up triangles = [tuple(reversed(tri)) for tri in triangles] - print(str(len(triangles)) + " triangles") + debug_prints(text=str(len(triangles)) + " triangles") # Create new mesh structure - print("Create mesh...") + debug_prints(text="Create mesh...") tinMesh = bpy.data.meshes.new("TIN") # create a new mesh tinMesh.from_pydata(verts, [], triangles) # Fill the mesh with triangles tinMesh.update(calc_edges=True) # Update mesh with new data # Create an object with that mesh tinObj = bpy.data.objects.new("TIN", tinMesh) + # Place object tinObj.location = obj.location.copy() tinObj.rotation_euler = r tinObj.scale = s + # Update scene bpy.context.scene.objects.link(tinObj) # Link object to scene bpy.context.scene.objects.active = tinObj tinObj.select = True obj.select = False - # Report - self.report({'INFO'}, "Mesh created (" + str(len(triangles)) + " triangles)") + + self.report({"INFO"}, + "Mesh created (" + str(len(triangles)) + " triangles)") + return {'FINISHED'} class OBJECT_OT_VoronoiButton(Operator): bl_idname = "voronoi.tesselation" bl_label = "Diagram" - bl_description = "Points cloud Voronoi diagram in 2D" + bl_description = ("Points cloud Voronoi diagram in 2D\n" + "Needs an existing Active Mesh Object") bl_options = {"REGISTER", "UNDO"} meshType = EnumProperty( - items=[("Edges", "Edges", ""), ("Faces", "Faces", "")], - name="Mesh type", - description="" - ) + items=[('Edges', "Edges", "Edges Only - do not fill Faces"), + ('Faces', "Faces", "Fill Faces in the new Object")], + name="Mesh type", + description="Type of geometry to generate" + ) - def execute(self, context): - # Get selected obj - objs = bpy.context.selected_objects - if len(objs) == 0 or len(objs) > 1: - self.report({'INFO'}, "Selection is empty or too much object selected") - return {'FINISHED'} + @classmethod + def poll(cls, context): + obj = context.active_object + return (obj is not None and obj.type == "MESH") - obj = objs[0] - if obj.type != 'MESH': - self.report({'INFO'}, "Selection isn't a mesh") - return {'FINISHED'} + def execute(self, context): + # move the check into the poll + obj = context.active_object # Get points coodinates r = obj.rotation_euler @@ -170,28 +196,36 @@ class OBJECT_OT_VoronoiButton(Operator): nDupli, nZcolinear = unique(verts) nVerts = len(verts) - print(str(nDupli) + " duplicates points ignored") - print(str(nZcolinear) + " z colinear points excluded") + debug_prints(text=str(nDupli) + " duplicates points ignored") + debug_prints(text=str(nZcolinear) + " z colinear points excluded") if nVerts < 3: - self.report({'ERROR'}, "Not enough points") - return {'FINISHED'} + self.report({"WARNING"}, + "Not enough points to continue. Operation Cancelled") + + return {"CANCELLED"} # Check colinear xValues = [pt[0] for pt in verts] yValues = [pt[1] for pt in verts] + if checkEqual(xValues) or checkEqual(yValues): - self.report({'ERROR'}, "Points are colinear") - return {'FINISHED'} + self.report({"WARNING"}, + "Points are colinear. Operation Cancelled") + + return {"CANCELLED"} # Create diagram - print("Tesselation... (" + str(nVerts) + " points)") + debug_prints(text="Tesselation... (" + str(nVerts) + " points)") + xbuff, ybuff = 5, 5 zPosition = 0 vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts] + if self.meshType == "Edges": pts, edgesIdx = computeVoronoiDiagram( - vertsPts, xbuff, ybuff, polygonsOutput=False, formatOutput=True + vertsPts, xbuff, ybuff, + polygonsOutput=False, formatOutput=True ) else: pts, polyIdx = computeVoronoiDiagram( @@ -227,8 +261,8 @@ class OBJECT_OT_VoronoiButton(Operator): # Report if self.meshType == "Edges": - self.report({'INFO'}, "Mesh created (" + str(len(edgesIdx)) + " edges)") + self.report({"INFO"}, "Mesh created (" + str(len(edgesIdx)) + " edges)") else: - self.report({'INFO'}, "Mesh created (" + str(len(polyIdx)) + " polygons)") + self.report({"INFO"}, "Mesh created (" + str(len(polyIdx)) + " polygons)") return {'FINISHED'} diff --git a/add_advanced_objects/delaunay_voronoi/oscurart_constellation.py b/add_advanced_objects/delaunay_voronoi/oscurart_constellation.py index babbfdc5..adde96c0 100644 --- a/add_advanced_objects/delaunay_voronoi/oscurart_constellation.py +++ b/add_advanced_objects/delaunay_voronoi/oscurart_constellation.py @@ -19,15 +19,17 @@ bl_info = { "name": "Mesh: Constellation", "author": "Oscurart", - "version": (1, 0), + "version": (1, 1, 1), "blender": (2, 67, 0), "location": "Add > Mesh > Constellation", - "description": "Adds a new Mesh From Selected", + "description": "Create a new Mesh From Selected", "warning": "", "wiki_url": "", - "tracker_url": "", "category": "Add Mesh"} +# Note the setting is moved to __init__ search for +# the adv_obj and advanced_objects patterns + import bpy from bpy.types import Operator from bpy.props import FloatProperty @@ -56,51 +58,66 @@ def OscConstellation(limit): edgei += 2 mesh = bpy.data.meshes.new("rsdata") - object = bpy.data.objects.new("rsObject", mesh) - bpy.context.scene.objects.link(object) + obj = bpy.data.objects.new("rsObject", mesh) + bpy.context.scene.objects.link(obj) mesh.from_pydata(vertlist, edgelist, []) -class Oscurart_Constellation (Operator): +class Oscurart_Constellation(Operator): bl_idname = "mesh.constellation" bl_label = "Constellation" - bl_description = "Create a Constellation Mesh" + bl_description = ("Create a Constellation Mesh - Cloud of Vertices\n" + "Note: can produce a lot of geometry\n" + "Needs an existing Active Mesh Object") bl_options = {'REGISTER', 'UNDO'} limit = FloatProperty( - name='Limit', - default=2, - min=0 - ) + name="Threshold", + description="Edges will be created only if the distance\n" + "between vertices is smaller than this value", + default=2, + min=0 + ) @classmethod def poll(cls, context): - return(bpy.context.active_object.type == "MESH") + obj = context.active_object + return (obj and obj.type == "MESH") + + def invoke(self, context, event): + adv_obj = context.scene.advanced_objects + self.limit = adv_obj.constellation_limit + + return self.execute(context) + + def draw(self, context): + layout = self.layout + + layout.prop(self, "limit") def execute(self, context): - OscConstellation(self.limit) + try: + OscConstellation(self.limit) + except Exception as e: + print("\n[Add Advanced Objects]\nOperator: mesh.constellation\n{}".format(e)) - return {'FINISHED'} + self.report({"WARNING"}, + "Constellation Operation could not be Completed (See Console for more Info)") + return {"CANCELLED"} -# Register + return {'FINISHED'} -def add_osc_constellation_button(self, context): - self.layout.operator( - Oscurart_Constellation.bl_idname, - text="Constellation", - icon="PLUGIN") +# Register def register(): bpy.utils.register_class(Oscurart_Constellation) - bpy.types.INFO_MT_mesh_add.append(add_osc_constellation_button) def unregister(): bpy.utils.unregister_class(Oscurart_Constellation) - bpy.types.INFO_MT_mesh_add.remove(add_osc_constellation_button) -if __name__ == '__main__': +if __name__ == "__main__": register() diff --git a/add_advanced_objects/drop_to_ground.py b/add_advanced_objects/drop_to_ground.py index 801b7e94..744a2d6b 100644 --- a/add_advanced_objects/drop_to_ground.py +++ b/add_advanced_objects/drop_to_ground.py @@ -19,14 +19,13 @@ bl_info = { "name": "Drop to Ground1", "author": "Unnikrishnan(kodemax), Florian Meyer(testscreenings)", - "version": (1, 2), + "version": (1, 2, 1), "blender": (2, 71, 0), "location": "3D View > Toolshelf > Tools Tab", "description": "Drop selected objects on active object", "warning": "", "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/Object/Drop_to_ground", - "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", "category": "Object"} @@ -59,6 +58,7 @@ def transform_ground_to_world(sc, ground): tmp_ground = bpy.data.objects.new('tmpGround', tmpMesh) sc.objects.link(tmp_ground) sc.update() + return tmp_ground @@ -120,12 +120,16 @@ def drop_objectsall(self, context): lowest_world_co = ob.location else: lowest_world_co = get_lowest_world_co(context, ob) + if not lowest_world_co: - print(ob.type, 'is not supported. Failed to drop', ob.name) + message = "Type {} is not supported. Failed to drop {}".format(ob.type, ob.name) + self.reported.append(message) continue is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down) + if not is_hit: - print(ob.name, 'didn\'t hit the ground') + message = ob.name + " did not hit the Ground" + self.reported.append(message) continue # simple drop down @@ -169,12 +173,16 @@ def drop_objects(self, context): lowest_world_co = ob.location else: lowest_world_co = get_lowest_world_co(context, ob) + if not lowest_world_co: - print(ob.type, 'is not supported. Failed to drop', ob.name) + message = "Type {} is not supported. Failed to drop {}".format(ob.type, ob.name) + self.reported.append(message) continue + is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down) if not is_hit: - print(ob.name, 'didn\'t hit the ground') + message = ob.name + " did not hit the Ground" + self.reported.append(message) continue # simple drop down @@ -209,8 +217,8 @@ def drop_objects(self, context): class OBJECT_OT_drop_to_ground(Operator): bl_idname = "object.drop_on_active" bl_label = "Drop to Ground" + bl_description = "Drop selected objects on the active object" bl_options = {'REGISTER', 'UNDO'} - bl_description = "Drop selected objects on active object" align = BoolProperty( name="Align to ground", @@ -220,22 +228,31 @@ class OBJECT_OT_drop_to_ground(Operator): name="Use Center", description="Drop to objects origins", default=False) + reported = [] @classmethod def poll(cls, context): return len(context.selected_objects) >= 2 def execute(self, context): - print('\nDropping Objects') drop_objects(self, context) + + if self.reported: + self.report({"INFO"}, + "Operation failed on some objects. See the Console for more Info") + report_items = " \n".join(self.reported) + print("\n[Drop to Ground Report]\n{}\n".format(report_items)) + + self.reported = [] + return {'FINISHED'} class OBJECT_OT_drop_all_ground(Operator): bl_idname = "object.drop_all_active" - bl_label = "Drop to Ground" - bl_options = {'REGISTER', 'UNDO'} + bl_label = "Drop All to Ground (Active Object)" bl_description = "Drop selected objects on active object" + bl_options = {'REGISTER', 'UNDO'} align = BoolProperty( name="Align to ground", @@ -245,33 +262,45 @@ class OBJECT_OT_drop_all_ground(Operator): name="Use Center", description="Drop to objects origins", default=False) + reported = [] + + @classmethod + def poll(cls, context): + return context.active_object is not None def execute(self, context): - print('\nDropping Objects') drop_objectsall(self, context) + if self.reported: + self.report({"INFO"}, + "Operation failed on some objects. See the Console for more Info") + report_items = " \n".join(self.reported) + print("\n[Drop All to Ground Report]\n{}\n".format(report_items)) + + self.reported = [] + return {'FINISHED'} -class drop_help(Operator): +class Drop_help(Operator): bl_idname = "help.drop" bl_label = "" + is_all = BoolProperty( + default=True, + options={"HIDDEN"} + ) + def draw(self, context): layout = self.layout layout.label("To use:") - layout.label("___________________________") - layout.label("Drop selected :-") - - layout.label("Name the base object 'Ground'") - layout.label("Select the object/s to drop") - layout.label("Then Shift Select 'Ground'") - layout.label("___________________________") - - layout.label("Drop all :-") - - layout.label("select the ground mesh , and press Drop all") + if self.is_all is False: + layout.label("Name the base object 'Ground'") + layout.label("Select the object's to drop") + layout.label("Then Shift Select 'Ground'") + else: + layout.label("Select the ground mesh and press Drop all") def execute(self, context): return {'FINISHED'} @@ -290,25 +319,31 @@ class Drop_Operator_Panel(Panel): def draw(self, context): layout = self.layout - row = layout.row() - row = layout.split(0.80) + + row = layout.split(percentage=0.8, align=True) row.operator(OBJECT_OT_drop_to_ground.bl_idname, text="Drop Selected") - row = layout.row() + row.operator("help.drop", icon="LAYER_USED").is_all = False + + row = layout.split(percentage=0.8, align=True) row.operator(OBJECT_OT_drop_all_ground.bl_idname, text="Drop All") - row.operator('help.drop', icon='INFO') + row.operator("help.drop", icon="LAYER_USED").is_all = True # Register def register(): - bpy.utils.register_module(__name__) - pass + bpy.utils.register_class(OBJECT_OT_drop_all_ground) + bpy.utils.register_class(OBJECT_OT_drop_to_ground) + bpy.utils.register_class(Drop_Operator_Panel) + bpy.utils.register_class(Drop_help) def unregister(): - bpy.utils.unregister_module(__name__) - pass + bpy.utils.unregister_class(OBJECT_OT_drop_all_ground) + bpy.utils.unregister_class(OBJECT_OT_drop_to_ground) + bpy.utils.unregister_class(Drop_Operator_Panel) + bpy.utils.unregister_class(Drop_help) if __name__ == "__main__": diff --git a/add_advanced_objects/make_struts.py b/add_advanced_objects/make_struts.py index 7f754ea8..58e149ab 100644 --- a/add_advanced_objects/make_struts.py +++ b/add_advanced_objects/make_struts.py @@ -21,22 +21,9 @@ # <pep8 compliant> -""" -bl_info = { - "name": "Strut Generator", - "author": "Bill Currie", - "blender": (2, 6, 3), - "api": 35622, - "location": "View3D > Add > Mesh > Struts", - "description": "Add struts meshes based on selected truss meshes", - "warning": "can get very high-poly", - "wiki_url": "", - "tracker_url": "", - "category": "Add Mesh"} -""" - import bpy import bmesh +from bpy.types import Operator from bpy.props import ( FloatProperty, IntProperty, @@ -48,8 +35,7 @@ from mathutils import ( Quaternion, ) from math import ( - pi, - cos, + pi, cos, sin, ) @@ -71,6 +57,7 @@ def build_cossin(n): def select_up(axis): + # if axis.length != 0 and (abs(axis[0] / axis.length) < 1e-5 and abs(axis[1] / axis.length) < 1e-5): if (abs(axis[0] / axis.length) < 1e-5 and abs(axis[1] / axis.length) < 1e-5): up = Vector((-1, 0, 0)) else: @@ -109,21 +96,21 @@ def select_up(axis): # vertices for the created strut. -def make_strut(v1, v2, id, od, n, solid, loops): +def make_strut(v1, v2, ind, od, n, solid, loops): v1 = Vector(v1) v2 = Vector(v2) axis = v2 - v1 pos = [(0, od / 2)] if loops: - pos += [((od - id) / 2, od / 2), - (axis.length - (od - id) / 2, od / 2)] + pos += [((od - ind) / 2, od / 2), + (axis.length - (od - ind) / 2, od / 2)] pos += [(axis.length, od / 2)] if solid: - pos += [(axis.length, id / 2)] + pos += [(axis.length, ind / 2)] if loops: - pos += [(axis.length - (od - id) / 2, id / 2), - ((od - id) / 2, id / 2)] - pos += [(0, id / 2)] + pos += [(axis.length - (od - ind) / 2, ind / 2), + ((od - ind) / 2, ind / 2)] + pos += [(0, ind / 2)] vps = len(pos) fps = vps if not solid: @@ -156,7 +143,7 @@ def make_strut(v1, v2, id, od, n, solid, loops): for j in range(fps): f = (i - 1) * fps + j faces[f] = [base + j, j, (j + 1) % vps, base + (j + 1) % vps] - # print(verts,faces) + return verts, faces @@ -452,7 +439,7 @@ def make_manifold_struts(truss_obj, od, segments): return verts, faces -def make_simple_struts(truss_mesh, id, od, segments, solid, loops): +def make_simple_struts(truss_mesh, ind, od, segments, solid, loops): vps = 2 if solid: vps *= 2 @@ -466,10 +453,11 @@ def make_simple_struts(truss_mesh, id, od, segments, solid, loops): faces = [None] * len(truss_mesh.edges) * segments * fps vbase = 0 fbase = 0 + for e in truss_mesh.edges: v1 = truss_mesh.vertices[e.vertices[0]] v2 = truss_mesh.vertices[e.vertices[1]] - v, f = make_strut(v1.co, v2.co, id, od, segments, solid, loops) + v, f = make_strut(v1.co, v2.co, ind, od, segments, solid, loops) for fv in f: for i in range(len(fv)): fv[i] += vbase @@ -481,14 +469,13 @@ def make_simple_struts(truss_mesh, id, od, segments, solid, loops): # print (base * 100 / len(verts)) vbase += vps * segments fbase += fps * segments - # print(verts,faces) + return verts, faces -def create_struts(self, context, id, od, segments, solid, loops, manifold): +def create_struts(self, context, ind, od, segments, solid, loops, manifold): build_cossin(segments) - bpy.context.user_preferences.edit.use_global_undo = False for truss_obj in bpy.context.scene.objects: if not truss_obj.select: continue @@ -499,7 +486,7 @@ def create_struts(self, context, id, od, segments, solid, loops, manifold): if manifold: verts, faces = make_manifold_struts(truss_obj, od, segments) else: - verts, faces = make_simple_struts(truss_mesh, id, od, segments, + verts, faces = make_simple_struts(truss_mesh, ind, od, segments, solid, loops) mesh = bpy.data.meshes.new("Struts") mesh.from_pydata(verts, [], faces) @@ -509,58 +496,92 @@ def create_struts(self, context, id, od, segments, solid, loops, manifold): obj.location = truss_obj.location bpy.context.scene.objects.active = obj mesh.update() - bpy.context.user_preferences.edit.use_global_undo = True - return {'FINISHED'} -class Struts(bpy.types.Operator): - """Add one or more struts meshes based on selected truss meshes""" +class Struts(Operator): bl_idname = "mesh.generate_struts" bl_label = "Struts" - bl_description = """Add one or more struts meshes based on selected truss meshes""" + bl_description = ("Add one or more struts meshes based on selected truss meshes \n" + "Note: can get very high poly\n" + "Needs an existing Active Mesh Object") bl_options = {'REGISTER', 'UNDO'} - id = FloatProperty(name="Inside Diameter", - description="diameter of inner surface", - min=0.0, - soft_min=0.0, - max=100, - soft_max=100, - default=0.04) - od = FloatProperty(name="Outside Diameter", - description="diameter of outer surface", - min=0.001, - soft_min=0.001, - max=100, - soft_max=100, - default=0.05) - manifold = BoolProperty(name="Manifold", - description="Connect struts to form a single solid.", - default=False) - solid = BoolProperty(name="Solid", - description="Create inner surface.", - default=False) - loops = BoolProperty(name="Loops", - description="Create sub-surf friendly loops.", - default=False) - segments = IntProperty(name="Segments", - description="Number of segments around strut", - min=3, soft_min=3, - max=64, soft_max=64, - default=12) + ind = FloatProperty( + name="Inside Diameter", + description="Diameter of inner surface", + min=0.0, soft_min=0.0, + max=100, soft_max=100, + default=0.04 + ) + od = FloatProperty( + name="Outside Diameter", + description="Diameter of outer surface", + min=0.001, soft_min=0.001, + max=100, soft_max=100, + default=0.05 + ) + manifold = BoolProperty( + name="Manifold", + description="Connect struts to form a single solid", + default=False + ) + solid = BoolProperty( + name="Solid", + description="Create inner surface", + default=False + ) + loops = BoolProperty( + name="Loops", + description="Create sub-surf friendly loops", + default=False + ) + segments = IntProperty( + name="Segments", + description="Number of segments around strut", + min=3, soft_min=3, + max=64, soft_max=64, + default=12 + ) + + def draw(self, context): + layout = self.layout + + col = layout.column(align=True) + col.prop(self, "ind") + col.prop(self, "od") + col.prop(self, "segments") + col.separator() + + col.prop(self, "manifold") + col.prop(self, "solid") + col.prop(self, "loops") + + @classmethod + def poll(cls, context): + obj = context.active_object + return obj is not None and obj.type == "MESH" def execute(self, context): + store_undo = bpy.context.user_preferences.edit.use_global_undo + bpy.context.user_preferences.edit.use_global_undo = False keywords = self.as_keywords() - return create_struts(self, context, **keywords) + try: + create_struts(self, context, **keywords) + bpy.context.user_preferences.edit.use_global_undo = store_undo + + return {"FINISHED"} -def menu_func(self, context): - self.layout.operator(Struts.bl_idname, text="Struts", icon='PLUGIN') + except Exception as e: + bpy.context.user_preferences.edit.use_global_undo = store_undo + self.report({"WARNING"}, + "Make Struts could not be performed. Operation Cancelled") + print("\n[mesh.generate_struts]\n{}".format(e)) + return {"CANCELLED"} def register(): bpy.utils.register_module(__name__) - bpy.types.INFO_MT_mesh_add.append(menu_func) def unregister(): diff --git a/add_advanced_objects/mesh_easylattice.py b/add_advanced_objects/mesh_easylattice.py index 13512adf..91a167dc 100644 --- a/add_advanced_objects/mesh_easylattice.py +++ b/add_advanced_objects/mesh_easylattice.py @@ -16,11 +16,12 @@ # # ##### END GPL LICENSE BLOCK ##### +# TODO: find a better solution for allowing more than one lattice per scene bl_info = { "name": "Easy Lattice Object", "author": "Kursad Karatas", - "version": (0, 5), + "version": (0, 5, 1), "blender": (2, 66, 0), "location": "View3D > Easy Lattice", "description": "Create a lattice for shape editing", @@ -35,9 +36,11 @@ from mathutils import ( Matrix, Vector, ) +from bpy.types import Operator from bpy.props import ( EnumProperty, IntProperty, + StringProperty, ) @@ -86,7 +89,8 @@ def createLattice(obj, size, pos, props): # the size from bbox ob.scale = size - # the rotation comes from the combined obj world matrix which was converted to euler pairs + # the rotation comes from the combined obj world + # matrix which was converted to euler pairs ob.rotation_euler = buildRot_World(obj) ob.show_x_ray = True @@ -263,10 +267,11 @@ def run(lat_props): if obj.type == "MESH": # set global property for the currently active latticed object - bpy.types.Scene.activelatticeobject = bpy.props.StringProperty( - name="currentlatticeobject", - default="" - ) + # removed in __init__ on unregister if created + bpy.types.Scene.activelatticeobject = StringProperty( + name="currentlatticeobject", + default="" + ) bpy.types.Scene.activelatticeobject = obj.name modifiersDelete(obj) @@ -311,70 +316,85 @@ def main(context, latticeprops): run(latticeprops) -class EasyLattice(bpy.types.Operator): - """Adds a Lattice modifier ready to edit""" +class EasyLattice(Operator): bl_idname = "object.easy_lattice" bl_label = "Easy Lattice Creator" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" + bl_description = ("Create a Lattice modifier ready to edit\n" + "Needs an existing Active Mesh Object\n" + "Note: Works only with one lattice per scene") lat_u = IntProperty( - name="Lattice u", - default=3 - ) + name="Lattice u", + description="Points in u direction", + default=3 + ) lat_w = IntProperty( - name="Lattice w", - default=3 - ) + name="Lattice w", + description="Points in w direction", + default=3 + ) lat_m = IntProperty( - name="Lattice m", - default=3 + name="Lattice m", + description="Points in m direction", + default=3 + ) + lat_types = (('KEY_LINEAR', "Linear", "Linear Interpolation type"), + ('KEY_CARDINAL', "Cardinal", "Cardinal Interpolation type"), + ('KEY_BSPLINE', "BSpline", "Key BSpline Interpolation Type") ) - lat_types = (('0', 'KEY_LINEAR', '0'), - ('1', 'KEY_CARDINAL', '1'), - ('2', 'KEY_BSPLINE', '2')) lat_type = EnumProperty( - name="Lattice Type", - items=lat_types, - default='0' - ) + name="Lattice Type", + description="Choose Lattice Type", + items=lat_types, + default='KEY_LINEAR' + ) @classmethod def poll(cls, context): - return context.active_object is not None + obj = context.active_object + return obj is not None and obj.type == "MESH" + + def draw(self, context): + layout = self.layout + + col = layout.column(align=True) + col.prop(self, "lat_u") + col.prop(self, "lat_w") + col.prop(self, "lat_m") + + layout.prop(self, "lat_type") def execute(self, context): lat_u = self.lat_u lat_w = self.lat_w lat_m = self.lat_m - # this is a reference to the "items" used to generate the - # enum property - lat_type = self.lat_types[int(self.lat_type)][1] + # enum property no need to complicate things + lat_type = self.lat_type lat_props = [lat_u, lat_w, lat_m, lat_type] + try: + main(context, lat_props) + + except Exception as e: + print("\n[Add Advanced Objects]\nOperator:object.easy_lattice\n{}\n".format(e)) + self.report({'WARNING'}, + "Easy Lattice Creator could not be completed (See Console for more info)") - main(context, lat_props) + return {"CANCELLED"} - return {'FINISHED'} + return {"FINISHED"} def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) -def menu_draw(self, context): - self.layout.operator_context = 'INVOKE_REGION_WIN' - self.layout.operator(EasyLattice.bl_idname, "Easy Lattice") - - def register(): bpy.utils.register_class(EasyLattice) - bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_draw) def unregister(): bpy.utils.unregister_class(EasyLattice) - bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_draw) if __name__ == "__main__": diff --git a/add_advanced_objects/object_add_chain.py b/add_advanced_objects/object_add_chain.py index 57babfd6..8b182c82 100644 --- a/add_advanced_objects/object_add_chain.py +++ b/add_advanced_objects/object_add_chain.py @@ -30,20 +30,18 @@ bl_info = { } import bpy -from bpy.types import ( - Operator, - Panel, - ) +from bpy.types import Operator def Add_Chain(): # Adds Empty to scene - bpy.ops.object.add(type='EMPTY', - view_align=False, - enter_editmode=False, - location=(0, 0, 0), - rotation=(0, 0, 0), - ) + bpy.ops.object.add( + type='EMPTY', + view_align=False, + enter_editmode=False, + location=(0, 0, 0), + rotation=(0, 0, 0), + ) # Changes name of Empty to rot_link adds variable emp emp = bpy.context.object @@ -53,24 +51,26 @@ def Add_Chain(): emp.rotation_euler = [1.570796, 0, 0] # Adds Curve Path to scene - bpy.ops.curve.primitive_nurbs_path_add(view_align=False, - enter_editmode=False, - location=(0, 0, 0), - rotation=(0, 0, 0), - ) + bpy.ops.curve.primitive_nurbs_path_add( + view_align=False, + enter_editmode=False, + location=(0, 0, 0), + rotation=(0, 0, 0), + ) # Change Curve name to deform adds variable curv curv = bpy.context.object curv.name = "deform" # Inserts Torus primitive - bpy.ops.mesh.primitive_torus_add(major_radius=1, - minor_radius=0.25, - major_segments=12, - minor_segments=4, - abso_major_rad=1, - abso_minor_rad=0.5, - ) + bpy.ops.mesh.primitive_torus_add( + major_radius=1, + minor_radius=0.25, + major_segments=12, + minor_segments=4, + abso_major_rad=1, + abso_minor_rad=0.5, + ) # Positions Torus primitive to center of scene bpy.context.active_object.location = 0.0, 0.0, 0.0 @@ -98,22 +98,23 @@ def Add_Chain(): # Toggle into editmode bpy.ops.object.editmode_toggle() - # TODO, may be better to move objects directly. + # TODO, may be better to move objects directly # Translate curve object - bpy.ops.transform.translate(value=(2, 0, 0), - constraint_axis=(True, False, False), - constraint_orientation='GLOBAL', - mirror=False, - proportional='DISABLED', - proportional_edit_falloff='SMOOTH', - proportional_size=1, - snap=False, - snap_target='CLOSEST', - snap_point=(0, 0, 0), - snap_align=False, - snap_normal=(0, 0, 0), - release_confirm=False, - ) + bpy.ops.transform.translate( + value=(2, 0, 0), + constraint_axis=(True, False, False), + constraint_orientation='GLOBAL', + mirror=False, + proportional='DISABLED', + proportional_edit_falloff='SMOOTH', + proportional_size=1, + snap=False, + snap_target='CLOSEST', + snap_point=(0, 0, 0), + snap_align=False, + snap_normal=(0, 0, 0), + release_confirm=False, + ) # Toggle into objectmode bpy.ops.object.editmode_toggle() @@ -151,19 +152,27 @@ class AddChain(Operator): bl_options = {'REGISTER', 'UNDO'} def execute(self, context): - Add_Chain() + try: + Add_Chain() + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "mesh.primitive_chain_add\nError: {}".format(e)) + + return {'CANCELLED'} return {'FINISHED'} def register(): - bpy.utils.register_module(__name__) - pass + bpy.utils.register_class(AddChain) def unregister(): - bpy.utils.unregister_module(__name__) - pass + bpy.utils.unregister_class(AddChain) if __name__ == "__main__": diff --git a/add_advanced_objects/object_laplace_lightning.py b/add_advanced_objects/object_laplace_lightning.py index ceaf6cd6..857682b0 100644 --- a/add_advanced_objects/object_laplace_lightning.py +++ b/add_advanced_objects/object_laplace_lightning.py @@ -16,18 +16,18 @@ # # ##### END GPL LICENSE BLOCK ##### -# NOTE Needs cleanup, reorganizing, make prints optional +# NOTE: moved the winmgr properties to __init__ and scene +# search for context.scene.advanced_objects bl_info = { "name": "Laplacian Lightning", "author": "teldredge", - "version": (0, 2, 7), - "blender": (2, 71, 0), + "version": (0, 2, 9), + "blender": (2, 78, 0), "location": "View3D > Toolshelf > Create Tab", "description": "Lightning mesh generator using laplacian growth algorithm", - "warning": "Beta", + "warning": "", "wiki_url": "http://www.funkboxing.com/wordpress/?p=301", - "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", "category": "Object"} # BLENDER LAPLACIAN LIGHTNING @@ -110,19 +110,43 @@ v0.x - import bpy import time import random -from math import sqrt +from bpy.types import ( + Operator, + Panel, + ) +# from math import sqrt from mathutils import Vector import struct import bisect import os.path + +# -- Globals -- notZero = 0.0000000001 -winmgr = bpy.context.window_manager +# set to True to enable debug prints +DEBUG = False + + +# Utility Functions +# func - function name, text - message, var - variable to print +# it can have one variable to observe +def debug_prints(func="", text="Message", var=None): + global DEBUG + if DEBUG: + print("\n[{}]\nmessage: {}".format(func, text)) + if var: + print("variable: ", var) + + +# pass variables just like for the regular prints +def debug_print_vars(*args, **kwargs): + global DEBUG + if DEBUG: + print(*args, **kwargs) -# UTILITY FXNS # def within(x, y, d): - # CHECK IF x-d <= y <= x+d + # CHECK IF x - d <= y <= x + d if x - d <= y and x + d >= y: return True else: @@ -196,7 +220,7 @@ def jitterCells(aList, jit): def deDupe(seq, idfun=None): - # THANKS TO THIS GUY - http://www.peterbe.com/plog/uniqifiers-benchmark + # Thanks to this guy - http://www.peterbe.com/plog/uniqifiers-benchmark if idfun is None: def idfun(x): return x @@ -211,7 +235,7 @@ def deDupe(seq, idfun=None): return result -# VISUALIZATION FXNS # +# Visulization functions def writeArrayToVoxel(arr, filename): gridS = 64 @@ -222,7 +246,8 @@ def writeArrayToVoxel(arr, filename): try: aGrid[a[0] + half][a[1] + half][a[2] + half] = bitOn except: - print('Particle beyond voxel domain') + debug_prints(func="writeArrayToVoxel", text="Particle beyond voxel domain") + file = open(filename, "wb") for z in range(gridS): for y in range(gridS): @@ -278,15 +303,12 @@ def makeMeshCube(msize): # verts = [(0,0,0),(0,5,0),(5,5,0),(5,0,0),(0,0,5),(0,5,5),(5,5,5),(5,0,5)] verts = [(-m2, -m2, -m2), (-m2, m2, -m2), (m2, m2, -m2), (m2, -m2, -m2), (-m2, -m2, m2), (-m2, m2, m2), (m2, m2, m2), (m2, -m2, m2)] - faces = [(0, 1, 2, 3), (4, 5, 6, 7), (0, 4, 5, 1), (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0)] - + faces = [ + (0, 1, 2, 3), (4, 5, 6, 7), (0, 4, 5, 1), + (1, 5, 6, 2), (2, 6, 7, 3), (3, 7, 4, 0) + ] # Define mesh and object mmesh = bpy.data.meshes.new("Cube") - # mobject = bpy.data.objects.new("Cube", mmesh) - - # Set location and scene of object - # mobject.location = bpy.context.scene.cursor_location - # bpy.context.scene.objects.link(mobject) # Create mesh mmesh.from_pydata(verts, [], faces) @@ -304,8 +326,9 @@ def writeArrayToCubes(arr, gridBU, orig, cBOOL=False, jBOOL=True): ob.location.x = (x * gridBU) + orig[0] ob.location.y = (y * gridBU) + orig[1] ob.location.z = (z * gridBU) + orig[2] - if cBOOL: # MOSTLY UNUSED - # POS+BLUE, NEG-RED, ZERO:BLACK + + if cBOOL: # mostly unused + # pos + blue, neg - red, zero: black col = (1.0, 1.0, 1.0, 1.0) if a[3] == 0: col = (0.0, 0.0, 0.0, 1.0) @@ -316,9 +339,10 @@ def writeArrayToCubes(arr, gridBU, orig, cBOOL=False, jBOOL=True): ob.color = col bpy.context.scene.objects.link(ob) bpy.context.scene.update() + if jBOOL: - # SELECTS ALL CUBES w/ ?bpy.ops.object.join() b/c - # CAN'T JOIN ALL CUBES TO A SINGLE MESH RIGHT... ARGH... + # Selects all cubes w/ ?bpy.ops.object.join() b/c + # Can't join all cubes to a single mesh right... argh... for q in bpy.context.scene.objects: q.select = False if q.name[0:5] == 'xCUBE': @@ -365,7 +389,7 @@ def writeArrayToMesh(mname, arr, gridBU, rpt=None): return mob -# OUT OF ORDER - SOME PROBLEM WITH IT ADDING (0,0,0) +# out of order - some problem with it adding (0,0,0) def writeArrayToCurves(cname, arr, gridBU, bd=.05, rpt=None): cur = bpy.data.curves.new('fslg_curve', 'CURVE') cur.use_fill_front = False @@ -374,12 +398,14 @@ def writeArrayToCurves(cname, arr, gridBU, bd=.05, rpt=None): cur.bevel_resolution = 2 cob = bpy.data.objects.new(cname, cur) cob.scale = (gridBU, gridBU, gridBU) + if rpt: addReportProp(cob, rpt) bpy.context.scene.objects.link(cob) cur.splines.new('BEZIER') cspline = cur.splines[0] - div = 1 # SPACING FOR HANDLES (2 - 1/2 WAY, 1 - NEXT BEZIER) + div = 1 # spacing for handles (2 - 1/2 way, 1 - next bezier) + for a in range(len(arr)): cspline.bezier_points.add(1) bp = cspline.bezier_points[len(cspline.bezier_points) - 1] @@ -418,17 +444,18 @@ def addMaterial(ob, matname): def writeStokeToMesh(arr, jarr, MAINi, HORDERi, TIPSi, orig, gs, rpt=None): - # MAIN BRANCH - print(' WRITING MAIN BRANCH') + # main branch + debug_prints(func="writeStokeToMesh", text='Writing main branch') llmain = [] + for x in MAINi: llmain.append(jarr[x]) mob = writeArrayToMesh('la0MAIN', llmain, gs) mob.location = orig - # hORDER BRANCHES + # horder branches for hOi in range(len(HORDERi)): - print(' WRITING ORDER', hOi) + debug_prints(func="writeStokeToMesh", text="Writing order", var=hOi) hO = HORDERi[hOi] hob = newMesh('la1H' + str(hOi)) @@ -440,8 +467,8 @@ def writeStokeToMesh(arr, jarr, MAINi, HORDERi, TIPSi, orig, gs, rpt=None): hob.scale = (gs, gs, gs) hob.location = orig - # TIPS - print(' WRITING TIP PATHS') + # tips + debug_prints(func="writeStokeToMesh", text="Writing tip paths") tob = newMesh('la2TIPS') for y in TIPSi: llt = [] @@ -451,16 +478,17 @@ def writeStokeToMesh(arr, jarr, MAINi, HORDERi, TIPSi, orig, gs, rpt=None): tob.scale = (gs, gs, gs) tob.location = orig - # ADD MATERIALS TO OBJECTS (IF THEY EXIST) + # add materials to objects (if they exist) try: addMaterial(mob, 'edgeMAT-h0') addMaterial(hob, 'edgeMAT-h1') addMaterial(tob, 'edgeMAT-h2') - print(' ADDED MATERIALS') + debug_prints(func="writeStokeToMesh", text="Added materials") + except: - print(' MATERIALS NOT FOUND') + debug_prints(func="writeStokeToMesh", text="Materials not found") - # ADD GENERATION REPORT TO ALL MESHES + # add generation report to all meshes if rpt: addReportProp(mob, rpt) addReportProp(hob, rpt) @@ -486,62 +514,66 @@ def writeStokeToSingleMesh(arr, jarr, orig, gs, mct, rpt=None): def visualizeArray(cg, oob, gs, vm, vs, vc, vv, rst): + winmgr = bpy.context.scene.advanced_objects # IN: (cellgrid, origin, gridscale, # mulimesh, single mesh, cubes, voxels, report sting) origin = oob.location - # DEAL WITH VERT MULTI-ORIGINS + # deal with vert multi-origins oct = 2 if oob.type == 'MESH': oct = len(oob.data.vertices) - # JITTER CELLS + # jitter cells if vm or vs: cjarr = jitterCells(cg, 1) - if vm: # WRITE ARRAY TO MULTI MESH + if vm: # write array to multi mesh aMi, aHi, aTi = classifyStroke(cg, oct, winmgr.HORDER) - print(':::WRITING TO MULTI-MESH') + debug_prints(func="visualizeArray", text="Writing to multi-mesh") writeStokeToMesh(cg, cjarr, aMi, aHi, aTi, origin, gs, rst) - print(':::MULTI-MESH WRITTEN') + debug_prints(func="visualizeArray", text="Multi-mesh written") - if vs: # WRITE TO SINGLE MESH - print(':::WRITING TO SINGLE MESH') + if vs: # write to single mesh + debug_prints(func="visualizeArray", text="Writing to single mesh") writeStokeToSingleMesh(cg, cjarr, origin, gs, oct, rst) - print(':::SINGLE MESH WRITTEN') + debug_prints(func="visualizeArray", text="Single mesh written") - if vc: # WRITE ARRAY TO CUBE OBJECTS - print(':::WRITING TO CUBES') + if vc: # write array to cube objects + debug_prints(func="visualizeArray", text="Writing to cubes") writeArrayToCubes(cg, gs, origin) - print(':::CUBES WRITTEN') + debug_prints(func="visualizeArray", text="Cubes written") - if vv: # WRITE ARRAY TO VOXEL DATA FILE - print(':::WRITING TO VOXELS') + if vv: # write array to voxel data file + debug_prints(func="visualizeArray", text="Writing to voxels") fname = "FSLGvoxels.raw" path = os.path.dirname(bpy.data.filepath) writeArrayToVoxel(cg, path + "\\" + fname) - print(':::VOXEL DATA WRITTEN TO - ', path + "\\" + fname) - # READ/WRITE ARRAY TO FILE (MIGHT NOT BE NECESSARY) + debug_prints(func="visualizeArray", + text="Voxel data written to:", var=path + "\\" + fname) + + # read/write array to file (might not be necessary) # tfile = 'c:\\testarr.txt' # writeArrayToFile(cg, tfile) # cg = readArrayFromFile(tfile) - # READ/WRITE ARRAY TO CURVES (OUT OF ORDER) + # read/write array to curves (out of order) # writeArrayToCurves('laMAIN', llmain, .10, .25) -# ALGORITHM FXNS # -# FROM FALUAM PAPER # -# PLUS SOME STUFF I MADE UP # +# Algorithm functions +# from faluam paper +# plus some stuff i made up def buildCPGraph(arr, sti=2): - # IN -XYZ ARRAY AS BUILT BY GENERATOR - # OUT -[(CHILDindex, PARENTindex)] - # sti - start index, 2 for Empty, len(me.vertices) for Mesh + # in -xyz array as built by generator + # out -[(childindex, parentindex)] + # sti - start index, 2 for empty, len(me.vertices) for mesh sgarr = [] sgarr.append((1, 0)) + for ai in range(sti, len(arr)): cs = arr[ai] cpts = arr[0:ai] @@ -552,13 +584,14 @@ def buildCPGraph(arr, sti=2): if ct > 0: cti = cpts.index(nc) sgarr.append((ai, cti)) + return sgarr def buildCPGraph_WORKINPROGRESS(arr, sti=2): - # IN -XYZ ARRAY AS BUILT BY GENERATOR - # OUT -[(CHILDindex, PARENTindex)] - # sti - start index, 2 for Empty, len(me.vertices) for Mesh + # in -xyz array as built by generator + # out -[(childindex, parentindex)] + # sti - start index, 2 for empty, len(me.vertices) for mesh sgarr = [] sgarr.append((1, 0)) ctix = 0 @@ -575,13 +608,14 @@ def buildCPGraph_WORKINPROGRESS(arr, sti=2): ctix = cpts.index(nc) sgarr.append((ai, cti)) + return sgarr def findChargePath(oc, fc, ngraph, restrict=[], partial=True): - # oc -ORIGIN CHARGE INDEX, fc -FINAL CHARGE INDEX - # ngraph -NODE GRAPH, restrict- INDEX OF SITES CANNOT TRAVERSE - # partial -RETURN PARTIAL PATH IF RESTRICTION ENCOUNTERD + # oc -origin charge index, fc -final charge index + # ngraph -node graph, restrict- index of sites cannot traverse + # partial -return partial path if restriction encounterd cList = splitList(ngraph, 0) pList = splitList(ngraph, 1) aRi = [] @@ -591,13 +625,13 @@ def findChargePath(oc, fc, ngraph, restrict=[], partial=True): aRi.append(cNODE) cNODE = pNODE npNODECOUNT = cList.count(pNODE) - if cNODE == oc: # STOP IF ORIGIN FOUND - aRi.append(cNODE) # RETURN PATH + if cNODE == oc: # stop if origin found + aRi.append(cNODE) # return path return aRi - if npNODECOUNT == 0: # STOP IF NO PARENTS - return [] # RETURN [] - if pNODE in restrict: # STOP IF PARENT IS IN RESTRICTION - if partial: # RETURN PARTIAL OR [] + if npNODECOUNT == 0: # stop if no parents + return [] # return [] + if pNODE in restrict: # stop if parent is in restriction + if partial: # return partial or [] aRi.append(cNODE) return aRi else: @@ -615,6 +649,7 @@ def findTips(arr): cCOUNT += 1 if cCOUNT == 0: lt.append(a) + return lt @@ -626,6 +661,7 @@ def findChannelRoots(path, ngraph, restrict=[]): if par in path and chi not in path and chi not in restrict: roots.append(par) droots = deDupe(roots) + return droots @@ -648,10 +684,14 @@ def findChannels(roots, tips, ngraph, restrict): tTEMP = t tiTEMP = ti if len(sPATHi) > 0: - print(' found path/idex from', ri, 'of', - len(roots), 'possible | tips:', tTEMP, tiTEMP) + debug_print_vars( + "\n[findChannels]\n", + "found path/idex from", ri, 'of', + len(roots), "possible | tips:", tTEMP, tiTEMP + ) cPATHS.append(sPATHi) tips.remove(tTEMP) + return cPATHS @@ -662,13 +702,12 @@ def findChannels_WORKINPROGRESS(roots, ttips, ngraph, restrict): r = roots[ri] sL = 1 sPATHi = [] - tipREMOVE = [] # CHECKED TIP INDEXES, TO BE REMOVED FOR NEXT LOOP + tipREMOVE = [] # checked tip indexes, to be removed for next loop for ti in range(len(tips)): t = tips[ti] - # print('-CHECKING RT/IDEX:', r, ri, 'AGAINST TIP', t, ti) - # if t < r: continue if ti < ri: continue + tPATHi = findChargePath(r, t, ngraph, restrict, False) tL = len(tPATHi) if tL > sL: @@ -680,9 +719,13 @@ def findChannels_WORKINPROGRESS(roots, ttips, ngraph, restrict): if tL > 0: tipREMOVE.append(t) if len(sPATHi) > 0: - print(' found path from root idex', ri, 'of', - len(roots), 'possible roots | #oftips=', len(tips)) + debug_print_vars( + "\n[findChannels_WORKINPROGRESS]\n", + "found path from root idex", ri, 'of', + len(roots), "possible roots | of tips= ", len(tips) + ) cPATHS.append(sPATHi) + for q in tipREMOVE: tips.remove(q) @@ -690,65 +733,72 @@ def findChannels_WORKINPROGRESS(roots, ttips, ngraph, restrict): def countChildrenOnPath(aPath, ngraph, quick=True): - # RETURN HOW MANY BRANCHES - # COUNT WHEN NODE IS A PARENT >1 TIMES - # quick -STOP AND RETURN AFTER FIRST + # return how many branches + # count when node is a parent >1 times + # quick -stop and return after first cCOUNT = 0 pList = splitList(ngraph, 1) + for ai in range(len(aPath) - 1): ap = aPath[ai] pc = pList.count(ap) + if quick and pc > 1: return pc + return cCOUNT -# CLASSIFY CHANNELS INTO 'MAIN', 'hORDER/SECONDARY' and 'SIDE' +# classify channels into 'main', 'hORDER/secondary' and 'side' def classifyStroke(sarr, mct, hORDER=1): - print(':::CLASSIFYING STROKE') - # BUILD CHILD/PARENT GRAPH (INDEXES OF sarr) + debug_prints(func="classifyStroke", text="Classifying stroke") + # build child/parent graph (indexes of sarr) sgarr = buildCPGraph(sarr, mct) - # FIND MAIN CHANNEL - print(' finding MAIN') + # find main channel + debug_prints(func="classifyStroke", text="Finding MAIN") oCharge = sgarr[0][1] fCharge = sgarr[len(sgarr) - 1][0] aMAINi = findChargePath(oCharge, fCharge, sgarr) - # FIND TIPS - print(' finding TIPS') + # find tips + debug_prints(func="classifyStroke", text="Finding TIPS") aTIPSi = findTips(sgarr) - # FIND hORDER CHANNEL ROOTS - # hCOUNT = ORDERS BEWTEEN MAIN and SIDE/TIPS - # !!!STILL BUGGY!!! - hRESTRICT = list(aMAINi) # ADD TO THIS AFTER EACH TIME - allHPATHSi = [] # ALL hO PATHS: [[h0], [h1]...] - curPATHSi = [aMAINi] # LIST OF PATHS FIND ROOTS ON + # find horder channel roots + # hcount = orders bewteen main and side/tips + # !!!still buggy!!! + hRESTRICT = list(aMAINi) # add to this after each time + allHPATHSi = [] # all ho paths: [[h0], [h1]...] + curPATHSi = [aMAINi] # list of paths find roots on + for h in range(hORDER): allHPATHSi.append([]) - for pi in range(len(curPATHSi)): # LOOP THROUGH ALL PATHS IN THIS ORDER + for pi in range(len(curPATHSi)): # loop through all paths in this order p = curPATHSi[pi] - # GET ROOTS FOR THIS PATH + # get roots for this path aHROOTSi = findChannelRoots(p, sgarr, hRESTRICT) - print(' found', len(aHROOTSi), 'roots in ORDER', h, ':#paths:', len(curPATHSi)) - # GET CHANNELS FOR THESE ROOTS + debug_print_vars( + "\n[classifyStroke]\n", + "found", len(aHROOTSi), "roots in ORDER", h, ":paths:", len(curPATHSi) + ) + # get channels for these roots if len(aHROOTSi) == 0: - print('NO ROOTS FOR FOUND FOR CHANNEL') + debug_prints(func="classifyStroke", text="No roots for found for channel") aHPATHSi = [] continue else: aHPATHSiD = findChannels(aHROOTSi, aTIPSi, sgarr, hRESTRICT) aHPATHSi = aHPATHSiD allHPATHSi[h] += aHPATHSi - # SET THESE CHANNELS AS RESTRICTIONS FOR NEXT ITERATIONS + # set these channels as restrictions for next iterations for hri in aHPATHSi: hRESTRICT += hri curPATHSi = aHPATHSi - # SIDE BRANCHES, FINAL ORDER OF HEIRARCHY - # FROM TIPS THAT ARE NOT IN AN EXISTING PATH - # BACK TO ANY OTHER POINT THAT IS ALREADY ON A PATH + # side branches, final order of heirarchy + # from tips that are not in an existing path + # back to any other point that is already on a path aDRAWNi = [] aDRAWNi += aMAINi for oH in allHPATHSi: @@ -765,24 +815,25 @@ def classifyStroke(sarr, mct, hORDER=1): def voxelByVertex(ob, gs): - # 'VOXELIZES' VERTS IN A MESH TO LIST [(x,y,z),(x,y,z)] - # W/ RESPECT GSCALE AND OB ORIGIN (B/C SHOULD BE ORIGIN OBJ) - orig = ob.location + # 'voxelizes' verts in a mesh to list [(x,y,z),(x,y,z)] + # w/ respect gscale and ob origin (b/c should be origin obj) + # orig = ob.location ll = [] for v in ob.data.vertices: x = int(v.co.x / gs) y = int(v.co.y / gs) z = int(v.co.z / gs) ll.append((x, y, z)) + return ll def voxelByRays(ob, orig, gs): - # MESH INTO A 3DGRID W/ RESPECT GSCALE AND BOLT ORIGIN - # -DOES NOT TAKE OBJECT ROTATION/SCALE INTO ACCOUNT - # -THIS IS A HORRIBLE, INEFFICIENT FUNCTION - # MAYBE THE RAYCAST/GRID THING ARE A BAD IDEA. BUT I - # HAVE TO 'VOXELIZE THE OBJECT W/ RESCT TO GSCALE/ORIGIN + # mesh into a 3dgrid w/ respect gscale and bolt origin + # - does not take object rotation/scale into account + # - this is a horrible, inefficient function + # maybe the raycast/grid thing are a bad idea. but i + # have to 'voxelize the object w/ resct to gscale/origin bbox = ob.bound_box bbxL = bbox[0][0] bbxR = bbox[4][0] @@ -796,11 +847,17 @@ def voxelByRays(ob, orig, gs): xs = int(xct / 2) ys = int(yct / 2) zs = int(zct / 2) - print(' CASTING', xct, '/', yct, '/', zct, 'cells, total:', xct * yct * zct, 'in obj-', ob.name) + + debug_print_vars( + "\n[voxelByRays]\n", + "Casting", xct, '/', yct, '/', zct, 'cells, total:', + xct * yct * zct, 'in obj-', ob.name + ) ll = [] - rc = 100 # DISTANCE TO CAST FROM - # RAYCAST TOP/BOTTOM - print(' RAYCASTING TOP/BOTTOM') + rc = 100 # distance to cast from + # raycast top/bottom + debug_prints(func="voxelByRays", text="Raycasting top/bottom") + for x in range(xct): for y in range(yct): xco = bbxL + (x * gs) @@ -809,12 +866,20 @@ def voxelByRays(ob, orig, gs): v2 = ((xco, yco, -rc)) vz1 = ob.ray_cast(v1, v2) vz2 = ob.ray_cast(v2, v1) - if vz1[2] != -1: - ll.append((x - xs, y - ys, int(vz1[0][2] * (1 / gs)))) - if vz2[2] != -1: - ll.append((x - xs, y - ys, int(vz2[0][2] * (1 / gs)))) - # RAYCAST FRONT/BACK - print(' RAYCASTING FRONT/BACK') + + debug_print_vars( + "\n[voxelByRays]\n", "vz1 is: ", vz1, "\nvz2 is: ", vz2 + ) + # Note: the API raycast return has changed now it is + # (result, location, normal, index) - result is a boolean + if vz1[0] is True: + ll.append((x - xs, y - ys, int(vz1[1][2] * (1 / gs)))) + if vz2[0] is True: + ll.append((x - xs, y - ys, int(vz2[1][2] * (1 / gs)))) + + # raycast front/back + debug_prints(func="voxelByRays", text="Raycasting front/back") + for x in range(xct): for z in range(zct): xco = bbxL + (x * gs) @@ -823,12 +888,14 @@ def voxelByRays(ob, orig, gs): v2 = ((xco, -rc, zco)) vy1 = ob.ray_cast(v1, v2) vy2 = ob.ray_cast(v2, v1) - if vy1[2] != -1: - ll.append((x - xs, int(vy1[0][1] * (1 / gs)), z - zs)) - if vy2[2] != -1: - ll.append((x - xs, int(vy2[0][1] * (1 / gs)), z - zs)) - # RAYCAST LEFT/RIGHT - print(' RAYCASTING LEFT/RIGHT') + if vy1[0] is True: + ll.append((x - xs, int(vy1[1][1] * (1 / gs)), z - zs)) + if vy2[0] is True: + ll.append((x - xs, int(vy2[1][1] * (1 / gs)), z - zs)) + + # raycast left/right + debug_prints(func="voxelByRays", text="Raycasting left/right") + for y in range(yct): for z in range(zct): yco = bbyL + (y * gs) @@ -837,24 +904,24 @@ def voxelByRays(ob, orig, gs): v2 = ((-rc, yco, zco)) vx1 = ob.ray_cast(v1, v2) vx2 = ob.ray_cast(v2, v1) - if vx1[2] != -1: - ll.append((int(vx1[0][0] * (1 / gs)), y - ys, z - zs)) - if vx2[2] != -1: - ll.append((int(vx2[0][0] * (1 / gs)), y - ys, z - zs)) + if vx1[0] is True: + ll.append((int(vx1[1][0] * (1 / gs)), y - ys, z - zs)) + if vx2[0] is True: + ll.append((int(vx2[1][0] * (1 / gs)), y - ys, z - zs)) - # ADD IN NEIGHBORS SO BOLT WONT GO THRU + # add in neighbors so bolt wont go through nlist = [] for l in ll: nl = getStencil3D_26(l[0], l[1], l[2]) nlist += nl - # DEDUPE - print(' ADDED NEIGHBORS, DEDUPING...') + # dedupe + debug_prints(func="voxelByRays", text="Added neighbors, deduping...") rlist = deDupe(ll + nlist) qlist = [] - # RELOCATE GRID W/ RESPECT GSCALE AND BOLT ORIGIN - # !!!NEED TO ADD IN OBJ ROT/SCALE HERE SOMEHOW... + # relocate grid w/ respect gscale and bolt origin + # !!!need to add in obj rot/scale here somehow... od = Vector( ((ob.location[0] - orig[0]) / gs, (ob.location[1] - orig[1]) / gs, @@ -874,24 +941,25 @@ def fakeGroundChargePlane(z, charge): eCL += [(0, xy, z, charge)] eCL += [(-xy, 0, z, charge)] eCL += [(0, -xy, z, charge)] + return eCL def addCharges(ll, charge): - # IN: ll - [(x,y,z), (x,y,z)], charge - w - # OUT clist - [(x,y,z,w), (x,y,z,w)] + # in: ll - [(x,y,z), (x,y,z)], charge - w + # out clist - [(x,y,z,w), (x,y,z,w)] clist = [] for l in ll: clist.append((l[0], l[1], l[2], charge)) return clist -# ALGORITHM FXNS # -# FROM FSLG # +# algorithm functions # +# from fslg # def getGrowthProbability_KEEPFORREFERENCE(uN, aList): - # IN: uN -USER TERM, cList -CANDIDATE SITES, oList -CANDIDATE SITE CHARGES - # OUT: LIST OF [(XYZ), POT, PROB] + # in: un -user term, clist -candidate sites, olist -candidate site charges + # out: list of [(xyz), pot, prob] cList = splitList(aList, 0) oList = splitList(aList, 1) Omin, Omax = getLowHigh(oList) @@ -900,20 +968,21 @@ def getGrowthProbability_KEEPFORREFERENCE(uN, aList): Omin -= notZero PdL = [] E = 0 - E = notZero # DIVISOR FOR (FSLG - Eqn. 12) + E = notZero # divisor for (fslg - eqn. 12) for o in oList: - Uj = (o - Omin) / (Omax - Omin) # (FSLG - Eqn. 13) + Uj = (o - Omin) / (Omax - Omin) # (fslg - eqn. 13) E += pow(Uj, uN) for oi in range(len(oList)): o = oList[oi] Ui = (o - Omin) / (Omax - Omin) - Pd = (pow(Ui, uN)) / E # (FSLG - Eqn. 12) + Pd = (pow(Ui, uN)) / E # (fslg - eqn. 12) PdINT = Pd * 100 PdL.append(Pd) + return PdL -# WORK IN PROGRESS, TRYING TO SPEED THESE UP +# work in progress, trying to speed these up def fslg_e13(x, min, max, u): return pow((x - min) / (max - min), u) @@ -927,14 +996,16 @@ def fslg_e12(x, min, max, u, e): def getGrowthProbability(uN, aList): - # IN: uN -USER TERM, cList -CANDIDATE SITES, oList -CANDIDATE SITE CHARGES - # OUT: LIST OF PROB + # In: uN - user_term, cList - candidate sites, oList - candidate site charges + # Out: list of prob cList = splitList(aList, 0) oList = splitList(aList, 1) Omin, Omax = getLowHigh(oList) + if Omin == Omax: Omax += notZero Omin -= notZero + PdL = [] E = notZero minL = [Omin for q in range(len(oList))] @@ -943,15 +1014,17 @@ def getGrowthProbability(uN, aList): E = sum(map(fslg_e13, oList, minL, maxL, uNL)) EL = [E for q in range(len(oList))] mp = map(fslg_e12, oList, minL, maxL, uNL, EL) + for m in mp: PdL.append(m) + return PdL def updatePointCharges(p, cList, eList=[]): - # IN: pNew -NEW GROWTH CELL - # cList -OLD CANDIDATE SITES, eList -SAME - # OUT: LIST OF NEW CHARGE AT CANDIDATE SITES + # In: pNew - new growth cell + # cList - old candidate sites, eList -SAME + # Out: list of new charge at candidate sites r1 = 1 / 2 # (FSLG - Eqn. 10) nOiL = [] for oi in range(len(cList)): @@ -962,12 +1035,13 @@ def updatePointCharges(p, cList, eList=[]): iOe += (1 - (r1 / rit)) Oit = o + iOe nOiL.append((c, Oit)) + return nOiL def initialPointCharges(pList, cList, eList=[]): - # IN: p -CHARGED CELL (XYZ), cList -CANDIDATE SITES (XYZ, POT, PROB) - # OUT: cList -WITH POTENTIAL CALCULATED + # In: p -CHARGED CELL (XYZ), cList -candidate sites (XYZ, POT, PROB) + # Out: cList -with potential calculated r1 = 1 / 2 # (FSLG - Eqn. 10) npList = [] for p in pList: @@ -980,15 +1054,15 @@ def initialPointCharges(pList, cList, eList=[]): for j in npList: if i != j[0]: rij = dist(i[0], i[1], i[2], j[0][0], j[0][1], j[0][2]) - Oi += (1 - (r1 / rij)) * j[1] # CHARGE INFLUENCE + Oi += (1 - (r1 / rij)) * j[1] # charge influence OiL.append(((i[0], i[1], i[2]), Oi)) + return OiL def getCandidateSites(aList, iList=[]): - # IN: aList -(X,Y,Z) OF CHARGED CELL SITES, iList -insulator sites - # OUT: CANDIDATE LIST OF GROWTH SITES [(X,Y,Z)] - tt1 = time.clock() + # In: aList -(X,Y,Z) of charged cell sites, iList - insulator sites + # Out: candidate list of growth sites [(X,Y,Z)] cList = [] for c in aList: tempList = getStencil3D_26(c[0], c[1], c[2]) @@ -996,14 +1070,14 @@ def getCandidateSites(aList, iList=[]): if t not in aList and t not in iList: cList.append(t) ncList = deDupe(cList) - tt2 = time.clock() return ncList -# SETUP FXNS # +# Setup functions def setupObjects(): + winmgr = bpy.context.scene.advanced_objects oOB = bpy.data.objects.new('ELorigin', None) oOB.location = ((0, 0, 10)) bpy.context.scene.objects.link(oOB) @@ -1041,27 +1115,38 @@ def setupObjects(): def checkSettings(): check = True + winmgr = bpy.context.scene.advanced_objects + message = "" if winmgr.OOB == "": - print('ERROR: NO ORIGIN OBJECT SELECTED') + message = "Error: no origin object selected" check = False + if winmgr.GROUNDBOOL and winmgr.GOB == "": - print('ERROR: NO GROUND OBJECT SELECTED') + message = "Error: no ground object selected" check = False + if winmgr.CLOUDBOOL and winmgr.COB == "": - print('ERROR: NO CLOUD OBJECT SELECTED') + message = "Error: no cloud object selected" check = False + if winmgr.IBOOL and winmgr.IOB == "": - print('ERROR: NO INSULATOR OBJECT SELECTED') + message = "Error: no insulator object selected" check = False - # should make a popup here - return check + if check is False: + debug_prints(func="checkSettings", text=message) -# MAIN # + # return state and the message for the operator report + return check, message + + +# Main def FSLG(): - # FAST SIMULATION OF LAPLACIAN GROWTH # - print('\n<<<<<<------GO GO GADGET: FAST SIMULATION OF LAPLACIAN GROWTH!') + winmgr = bpy.context.scene.advanced_objects + # fast simulation of laplacian growth + debug_prints(func="FSLG", + text="Go go gadget: fast simulation of laplacian growth") tc1 = time.clock() TSTEPS = winmgr.TSTEPS @@ -1070,214 +1155,183 @@ def FSLG(): winmgr.ORIGIN = obORIGIN.location winmgr.GROUNDZ = int((obGROUND.location[2] - winmgr.ORIGIN[2]) / winmgr.GSCALE) - # 1) INSERT INTIAL CHARGE(S) POINT (USES VERTS IF MESH) + # 1) insert intial charge(s) point (uses verts if mesh) cgrid = [(0, 0, 0)] + if obORIGIN.type == 'MESH': - print("<<<<<<------ORIGIN OBJECT IS MESH, 'VOXELIZING' INTIAL CHARGES FROM VERTS") + debug_prints( + func="FSLG", + text="Origin object is mesh, 'voxelizing' intial charges from verts" + ) cgrid = voxelByVertex(obORIGIN, winmgr.GSCALE) + if winmgr.VMMESH: - print("<<<<<<------CANNOT CLASSIFY STROKE FROM VERT ORIGINS YET, NO MULTI-MESH OUTPUT") + debug_prints( + func="FSLG", + text="Cannot classify stroke from vert origins yet, no multi-mesh output" + ) winmgr.VMMESH = False winmgr.VSMESH = True - # GROUND CHARGE CELL / INSULATOR LISTS (eChargeList/icList) + # ground charge cell / insulator lists (echargelist/iclist) eChargeList = [] icList = [] if winmgr.GROUNDBOOL: eChargeList = fakeGroundChargePlane(winmgr.GROUNDZ, winmgr.GROUNDC) + if winmgr.CLOUDBOOL: - print("<<<<<<------'VOXELIZING' CLOUD OBJECT (COULD TAKE SOME TIME)") + debug_prints( + func="FSLG", + text="'Voxelizing' cloud object (could take some time)" + ) obCLOUD = bpy.context.scene.objects[winmgr.COB] eChargeListQ = voxelByRays(obCLOUD, winmgr.ORIGIN, winmgr.GSCALE) eChargeList = addCharges(eChargeListQ, winmgr.CLOUDC) - print('<<<<<<------CLOUD OBJECT CELL COUNT = ', len(eChargeList)) + debug_prints( + func="FSLG", + text="cloud object cell count", var=len(eChargeList) + ) + if winmgr.IBOOL: - print("<<<<<<------'VOXELIZING' INSULATOR OBJECT (COULD TAKE SOME TIME)") + debug_prints( + func="FSLG", + text="'Voxelizing' insulator object (could take some time)" + ) obINSULATOR = bpy.context.scene.objects[winmgr.IOB] icList = voxelByRays(obINSULATOR, winmgr.ORIGIN, winmgr.GSCALE) - print('<<<<<<------INSULATOR OBJECT CELL COUNT = ', len(icList)) - # 2) LOCATE CANDIDATE SITES AROUND CHARGE + debug_prints( + func="FSLG", + text="Insulator object cell count", var=len(icList) + ) + + # 2) locate candidate sites around charge cSites = getCandidateSites(cgrid, icList) - # 3) CALC POTENTIAL AT EACH SITE (Eqn. 10) + # 3) calc potential at each site (eqn. 10) cSites = initialPointCharges(cgrid, cSites, eChargeList) ts = 1 while ts <= TSTEPS: - # 1) SELECT NEW GROWTH SITE (Eqn. 12) - # GET PROBABILITIES AT CANDIDATE SITES + # 1) select new growth site (eqn. 12) + # get probabilities at candidate sites gProbs = getGrowthProbability(winmgr.BIGVAR, cSites) - # CHOOSE NEW GROWTH SITE BASED ON PROBABILITIES + # choose new growth site based on probabilities gSitei = weightedRandomChoice(gProbs) gsite = cSites[gSitei][0] - # 2) ADD NEW POINT CHARGE AT GROWTH SITE - # ADD NEW GROWTH CELL TO GRID + # 2) add new point charge at growth site + # add new growth cell to grid cgrid.append(gsite) - # REMOVE NEW GROWTH CELL FROM CANDIDATE SITES + # remove new growth cell from candidate sites cSites.remove(cSites[gSitei]) - # 3) UPDATE POTENTIAL AT CANDIDATE SITES (Eqn. 11) + # 3) update potential at candidate sites (eqn. 11) cSites = updatePointCharges(gsite, cSites, eChargeList) - # 4) ADD NEW CANDIDATES SURROUNDING GROWTH SITE - # GET CANDIDATE 'STENCIL' + # 4) add new candidates surrounding growth site + # get candidate 'stencil' ncSitesT = getCandidateSites([gsite], icList) - # REMOVE CANDIDATES ALREADY IN CANDIDATE LIST OR CHARGE GRID + # remove candidates already in candidate list or charge grid ncSites = [] cSplit = splitList(cSites, 0) for cn in ncSitesT: if cn not in cSplit and cn not in cgrid: ncSites.append((cn, 0)) - # 5) CALC POTENTIAL AT NEW CANDIDATE SITES (Eqn. 10) + # 5) calc potential at new candidate sites (eqn. 10) ncSplit = splitList(ncSites, 0) ncSites = initialPointCharges(cgrid, ncSplit, eChargeList) - # ADD NEW CANDIDATE SITES TO CANDIDATE LIST + # add new candidate sites to candidate list for ncs in ncSites: cSites.append(ncs) - # ITERATION COMPLETE + # iteration complete istr1 = ':::T-STEP: ' + str(ts) + '/' + str(TSTEPS) istr12 = ' | GROUNDZ: ' + str(winmgr.GROUNDZ) + ' | ' istr2 = 'CANDS: ' + str(len(cSites)) + ' | ' istr3 = 'GSITE: ' + str(gsite) - print(istr1 + istr12 + istr2 + istr3) + debug_prints( + func="FSLG", + text="Iteration complete", + var=istr1 + istr12 + istr2 + istr3 + ) ts += 1 - # EARLY TERMINATION FOR GROUND/CLOUD STRIKE + # early termination for ground/cloud strike if winmgr.GROUNDBOOL: if gsite[2] == winmgr.GROUNDZ: ts = TSTEPS + 1 - print('<<<<<<------EARLY TERMINATION DUE TO GROUNDSTRIKE') + debug_prints( + func="FSLG", + text="Early termination due to groundstrike" + ) continue + if winmgr.CLOUDBOOL: if gsite in splitListCo(eChargeList): ts = TSTEPS + 1 - print('<<<<<<------EARLY TERMINATION DUE TO CLOUDSTRIKE') + debug_prints( + func="FSLG", + text="Early termination due to cloudstrike" + ) continue tc2 = time.clock() tcRUN = tc2 - tc1 - print('<<<<<<------LAPLACIAN GROWTH LOOP COMPLETED: ' + str(len(cgrid)) + ' / ' + str(tcRUN)[0:5] + ' SECONDS') - print('<<<<<<------VISUALIZING DATA') + debug_prints( + func="FSLG", + text="Laplacian growth loop completed", + var=str(len(cgrid)) + " / " + str(tcRUN)[0:5] + " Seconds" + ) + debug_prints(func="FSLG", text="Visualizing data") reportSTRING = getReportString(tcRUN) - # VISUALIZE ARRAY - visualizeArray( - cgrid, obORIGIN, winmgr.GSCALE, - winmgr.VMMESH, winmgr.VSMESH, - winmgr.VCUBE, winmgr.VVOX, reportSTRING - ) - print('<<<<<<------COMPLETE') - -# GUI # + # Visualize array + visualizeArray( + cgrid, obORIGIN, winmgr.GSCALE, + winmgr.VMMESH, winmgr.VSMESH, + winmgr.VCUBE, winmgr.VVOX, reportSTRING + ) -# NOT IN UI -bpy.types.WindowManager.ORIGIN = bpy.props.FloatVectorProperty(name="origin charge") -bpy.types.WindowManager.GROUNDZ = bpy.props.IntProperty(name="ground Z coordinate") -bpy.types.WindowManager.HORDER = bpy.props.IntProperty(name="secondary paths orders") -# IN UI -bpy.types.WindowManager.TSTEPS = bpy.props.IntProperty( - name="iterations", - description="number of cells to create, will end early if hits ground plane or cloud") -bpy.types.WindowManager.GSCALE = bpy.props.FloatProperty( - name="grid unit size", - description="scale of cells, .25 = 4 cells per blenderUnit") -bpy.types.WindowManager.BIGVAR = bpy.props.FloatProperty( - name="straightness", - description="straightness/branchiness of bolt, <2 is mush, >12 is staight line, 6.3 is good") -bpy.types.WindowManager.GROUNDBOOL = bpy.props.BoolProperty( - name="use ground object", description="use ground plane or not") -bpy.types.WindowManager.GROUNDC = bpy.props.IntProperty( - name="ground charge", description="charge of ground plane") -bpy.types.WindowManager.CLOUDBOOL = bpy.props.BoolProperty( - name="use cloud object", - description="use cloud obj, attracts and terminates like ground but " - "any obj instead of z plane, can slow down loop if obj is large, overrides ground") -bpy.types.WindowManager.CLOUDC = bpy.props.IntProperty( - name="cloud charge", - description="charge of a cell in cloud object (so total charge also depends on obj size)") - -bpy.types.WindowManager.VMMESH = bpy.props.BoolProperty( - name="multi mesh", - description="output to multi-meshes for different materials on main/sec/side branches") -bpy.types.WindowManager.VSMESH = bpy.props.BoolProperty( - name="single mesh", - description="output to single mesh for using build modifier and particles for effects") -bpy.types.WindowManager.VCUBE = bpy.props.BoolProperty( - name="cubes", description="CTRL-J after run to JOIN, outputs a bunch of cube objest, mostly for testing") -bpy.types.WindowManager.VVOX = bpy.props.BoolProperty( - name="voxel (experimental)", - description="output to a voxel file to bpy.data.filepath\FSLGvoxels.raw - doesn't work well right now") -bpy.types.WindowManager.IBOOL = bpy.props.BoolProperty( - name="use insulator object", description="use insulator mesh object to prevent growth of bolt in areas") -bpy.types.WindowManager.OOB = bpy.props.StringProperty( - description="origin of bolt, can be an Empty, if obj is mesh will use all verts as charges") -bpy.types.WindowManager.GOB = bpy.props.StringProperty( - description="object to use as ground plane, uses z coord only") -bpy.types.WindowManager.COB = bpy.props.StringProperty( - description="object to use as cloud, best to use a cube") -bpy.types.WindowManager.IOB = bpy.props.StringProperty( - description="object to use as insulator, 'voxelized' before generating bolt, can be slow") - -# DEFAULT USER SETTINGS -winmgr.TSTEPS = 350 -winmgr.HORDER = 1 -winmgr.GSCALE = 0.12 -winmgr.BIGVAR = 6.3 -winmgr.GROUNDBOOL = True -winmgr.GROUNDC = -250 -winmgr.CLOUDBOOL = False -winmgr.CLOUDC = -1 -winmgr.VMMESH = True -winmgr.VSMESH = False -winmgr.VCUBE = False -winmgr.VVOX = False -winmgr.IBOOL = False -try: - winmgr.OOB = "ELorigin" - winmgr.GOB = "ELground" - winmgr.COB = "ELcloud" - winmgr.IOB = "ELinsulator" -except: - pass + debug_prints(func="FSLG", text="COMPLETE") -# TESTING USER SETTINGS -if False: - winmgr.TSTEPS = 40 - winmgr.GROUNDBOOL = True - winmgr.CLOUDBOOL = True - winmgr.IBOOL = True +# GUI # -class runFSLGLoopOperator(bpy.types.Operator): - '''By The Mighty Hammer Of Thor!!!''' +class runFSLGLoopOperator(Operator): bl_idname = "object.runfslg_operator" bl_label = "run FSLG Loop Operator" + bl_description = "By The Mighty Hammer Of Thor!!!" def execute(self, context): - if checkSettings(): + # tuple - state, report text + is_conditions, message = checkSettings() + + if is_conditions: FSLG() else: - pass + self.report({'WARNING'}, message + " Operation Cancelled") + + return {'CANCELLED'} + return {'FINISHED'} -class setupObjectsOperator(bpy.types.Operator): - '''create origin/ground/cloud/insulator objects''' +class setupObjectsOperator(Operator): bl_idname = "object.setup_objects_operator" bl_label = "Setup Objects Operator" + bl_description = "Create origin/ground/cloud/insulator objects" def execute(self, context): setupObjects() + return {'FINISHED'} -class OBJECT_PT_fslg(bpy.types.Panel): +class OBJECT_PT_fslg(Panel): bl_label = "Laplacian Lightning" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" @@ -1287,30 +1341,50 @@ class OBJECT_PT_fslg(bpy.types.Panel): def draw(self, context): layout = self.layout - colR = layout.column() - colR.label('-for progress open console-') - colR.label('Help > Toggle System Console') - colR.prop(winmgr, 'TSTEPS') - colR.prop(winmgr, 'GSCALE') - colR.prop(winmgr, 'BIGVAR') - colR.operator('object.setup_objects_operator', text='create setup objects') - colR.label('origin object') - colR.prop_search(winmgr, "OOB", context.scene, "objects") - colR.prop(winmgr, 'GROUNDBOOL') - colR.prop_search(winmgr, "GOB", context.scene, "objects") - colR.prop(winmgr, 'GROUNDC') - colR.prop(winmgr, 'CLOUDBOOL') - colR.prop_search(winmgr, "COB", context.scene, "objects") - colR.prop(winmgr, 'CLOUDC') - colR.prop(winmgr, 'IBOOL') - colR.prop_search(winmgr, "IOB", context.scene, "objects") - colR.operator('object.runfslg_operator', text='generate lightning') - colR.prop(winmgr, 'VMMESH') - colR.prop(winmgr, 'VSMESH') - colR.prop(winmgr, 'VCUBE') + winmgr = context.scene.advanced_objects + + col = layout.column(align=True) + col.prop(winmgr, "TSTEPS") + col.prop(winmgr, "GSCALE") + col.prop(winmgr, "BIGVAR") + + col = layout.column() + col.operator("object.setup_objects_operator", text="Create Setup objects") + col.label("Origin object") + col.prop_search(winmgr, "OOB", context.scene, "objects") + + box = layout.box() + col = box.column() + col.prop(winmgr, "GROUNDBOOL") + if winmgr.GROUNDBOOL: + col.prop_search(winmgr, "GOB", context.scene, "objects") + col.prop(winmgr, "GROUNDC") + + box = layout.box() + col = box.column() + col.prop(winmgr, "CLOUDBOOL") + if winmgr.CLOUDBOOL: + col.prop_search(winmgr, "COB", context.scene, "objects") + col.prop(winmgr, "CLOUDC") + + box = layout.box() + col = box.column() + col.prop(winmgr, "IBOOL") + if winmgr.IBOOL: + col.prop_search(winmgr, "IOB", context.scene, "objects") + + col = layout.column() + col.operator("object.runfslg_operator", + text="Generate Lightning", icon="RNDCURVE") + + row = layout.row(align=True) + row.prop(winmgr, "VMMESH", toggle=True) + row.prop(winmgr, "VSMESH", toggle=True) + row.prop(winmgr, "VCUBE", toggle=True) def getReportString(rtime): + winmgr = bpy.context.scene.advanced_objects rSTRING1 = 't:' + str(winmgr.TSTEPS) + ',sc:' + str(winmgr.GSCALE)[0:4] + ',uv:' + str(winmgr.BIGVAR)[0:4] + ',' rSTRING2 = 'ori:' + str(winmgr. ORIGIN[0]) + '/' + str(winmgr. ORIGIN[1]) + '/' + str(winmgr. ORIGIN[2]) + ',' rSTRING3 = 'gz:' + str(winmgr.GROUNDZ) + ',gc:' + str(winmgr.GROUNDC) + ',rtime:' + str(int(rtime)) @@ -1336,20 +1410,16 @@ def unregister(): if __name__ == "__main__": - # RUN FOR TESTING - # FSLG() - - # UI register() pass -# FXN BENCHMARKS # +# Benchmarks Function def BENCH(): - print('\n\n\n--->BEGIN BENCHMARK') + debug_prints(func="BENCH", text="BEGIN BENCHMARK") bt0 = time.clock() - # MAKE A BIG LIST + # make a big list tsize = 25 tlist = [] for x in range(tsize): @@ -1358,13 +1428,15 @@ def BENCH(): tlist.append((x, y, z)) tlist.append((x, y, z)) - # FUNCTION TO TEST + # function to test bt1 = time.clock() - # print('LENS - ', len(tlist), len(ll)) - bt2 = time.clock() btRUNb = bt2 - bt1 btRUNa = bt1 - bt0 - print('--->SETUP TIME : ', btRUNa) - print('--->BENCHMARK TIME: ', btRUNb) - print('--->GRIDSIZE: ', tsize, ' - ', tsize * tsize * tsize) + + debug_prints(func="BENCH", text="SETUP TIME", var=btRUNa) + debug_prints(func="BENCH", text="BENCHMARK TIME", var=btRUNb) + debug_print_vars( + "\n[BENCH]\n", + "GRIDSIZE: ", tsize, ' - ', tsize * tsize * tsize + ) diff --git a/add_advanced_objects/object_mangle_tools.py b/add_advanced_objects/object_mangle_tools.py index 81110ad3..9f5b44ee 100644 --- a/add_advanced_objects/object_mangle_tools.py +++ b/add_advanced_objects/object_mangle_tools.py @@ -1,7 +1,6 @@ # mangle_tools.py (c) 2011 Phil Cote (cotejrp1) -# -# ***** BEGIN GPL LICENSE BLOCK ***** -# + +# ###### BEGIN GPL LICENSE BLOCK ###### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -10,36 +9,43 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -# ***** END GPL LICENCE BLOCK ***** +# ###### END GPL LICENCE BLOCK ###### + +# Note: properties are moved into __init__ bl_info = { "name": "Mangle Tools", "author": "Phil Cote", - "version": (0, 2), + "version": (0, 2, 2), "blender": (2, 71, 0), "location": "View3D > Toolshelf > Tools Tab", "description": "Set of tools to mangle curves, meshes, and shape keys", - "warning": "", # used for warning icon and text in addons panel + "warning": "", "wiki_url": "", - "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", "category": "Object"} import bpy import random +from bpy.types import ( + Operator, + Panel, + ) import time from math import pi import bmesh + def move_coordinate(context, co, is_curve=False): - xyz_const = context.scene.constraint_vector + advanced_objects = context.scene.advanced_objects + xyz_const = advanced_objects.mangle_constraint_vector random.seed(time.time()) multiplier = 1 @@ -47,95 +53,100 @@ def move_coordinate(context, co, is_curve=False): # This helps make curve changes more noticable. if is_curve: multiplier = 2 * pi - random_mag = context.scene.random_magnitude + random_mag = advanced_objects.mangle_random_magnitude if xyz_const[0]: - co.x += .01 * random.randrange( -random_mag, random_mag ) * multiplier + co.x += .01 * random.randrange(-random_mag, random_mag) * multiplier if xyz_const[1]: - co.y += .01 * random.randrange( -random_mag, random_mag ) * multiplier + co.y += .01 * random.randrange(-random_mag, random_mag) * multiplier if xyz_const[2]: - co.z += .01 * random.randrange( -random_mag, random_mag ) * multiplier + co.z += .01 * random.randrange(-random_mag, random_mag) * multiplier -class MeshManglerOperator(bpy.types.Operator): - """Push vertices on the selected object around in random """ \ - """directions to create a crumpled look""" +class MeshManglerOperator(Operator): bl_idname = "ba.mesh_mangler" bl_label = "Mangle Mesh" - bl_options = { "REGISTER", "UNDO" } + bl_description = ("Push vertices on the selected object around in random\n" + "directions to create a crumpled look") + bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): ob = context.active_object - return ob != None and ob.type == 'MESH' + return ob is not None and ob.type == 'MESH' def execute(self, context): mesh = context.active_object.data bm = bmesh.new() bm.from_mesh(mesh) - verts, faces = bm.verts, bm.faces - randomMag = context.scene.random_magnitude - random.seed( time.time() ) - - if mesh.shape_keys != None: - self.report({'INFO'}, "Cannot mangle mesh: Shape keys present") + verts = bm.verts + advanced_objects = context.scene.advanced_objects + randomMag = advanced_objects.mangle_random_magnitude + random.seed(time.time()) + + if mesh.shape_keys is not None: + self.report({'INFO'}, + "Cannot mangle mesh: Shape keys present. Operation Cancelled") return {'CANCELLED'} for vert in verts: - xVal = .01 * random.randrange( -randomMag, randomMag ) - yVal = .01 * random.randrange( -randomMag, randomMag) - zVal = .01 * random.randrange( -randomMag, randomMag ) + xVal = .01 * random.randrange(-randomMag, randomMag) + yVal = .01 * random.randrange(-randomMag, randomMag) + zVal = .01 * random.randrange(-randomMag, randomMag) + vert.co.x = vert.co.x + xVal vert.co.y = vert.co.y + yVal vert.co.z = vert.co.z + zVal + del verts + bm.to_mesh(mesh) mesh.update() + return {'FINISHED'} -class AnimanglerOperator(bpy.types.Operator): - """Make a shape key and pushes the verts around on it """ \ - """to set up for random pulsating animation""" +class AnimanglerOperator(Operator): bl_idname = "ba.ani_mangler" bl_label = "Mangle Shape Key" - + bl_description = ("Make a shape key and pushes the verts around on it\n" + "to set up for random pulsating animation") @classmethod def poll(cls, context): ob = context.active_object - return ob != None and ob.type in [ 'MESH', 'CURVE' ] + return ob is not None and ob.type in ['MESH', 'CURVE'] def execute(self, context): - scn = context.scene + scn = context.scene.advanced_objects mangleName = scn.mangle_name ob = context.object - shapeKey = ob.shape_key_add( name=mangleName ) + shapeKey = ob.shape_key_add(name=mangleName) verts = shapeKey.data for vert in verts: - move_coordinate(context, vert.co, is_curve=ob.type=='CURVE') + move_coordinate(context, vert.co, is_curve=ob.type == 'CURVE') return {'FINISHED'} -class CurveManglerOp(bpy.types.Operator): - """Mangle a curve to the degree the user specifies""" +class CurveManglerOp(Operator): bl_idname = "ba.curve_mangler" bl_label = "Mangle Curve" - bl_options = { 'REGISTER', 'UNDO' } + bl_description = "Mangle a curve to the degree the user specifies" + bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): ob = context.active_object - return ob != None and ob.type == "CURVE" - + return ob is not None and ob.type == "CURVE" def execute(self, context): - ob = context.active_object - if ob.data.shape_keys != None: - self.report({'INFO'}, "Cannot mangle curve. Shape keys present") + if ob.data.shape_keys is not None: + self.report({'INFO'}, + "Cannot mangle curve. Shape keys present. Operation Cancelled") return {'CANCELLED'} + splines = context.object.data.splines for spline in splines: @@ -150,56 +161,43 @@ class CurveManglerOp(bpy.types.Operator): return {'FINISHED'} -class MangleToolsPanel(bpy.types.Panel): +class MangleToolsPanel(Panel): bl_label = "Mangle Tools" bl_space_type = "VIEW_3D" bl_context = "objectmode" - bl_region_type="TOOLS" + bl_region_type = "TOOLS" bl_category = "Create" bl_options = {'DEFAULT_CLOSED'} - def draw(self, context): - scn = context.scene + scn = context.scene.advanced_objects obj = context.object - if obj.type in ['MESH',]: + + if obj and obj.type in ['MESH']: layout = self.layout + + row = layout.row(align=True) + row.prop(scn, "mangle_constraint_vector", toggle=True) + col = layout.column() - col.prop(scn, "constraint_vector") - col.prop(scn, "random_magnitude") + col.prop(scn, "mangle_random_magnitude") col.operator("ba.mesh_mangler") col.separator() + col.prop(scn, "mangle_name") col.operator("ba.ani_mangler") else: layout = self.layout - col = layout.column() - col.label("Please Select Mesh Object") + layout.label(text="Please select a Mesh Object", icon="INFO") -IntProperty = bpy.props.IntProperty -StringProperty = bpy.props.StringProperty -BoolVectorProperty = bpy.props.BoolVectorProperty def register(): bpy.utils.register_class(AnimanglerOperator) bpy.utils.register_class(MeshManglerOperator) bpy.utils.register_class(CurveManglerOp) bpy.utils.register_class(MangleToolsPanel) - scnType = bpy.types.Scene - - - scnType.constraint_vector = BoolVectorProperty(name="Mangle Constraint", - default=(True,True,True), - subtype='XYZ', - description="Constrains Mangle Direction") - scnType.random_magnitude = IntProperty( name = "Mangle Severity", - default = 5, min = 1, max = 30, - description = "Severity of mangling") - scnType.mangle_name = StringProperty(name="Shape Key Name", - default="mangle", - description="Name given for mangled shape keys") def unregister(): bpy.utils.unregister_class(AnimanglerOperator) bpy.utils.unregister_class(MeshManglerOperator) diff --git a/add_advanced_objects/oscurart_chain_maker.py b/add_advanced_objects/oscurart_chain_maker.py index c336e44d..6dcd6f80 100644 --- a/add_advanced_objects/oscurart_chain_maker.py +++ b/add_advanced_objects/oscurart_chain_maker.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -# TODO: translate the comments into English +# TODO: find English versions of created object names bl_info = { "name": "Oscurart Chain Maker", @@ -27,7 +27,6 @@ bl_info = { "description": "Create chain links from armatures", "warning": "", "wiki_url": "oscurart.blogspot.com", - "tracker_url": "", "category": "Object"} @@ -52,7 +51,7 @@ def makeChain(self, context, mult, curverig): def creahuesocero(hueso): # create data to link mesh = bpy.data.meshes.new("objectData" + str(hueso.name)) - object = bpy.data.objects.new("EslabonCero" + str(hueso.name), mesh) + object = bpy.data.objects.new("HardLink" + str(hueso.name), mesh) mesh.from_pydata( [(-0.04986128956079483, -0.6918092370033264, -0.17846597731113434), (-0.04986128956079483, -0.6918091773986816, 0.17846640944480896), @@ -93,18 +92,18 @@ def makeChain(self, context, mult, curverig): mesh.validate() bpy.context.scene.objects.link(object) # scale to the bone - bpy.data.objects['EslabonCero' + str(hueso.name)].scale = (hueso.length * mult, - hueso.length * mult, - hueso.length * mult) + bpy.data.objects["HardLink" + str(hueso.name)].scale = (hueso.length * mult, + hueso.length * mult, + hueso.length * mult) # Parent Objects - bpy.data.objects['EslabonCero' + str(hueso.name)].parent = ARMATURE - bpy.data.objects['EslabonCero' + str(hueso.name)].parent_type = 'BONE' - bpy.data.objects['EslabonCero' + str(hueso.name)].parent_bone = hueso.name + bpy.data.objects["HardLink" + str(hueso.name)].parent = ARMATURE + bpy.data.objects["HardLink" + str(hueso.name)].parent_type = 'BONE' + bpy.data.objects["HardLink" + str(hueso.name)].parent_bone = hueso.name def creahuesonoventa(hueso): # create data to link mesh = bpy.data.meshes.new("objectData" + str(hueso.name)) - object = bpy.data.objects.new("EslabonNov" + str(hueso.name), mesh) + object = bpy.data.objects.new("NewLink" + str(hueso.name), mesh) mesh.from_pydata( [(0.1784660965204239, -0.6918091773986816, -0.049861203879117966), (-0.1784662902355194, -0.6918091773986816, -0.04986126348376274), @@ -145,13 +144,13 @@ def makeChain(self, context, mult, curverig): mesh.validate() bpy.context.scene.objects.link(object) # scale to the bone - bpy.data.objects['EslabonNov' + str(hueso.name)].scale = (hueso.length * mult, + bpy.data.objects["NewLink" + str(hueso.name)].scale = (hueso.length * mult, hueso.length * mult, hueso.length * mult) # Parent objects - bpy.data.objects['EslabonNov' + str(hueso.name)].parent = ARMATURE - bpy.data.objects['EslabonNov' + str(hueso.name)].parent_type = 'BONE' - bpy.data.objects['EslabonNov' + str(hueso.name)].parent_bone = hueso.name + bpy.data.objects["NewLink" + str(hueso.name)].parent = ARMATURE + bpy.data.objects["NewLink" + str(hueso.name)].parent_type = 'BONE' + bpy.data.objects["NewLink" + str(hueso.name)].parent_bone = hueso.name for hueso in bpy.context.active_object.pose.bones: if VAR_SWITCH == 1: @@ -182,18 +181,16 @@ def makeChain(self, context, mult, curverig): # create the list of tail and head coordinates LISTA_POINTC.append(( - ACTARM.data.bones[0].head_local[0], - ACTARM.data.bones[0].head_local[1], - ACTARM.data.bones[0].head_local[2], - 1 - )) + ACTARM.data.bones[0].head_local[0], + ACTARM.data.bones[0].head_local[1], + ACTARM.data.bones[0].head_local[2], 1 + )) for hueso in ACTARM.data.bones: LISTA_POINTC.append(( hueso.tail_local[0], hueso.tail_local[1], - hueso.tail_local[2], - 1 + hueso.tail_local[2], 1 )) # create the Spline @@ -249,14 +246,14 @@ class MESH_OT_primitive_oscurart_chain_add(Operator): bl_options = {'REGISTER', 'UNDO'} curverig = BoolProperty( - name="Curve Rig", - default=False - ) + name="Curve Rig", + default=False + ) multiplier = FloatProperty( - name="Scale", - default=1, - min=0.01, max=100.0 - ) + name="Scale", + default=1, + min=0.01, max=100.0 + ) @classmethod def poll(cls, context): @@ -264,7 +261,18 @@ class MESH_OT_primitive_oscurart_chain_add(Operator): return (obj is not None and obj.type == "ARMATURE") def execute(self, context): - makeChain(self, context, self.multiplier, self.curverig) + try: + makeChain(self, context, self.multiplier, self.curverig) + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "mesh.primitive_oscurart_chain_add\nError: {}".format(e)) + + return {'CANCELLED'} + return {'FINISHED'} diff --git a/add_advanced_objects/pixelate_3d.py b/add_advanced_objects/pixelate_3d.py index 90e129da..d2b28971 100644 --- a/add_advanced_objects/pixelate_3d.py +++ b/add_advanced_objects/pixelate_3d.py @@ -1,28 +1,25 @@ -####################################################### +# gpl author: liero # very simple 'pixelization' or 'voxelization' engine # -####################################################### bl_info = { - "name": "3D Pix", + "name": "3D Pixelate", "author": "liero", - "version": (0, 5, 1), + "version": (0, 5, 2), "blender": (2, 74, 0), "location": "View3D > Tool Shelf", - "description": "Creates a 3d pixelated version of the object.", + "description": "Creates a 3d pixelated version of the object", "category": "Object"} -import bpy -import mathutils -from mathutils import Vector +# Note: winmgr properties are moved into __init__ +# search for patterns advanced_objects and adv_obj -bpy.types.WindowManager.size = bpy.props.FloatProperty(name='Size', min=.05, max=5, default=.25, description='Size of the cube / grid') -bpy.types.WindowManager.gap = bpy.props.IntProperty(name='Gap', min=0, max=90, default=10, subtype='PERCENTAGE', description='Separation - percent of size') -bpy.types.WindowManager.smooth = bpy.props.FloatProperty(name='Smooth', min=0, max=1, default=.0, description='Smooth factor when subdividing mesh') +import bpy +from bpy.types import Operator def pix(obj): sce = bpy.context.scene - wm = bpy.context.window_manager + props = sce.advanced_objects obj.hide = obj.hide_render = True mes = obj.to_mesh(sce, True, 'RENDER') mes.transform(obj.matrix_world) @@ -37,12 +34,12 @@ def pix(obj): fin = True for i in dup.data.edges: d = ver[i.vertices[0]].co - ver[i.vertices[1]].co - if d.length > wm.size: + if d.length > props.pixelate_3d_size: ver[i.vertices[0]].select = True ver[i.vertices[1]].select = True fin = False bpy.ops.object.editmode_toggle() - bpy.ops.mesh.subdivide(number_cuts=1, smoothness=wm.smooth) + bpy.ops.mesh.subdivide(number_cuts=1, smoothness=props.pixelate_3d_smooth) bpy.ops.mesh.select_all(action='DESELECT') bpy.ops.object.editmode_toggle() if fin: @@ -50,55 +47,68 @@ def pix(obj): for i in ver: for n in range(3): - i.co[n] -= (.001 + i.co[n]) % wm.size + i.co[n] -= (.001 + i.co[n]) % props.pixelate_3d_size bpy.ops.object.mode_set(mode='EDIT', toggle=False) bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.remove_doubles(threshold=0.0001) bpy.ops.mesh.delete(type='EDGE_FACE') bpy.ops.object.mode_set() - sca = wm.size * (100 - wm.gap) * .005 + sca = props.pixelate_3d_size * (100 - props.pixelate_3d_gap) * .005 bpy.ops.mesh.primitive_cube_add(layers=[True] + [False] * 19) bpy.ops.transform.resize(value=[sca] * 3) bpy.context.scene.objects.active = dup bpy.ops.object.parent_set(type='OBJECT') -class Pixelate(bpy.types.Operator): - bl_idname = 'object.pixelate' - bl_label = 'Pixelate Object' - bl_description = 'Create a 3d pixelated version of the object.' +class Pixelate(Operator): + bl_idname = "object.pixelate" + bl_label = "Pixelate Object" + bl_description = ("Create a 3d pixelated version of the object\n" + "using a Duplivert Box around each copied vertex\n" + "With high poly objects, it can take some time\n" + "Needs an existing Active Mesh Object") bl_options = {'REGISTER', 'UNDO'} @classmethod def poll(cls, context): - return (context.active_object and context.active_object.type == 'MESH' and context.mode == 'OBJECT') + return (context.active_object and + context.active_object.type == 'MESH' and + context.mode == 'OBJECT') def draw(self, context): layout = self.layout + adv_obj = context.scene.advanced_objects - column = layout.column(align=True) - column.prop(context.window_manager, "size") - column.prop(context.window_manager, "gap") - layout.prop(context.window_manager, "smooth") + col = layout.column(align=True) + col.prop(adv_obj, "pixelate_size") + col.prop(adv_obj, "pixelate_gap") + layout.prop(adv_obj, "pixelate_smooth") def execute(self, context): - objeto = bpy.context.object - pix(objeto) + objeto = context.active_object + try: + pix(objeto) + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "object.pixelate\nError: {}".format(e)) + + return {'CANCELLED'} + return {'FINISHED'} -classes = ( - Pixelate, -) def register(): - for cls in classes: - bpy.utils.register_class(cls) + bpy.utils.register_class(Pixelate) def unregister(): - for cls in classes: - bpy.utils.unregister_class(cls) + bpy.utils.unregister_class(Pixelate) + if __name__ == '__main__': register() diff --git a/add_advanced_objects/random_box_structure.py b/add_advanced_objects/random_box_structure.py index 5440b8b0..fa4b6497 100644 --- a/add_advanced_objects/random_box_structure.py +++ b/add_advanced_objects/random_box_structure.py @@ -1,9 +1,11 @@ +# gpl: author Dannyboy + bl_info = { "name": "Add Random Box Structure", "author": "Dannyboy", - "version": (1, 0), + "version": (1, 0, 1), "location": "View3D > Add > Make Box Structure", - "description": "Fill selected box shaped meshes with randomly sized cubes.", + "description": "Fill selected box shaped meshes with randomly sized cubes", "warning": "", "wiki_url": "", "tracker_url": "dannyboypython.blogspot.com", @@ -23,45 +25,70 @@ from bpy.props import ( class makestructure(Operator): bl_idname = "object.make_structure" bl_label = "Add Random Box Structure" + bl_description = ("Create a randomized structure made of boxes\n" + "with various control parameters\n" + "Needs an existing Active Mesh Object") bl_options = {'REGISTER', 'UNDO'} dc = BoolProperty( - name="Delete Base Mesh(s)?", - default=True - ) + name="Delete Base Mesh(es)", + default=True + ) wh = BoolProperty( - name="Stay Within Base Mesh(s)?", - description="Keeps cubes from exceeding base mesh bounds", - default=True - ) + name="Stay Within Bounds", + description="Keeps cubes from exceeding base mesh bounds", + default=True + ) uf = BoolProperty( - name="Uniform Cube Quantity", - default=False - ) + name="Uniform Cube Quantity", + default=False + ) qn = IntProperty( - name="Cube Quantity", - default=10, - min=1, max=1500 - ) + name="Cube Quantity", + default=10, + min=1, max=1500 + ) mn = FloatVectorProperty( - name="Min Scales", - default=(0.1, 0.1, 0.1), - subtype='XYZ' - ) + name="Min Scales", + default=(0.1, 0.1, 0.1), + subtype='XYZ' + ) mx = FloatVectorProperty( - name="Max Scales", - default=(2.0, 2.0, 2.0), - subtype='XYZ' - ) + name="Max Scales", + default=(2.0, 2.0, 2.0), + subtype='XYZ' + ) lo = FloatVectorProperty( - name="XYZ Offset", - default=(0.0, 0.0, 0.0), - subtype='XYZ' - ) + name="XYZ Offset", + default=(0.0, 0.0, 0.0), + subtype='XYZ' + ) rsd = FloatProperty( - name="Random Seed", - default=1 - ) + name="Random Seed", + default=1 + ) + + @classmethod + def poll(cls, context): + obj = context.active_object + return obj is not None and obj.type == "MESH" and obj.mode == "OBJECT" + + def draw(self, context): + layout = self.layout + + box = layout.box() + box.label(text="Options:") + box.prop(self, "dc") + box.prop(self, "wh") + box.prop(self, "uf") + + box = layout.box() + box.label(text="Parameters:") + box.prop(self, "qn") + box.prop(self, "mn") + box.prop(self, "mx") + box.prop(self, "lo") + box.prop(self, "rsd") def execute(self, context): rsdchange = self.rsd @@ -69,8 +96,10 @@ class makestructure(Operator): uvyes = 0 bpy.ops.group.create(name='Cubagrouper') bpy.ops.group.objects_remove() + for ob in bpy.context.selected_objects: oblst.append(ob) + for obj in oblst: bpy.ops.object.select_pattern(pattern=obj.name) # Select base mesh bpy.context.scene.objects.active = obj @@ -85,18 +114,19 @@ class makestructure(Operator): area = dim.x * dim.y * dim.z else: area = 75 + for cube in range(round((area / 75) * self.qn)): random.seed(rsdchange) pmn = self.mn # Proxy values pmx = self.mx if self.wh is True: - if dim.x < pmx.x: # Keeping things from exceeding proper size. + if dim.x < pmx.x: # Keeping things from exceeding proper size pmx.x = dim.x if dim.y < pmx.y: pmx.y = dim.y if dim.z < pmx.z: pmx.z = dim.z - if 0.0 > pmn.x: # Keeping things from going under zero. + if 0.0 > pmn.x: # Keeping things from going under zero pmn.x = 0.0 if 0.0 > pmn.y: pmn.y = 0.0 @@ -105,7 +135,7 @@ class makestructure(Operator): sx = (random.random() * (pmx.x - pmn.x)) + pmn.x # Just changed self.mx and .mn to pmx. sy = (random.random() * (pmx.y - pmn.y)) + pmn.y sz = (random.random() * (pmx.z - pmn.z)) + pmn.z - if self.wh is True: # This keeps the cubes within the base mesh. + if self.wh is True: # This keeps the cubes within the base mesh ex = (random.random() * (dim.x - sx)) - ((dim.x - sx) / 2) + obj.location.x wy = (random.random() * (dim.y - sy)) - ((dim.y - sy) / 2) + obj.location.y ze = (random.random() * (dim.z - sz)) - ((dim.z - sz) / 2) + obj.location.z @@ -119,74 +149,52 @@ class makestructure(Operator): bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.transform.resize( - value=(sx, sy, sz), constraint_axis=(True, True, True), - constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True - ) + value=(sx, sy, sz), constraint_axis=(True, True, True), + constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True + ) bpy.ops.object.mode_set(mode='OBJECT') - select = bpy.context.object # This is used to keep something selected for poll(). + select = bpy.context.object # This is used to keep something selected for poll() bpy.ops.object.group_link(group='Cubagrouper') rsdchange += 3 bpy.ops.object.select_grouped(type='GROUP') bpy.ops.transform.rotate( - value=rot[0], axis=(1, 0, 0), constraint_axis=(False, False, False), - constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True - ) + value=rot[0], axis=(1, 0, 0), constraint_axis=(False, False, False), + constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True + ) bpy.ops.transform.rotate( - value=rot[1], axis=(0, 1, 0), constraint_axis=(False, False, False), - constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True - ) + value=rot[1], axis=(0, 1, 0), constraint_axis=(False, False, False), + constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True + ) bpy.ops.transform.rotate( - value=rot[2], axis=(0, 0, 1), constraint_axis=(False, False, False), - constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True - ) - bpy.context.scene.objects.active = obj # Again needed to avoid poll() taking me down. + value=rot[2], axis=(0, 0, 1), constraint_axis=(False, False, False), + constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True + ) + bpy.context.scene.objects.active = obj # Again needed to avoid poll() taking me down bpy.ops.object.make_links_data(type='MODIFIERS') bpy.ops.object.make_links_data(type='MATERIAL') + if uvyes == 1: bpy.ops.object.join_uvs() + bpy.ops.group.objects_remove() bpy.context.scene.objects.active = select + if self.dc is True: bpy.context.scene.objects.unlink(obj) - return {'FINISHED'} - - @classmethod - def poll(cls, context): - ob = context.active_object - return ob is not None and ob.mode == 'OBJECT' - - def draw(self, context): - layout = self.layout - box = layout.box() - box.label(text="Options") - box.prop(self, "dc") - box.prop(self, "wh") - box.prop(self, "uf") - box = layout.box() - box.label(text="Parameters") - box.prop(self, "qn") - box.prop(self, "mn") - box.prop(self, "mx") - box.prop(self, "lo") - box.prop(self, "rsd") - -def add_object_button(self, context): - self.layout.operator(makestructure.bl_idname, text="Add Random Box structure", icon='PLUGIN') + return {'FINISHED'} def register(): bpy.utils.register_class(makestructure) - bpy.types.INFO_MT_add.append(add_object_button) def unregister(): bpy.utils.unregister_class(makestructure) - bpy.types.INFO_MT_add.remove(add_object_button) if __name__ == "__main__": diff --git a/add_advanced_objects/rope_alpha.py b/add_advanced_objects/rope_alpha.py index f0406148..904168a1 100644 --- a/add_advanced_objects/rope_alpha.py +++ b/add_advanced_objects/rope_alpha.py @@ -18,18 +18,17 @@ # # ##### END GPL LICENSE BLOCK ##### -# TODO : translate comments, prop names into English, add missing tooltips +# TODO : prop names into English, add missing tooltips bl_info = { "name": "Rope Creator", "description": "Dynamic rope (with cloth) creator", "author": "Jorge Hernandez - Melenedez", - "version": (0, 2), + "version": (0, 2, 2), "blender": (2, 7, 3), "location": "Left Toolbar > ClothRope", "warning": "", "wiki_url": "", - "tracker_url": "", "category": "Add Mesh" } @@ -60,29 +59,34 @@ def seleccionar_todo(): def salir_de_editmode(): - if bpy.context.mode == "EDIT" or bpy.context.mode == "EDIT_CURVE" or bpy.context.mode == "EDIT_MESH": + if bpy.context.mode in ["EDIT", "EDIT_MESH", "EDIT_CURVE"]: bpy.ops.object.mode_set(mode='OBJECT') -# Clear scene: - +# Clear scene: def reset_scene(): desocultar("todo") - # el play back al principio + # playback to the start bpy.ops.screen.frame_jump(end=False) try: salir_de_editmode() except: pass - area = bpy.context.area - # en el outliner expando todo para poder seleccionar los emptys hijos - old_type = area.type - area.type = 'OUTLINER' - bpy.ops.outliner.expanded_toggle() - area.type = old_type - # vuelvo al contexto donde estaba - seleccionar_todo() - bpy.ops.object.delete(use_global=False) + try: + area = bpy.context.area + # expand everything in the outliner to be able to select children + old_type = area.type + area.type = 'OUTLINER' + bpy.ops.outliner.expanded_toggle() + + # restore the original context + area.type = old_type + + seleccionar_todo() + bpy.ops.object.delete(use_global=False) + + except Exception as e: + print("\n[rope_alpha]\nfunction: reset_scene\nError: %s" % e) def entrar_en_editmode(): @@ -100,7 +104,6 @@ def select_all_in_edit_mode(ob): if not v.select: v.select = True entrar_en_editmode() - # bpy.ops.mesh.select_all(action="SELECT") def deselect_all_in_edit_mode(ob): @@ -119,17 +122,17 @@ def which_vertex_are_selected(ob): for v in ob.data.vertices: if v.select: print(str(v.index)) - print("el vertice " + str(v.index) + " esta seleccionado") + print("Vertex " + str(v.index) + " is selected") def seleccionar_por_nombre(nombre): scn = bpy.context.scene bpy.data.objects[nombre].select = True + scn.objects.active = bpy.data.objects[nombre] def deseleccionar_por_nombre(nombre): - # scn = bpy.context.scene bpy.data.objects[nombre].select = False @@ -143,14 +146,9 @@ def borrar_elementos_seleccionados(tipo): bpy.ops.mesh.delete(type='VERT') -def tab_editmode(): - bpy.ops.object.editmode_toggle() - - def obtener_coords_vertex_seleccionados(): coordenadas_de_vertices = [] for ob in bpy.context.selected_objects: - print(ob.name) if ob.type == 'MESH': for v in ob.data.vertices: if v.select: @@ -160,28 +158,28 @@ def obtener_coords_vertex_seleccionados(): def crear_locator(pos): bpy.ops.object.empty_add( - type='PLAIN_AXES', radius=1, view_align=False, - location=(pos[0], pos[1], pos[2]), - layers=(True, False, False, False, False, False, False, - False, False, False, False, False, False, False, - False, False, False, False, False, False) - ) + type='PLAIN_AXES', radius=1, view_align=False, + location=(pos[0], pos[1], pos[2]), + layers=(True, False, False, False, False, False, False, + False, False, False, False, False, False, False, + False, False, False, False, False, False) + ) def extruir_vertices(longitud, cuantos_segmentos): bpy.ops.mesh.extrude_region_move( - MESH_OT_extrude_region={"mirror": False}, - TRANSFORM_OT_translate={ - "value": (longitud / cuantos_segmentos, 0, 0), - "constraint_axis": (True, False, False), - "constraint_orientation": 'GLOBAL', "mirror": False, - "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', - "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', - "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), - "gpencil_strokes": False, "texture_space": False, - "remove_on_cancel": False, "release_confirm": False - } - ) + MESH_OT_extrude_region={"mirror": False}, + TRANSFORM_OT_translate={ + "value": (longitud / cuantos_segmentos, 0, 0), + "constraint_axis": (True, False, False), + "constraint_orientation": 'GLOBAL', "mirror": False, + "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', + "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', + "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), + "gpencil_strokes": False, "texture_space": False, + "remove_on_cancel": False, "release_confirm": False + } + ) def select_all_vertex_in_curve_bezier(bc): @@ -203,135 +201,154 @@ def ocultar_relationships(): class ClothRope(Operator): bl_idname = "clot.rope" bl_label = "Rope Cloth" + bl_description = ("Create a new Scene with a Cloth modifier\n" + "Rope Simulation with hooked Helper Objects") ropelenght = IntProperty( - name="longitud", - default=5 - ) + name="Rope Length", + description="Length of the generated Rope", + default=5 + ) ropesegments = IntProperty( - name="rsegments", - default=5 - ) + name="Rope Segments", + description="Number of the Rope Segments", + default=5 + ) qcr = IntProperty( - name="qualcolr", - min=1, max=20, - default=20 - ) + name="Collision Quality", + description="Rope's Cloth modifier collsion quality", + min=1, max=20, + default=20 + ) substeps = IntProperty( - name="rsubsteps", - min=4, max=80, - default=50 - ) + name="Rope Substeps", + description="Rope's Cloth modifier quality", + min=4, max=80, + default=50 + ) resrope = IntProperty( - name="resr", - default=5 - ) + name="Rope Resolution", + description="Rope's Bevel resolution", + default=5 + ) radiusrope = FloatProperty( - name="radius", - min=0.04, max=1, - default=0.04 - ) + name="Radius", + description="Rope's Radius", + min=0.04, max=1, + default=0.04 + ) hide_emptys = BoolProperty( - name="hemptys", - default=False - ) + name="Hide Empties", + description="Hide Helper Objects", + default=False + ) def execute(self, context): - # add new scene + # add a new scene bpy.ops.scene.new(type="NEW") scene = bpy.context.scene scene.name = "Test Rope" seleccionar_todo() longitud = self.ropelenght - # para que desde el primer punto hasta el ultimo, entre - # medias tenga x segmentos debo sumarle 1 a la cantidad: + + # For the middle to have x segments between the first and + # last point, must add 1 to the quantity: cuantos_segmentos = self.ropesegments + 1 calidad_de_colision = self.qcr substeps = self.substeps deseleccionar_todo() - # creamos el empty que sera el padre de todo + # collect the possible empties that already exist in the data + empties_prev = [obj.name for obj in bpy.data.objects if obj.type == "EMPTY"] + + # create an empty that will be the parent of everything bpy.ops.object.empty_add( - type='SPHERE', radius=1, view_align=False, location=(0, 0, 0), - layers=(True, False, False, False, False, False, False, False, - False, False, False, False, False, False, False, False, - False, False, False, False) - ) + type='SPHERE', radius=1, view_align=False, location=(0, 0, 0), + layers=(True, False, False, False, False, False, False, False, + False, False, False, False, False, False, False, False, + False, False, False, False) + ) ob = bpy.context.selected_objects[0] ob.name = "Rope" + # .001 and friends + rope_name = ob.name deseleccionar_todo() - # creamos un plano y lo borramos + + # create a plane and delete it bpy.ops.mesh.primitive_plane_add( - radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), - layers=(True, False, False, False, False, False, False, False, False, - False, False, False, False, False, False, False, False, - False, False, False) - ) + radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), + layers=(True, False, False, False, False, False, False, False, False, + False, False, False, False, False, False, False, False, + False, False, False) + ) ob = bpy.context.selected_objects[0] - # renombrar: + # rename: ob.name = "cuerda" - entrar_en_editmode() # entramos en edit mode + # .001 and friends + cuerda_1_name = ob.name + + entrar_en_editmode() # enter edit mode select_all_in_edit_mode(ob) - # seleccionar_todo() # ya viene por default seleccionado + borrar_elementos_seleccionados("vertices") - salir_de_editmode() # salimos de edit mode - crear_vertices(ob) # creamos un vertex - # creando el grupo Group para el PIN - # Group contiene los vertices del pin y Group.001 contiene la linea unica principal - entrar_en_editmode() # entramos en edit mode - bpy.ops.object.vertex_group_add() # creamos un grupo + salir_de_editmode() # leave edit mode + crear_vertices(ob) # create a vertex + + # Creating a Group for the PIN + # Group contains the vertices of the pin and the Group.001 contains the single main line + entrar_en_editmode() # enter edit mode + bpy.ops.object.vertex_group_add() # create a group select_all_in_edit_mode(ob) - bpy.ops.object.vertex_group_assign() # y lo asignamos - # los hooks van a la curva no a la guia poligonal... - # creo el primer hook sin necesidad de crear luego el locator a mano: - # bpy.ops.object.hook_add_newob() - salir_de_editmode() # salimos de edit mode + bpy.ops.object.vertex_group_assign() # assign it + + salir_de_editmode() # leave edit mode ob.vertex_groups[0].name = "Pin" deseleccionar_todo() - seleccionar_por_nombre("cuerda") - # hago los extrudes del vertice: + seleccionar_por_nombre(cuerda_1_name) + + # extrude vertices: for i in range(cuantos_segmentos): entrar_en_editmode() extruir_vertices(longitud, cuantos_segmentos) - # y los ELIMINO del grupo PIN + # delete the PIN group bpy.ops.object.vertex_group_remove_from() - # obtengo la direccion para lego crear el locator en su posicion + # get the direction to create the locator on it's position pos = obtener_coords_vertex_seleccionados() - # los hooks van a la curva no a la guia poligonal... - # creo el hook sin necesidad de crear el locator a mano: - # bpy.ops.object.hook_add_newob() - salir_de_editmode() # salimos de edit mode - # creo el locator en su sitio + + salir_de_editmode() # leave edit mode + # create locator at position crear_locator(pos) deseleccionar_todo() - seleccionar_por_nombre("cuerda") + seleccionar_por_nombre(cuerda_1_name) deseleccionar_todo() - seleccionar_por_nombre("cuerda") - # vuelvo a seleccionar la cuerda + + seleccionar_por_nombre(cuerda_1_name) # select the rope entrar_en_editmode() - pos = obtener_coords_vertex_seleccionados() # y obtenemos su posicion + + pos = obtener_coords_vertex_seleccionados() # get their positions salir_de_editmode() - # creamos el ultimo locator + # create the last locator crear_locator(pos) deseleccionar_todo() - seleccionar_por_nombre("cuerda") - entrar_en_editmode() # entramos en edit mode - bpy.ops.object.vertex_group_add() # CREANDO GRUPO GUIA MAESTRA + seleccionar_por_nombre(cuerda_1_name) + entrar_en_editmode() # enter edit mode + bpy.ops.object.vertex_group_add() # Creating Master guide group select_all_in_edit_mode(ob) - bpy.ops.object.vertex_group_assign() # y lo asignamos + bpy.ops.object.vertex_group_assign() # and assing it ob.vertex_groups[1].name = "Guide_rope" - # extruimos la curva para que tenga un minimo grosor para colisionar + + # extrude the Curve so it has a minumum thickness for collide bpy.ops.mesh.extrude_region_move( - MESH_OT_extrude_region={"mirror": False}, - TRANSFORM_OT_translate={ - "value": (0, 0.005, 0), "constraint_axis": (False, True, False), - "constraint_orientation": 'GLOBAL', "mirror": False, - "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', - "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', - "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), - "gpencil_strokes": False, "texture_space": False, - "remove_on_cancel": False, "release_confirm": False - } - ) + MESH_OT_extrude_region={"mirror": False}, + TRANSFORM_OT_translate={ + "value": (0, 0.005, 0), "constraint_axis": (False, True, False), + "constraint_orientation": 'GLOBAL', "mirror": False, + "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', + "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', + "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), + "gpencil_strokes": False, "texture_space": False, + "remove_on_cancel": False, "release_confirm": False + } + ) bpy.ops.object.vertex_group_remove_from() deselect_all_in_edit_mode(ob) salir_de_editmode() @@ -340,54 +357,70 @@ class ClothRope(Operator): bpy.context.object.modifiers["Cloth"].settings.vertex_group_mass = "Pin" bpy.context.object.modifiers["Cloth"].collision_settings.collision_quality = calidad_de_colision bpy.context.object.modifiers["Cloth"].settings.quality = substeps - # DUPLICAMOS para convertir a curva: - # selecciono los vertices que forman parte del grupo Group.001 - seleccionar_por_nombre("cuerda") + + # Duplicate to convert into Curve: + # select the vertices that are the part of the Group.001 + seleccionar_por_nombre(cuerda_1_name) entrar_en_editmode() bpy.ops.mesh.select_all(action="DESELECT") bpy.context.tool_settings.mesh_select_mode = (True, False, False) salir_de_editmode() gi = ob.vertex_groups["Guide_rope"].index # get group index + for v in ob.data.vertices: for g in v.groups: if g.group == gi: # compare with index in VertexGroupElement v.select = True + + # now we have to make a table of names of cuerdas to see which one will be new + cuerda_names = [obj.name for obj in bpy.data.objects if "cuerda" in obj.name] + entrar_en_editmode() - # ya tenemos la guia seleccionada: - # la duplicamos: + + # we already have the selected guide: + # duplicate it: bpy.ops.mesh.duplicate_move( - MESH_OT_duplicate={"mode": 1}, - TRANSFORM_OT_translate={ - "value": (0, 0, 0), "constraint_axis": (False, False, False), - "constraint_orientation": 'GLOBAL', "mirror": False, - "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', - "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', - "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), - "gpencil_strokes": False, "texture_space": False, - "remove_on_cancel": False, "release_confirm": False - } - ) - # separamos por seleccion: + MESH_OT_duplicate={"mode": 1}, + TRANSFORM_OT_translate={ + "value": (0, 0, 0), "constraint_axis": (False, False, False), + "constraint_orientation": 'GLOBAL', "mirror": False, + "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', + "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', + "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), + "gpencil_strokes": False, "texture_space": False, + "remove_on_cancel": False, "release_confirm": False + } + ) + # separate the selections: bpy.ops.mesh.separate(type='SELECTED') salir_de_editmode() deseleccionar_todo() - seleccionar_por_nombre("cuerda.001") - # a la nueva curva copiada le quitamos el cloth: + + cuerda_2_name = "cuerda.001" + test = [] + for obj in bpy.data.objects: + if "cuerda" in obj.name and obj.name not in cuerda_names: + cuerda_2_name = obj.name + test.append(obj.name) + + seleccionar_por_nombre(cuerda_2_name) + + # from the newly created curve remove the Cloth: bpy.ops.object.modifier_remove(modifier="Cloth") - # la convertimos en curva: + # convert the Curve: bpy.ops.object.convert(target='CURVE') - # todos los emptys: + + # all Empties that are not previously present emptys = [] for eo in bpy.data.objects: - if eo.type == 'EMPTY': - if eo.name != "Rope": + if eo.type == 'EMPTY' and eo.name not in empties_prev: + if eo.name != rope_name: emptys.append(eo) - # print(emptys) - # cuantos puntos tiene la becier: - # len(bpy.data.objects['cuerda.001'].data.splines[0].points) - # seleccionar y deseleccionar: - bc = bpy.data.objects['cuerda.001'] + + # select and deselect: + bc = bpy.data.objects[cuerda_2_name] n = 0 + for e in emptys: deseleccionar_todo() seleccionar_por_nombre(e.name) @@ -398,9 +431,10 @@ class ClothRope(Operator): bpy.ops.object.hook_add_selob(use_bone=False) salir_de_editmode() n = n + 1 - # entrar_en_editmode() - ob = bpy.data.objects['cuerda'] + + ob = bpy.data.objects[cuerda_1_name] n = 0 + for e in emptys: deseleccionar_todo() seleccionar_por_nombre(e.name) @@ -409,29 +443,32 @@ class ClothRope(Operator): bpy.ops.mesh.select_all(action="DESELECT") bpy.context.tool_settings.mesh_select_mode = (True, False, False) salir_de_editmode() + for v in ob.data.vertices: if v.select: v.select = False ob.data.vertices[n].select = True entrar_en_editmode() bpy.ops.object.vertex_parent_set() - # deselect_all_in_edit_mode(ob) + salir_de_editmode() n = n + 1 - # ocultar los emptys: - # for e in emptys: + # hide the Empties: deseleccionar_todo() - # emparentando todo al empty esferico: - seleccionar_por_nombre("cuerda.001") - seleccionar_por_nombre("cuerda") - seleccionar_por_nombre("Rope") + + # all parented to the spherical empty: + seleccionar_por_nombre(cuerda_2_name) + seleccionar_por_nombre(cuerda_1_name) + seleccionar_por_nombre(rope_name) bpy.ops.object.parent_set(type='OBJECT', keep_transform=True) deseleccionar_todo() - # display que no muestre las relaciones + + # do not display the relations ocultar_relationships() - seleccionar_por_nombre("cuerda.001") - # cuerda curva settings: + seleccionar_por_nombre(cuerda_2_name) + + # curved rope settings: bpy.context.object.data.fill_mode = 'FULL' bpy.context.object.data.bevel_depth = self.radiusrope bpy.context.object.data.bevel_resolution = self.resrope @@ -439,82 +476,97 @@ class ClothRope(Operator): return {'FINISHED'} def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width=310) + return context.window_manager.invoke_props_dialog(self, width=350) def draw(self, context): layout = self.layout box = layout.box() - col = box.column() + col = box.column(align=True) + col.label("Rope settings:") rowsub0 = col.row() - rowsub0.prop(self, "ropelenght", text='Length') - rowsub0.prop(self, "ropesegments", text='Segments') - rowsub0.prop(self, "radiusrope", text='Radius') + rowsub0.prop(self, "ropelenght", text="Length") + rowsub0.prop(self, "ropesegments", text="Segments") + rowsub0.prop(self, "radiusrope", text="Radius") col.label("Quality Settings:") - col.prop(self, "resrope", text='Resolution curve') - col.prop(self, "qcr", text='Quality Collision') - col.prop(self, "substeps", text='Substeps') + col.prop(self, "resrope", text="Resolution curve") + col.prop(self, "qcr", text="Quality Collision") + col.prop(self, "substeps", text="Substeps") class BallRope(Operator): bl_idname = "ball.rope" - bl_label = "Rope Ball" + bl_label = "Wrecking Ball" + bl_description = ("Create a new Scene with a Rigid Body simulation of\n" + "Wrecking Ball on a rope") # defaults rope ball ropelenght2 = IntProperty( - name="longitud", - default=10 - ) + name="Rope Length", + description="Length of the Wrecking Ball rope", + default=10 + ) ropesegments2 = IntProperty( - name="rsegments", - min=0, max=999, - default=6 - ) + name="Rope Segments", + description="Number of the Wrecking Ball rope segments", + min=0, max=999, + default=6 + ) radiuscubes = FloatProperty( - name="radius", - default=0.5 - ) + name="Cube Radius", + description="Size of the Linked Cubes helpers", + default=0.5 + ) radiusrope = FloatProperty( - name="radius", - default=0.4 - ) + name="Rope Radius", + description="Radius of the Rope", + default=0.4 + ) worldsteps = IntProperty( - name="worldsteps", - min=60, max=1000, - default=250 - ) + name="World Steps", + description="Rigid Body Solver world steps per second (update)", + min=60, max=1000, + default=250 + ) solveriterations = IntProperty( - name="solveriterations", - min=10, max=100, - default=50 - ) + name="Solver Iterations", + description="How many times the Rigid Body Solver should run", + min=10, max=100, + default=50 + ) massball = IntProperty( - name="massball", - default=1 - ) + name="Ball Mass", + description="Mass of the Wrecking Ball", + default=1 + ) resrope = IntProperty( - name="resolucion", - default=4 - ) + name="Resolution", + description="Rope resolution", + default=4 + ) grados = FloatProperty( - name="grados", - default=45 - ) + name="Degrees", + description="Angle of the Wrecking Ball compared to the Ground Plane", + default=45 + ) separacion = FloatProperty( - name="separacion", - default=0.1 - ) + name="Link Cubes Gap", + description="Space between the Rope's Linked Cubes", + default=0.1 + ) hidecubes = BoolProperty( - name="hidecubes", - default=False - ) + name="Hide Link Cubes", + description="Hide helper geometry for the Rope", + default=False + ) def execute(self, context): world_steps = self.worldsteps solver_iterations = self.solveriterations longitud = self.ropelenght2 - # hago un + 2 para que los segmentos sean los que hay entre los dos extremos... + + # make a + 2, so the segments will be between the two end points... segmentos = self.ropesegments2 + 2 offset_del_suelo = 1 offset_del_suelo_real = (longitud / 2) + (segmentos / 2) @@ -525,39 +577,48 @@ class BallRope(Operator): rotrope = self.grados separation = self.separacion hidecubeslinks = self.hidecubes + # add new scene bpy.ops.scene.new(type="NEW") scene = bpy.context.scene scene.name = "Test Ball" - # suelo: + + # collect the possible constraint empties that already exist in the data + constraint_prev = [obj.name for obj in bpy.data.objects if + obj.type == "EMPTY" and "Constraint" in obj.name] + # floor: bpy.ops.mesh.primitive_cube_add( - radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), - layers=(True, False, False, False, False, False, False, False, False, - False, False, False, False, False, False, False, False, - False, False, False) - ) + radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), + layers=(True, False, False, False, False, False, False, False, False, + False, False, False, False, False, False, False, False, + False, False, False) + ) bpy.context.object.scale.x = 10 + longitud bpy.context.object.scale.y = 10 + longitud bpy.context.object.scale.z = 0.05 bpy.context.object.name = "groundplane" + # The secret agents .001, 002 etc. + groundplane_name = bpy.context.object.name + bpy.ops.rigidbody.objects_add(type='PASSIVE') - # creamos el primer cubo: + + # create the first cube: cuboslink = [] n = 0 for i in range(segmentos): - # si es 0 le digo que empieza desde 1 + # if 0 start from 1 if i == 0: i = offset_del_suelo - else: # si no es 0 les tengo que sumar uno para que no se pisen al empezar el primero desde 1 + else: # if it is not 0, add one so it doesn't step on the first one starting from 1 i = i + offset_del_suelo - separacion = longitud * 2 / segmentos # distancia entre los cubos link + separacion = longitud * 2 / segmentos # distance between linked cubes bpy.ops.mesh.primitive_cube_add( - radius=1, view_align=False, enter_editmode=False, - location=(0, 0, i * separacion), - layers=(True, False, False, False, False, False, False, False, - False, False, False, False, False, False, False, False, - False, False, False, False) - ) + radius=1, view_align=False, enter_editmode=False, + location=(0, 0, i * separacion), + layers=(True, False, False, False, False, False, False, False, + False, False, False, False, False, False, False, False, + False, False, False, False) + ) bpy.ops.rigidbody.objects_add(type='ACTIVE') bpy.context.object.name = "CubeLink" if n != 0: @@ -568,6 +629,7 @@ class BallRope(Operator): bpy.context.object.scale.x = radio bpy.context.object.scale.y = radio cuboslink.append(bpy.context.object) + for i in range(len(cuboslink)): deseleccionar_todo() if i != len(cuboslink) - 1: @@ -576,111 +638,115 @@ class BallRope(Operator): seleccionar_por_nombre(nombre1.name) seleccionar_por_nombre(nombre2.name) bpy.ops.rigidbody.connect() - seleccionar_por_nombre - for i in range(segmentos - 1): - if i == 0: - seleccionar_por_nombre("Constraint") - else: - if i <= 9 and i > 0: - seleccionar_por_nombre("Constraint.00" + str(i)) - else: - if i <= 99 and i > 9: - seleccionar_por_nombre("Constraint.0" + str(i)) - else: - if i <= 999 and i > 99: - seleccionar_por_nombre("Constraint." + str(i)) - for c in bpy.context.selected_objects: - c.rigid_body_constraint.type = 'POINT' + + # select by name + constraint_new = [ + obj.name for obj in bpy.data.objects if + obj.type == "EMPTY" and "Constraint" in obj.name and + obj.name not in constraint_prev + ] + + for names in constraint_new: + seleccionar_por_nombre(names) + + for c in bpy.context.selected_objects: + c.rigid_body_constraint.type = 'POINT' deseleccionar_todo() - # creamos la curva bezier: + # create a Bezier curve: bpy.ops.curve.primitive_bezier_curve_add( - radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), - layers=(True, False, False, False, False, False, False, False, False, - False, False, False, False, False, False, False, False, False, False, False) - ) + radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), + layers=(True, False, False, False, False, False, False, False, False, + False, False, False, False, False, False, False, False, False, False, False) + ) bpy.context.object.name = "Cuerda" + # Blender will automatically append the .001 + # if it is already in data + real_name = bpy.context.object.name + for i in range(len(cuboslink)): cubonombre = cuboslink[i].name seleccionar_por_nombre(cubonombre) - seleccionar_por_nombre("Cuerda") + seleccionar_por_nombre(real_name) x = cuboslink[i].location[0] y = cuboslink[i].location[1] z = cuboslink[i].location[2] - # si es 0 le digo que empieza desde 1 es el offset desde el suelo... + + # if it is 0 make it start from 1 as the offset from the ground... if i == 0: i = offset_del_suelo - else: # si no es 0 les tengo que sumar uno para que no se pisen al empezar el primero desde 1 + else: # if it is not 0, add one so it doesn't step on the first one starting from 1 i = i + offset_del_suelo + salir_de_editmode() - # entrar_en_editmode() - tab_editmode() + entrar_en_editmode() + if i == 1: - # selecciono todos los vertices y los borro - select_all_vertex_in_curve_bezier(bpy.data.objects["Cuerda"]) + # select all the vertices and delete them + select_all_vertex_in_curve_bezier(bpy.data.objects[real_name]) bpy.ops.curve.delete(type='VERT') - # creamos el primer vertice: + # create the first vertex: bpy.ops.curve.vertex_add(location=(x, y, z)) else: - # extruimos el resto: + # extrude the rest: bpy.ops.curve.extrude_move( - CURVE_OT_extrude={"mode": 'TRANSLATION'}, - TRANSFORM_OT_translate={ - "value": (0, 0, z / i), - "constraint_axis": (False, False, True), - "constraint_orientation": 'GLOBAL', "mirror": False, - "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', - "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', - "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), - "gpencil_strokes": False, "texture_space": False, - "remove_on_cancel": False, "release_confirm": False - } - ) + CURVE_OT_extrude={"mode": 'TRANSLATION'}, + TRANSFORM_OT_translate={ + "value": (0, 0, z / i), + "constraint_axis": (False, False, True), + "constraint_orientation": 'GLOBAL', "mirror": False, + "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH', + "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST', + "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0), + "gpencil_strokes": False, "texture_space": False, + "remove_on_cancel": False, "release_confirm": False + } + ) bpy.ops.object.hook_add_selob(use_bone=False) salir_de_editmode() bpy.context.object.data.bevel_resolution = resolucion deseleccionar_todo() - # creando la esfera ball: + # create a sphere ball: deseleccionar_todo() seleccionar_por_nombre(cuboslink[0].name) entrar_en_editmode() z = cuboslink[0].scale.z + longitud / 2 bpy.ops.view3d.snap_cursor_to_selected() bpy.ops.mesh.primitive_uv_sphere_add( - view_align=False, enter_editmode=False, - layers=(True, False, False, False, False, False, False, - False, False, False, False, False, False, False, - False, False, False, False, False, False) - ) + view_align=False, enter_editmode=False, + layers=(True, False, False, False, False, False, False, + False, False, False, False, False, False, False, + False, False, False, False, False, False) + ) bpy.ops.transform.translate( - value=(0, 0, -z + 2), constraint_axis=(False, False, True), - constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1 - ) + value=(0, 0, -z + 2), constraint_axis=(False, False, True), + constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1 + ) bpy.ops.transform.resize( - value=(longitud / 2, longitud / 2, longitud / 2), - constraint_axis=(False, False, False), - constraint_orientation='GLOBAL', - mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1 - ) + value=(longitud / 2, longitud / 2, longitud / 2), + constraint_axis=(False, False, False), + constraint_orientation='GLOBAL', + mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1 + ) deselect_all_in_edit_mode(cuboslink[0]) salir_de_editmode() bpy.ops.object.shade_smooth() bpy.context.object.rigid_body.mass = masa bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS') - # lo subo todo para arriba un poco mas: + # move it all up a bit more: seleccionar_todo() - deseleccionar_por_nombre("groundplane") + deseleccionar_por_nombre(groundplane_name) bpy.ops.transform.translate( - value=(0, 0, offset_del_suelo_real), - constraint_axis=(False, False, True), - constraint_orientation='GLOBAL', mirror=False, - proportional='DISABLED', proportional_edit_falloff='SMOOTH', - proportional_size=1 - ) + value=(0, 0, offset_del_suelo_real), + constraint_axis=(False, False, True), + constraint_orientation='GLOBAL', mirror=False, + proportional='DISABLED', proportional_edit_falloff='SMOOTH', + proportional_size=1 + ) deseleccionar_todo() seleccionar_por_nombre(cuboslink[-1].name) @@ -689,25 +755,25 @@ class BallRope(Operator): bpy.context.scene.rigidbody_world.steps_per_second = world_steps bpy.context.scene.rigidbody_world.solver_iterations = solver_iterations - # para mover todo desde el primero de arriba: + # move everything from the top one: seleccionar_por_nombre(cuboslink[-1].name) bpy.ops.view3d.snap_cursor_to_selected() seleccionar_todo() - deseleccionar_por_nombre("groundplane") + deseleccionar_por_nombre(groundplane_name) deseleccionar_por_nombre(cuboslink[-1].name) bpy.context.space_data.pivot_point = 'CURSOR' bpy.ops.transform.rotate( - value=rotrope, axis=(1, 0, 0), - constraint_axis=(True, False, False), - constraint_orientation='GLOBAL', - mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', - proportional_size=1 - ) + value=rotrope, axis=(1, 0, 0), + constraint_axis=(True, False, False), + constraint_orientation='GLOBAL', + mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', + proportional_size=1 + ) bpy.context.space_data.pivot_point = 'MEDIAN_POINT' deseleccionar_todo() - seleccionar_por_nombre("Cuerda") + seleccionar_por_nombre(real_name) bpy.context.object.data.fill_mode = 'FULL' bpy.context.object.data.bevel_depth = radiorope for ob in bpy.data.objects: @@ -722,30 +788,34 @@ class BallRope(Operator): return {'FINISHED'} def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width=310) + return context.window_manager.invoke_props_dialog(self, width=350) def draw(self, context): layout = self.layout box = layout.box() - col = box.column() + col = box.column(align=True) + col.label("Rope settings:") rowsub0 = col.row() - rowsub0.prop(self, "hidecubes", text='Hide Link Cubes') - rowsub1 = col.row() - rowsub1.prop(self, "ropelenght2", text='Length') - rowsub1.prop(self, "ropesegments2", text='Segments') - rowsub2 = col.row() - rowsub2.prop(self, "radiuscubes", text='Radius Link Cubes') - rowsub2.prop(self, "radiusrope", text='Radius Rope') - rowsub3 = col.row() - rowsub3.prop(self, "grados", text='Degrees') - rowsub3.prop(self, "separacion", text='Separation Link Cubes') + rowsub0.prop(self, "hidecubes", text="Hide Link Cubes") + + rowsub1 = col.row(align=True) + rowsub1.prop(self, "ropelenght2", text="Length") + rowsub1.prop(self, "ropesegments2", text="Segments") + + rowsub2 = col.row(align=True) + rowsub2.prop(self, "radiuscubes", text="Radius Link Cubes") + rowsub2.prop(self, "radiusrope", text="Radius Rope") + + rowsub3 = col.row(align=True) + rowsub3.prop(self, "grados", text="Degrees") + rowsub3.prop(self, "separacion", text="Separation Link Cubes") col.label("Quality Settings:") - col.prop(self, "resrope", text='Resolution Rope') - col.prop(self, "massball", text='Ball Mass') - col.prop(self, "worldsteps", text='World Steps') - col.prop(self, "solveriterations", text='Solver Iterarions') + col.prop(self, "resrope", text="Resolution Rope") + col.prop(self, "massball", text="Ball Mass") + col.prop(self, "worldsteps", text="World Steps") + col.prop(self, "solveriterations", text="Solver Iterarions") # Register diff --git a/add_advanced_objects/scene_objects_bi.py b/add_advanced_objects/scene_objects_bi.py index db3ec9cd..f189bb11 100644 --- a/add_advanced_objects/scene_objects_bi.py +++ b/add_advanced_objects/scene_objects_bi.py @@ -7,179 +7,189 @@ from bpy.types import Operator class add_BI_scene(Operator): bl_idname = "bi.add_scene" bl_label = "Create test scene" - bl_description = "BI Scene with Objects" + bl_description = "Blender Internal renderer Scene with Objects" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): - blend_data = context.blend_data - # ob = bpy.context.active_object - - # add new scene - bpy.ops.scene.new(type="NEW") - scene = bpy.context.scene - scene.name = "scene_materials" - - # render settings - render = scene.render - render.resolution_x = 1920 - render.resolution_y = 1080 - render.resolution_percentage = 50 - - # add new world - world = bpy.data.worlds.new("Materials_World") - scene.world = world - world.use_sky_blend = True - world.use_sky_paper = True - world.horizon_color = (0.004393, 0.02121, 0.050) - world.zenith_color = (0.03335, 0.227, 0.359) - world.light_settings.use_ambient_occlusion = True - world.light_settings.ao_factor = 0.25 - - # add camera - bpy.ops.object.camera_add( + try: + blend_data = context.blend_data + # ob = bpy.context.active_object + + # add new scene + bpy.ops.scene.new(type="NEW") + scene = bpy.context.scene + scene.name = "scene_materials" + + # render settings + render = scene.render + render.resolution_x = 1920 + render.resolution_y = 1080 + render.resolution_percentage = 50 + + # add new world + world = bpy.data.worlds.new("Materials_World") + scene.world = world + world.use_sky_blend = True + world.use_sky_paper = True + world.horizon_color = (0.004393, 0.02121, 0.050) + world.zenith_color = (0.03335, 0.227, 0.359) + world.light_settings.use_ambient_occlusion = True + world.light_settings.ao_factor = 0.25 + + # add camera + bpy.ops.object.camera_add( location=(7.48113, -6.50764, 5.34367), rotation=(1.109319, 0.010817, 0.814928) ) - cam = bpy.context.active_object.data - cam.lens = 35 - cam.draw_size = 0.1 - bpy.ops.view3d.viewnumpad(type='CAMERA') + cam = bpy.context.active_object.data + cam.lens = 35 + cam.draw_size = 0.1 + bpy.ops.view3d.viewnumpad(type='CAMERA') - # add point lamp - bpy.ops.object.lamp_add( + # add point lamp + bpy.ops.object.lamp_add( type="POINT", location=(4.07625, 1.00545, 5.90386), rotation=(0.650328, 0.055217, 1.866391) ) - lamp1 = bpy.context.active_object.data - lamp1.name = "Point_Right" - lamp1.energy = 1.0 - lamp1.distance = 30.0 - lamp1.shadow_method = "RAY_SHADOW" - lamp1.use_sphere = True - - # add point lamp2 - bpy.ops.object.lamp_add( + lamp1 = bpy.context.active_object.data + lamp1.name = "Point_Right" + lamp1.energy = 1.0 + lamp1.distance = 30.0 + lamp1.shadow_method = "RAY_SHADOW" + lamp1.use_sphere = True + + # add point lamp2 + bpy.ops.object.lamp_add( type="POINT", location=(-0.57101, -4.24586, 5.53674), rotation=(1.571, 0, 0.785) ) - lamp2 = bpy.context.active_object.data - lamp2.name = "Point_Left" - lamp2.energy = 1.0 - lamp2.distance = 30.0 - - # Add cube - bpy.ops.mesh.primitive_cube_add() - bpy.ops.object.editmode_toggle() - bpy.ops.mesh.subdivide(number_cuts=2) - bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) - bpy.ops.object.editmode_toggle() - - cube = bpy.context.active_object - # add new material - cubeMaterial = blend_data.materials.new("Cube_Material") - bpy.ops.object.material_slot_add() - cube.material_slots[0].material = cubeMaterial - # Diffuse - cubeMaterial.preview_render_type = "CUBE" - cubeMaterial.diffuse_color = (1.000, 0.373, 0.00) - cubeMaterial.diffuse_shader = 'OREN_NAYAR' - cubeMaterial.diffuse_intensity = 1.0 - cubeMaterial.roughness = 0.09002 - # Specular - cubeMaterial.specular_color = (1.000, 0.800, 0.136) - cubeMaterial.specular_shader = "PHONG" - cubeMaterial.specular_intensity = 1.0 - cubeMaterial.specular_hardness = 511.0 - # Shading - cubeMaterial.ambient = 1.00 - cubeMaterial.use_cubic = False - # Transparency - cubeMaterial.use_transparency = False - cubeMaterial.alpha = 0 - # Mirror - cubeMaterial.raytrace_mirror.use = True - cubeMaterial.mirror_color = (1.000, 0.793, 0.0) - cubeMaterial.raytrace_mirror.reflect_factor = 0.394 - cubeMaterial.raytrace_mirror.fresnel = 2.0 - cubeMaterial.raytrace_mirror.fresnel_factor = 1.641 - cubeMaterial.raytrace_mirror.fade_to = "FADE_TO_SKY" - cubeMaterial.raytrace_mirror.gloss_anisotropic = 1.0 - # Shadow - cubeMaterial.use_transparent_shadows = True - - # Add a texture - cubetex = blend_data.textures.new("CloudTex", type='CLOUDS') - cubetex.noise_type = 'SOFT_NOISE' - cubetex.noise_scale = 0.25 - mtex = cubeMaterial.texture_slots.add() - mtex.texture = cubetex - mtex.texture_coords = 'ORCO' - mtex.scale = (0.800, 0.800, 0.800) - mtex.use_map_mirror = True - mtex.mirror_factor = 0.156 - mtex.use_map_color_diffuse = True - mtex.diffuse_color_factor = 0.156 - mtex.use_map_normal = True - mtex.normal_factor = 0.010 - mtex.blend_type = "ADD" - mtex.use_rgb_to_intensity = True - mtex.color = (1.000, 0.207, 0.000) - - # Add monkey - bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505)) - bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1)) - bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0)) - bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0)) - bpy.ops.object.modifier_add(type='SUBSURF') - bpy.ops.object.shade_smooth() - monkey = bpy.context.active_object - # add new material - monkeyMaterial = blend_data.materials.new("Monkey_Material") - bpy.ops.object.material_slot_add() - monkey.material_slots[0].material = monkeyMaterial - # Material settings - monkeyMaterial.preview_render_type = "MONKEY" - monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288) - monkeyMaterial.specular_color = (0.604, 0.465, 0.136) - monkeyMaterial.diffuse_shader = 'LAMBERT' - monkeyMaterial.diffuse_intensity = 1.0 - monkeyMaterial.specular_intensity = 0.3 - monkeyMaterial.ambient = 0 - monkeyMaterial.type = 'SURFACE' - monkeyMaterial.use_cubic = True - monkeyMaterial.use_transparency = False - monkeyMaterial.alpha = 0 - monkeyMaterial.use_transparent_shadows = True - monkeyMaterial.raytrace_mirror.use = True - monkeyMaterial.raytrace_mirror.reflect_factor = 0.65 - monkeyMaterial.raytrace_mirror.fade_to = "FADE_TO_MATERIAL" - - # Add plane - bpy.ops.mesh.primitive_plane_add( - radius=50, view_align=False, enter_editmode=False, location=(0, 0, -1) - ) - bpy.ops.object.editmode_toggle() - bpy.ops.transform.rotate( - value=-0.8, axis=(0, 0, 1), constraint_axis=(False, False, True), - constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1 - ) - bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) - bpy.ops.object.editmode_toggle() - plane = bpy.context.active_object - # add new material - planeMaterial = blend_data.materials.new("Plane_Material") - bpy.ops.object.material_slot_add() - plane.material_slots[0].material = planeMaterial - # Material settings - planeMaterial.preview_render_type = "CUBE" - planeMaterial.diffuse_color = (0.2, 0.2, 0.2) - planeMaterial.specular_color = (0.604, 0.465, 0.136) - planeMaterial.specular_intensity = 0.3 - planeMaterial.ambient = 0 - planeMaterial.use_cubic = True - planeMaterial.use_transparency = False - planeMaterial.alpha = 0 - planeMaterial.use_transparent_shadows = True + lamp2 = bpy.context.active_object.data + lamp2.name = "Point_Left" + lamp2.energy = 1.0 + lamp2.distance = 30.0 + + # Add cube + bpy.ops.mesh.primitive_cube_add() + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.subdivide(number_cuts=2) + bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) + bpy.ops.object.editmode_toggle() + + cube = bpy.context.active_object + # add new material + cubeMaterial = blend_data.materials.new("Cube_Material") + bpy.ops.object.material_slot_add() + cube.material_slots[0].material = cubeMaterial + # Diffuse + cubeMaterial.preview_render_type = "CUBE" + cubeMaterial.diffuse_color = (1.000, 0.373, 0.00) + cubeMaterial.diffuse_shader = 'OREN_NAYAR' + cubeMaterial.diffuse_intensity = 1.0 + cubeMaterial.roughness = 0.09002 + # Specular + cubeMaterial.specular_color = (1.000, 0.800, 0.136) + cubeMaterial.specular_shader = "PHONG" + cubeMaterial.specular_intensity = 1.0 + cubeMaterial.specular_hardness = 511.0 + # Shading + cubeMaterial.ambient = 1.00 + cubeMaterial.use_cubic = False + # Transparency + cubeMaterial.use_transparency = False + cubeMaterial.alpha = 0 + # Mirror + cubeMaterial.raytrace_mirror.use = True + cubeMaterial.mirror_color = (1.000, 0.793, 0.0) + cubeMaterial.raytrace_mirror.reflect_factor = 0.394 + cubeMaterial.raytrace_mirror.fresnel = 2.0 + cubeMaterial.raytrace_mirror.fresnel_factor = 1.641 + cubeMaterial.raytrace_mirror.fade_to = "FADE_TO_SKY" + cubeMaterial.raytrace_mirror.gloss_anisotropic = 1.0 + # Shadow + cubeMaterial.use_transparent_shadows = True + + # Add a texture + cubetex = blend_data.textures.new("CloudTex", type='CLOUDS') + cubetex.noise_type = 'SOFT_NOISE' + cubetex.noise_scale = 0.25 + mtex = cubeMaterial.texture_slots.add() + mtex.texture = cubetex + mtex.texture_coords = 'ORCO' + mtex.scale = (0.800, 0.800, 0.800) + mtex.use_map_mirror = True + mtex.mirror_factor = 0.156 + mtex.use_map_color_diffuse = True + mtex.diffuse_color_factor = 0.156 + mtex.use_map_normal = True + mtex.normal_factor = 0.010 + mtex.blend_type = "ADD" + mtex.use_rgb_to_intensity = True + mtex.color = (1.000, 0.207, 0.000) + + # Add monkey + bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505)) + bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1)) + bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0)) + bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0)) + bpy.ops.object.modifier_add(type='SUBSURF') + bpy.ops.object.shade_smooth() + monkey = bpy.context.active_object + # add new material + monkeyMaterial = blend_data.materials.new("Monkey_Material") + bpy.ops.object.material_slot_add() + monkey.material_slots[0].material = monkeyMaterial + # Material settings + monkeyMaterial.preview_render_type = "MONKEY" + monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288) + monkeyMaterial.specular_color = (0.604, 0.465, 0.136) + monkeyMaterial.diffuse_shader = 'LAMBERT' + monkeyMaterial.diffuse_intensity = 1.0 + monkeyMaterial.specular_intensity = 0.3 + monkeyMaterial.ambient = 0 + monkeyMaterial.type = 'SURFACE' + monkeyMaterial.use_cubic = True + monkeyMaterial.use_transparency = False + monkeyMaterial.alpha = 0 + monkeyMaterial.use_transparent_shadows = True + monkeyMaterial.raytrace_mirror.use = True + monkeyMaterial.raytrace_mirror.reflect_factor = 0.65 + monkeyMaterial.raytrace_mirror.fade_to = "FADE_TO_MATERIAL" + + # Add plane + bpy.ops.mesh.primitive_plane_add( + radius=50, view_align=False, enter_editmode=False, location=(0, 0, -1) + ) + bpy.ops.object.editmode_toggle() + bpy.ops.transform.rotate( + value=-0.8, axis=(0, 0, 1), constraint_axis=(False, False, True), + constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', + proportional_edit_falloff='SMOOTH', proportional_size=1 + ) + bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) + bpy.ops.object.editmode_toggle() + plane = bpy.context.active_object + # add new material + planeMaterial = blend_data.materials.new("Plane_Material") + bpy.ops.object.material_slot_add() + plane.material_slots[0].material = planeMaterial + # Material settings + planeMaterial.preview_render_type = "CUBE" + planeMaterial.diffuse_color = (0.2, 0.2, 0.2) + planeMaterial.specular_color = (0.604, 0.465, 0.136) + planeMaterial.specular_intensity = 0.3 + planeMaterial.ambient = 0 + planeMaterial.use_cubic = True + planeMaterial.use_transparency = False + planeMaterial.alpha = 0 + planeMaterial.use_transparent_shadows = True + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "bi.add_scene\nError: {}".format(e)) + + return {'CANCELLED'} return {"FINISHED"} diff --git a/add_advanced_objects/scene_objects_cycles.py b/add_advanced_objects/scene_objects_cycles.py index 1e07b3e5..85e85867 100644 --- a/add_advanced_objects/scene_objects_cycles.py +++ b/add_advanced_objects/scene_objects_cycles.py @@ -7,124 +7,136 @@ from bpy.types import Operator class add_cycles_scene(Operator): bl_idname = "objects_cycles.add_scene" bl_label = "Create test scene" - bl_description = "Cycles Scene with Objects" + bl_description = "Cycles renderer Scene with Objects" bl_options = {'REGISTER'} def execute(self, context): - blend_data = context.blend_data - # ob = bpy.context.active_object - - # add new scene - bpy.ops.scene.new(type="NEW") - scene = bpy.context.scene - bpy.context.scene.render.engine = 'CYCLES' - scene.name = "scene_object_cycles" - - # render settings - render = scene.render - render.resolution_x = 1920 - render.resolution_y = 1080 - render.resolution_percentage = 50 - - # add new world - world = bpy.data.worlds.new("Cycles_Object_World") - scene.world = world - world.use_sky_blend = True - world.use_sky_paper = True - world.horizon_color = (0.004393, 0.02121, 0.050) - world.zenith_color = (0.03335, 0.227, 0.359) - world.light_settings.use_ambient_occlusion = True - world.light_settings.ao_factor = 0.25 - - # add camera - bpy.ops.object.camera_add( - location=(7.48113, -6.50764, 5.34367), - rotation=(1.109319, 0.010817, 0.814928) - ) - cam = bpy.context.active_object.data - cam.lens = 35 - cam.draw_size = 0.1 - bpy.ops.view3d.viewnumpad(type='CAMERA') - - # add point lamp - bpy.ops.object.lamp_add( - type="POINT", location=(4.07625, 1.00545, 5.90386), - rotation=(0.650328, 0.055217, 1.866391) - ) - lamp1 = bpy.context.active_object.data - lamp1.name = "Point_Right" - lamp1.energy = 1.0 - lamp1.distance = 30.0 - lamp1.shadow_method = "RAY_SHADOW" - lamp1.use_sphere = True - - # add point lamp2 - bpy.ops.object.lamp_add( - type="POINT", location=(-0.57101, -4.24586, 5.53674), - rotation=(1.571, 0, 0.785) - ) - lamp2 = bpy.context.active_object.data - lamp2.name = "Point_Left" - lamp2.energy = 1.0 - lamp2.distance = 30.0 - - # Add cube - bpy.ops.mesh.primitive_cube_add() - bpy.ops.object.editmode_toggle() - bpy.ops.mesh.subdivide(number_cuts=2) - bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) - bpy.ops.object.editmode_toggle() - cube = bpy.context.active_object - - # add cube material - cubeMaterial = blend_data.materials.new("Cycles_Cube_Material") - bpy.ops.object.material_slot_add() - cube.material_slots[0].material = cubeMaterial - # Diffuse - cubeMaterial.preview_render_type = "CUBE" - cubeMaterial.diffuse_color = (1.000, 0.373, 0.00) - # Cycles - cubeMaterial.use_nodes = True - - # Add monkey - bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505)) - bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1)) - bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0)) - bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0)) - - bpy.ops.object.modifier_add(type='SUBSURF') - bpy.ops.object.shade_smooth() - monkey = bpy.context.active_object - - # add monkey material - monkeyMaterial = blend_data.materials.new("Cycles_Monkey_Material") - bpy.ops.object.material_slot_add() - monkey.material_slots[0].material = monkeyMaterial - # Diffuse - monkeyMaterial.preview_render_type = "MONKEY" - monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288) - # Cycles - monkeyMaterial.use_nodes = True - - # Add plane - bpy.ops.mesh.primitive_plane_add( - radius=50, view_align=False, - enter_editmode=False, location=(0, 0, -1) - ) - bpy.ops.object.editmode_toggle() - bpy.ops.transform.rotate(value=-0.8, axis=(0, 0, 1), constraint_axis=(False, False, True)) - bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) - bpy.ops.object.editmode_toggle() - plane = bpy.context.active_object - - # add plane material - planeMaterial = blend_data.materials.new("Cycles_Plane_Material") - bpy.ops.object.material_slot_add() - plane.material_slots[0].material = planeMaterial - # Diffuse - planeMaterial.preview_render_type = "FLAT" - planeMaterial.diffuse_color = (0.2, 0.2, 0.2) - # Cycles - planeMaterial.use_nodes = True + try: + blend_data = context.blend_data + + # add new scene + bpy.ops.scene.new(type="NEW") + scene = bpy.context.scene + bpy.context.scene.render.engine = 'CYCLES' + scene.name = "scene_object_cycles" + + # render settings + render = scene.render + render.resolution_x = 1920 + render.resolution_y = 1080 + render.resolution_percentage = 50 + + # add new world + world = bpy.data.worlds.new("Cycles_Object_World") + scene.world = world + world.use_sky_blend = True + world.use_sky_paper = True + world.horizon_color = (0.004393, 0.02121, 0.050) + world.zenith_color = (0.03335, 0.227, 0.359) + world.light_settings.use_ambient_occlusion = True + world.light_settings.ao_factor = 0.25 + + # add camera + bpy.ops.object.camera_add( + location=(7.48113, -6.50764, 5.34367), + rotation=(1.109319, 0.010817, 0.814928) + ) + cam = bpy.context.active_object.data + cam.lens = 35 + cam.draw_size = 0.1 + bpy.ops.view3d.viewnumpad(type='CAMERA') + + # add point lamp + bpy.ops.object.lamp_add( + type="POINT", location=(4.07625, 1.00545, 5.90386), + rotation=(0.650328, 0.055217, 1.866391) + ) + lamp1 = bpy.context.active_object.data + lamp1.name = "Point_Right" + lamp1.energy = 1.0 + lamp1.distance = 30.0 + lamp1.shadow_method = "RAY_SHADOW" + lamp1.use_sphere = True + + # add point lamp2 + bpy.ops.object.lamp_add( + type="POINT", location=(-0.57101, -4.24586, 5.53674), + rotation=(1.571, 0, 0.785) + ) + lamp2 = bpy.context.active_object.data + lamp2.name = "Point_Left" + lamp2.energy = 1.0 + lamp2.distance = 30.0 + + # Add cube + bpy.ops.mesh.primitive_cube_add() + bpy.ops.object.editmode_toggle() + bpy.ops.mesh.subdivide(number_cuts=2) + bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) + bpy.ops.object.editmode_toggle() + cube = bpy.context.active_object + + # add cube material + cubeMaterial = blend_data.materials.new("Cycles_Cube_Material") + bpy.ops.object.material_slot_add() + cube.material_slots[0].material = cubeMaterial + # Diffuse + cubeMaterial.preview_render_type = "CUBE" + cubeMaterial.diffuse_color = (1.000, 0.373, 0.00) + # Cycles + cubeMaterial.use_nodes = True + + # Add monkey + bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505)) + bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1)) + bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0)) + bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0)) + + bpy.ops.object.modifier_add(type='SUBSURF') + bpy.ops.object.shade_smooth() + monkey = bpy.context.active_object + + # add monkey material + monkeyMaterial = blend_data.materials.new("Cycles_Monkey_Material") + bpy.ops.object.material_slot_add() + monkey.material_slots[0].material = monkeyMaterial + # Diffuse + monkeyMaterial.preview_render_type = "MONKEY" + monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288) + # Cycles + monkeyMaterial.use_nodes = True + + # Add plane + bpy.ops.mesh.primitive_plane_add( + radius=50, view_align=False, + enter_editmode=False, location=(0, 0, -1) + ) + bpy.ops.object.editmode_toggle() + bpy.ops.transform.rotate( + value=-0.8, axis=(0, 0, 1), + constraint_axis=(False, False, True) + ) + bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) + bpy.ops.object.editmode_toggle() + plane = bpy.context.active_object + + # add plane material + planeMaterial = blend_data.materials.new("Cycles_Plane_Material") + bpy.ops.object.material_slot_add() + plane.material_slots[0].material = planeMaterial + # Diffuse + planeMaterial.preview_render_type = "FLAT" + planeMaterial.diffuse_color = (0.2, 0.2, 0.2) + # Cycles + planeMaterial.use_nodes = True + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "objects_cycles.add_scene\nError: {}".format(e)) + + return {'CANCELLED'} return {'FINISHED'} diff --git a/add_advanced_objects/scene_texture_render.py b/add_advanced_objects/scene_texture_render.py index 2025b979..02d6490b 100644 --- a/add_advanced_objects/scene_texture_render.py +++ b/add_advanced_objects/scene_texture_render.py @@ -7,60 +7,72 @@ from bpy.types import Operator class add_texture_scene(Operator): bl_idname = "objects_texture.add_scene" bl_label = "Create test scene" - bl_description = "Cycles Scene: Camera aligned to plane" + bl_description = "Cycles renderer Scene: Camera aligned to a plane" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): - blend_data = context.blend_data - # ob = bpy.context.active_object + try: + blend_data = context.blend_data - # add new scene - bpy.ops.scene.new(type="NEW") - scene = bpy.context.scene - bpy.context.scene.render.engine = 'CYCLES' - scene.name = "scene_texture_cycles" + # add new scene + bpy.ops.scene.new(type="NEW") + scene = bpy.context.scene + bpy.context.scene.render.engine = 'CYCLES' + scene.name = "scene_texture_cycles" - # render settings - render = scene.render - render.resolution_x = 1080 - render.resolution_y = 1080 - render.resolution_percentage = 100 + # render settings + render = scene.render + render.resolution_x = 1080 + render.resolution_y = 1080 + render.resolution_percentage = 100 - # add new world - world = bpy.data.worlds.new("Cycles_Textures_World") - scene.world = world - world.use_sky_blend = True - world.use_sky_paper = True - world.horizon_color = (0.004393, 0.02121, 0.050) - world.zenith_color = (0.03335, 0.227, 0.359) - world.light_settings.use_ambient_occlusion = True - world.light_settings.ao_factor = 0.5 + # add new world + world = bpy.data.worlds.new("Cycles_Textures_World") + scene.world = world + world.use_sky_blend = True + world.use_sky_paper = True + world.horizon_color = (0.004393, 0.02121, 0.050) + world.zenith_color = (0.03335, 0.227, 0.359) + world.light_settings.use_ambient_occlusion = True + world.light_settings.ao_factor = 0.5 - # add camera - bpy.ops.view3d.viewnumpad(type='TOP') - bpy.ops.object.camera_add(location=(0, 0, 2.1850), rotation=(0, 0, 0), view_align=True) - cam = bpy.context.active_object.data - cam.lens = 35 - cam.draw_size = 0.1 + # add camera + bpy.ops.view3d.viewnumpad(type='TOP') + bpy.ops.object.camera_add( + location=(0, 0, 2.1850), rotation=(0, 0, 0), view_align=True + ) + cam = bpy.context.active_object.data + cam.lens = 35 + cam.draw_size = 0.1 - # add plane - bpy.ops.mesh.primitive_plane_add(enter_editmode=True, location=(0, 0, 0)) - bpy.ops.mesh.subdivide(number_cuts=10, smoothness=0) - bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) - bpy.ops.object.editmode_toggle() - plane = bpy.context.active_object + # add plane + bpy.ops.mesh.primitive_plane_add(enter_editmode=True, location=(0, 0, 0)) + bpy.ops.mesh.subdivide(number_cuts=10, smoothness=0) + bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001) + bpy.ops.object.editmode_toggle() + plane = bpy.context.active_object - # add plane material - planeMaterial = blend_data.materials.new("Cycles_Plane_Material") - bpy.ops.object.material_slot_add() - plane.material_slots[0].material = planeMaterial - # Diffuse - planeMaterial.preview_render_type = "FLAT" - planeMaterial.diffuse_color = (0.2, 0.2, 0.2) - # Cycles - planeMaterial.use_nodes = True + # add plane material + planeMaterial = blend_data.materials.new("Cycles_Plane_Material") + bpy.ops.object.material_slot_add() + plane.material_slots[0].material = planeMaterial + # Diffuse + planeMaterial.preview_render_type = "FLAT" + planeMaterial.diffuse_color = (0.2, 0.2, 0.2) + # Cycles + planeMaterial.use_nodes = True + + # Back to Scene + sc = bpy.context.scene + bpy.ops.view3d.viewnumpad(type='CAMERA') + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "objects_texture.add_scene\nError: {}".format(e)) + + return {'CANCELLED'} - # Back to Scene - sc = bpy.context.scene - bpy.ops.view3d.viewnumpad(type='CAMERA') return {'FINISHED'} diff --git a/add_advanced_objects/trilighting.py b/add_advanced_objects/trilighting.py index d23b4dbc..e0068e66 100644 --- a/add_advanced_objects/trilighting.py +++ b/add_advanced_objects/trilighting.py @@ -8,8 +8,7 @@ from bpy.props import ( IntProperty, ) from math import ( - sin, - cos, + sin, cos, radians, sqrt, ) @@ -19,67 +18,69 @@ class TriLighting(Operator): bl_idname = "object.trilighting" bl_label = "Tri-Lighting Creator" bl_description = ("Add 3 Point Lighting to Selected / Active Object\n" - "Needs an active object in the scene") + "Needs an existing Active Object") bl_options = {'REGISTER', 'UNDO'} height = FloatProperty( - name="Height", - default=5 - ) + name="Height", + default=5 + ) distance = FloatProperty( - name="Distance", - default=5, - min=0.1, - subtype="DISTANCE" - ) + name="Distance", + default=5, + min=0.1, + subtype="DISTANCE" + ) energy = IntProperty( - name="Base Energy", - default=3, - min=1 - ) + name="Base Energy", + default=3, + min=1 + ) contrast = IntProperty( - name="Contrast", - default=50, - min=-100, max=100, - subtype="PERCENTAGE" - ) + name="Contrast", + default=50, + min=-100, max=100, + subtype="PERCENTAGE" + ) leftangle = IntProperty( - name="Left Angle", - default=26, - min=1, max=90, - subtype="ANGLE" - ) + name="Left Angle", + default=26, + min=1, max=90, + subtype="ANGLE" + ) rightangle = IntProperty( - name="Right Angle", - default=45, - min=1, max=90, - subtype="ANGLE" - ) + name="Right Angle", + default=45, + min=1, max=90, + subtype="ANGLE" + ) backangle = IntProperty( - name="Back Angle", - default=235, - min=90, max=270, - subtype="ANGLE" - ) - Light_Type_List = [('POINT', 'Point', 'Point Light'), - ('SUN', 'Sun', 'Sun Light'), - ('SPOT', 'Spot', 'Spot Light'), - ('HEMI', 'Hemi', 'Hemi Light'), - ('AREA', 'Area', 'Area Light')] + name="Back Angle", + default=235, + min=90, max=270, + subtype="ANGLE" + ) + Light_Type_List = [ + ('POINT', "Point", "Point Light"), + ('SUN', "Sun", "Sun Light"), + ('SPOT', "Spot", "Spot Light"), + ('HEMI', "Hemi", "Hemi Light"), + ('AREA', "Area", "Area Light") + ] primarytype = EnumProperty( - attr='tl_type', - name="Key Type", - description="Choose the type off Key Light you would like", - items=Light_Type_List, - default='HEMI' - ) + attr='tl_type', + name="Key Type", + description="Choose the types of Key Lights you would like", + items=Light_Type_List, + default='HEMI' + ) secondarytype = EnumProperty( - attr='tl_type', - name="Fill + Back Type", - description="Choose the type off secondary Light you would like", - items=Light_Type_List, - default="POINT" - ) + attr='tl_type', + name="Fill + Back Type", + description="Choose the types of secondary Lights you would like", + items=Light_Type_List, + default="POINT" + ) @classmethod def poll(cls, context): @@ -111,115 +112,127 @@ class TriLighting(Operator): col.prop(self, "secondarytype", text="") def execute(self, context): - scene = context.scene - view = context.space_data - if view.type == 'VIEW_3D' and not view.lock_camera_and_layers: - camera = view.camera - else: - camera = scene.camera - - if (camera is None): - cam_data = bpy.data.cameras.new(name='Camera') - cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data) - scene.objects.link(cam_obj) - scene.camera = cam_obj - bpy.ops.view3d.camera_to_view() - camera = cam_obj - bpy.ops.view3d.viewnumpad(type='TOP') - - obj = bpy.context.scene.objects.active - - # Calculate Energy for each Lamp - if(self.contrast > 0): - keyEnergy = self.energy - backEnergy = (self.energy / 100) * abs(self.contrast) - fillEnergy = (self.energy / 100) * abs(self.contrast) - else: - keyEnergy = (self.energy / 100) * abs(self.contrast) - backEnergy = self.energy - fillEnergy = self.energy - - # Calculate Direction for each Lamp - - # Calculate current Distance and get Delta - obj_position = obj.location - cam_position = camera.location - - delta_position = cam_position - obj_position - vector_length = sqrt( - (pow(delta_position.x, 2) + - pow(delta_position.y, 2) + - pow(delta_position.z, 2)) - ) - if not vector_length: - # division by zero most likely - self.report({'WARNING'}, "Operation Cancelled. No viable object in the scene") - return {'CANCELLED'} + try: + scene = context.scene + view = context.space_data + if view.type == 'VIEW_3D' and not view.lock_camera_and_layers: + camera = view.camera + else: + camera = scene.camera + + if (camera is None): + cam_data = bpy.data.cameras.new(name='Camera') + cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data) + scene.objects.link(cam_obj) + scene.camera = cam_obj + bpy.ops.view3d.camera_to_view() + camera = cam_obj + bpy.ops.view3d.viewnumpad(type='TOP') + + obj = bpy.context.scene.objects.active + + # Calculate Energy for each Lamp + if(self.contrast > 0): + keyEnergy = self.energy + backEnergy = (self.energy / 100) * abs(self.contrast) + fillEnergy = (self.energy / 100) * abs(self.contrast) + else: + keyEnergy = (self.energy / 100) * abs(self.contrast) + backEnergy = self.energy + fillEnergy = self.energy + + # Calculate Direction for each Lamp + + # Calculate current Distance and get Delta + obj_position = obj.location + cam_position = camera.location + + delta_position = cam_position - obj_position + vector_length = sqrt( + (pow(delta_position.x, 2) + + pow(delta_position.y, 2) + + pow(delta_position.z, 2)) + ) + if not vector_length: + # division by zero most likely + self.report({'WARNING'}, + "Operation Cancelled. No viable object in the scene") + + return {'CANCELLED'} + + single_vector = (1 / vector_length) * delta_position + + # Calc back position + singleback_vector = single_vector.copy() + singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \ + (-sin(radians(self.backangle)) * single_vector.y) + + singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \ + (cos(radians(self.backangle)) * single_vector.y) + + backx = obj_position.x + self.distance * singleback_vector.x + backy = obj_position.y + self.distance * singleback_vector.y + + backData = bpy.data.lamps.new(name="TriLamp-Back", type=self.secondarytype) + backData.energy = backEnergy + + backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData) + scene.objects.link(backLamp) + backLamp.location = (backx, backy, self.height) + + trackToBack = backLamp.constraints.new(type="TRACK_TO") + trackToBack.target = obj + trackToBack.track_axis = "TRACK_NEGATIVE_Z" + trackToBack.up_axis = "UP_Y" + + # Calc right position + singleright_vector = single_vector.copy() + singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \ + (-sin(radians(self.rightangle)) * single_vector.y) + + singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \ + (cos(radians(self.rightangle)) * single_vector.y) + + rightx = obj_position.x + self.distance * singleright_vector.x + righty = obj_position.y + self.distance * singleright_vector.y + + rightData = bpy.data.lamps.new(name="TriLamp-Fill", type=self.secondarytype) + rightData.energy = fillEnergy + rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData) + scene.objects.link(rightLamp) + rightLamp.location = (rightx, righty, self.height) + trackToRight = rightLamp.constraints.new(type="TRACK_TO") + trackToRight.target = obj + trackToRight.track_axis = "TRACK_NEGATIVE_Z" + trackToRight.up_axis = "UP_Y" + + # Calc left position + singleleft_vector = single_vector.copy() + singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \ + (-sin(radians(-self.leftangle)) * single_vector.y) + singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \ + (cos(radians(-self.leftangle)) * single_vector.y) + leftx = obj_position.x + self.distance * singleleft_vector.x + lefty = obj_position.y + self.distance * singleleft_vector.y + + leftData = bpy.data.lamps.new(name="TriLamp-Key", type=self.primarytype) + leftData.energy = keyEnergy + + leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData) + scene.objects.link(leftLamp) + leftLamp.location = (leftx, lefty, self.height) + trackToLeft = leftLamp.constraints.new(type="TRACK_TO") + trackToLeft.target = obj + trackToLeft.track_axis = "TRACK_NEGATIVE_Z" + trackToLeft.up_axis = "UP_Y" + + except Exception as e: + self.report({'WARNING'}, + "Some operations could not be performed (See Console for more info)") + + print("\n[Add Advanced Objects]\nOperator: " + "object.trilighting\nError: {}".format(e)) - single_vector = (1 / vector_length) * delta_position - - # Calc back position - singleback_vector = single_vector.copy() - singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \ - (-sin(radians(self.backangle)) * single_vector.y) - - singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \ - (cos(radians(self.backangle)) * single_vector.y) - - backx = obj_position.x + self.distance * singleback_vector.x - backy = obj_position.y + self.distance * singleback_vector.y - - backData = bpy.data.lamps.new(name="TriLamp-Back", type=self.secondarytype) - backData.energy = backEnergy - - backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData) - scene.objects.link(backLamp) - backLamp.location = (backx, backy, self.height) - - trackToBack = backLamp.constraints.new(type="TRACK_TO") - trackToBack.target = obj - trackToBack.track_axis = "TRACK_NEGATIVE_Z" - trackToBack.up_axis = "UP_Y" - - # Calc right position - singleright_vector = single_vector.copy() - singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \ - (-sin(radians(self.rightangle)) * single_vector.y) - - singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \ - (cos(radians(self.rightangle)) * single_vector.y) - - rightx = obj_position.x + self.distance * singleright_vector.x - righty = obj_position.y + self.distance * singleright_vector.y - - rightData = bpy.data.lamps.new(name="TriLamp-Fill", type=self.secondarytype) - rightData.energy = fillEnergy - rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData) - scene.objects.link(rightLamp) - rightLamp.location = (rightx, righty, self.height) - trackToRight = rightLamp.constraints.new(type="TRACK_TO") - trackToRight.target = obj - trackToRight.track_axis = "TRACK_NEGATIVE_Z" - trackToRight.up_axis = "UP_Y" - - # Calc left position - singleleft_vector = single_vector.copy() - singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \ - (-sin(radians(-self.leftangle)) * single_vector.y) - singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \ - (cos(radians(-self.leftangle)) * single_vector.y) - leftx = obj_position.x + self.distance * singleleft_vector.x - lefty = obj_position.y + self.distance * singleleft_vector.y - - leftData = bpy.data.lamps.new(name="TriLamp-Key", type=self.primarytype) - leftData.energy = keyEnergy - - leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData) - scene.objects.link(leftLamp) - leftLamp.location = (leftx, lefty, self.height) - trackToLeft = leftLamp.constraints.new(type="TRACK_TO") - trackToLeft.target = obj - trackToLeft.track_axis = "TRACK_NEGATIVE_Z" - trackToLeft.up_axis = "UP_Y" + return {'CANCELLED'} return {'FINISHED'} diff --git a/add_advanced_objects/unfold_transition.py b/add_advanced_objects/unfold_transition.py index a621ca54..769386f5 100644 --- a/add_advanced_objects/unfold_transition.py +++ b/add_advanced_objects/unfold_transition.py @@ -1,18 +1,18 @@ +# gpl: authors Liero, Atom + bl_info = { "name": "Unfold transition", - "version": (0, 1, 0), + "author": "Liero, Atom", + "version": (0, 1, 2), "location": "Tool bar > Animation tab > UnFold Transition", - "description": "Simple unfold transition / animation, will separate faces and set up an armature", + "description": "Simple unfold transition / animation, will " + "separate faces and set up an armature", "category": "Animation"} +# Note the properties are moved to __init__ +# search for patterns advanced_objects, adv_obj + import bpy -from bpy.props import ( - BoolProperty, - EnumProperty, - FloatProperty, - IntProperty, - # PointerProperty, - ) from bpy.types import ( Operator, Panel, @@ -25,87 +25,22 @@ from mathutils import Vector from mathutils.geometry import intersect_point_line -bpy.types.WindowManager.modo = EnumProperty( - name="", - items=[("cursor", "3D Cursor", "Use the Distance to 3D Cursor"), - ("weight", "Weight Map", "Use a Painted Weight map"), - ("index", "Mesh Indices", "Use Faces and Vertices index")], - description="How to Sort Bones for animation", default="cursor" - ) -bpy.types.WindowManager.flip = BoolProperty( - name="Flipping Faces", - default=False, - description="Rotate faces around the Center & skip Scaling - " - "keep checked for both operators" - ) -bpy.types.WindowManager.fold_duration = IntProperty( - name="Total Time", - min=5, soft_min=25, - max=10000, soft_max=2500, - default=200, - description="Total animation length" - ) -bpy.types.WindowManager.sca_time = IntProperty( - name="Scale Time", - min=1, - max=5000, soft_max=500, - default=10, - description="Faces scaling time" - ) -bpy.types.WindowManager.rot_time = IntProperty( - name="Rotation Time", - min=1, soft_min=5, - max=5000, soft_max=500, - default=15, - description="Faces rotation time" - ) -bpy.types.WindowManager.rot_max = IntProperty( - name="Angle", - min=-180, - max=180, - default=135, - description="Faces rotation angle" - ) -bpy.types.WindowManager.fold_noise = IntProperty( - name="Noise", - min=0, - max=500, soft_max=50, - default=0, - description="Offset some faces animation" - ) -bpy.types.WindowManager.bounce = FloatProperty( - name="Bounce", - min=0, - max=10, soft_max=2.5, - default=0, - description="Add some bounce to rotation" - ) -bpy.types.WindowManager.from_point = BoolProperty( - name="Point", - default=False, - description="Scale faces from a Point instead of from an Edge" - ) -bpy.types.WindowManager.wiggle_rot = BoolProperty( - name="Wiggle", - default=False, - description="Use all Axis + Random Rotation instead of X Aligned" - ) - - class Set_Up_Fold(Operator): bl_idname = "object.set_up_fold" bl_label = "Set Up Unfold" - bl_description = "Set up Faces and Bones for animation" + bl_description = ("Set up Faces and Bones for animation\n" + "Needs an existing Active Mesh Object") bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): - return (bpy.context.object.type == "MESH") + obj = context.active_object + return (obj is not None and obj.type == "MESH") def execute(self, context): bpy.ops.object.mode_set() - wm = context.window_manager scn = bpy.context.scene + adv_obj = scn.advanced_objects obj = bpy.context.object dat = obj.data fac = dat.polygons @@ -118,6 +53,7 @@ class Set_Up_Fold(Operator): old_vg = [vg for vg in obj.vertex_groups if vg.name.startswith("bone.")] for vg in old_vg: obj.vertex_groups.remove(vg) + if "UnFold" in obj.modifiers: arm = obj.modifiers["UnFold"].object rig = arm.data @@ -130,7 +66,7 @@ class Set_Up_Fold(Operator): obj.modifiers.remove(obj.modifiers["UnFold"]) # try to obtain the face sequence from the vertex weights - if wm.modo == "weight": + if adv_obj.unfold_modo == "weight": if len(obj.vertex_groups): i = obj.vertex_groups.active.index W = [] @@ -154,6 +90,7 @@ class Set_Up_Fold(Operator): S = [x[1:] for x in W] else: self.report({"INFO"}, "First paint a Weight Map for this object") + return {"FINISHED"} # separate the faces and sort them @@ -161,14 +98,17 @@ class Set_Up_Fold(Operator): bpy.ops.mesh.select_all(action="SELECT") bpy.ops.mesh.edge_split() bpy.ops.mesh.select_all(action="SELECT") - if wm.modo == "cursor": + + if adv_obj.unfold_modo == "cursor": bpy.context.tool_settings.mesh_select_mode = [True, True, True] - bpy.ops.mesh.sort_elements(type="CURSOR_DISTANCE", elements={"VERT", "EDGE", "FACE"}) + bpy.ops.mesh.sort_elements( + type="CURSOR_DISTANCE", elements={"VERT", "EDGE", "FACE"} + ) bpy.context.tool_settings.mesh_select_mode = [False, False, True] bpy.ops.object.mode_set() # Get sequence of faces and edges from the face / vertex indices - if wm.modo != "weight": + if adv_obj.unfold_modo != "weight": S = [] for f in fac: E = list(f.edge_keys) @@ -183,6 +123,9 @@ class Set_Up_Fold(Operator): # create the armature and the modifier arm = bpy.data.armatures.new("arm") rig = bpy.data.objects.new("rig_" + obj.name, arm) + + # store the name for checking the right rig + adv_obj.unfold_arm_name = rig.name rig.matrix_world = obj.matrix_world scn.objects.link(rig) scn.objects.active = rig @@ -203,10 +146,11 @@ class Set_Up_Fold(Operator): for fb in S: f = fac[fb[0]] b = arm.edit_bones.new("bone.000") - if wm.flip: + if adv_obj.unfold_flip: b.tail, b.head = fb[2], fb[1] else: b.tail, b.head = fb[1], fb[2] + b.align_roll(f.normal) b.select = False b.layers = vis @@ -215,7 +159,8 @@ class Set_Up_Fold(Operator): vg.add(f.vertices, 1, "ADD") bpy.ops.object.mode_set() - if wm.modo == "weight": + + if adv_obj.unfold_modo == "weight": obj.vertex_groups.active_index = 0 scn.objects.active = rig obj.select = False @@ -226,66 +171,120 @@ class Set_Up_Fold(Operator): class Animate_Fold(Operator): bl_idname = "object.animate_fold" bl_label = "Animate Unfold" - bl_description = "Animate bones to simulate unfold... Starts on current frame" + bl_description = ("Animate bones to simulate unfold. Starts on current frame\n" + "Needs an existing Active Armature Object created in the previous step") bl_options = {"REGISTER", "UNDO"} + is_not_undo = False + @classmethod def poll(cls, context): + obj = context.active_object + return (obj is not None and obj.type == "ARMATURE" and obj.is_visible(bpy.context.scene)) + + def draw(self, context): + layout = self.layout + adv_obj = context.scene.advanced_objects + + if self.is_not_undo is True: + layout.label(text="Warning:", icon="INFO") + layout.label(text="The generated Armature was not selected or it was renamed") + layout.label(text="The animation can fail if it is not generated by the previous step") + layout.separator() + layout.label(text="Expected Armature name:", icon="BONE_DATA") + layout.label(text=str(adv_obj.unfold_arm_name), icon="TRIA_RIGHT") + layout.label(text="To Continue press OK, to Cancel click Outside the Pop-up") + layout.separator() + else: + return + + def invoke(self, context, event): obj = bpy.context.object - return (obj.type == "ARMATURE" and obj.is_visible(bpy.context.scene)) + scn = bpy.context.scene + adv_obj = scn.advanced_objects + + if obj.name != adv_obj.unfold_arm_name: + self.is_not_undo = True + return context.window_manager.invoke_props_dialog(self, width=400) + else: + return self.execute(context) def execute(self, context): obj = bpy.context.object scn = bpy.context.scene + adv_obj = scn.advanced_objects fra = scn.frame_current - wm = context.window_manager - + if obj.name != adv_obj.unfold_arm_name: + self.report({"INFO"}, + "The generated rig was not selected or renamed. The animation can fail") # clear the animation and get the list of bones if obj.animation_data: obj.animation_data_clear() bpy.ops.object.mode_set(mode="POSE") bones = obj.pose.bones[0].children_recursive - if wm.flip: + + if adv_obj.unfold_flip: rot = -3.141592 else: - rot = wm.rot_max / 57.3 - extra = wm.rot_time * wm.bounce - ruido = max(wm.rot_time + extra, wm.sca_time) + wm.fold_noise - vel = (wm.fold_duration - ruido) / len(bones) + rot = adv_obj.unfold_rot_max / 57.3 + + extra = adv_obj.unfold_rot_time * adv_obj.unfold_bounce + ruido = max(adv_obj.unfold_rot_time + extra, + adv_obj.unfold_sca_time) + adv_obj.unfold_fold_noise + + len_bones = len(bones) if len(bones) != 0 else 1 # possible division by zero + vel = (adv_obj.unfold_fold_duration - ruido) / len_bones # introduce scale and rotation keyframes for a, b in enumerate(bones): - t = fra + a * vel + randint(0, wm.fold_noise) - if wm.flip: + t = fra + a * vel + randint(0, adv_obj.unfold_fold_noise) + + if adv_obj.unfold_flip: b.scale = (1, 1, 1) - elif wm.from_point: + elif adv_obj.unfold_from_point: b.scale = (0, 0, 0) else: b.scale = (1, 0, 0) - if not wm.flip: + + if not adv_obj.unfold_flip: b.keyframe_insert("scale", frame=t) b.scale = (1, 1, 1) - b.keyframe_insert("scale", frame=t + wm.sca_time) - if wm.rot_max: + b.keyframe_insert("scale", frame=t + adv_obj.unfold_sca_time) + + if adv_obj.unfold_rot_max: b.rotation_mode = "XYZ" - if wm.wiggle_rot: + if adv_obj.unfold_wiggle_rot: euler = (uniform(-rot, rot), uniform(-rot, rot), uniform(-rot, rot)) else: euler = (rot, 0, 0) + b.rotation_euler = euler b.keyframe_insert("rotation_euler", frame=t) - if wm.bounce: - val = wm.bounce * -.10 + + if adv_obj.unfold_bounce: + val = adv_obj.unfold_bounce * -.10 b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2]) - b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + .25 * extra) - val = wm.bounce * .05 + b.keyframe_insert( + "rotation_euler", frame=t + adv_obj.unfold_rot_time + .25 * extra + ) + + val = adv_obj.unfold_bounce * .05 b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2]) - b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + .50 * extra) - val = wm.bounce * -.025 + b.keyframe_insert( + "rotation_euler", frame=t + adv_obj.unfold_rot_time + .50 * extra + ) + + val = adv_obj.unfold_bounce * -.025 b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2]) - b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + .75 * extra) + b.keyframe_insert( + "rotation_euler", frame=t + adv_obj.unfold_rot_time + .75 * extra + ) + b.rotation_euler = (0, 0, 0) - b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + extra) + b.keyframe_insert( + "rotation_euler", frame=t + adv_obj.unfold_rot_time + extra + ) + self.is_not_undo = False return {"FINISHED"} @@ -299,26 +298,34 @@ class PanelFOLD(Panel): bl_options = {"DEFAULT_CLOSED"} def draw(self, context): - wm = context.window_manager layout = self.layout - column = layout.column() - column.operator("object.set_up_fold", text="1. Set Up Unfold") - column.prop(wm, "modo") - column.prop(wm, "flip") - layout.separator() - column = layout.column() - column.operator("object.animate_fold", text="2. Animate Unfold") - column.prop(wm, "fold_duration") - column.prop(wm, "sca_time") - column.prop(wm, "rot_time") - column.prop(wm, "rot_max") - row = column.row(align=True) - row.prop(wm, "fold_noise") - row.prop(wm, "bounce") - row = column.row(align=True) - row.prop(wm, "wiggle_rot") - if not wm.flip: - row.prop(wm, "from_point") + adv_obj = context.scene.advanced_objects + + box = layout.box() + col = box.column() + col.operator("object.set_up_fold", text="1. Set Up Unfold") + col.separator() + col.label("Unfold Mode:") + col.prop(adv_obj, "unfold_modo") + col.prop(adv_obj, "unfold_flip") + + box = layout.box() + col = box.column(align=True) + col.operator("object.animate_fold", text="2. Animate Unfold") + col.separator() + col.prop(adv_obj, "unfold_fold_duration") + col.prop(adv_obj, "unfold_sca_time") + col.prop(adv_obj, "unfold_rot_time") + col.prop(adv_obj, "unfold_rot_max") + + row = col.row(align=True) + row.prop(adv_obj, "unfold_fold_noise") + row.prop(adv_obj, "unfold_bounce") + row = col.row(align=True) + row.prop(adv_obj, "unfold_wiggle_rot") + + if not adv_obj.unfold_flip: + row.prop(adv_obj, "unfold_from_point") def register(): |