diff --git a/add_advanced_objects_menu/__init__.py b/add_advanced_objects_menu/__init__.py deleted file mode 100644 index 91ddcb18472c64b7c000b3eb13f3d2ab2dbea58b..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/__init__.py +++ /dev/null @@ -1,547 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# Contributed to by: -# 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 # -# Atom, Dannyboy, Mano-Wii, Kursad Karatas, teldredge, Phil Cote # - -bl_info = { - "name": "Add Advanced Objects", - "author": "Meta Androcto", - "version": (0, 1, 6), - "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/Object/Add_Advanced", - "category": "Object"} - -if "bpy" in locals(): - import importlib - - importlib.reload(add_light_template) - importlib.reload(scene_objects_bi) - importlib.reload(scene_objects_cycles) - importlib.reload(scene_texture_render) - importlib.reload(trilighting) - importlib.reload(pixelate_3d) - importlib.reload(object_add_chain) - importlib.reload(oscurart_chain_maker) - importlib.reload(circle_array) - importlib.reload(copy2) - importlib.reload(make_struts) - importlib.reload(random_box_structure) - importlib.reload(cubester) - importlib.reload(rope_alpha) - importlib.reload(add_mesh_aggregate) - importlib.reload(arrange_on_curve) - importlib.reload(mesh_easylattice) - -else: - 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 circle_array - 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 arrange_on_curve - from . import mesh_easylattice - - -import bpy -from bpy.types import ( - AddonPreferences, - Menu, - PropertyGroup, -) -from bpy.props import ( - BoolProperty, - EnumProperty, - FloatProperty, - IntProperty, - StringProperty, - PointerProperty, -) - - -# Define the "Scenes" menu -class VIEW3D_MT_scene_elements_add(Menu): - bl_idname = "VIEW3D_MT_scene_elements" - bl_label = "Test Scenes" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("bi.add_scene", - text="Scene_Objects_BI") - layout.operator("objects_cycles.add_scene", - text="Scene_Objects_Cycles") - layout.operator("objects_texture.add_scene", - text="Scene_Textures_Cycles") - - -# Define the "Lights" menu -class VIEW3D_MT_mesh_lights_add(Menu): - bl_idname = "VIEW3D_MT_scene_lights" - bl_label = "Lighting Sets" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("object.add_light_template", - text="Add Light Template") - layout.operator("object.trilighting", - text="Add Tri Lighting") - - -# Define the "Chains" menu -class VIEW3D_MT_mesh_chain_add(Menu): - bl_idname = "VIEW3D_MT_mesh_chain" - bl_label = "Chains" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - layout.operator("mesh.primitive_chain_add", icon="LINKED") - layout.operator("mesh.primitive_oscurart_chain_add", icon="LINKED") - - -# Define the "Array" Menu -class VIEW3D_MT_array_mods_add(Menu): - bl_idname = "VIEW3D_MT_array_mods" - bl_label = "Array Mods" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - layout.menu("VIEW3D_MT_mesh_chain", icon="LINKED") - - layout.operator("objects.circle_array_operator", - text="Circle Array", icon="MOD_ARRAY") - layout.operator("object.agregate_mesh", - text="Aggregate Mesh", icon="MOD_ARRAY") - layout.operator("mesh.copy2", - text="Copy To Vert/Edge", icon="MOD_ARRAY") - - -# Define the "Blocks" Menu -class VIEW3D_MT_quick_blocks_add(Menu): - bl_idname = "VIEW3D_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") - 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 VIEW3D_MT_Physics_tools_add(Menu): - bl_idname = "VIEW3D_MT_physics_tools" - bl_label = "Physics Tools" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - layout.operator("ball.rope", - text="Wrecking Ball", icon='PHYSICS') - layout.operator("clot.rope", - text="Cloth Rope", icon='PHYSICS') - - -# Define "Extras" menu -def menu(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - self.layout.separator() - self.layout.menu("VIEW3D_MT_scene_elements", icon="SCENE_DATA") - self.layout.menu("VIEW3D_MT_scene_lights", icon="LIGHT_SPOT") - self.layout.separator() - self.layout.menu("VIEW3D_MT_array_mods", icon="MOD_ARRAY") - self.layout.menu("VIEW3D_MT_quick_tools", icon="MOD_BUILD") - self.layout.menu("VIEW3D_MT_physics_tools", icon="PHYSICS") - - -# Addons Preferences -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 - - 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="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="CubeSter", icon="LAYER_USED") - box.label(text="Arrange 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 - ) - - # 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', - ) - - -def register(): - bpy.utils.register_module(__name__) - - bpy.types.Scene.advanced_objects = PointerProperty( - type=AdvancedObjProperties - ) - - # Add "Extras" menu to the "Add" menu - bpy.types.VIEW3D_MT_add.append(menu) - try: - bpy.types.VIEW3D_MT_AddMenu.append(menu) - except: - pass - - -def unregister(): - # Remove "Extras" menu from the "Add" menu. - bpy.types.VIEW3D_MT_add.remove(menu) - try: - bpy.types.VIEW3D_MT_AddMenu.remove(menu) - except: - pass - - bpy.utils.unregister_module(__name__) - del bpy.types.Scene.advanced_objects - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/add_light_template.py b/add_advanced_objects_menu/add_light_template.py deleted file mode 100644 index 3d6d880e8c66325b12602506a9e045454e82e31a..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/add_light_template.py +++ /dev/null @@ -1,145 +0,0 @@ -# gpl: author Rebellion - -import bpy -from bpy.types import Operator -from bpy.props import BoolProperty - - -def add_lights(self, context): - - if self.bKeyLight: - keyLight = bpy.data.lights.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.collection.objects.link(ob) - ob.rotation_euler[2] = -0.785398 - - if self.bFillLight: - fillLight = bpy.data.lights.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.collection.objects.link(ob) - ob.rotation_euler[2] = 0.785398 - ob.data.energy = 0.3 - - if self.bBackLight: - backLight = bpy.data.lights.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.collection.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 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", - description="Enable Key Light in the Scene", - default=True - ) - bFillLight: BoolProperty( - name="Fill Light", - description="Enable Fill Light in the Scene", - default=True - ) - bBackLight: BoolProperty( - name="Back Light", - description="Enable Back Light in the Scene", - default=True - ) - camera_constraint: BoolProperty( - 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): - 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.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_lights(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'} - - -def register(): - bpy.utils.register_class(OBJECT_OT_add_light_template) - - -def unregister(): - bpy.utils.unregister_class(OBJECT_OT_add_light_template) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/add_mesh_aggregate.py b/add_advanced_objects_menu/add_mesh_aggregate.py deleted file mode 100644 index 2665df139bb1a1b245775f9f0309c9f0d631c249..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/add_mesh_aggregate.py +++ /dev/null @@ -1,338 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# Simple aggregate of particles / meshes -# Copy the selected objects on the active object -# Based on the position of the cursor and a defined volume -# Allows to control growth by using a Build modifier - -bl_info = { - "name": "Aggregate Mesh", - "author": "liero", - "version": (0, 0, 5), - "blender": (2, 70, 0), - "location": "View3D > Tool Shelf", - "description": "Adds geometry to a mesh like in DLA aggregators", - "category": "Object"} - - -import bpy -import bmesh -from random import ( - choice, - gauss, - seed, - ) -from mathutils import Matrix -from bpy.props import ( - BoolProperty, - FloatProperty, - IntProperty, - ) -from bpy.types import Operator - - -def use_random_seed(self): - seed(self.rSeed) - return - - -def rg(n): - return (round(gauss(0, n), 2)) - - -def remover(sel=False): - bpy.ops.object.editmode_toggle() - if sel: - bpy.ops.mesh.select_all(action='SELECT') - bpy.ops.mesh.remove_doubles(threshold=0.0001) - bpy.ops.object.mode_set() - - -class OBJECT_OT_agregate_mesh(Operator): - bl_idname = "object.agregate_mesh" - bl_label = "Aggregate" - bl_description = ("Adds geometry to a mesh like in DLA aggregators\n" - "Needs at least two selected Mesh objects") - bl_options = {'REGISTER', 'UNDO', 'PRESET'} - - volX: FloatProperty( - 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" - ) - volZ: FloatProperty( - 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" - ) - varSca: FloatProperty( - 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" - ) - rotY: FloatProperty( - 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" - ) - rSeed: IntProperty( - 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" - ) - nor: BoolProperty( - name="Normal Oriented", - default=False, - description="Align Z axis with Faces normals" - ) - cent: BoolProperty( - 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" - ) - anim: BoolProperty( - 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") - - col = layout.column(align=True) - col.separator() - - col = layout.column(align=True) - col.prop(self, "volX", slider=True) - col.prop(self, "volY", slider=True) - col.prop(self, "volZ", slider=True) - - layout.label(text="Particles:") - col = layout.column(align=True) - col.prop(self, "baseSca", slider=True) - col.prop(self, "varSca", slider=True) - - col = layout.column(align=True) - col.prop(self, "rotX", slider=True) - col.prop(self, "rotY", slider=True) - col.prop(self, "rotZ", slider=True) - - col = layout.column(align=True) - col.prop(self, "rSeed", slider=False) - col.prop(self, "numP") - - row = layout.row(align=True) - row.prop(self, "nor") - row.prop(self, "cent") - - 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') - - def invoke(self, context, event): - self.refresh = True - return self.execute(context) - - def execute(self, context): - if not self.refresh: - return {'PASS_THROUGH'} - - scn = bpy.context.scene - obj = bpy.context.active_object - - use_random_seed(self) - - mat = Matrix(( - (1, 0, 0, 0), - (0, 1, 0, 0), - (0, 0, 1, 0), - (0, 0, 0, 1)) - ) - if obj.matrix_world != mat: - 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'} - - bpy.ops.object.mode_set() - bpy.ops.object.select_all(action='DESELECT') - obj.select_set(True) - msv = [] - - for i in range(len(obj.modifiers)): - msv.append(obj.modifiers[i].show_viewport) - obj.modifiers[i].show_viewport = False - - cur = scn.cursor.location - for i in range(self.numP): - - mes = choice(par).data - newobj = bpy.data.objects.new('nuevo', mes) - scn.objects.link(newobj) - origen = (rg(self.volX) + cur[0], rg(self.volY) + cur[1], rg(self.volZ) + cur[2]) - - cpom = obj.closest_point_on_mesh(origen) - - if self.cent: - bm = bmesh.new() - bm.from_mesh(obj.data) - if hasattr(bm.verts, "ensure_lookup_table"): - bm.verts.ensure_lookup_table() - bm.faces.ensure_lookup_table() - - newobj.location = bm.faces[cpom[3]].calc_center_median() - - bm.free() - else: - newobj.location = cpom[1] - - if self.nor: - newobj.rotation_mode = 'QUATERNION' - newobj.rotation_quaternion = cpom[1].to_track_quat('Z', 'Y') - newobj.rotation_mode = 'XYZ' - newobj.rotation_euler[0] += rg(self.rotX) - newobj.rotation_euler[1] += rg(self.rotY) - newobj.rotation_euler[2] += rg(self.rotZ) - else: - newobj.rotation_euler = (rg(self.rotX), rg(self.rotY), rg(self.rotZ)) - - newobj.scale = [self.baseSca + self.baseSca * rg(self.varSca)] * 3 - - if self.anim: - newobj.select_set(True) - bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', obdata=True) - bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) - - bme = bmesh.new() - bme.from_mesh(obj.data) - - tmp = bmesh.new() - tmp.from_mesh(newobj.data) - - for f in tmp.faces: - # z = len(bme.verts) - for v in f.verts: - bme.verts.new(list(v.co)) - bme.faces.new(bme.verts[-len(f.verts):]) - - bme.to_mesh(obj.data) - remover(True) - # 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 - newobj.select_set(True) - bpy.ops.object.join() - - if self.track: - cur = scn.cursor.location = cpom[1] - - for i in range(len(msv)): - obj.modifiers[i].show_viewport = msv[i] - - for o in par: - o.select_set(True) - - obj.select_set(True) - - if self.auto_refresh is False: - self.refresh = False - - return{'FINISHED'} - - -def register(): - bpy.utils.register_class(OBJECT_OT_agregate_mesh) - - -def unregister(): - bpy.utils.unregister_class(OBJECT_OT_agregate_mesh) - - -if __name__ == '__main__': - register() diff --git a/add_advanced_objects_menu/arrange_on_curve.py b/add_advanced_objects_menu/arrange_on_curve.py deleted file mode 100644 index 38fbb92af4901c67ee2adbd0303ecb254c4837cc..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/arrange_on_curve.py +++ /dev/null @@ -1,356 +0,0 @@ -# gpl author: Mano-Wii - -bl_info = { - "name": "Arrange on Curve", - "author": "Mano-Wii", - "version": (6, 3, 0), - "blender": (2, 77, 0), - "location": "3D View > Toolshelf > Create > Arrange on Curve", - "description": "Arrange objects along a curve", - "warning": "Select curve", - "wiki_url": "", - "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 ( - Operator, - Panel, - ) -from bpy.props import ( - EnumProperty, - FloatProperty, - IntProperty, - ) - -FLT_MIN = 0.004 - - -class PanelDupliCurve(Panel): - bl_idname = "VIEW3D_PT_arranjar_numa_curva" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" - bl_context = "objectmode" - bl_category = "Create" - bl_label = "Arrange on Curve" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return context.object and context.mode == 'OBJECT' and context.object.type == 'CURVE' - - def draw(self, context): - layout = self.layout - 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( - adv_obj, "arrange_c_obj_arranjar", - bpy.data, "objects" - ) - elif adv_obj.arrange_c_select_type == 'G': - layout.column(align=True).prop_search( - adv_obj, "arrange_c_obj_arranjar", - bpy.data, "collections" - ) - 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 along a Curve" - bl_description = "Arange chosen / selected objects along the Active Curve" - bl_options = {'REGISTER', 'UNDO'} - - use_distance: EnumProperty( - 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="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, - ) - scale: FloatProperty( - name="Scale", - description="Object Scale", - default=1.0, - min=FLT_MIN, - unit='LENGTH', - ) - Yaw: FloatProperty( - name="X", - description="Rotate around the X axis (Yaw)", - default=0.0, - unit='ROTATION' - ) - Pitch: FloatProperty( - default=0.0, - description="Rotate around the Y axis (Pitch)", - name="Y", - unit='ROTATION' - ) - Roll: FloatProperty( - 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' - ) - offset: FloatProperty( - default=0.0, - name="Offset", - unit='LENGTH' - ) - - @classmethod - def poll(cls, context): - return context.mode == 'OBJECT' - - def draw(self, context): - layout = self.layout - col = layout.column() - col.prop(self, "use_distance", text="") - col = layout.column(align=True) - if self.use_distance == "D": - col.prop(self, "distance") - elif self.use_distance == "Q": - col.prop(self, "object_qt") - else: - col.prop(self, "distance") - col.prop(self, "max_angle") - col.prop(self, "offset") - - col = layout.column(align=True) - col.prop(self, "scale") - col.prop(self, "Yaw") - col.prop(self, "Pitch") - col.prop(self, "Roll") - - def Glpoints(self, curve): - Gpoints = [] - for i, spline in enumerate(curve.data.splines): - segments = len(spline.bezier_points) - if segments >= 2: - r = spline.resolution_u + 1 - - points = [] - for j in range(segments): - bp1 = spline.bezier_points[j] - inext = (j + 1) - if inext == segments: - if not spline.use_cyclic_u: - break - inext = 0 - bp2 = spline.bezier_points[inext] - if bp1.handle_right_type == bp2.handle_left_type == 'VECTOR': - _points = (bp1.co, bp2.co) if j == 0 else (bp2.co,) - else: - knot1 = bp1.co - handle1 = bp1.handle_right - handle2 = bp2.handle_left - knot2 = bp2.co - _points = mathutils.geometry.interpolate_bezier(knot1, handle1, handle2, knot2, r) - points.extend(_points) - Gpoints.append(tuple((curve.matrix_world * p for p in points))) - elif len(spline.points) >= 2: - l = [curve.matrix_world * p.co.xyz for p in spline.points] - if spline.use_cyclic_u: - l.append(l[0]) - Gpoints.append(tuple(l)) - - if self.use_distance == "R": - max_angle = self.max_angle - tmp_Gpoints = [] - sp = Gpoints[i] - sp2 = [sp[0], sp[1]] - lp = sp[1] - v1 = lp - sp[0] - for p in sp[2:]: - v2 = p - lp - try: - if (3.14158 - v1.angle(v2)) < max_angle: - tmp_Gpoints.append(tuple(sp2)) - sp2 = [lp] - except Exception as e: - print("\n[Add Advanced Objects]\nOperator: " - "object.arranjar_numa_curva\nError: {}".format(e)) - pass - sp2.append(p) - v1 = v2 - lp = p - tmp_Gpoints.append(tuple(sp2)) - Gpoints = Gpoints[:i] + tmp_Gpoints - - lengths = [] - if self.use_distance != "D": - for sp in Gpoints: - lp = sp[1] - leng = (lp - sp[0]).length - for p in sp[2:]: - leng += (p - lp).length - lp = p - lengths.append(leng) - return Gpoints, lengths - - def execute(self, context): - if context.object.type != 'CURVE': - return {'CANCELLED'} - - curve = context.active_object - Gpoints, lengs = self.Glpoints(curve) - adv_obj = context.scene.advanced_objects - - if adv_obj.arrange_c_use_selected: - G_Objeto = context.selected_objects - G_Objeto.remove(curve) - - if not G_Objeto: - return {'CANCELLED'} - - 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.collections[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 # max_angle is called in Glpoints - - if self.use_distance == "D": - dist = self.distance - for sp_points in Gpoints: - dx = 0.0 # Length of initial calculation of section - last_point = sp_points[0] - j = 0 - for point in sp_points[1:]: - vetorx = point - last_point # Vector spline section - quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z') # Tracking the selected objects - quat = quat.to_matrix().to_4x4() - - v_len = vetorx.length - if v_len > 0.0: - dx += v_len # Defined length calculation equal total length of the spline section - v_norm = vetorx / v_len - while dx > dist: - object = G_Objeto[j % len(G_Objeto)] - j += 1 - dx -= dist # Calculating the remaining length of the section - obj = object.copy() - context.collection.objects.link(obj) - obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix - # Placing in the correct position - obj.matrix_world.translation = point - v_norm * dx - obj.scale *= self.scale - last_point = point - - elif self.use_distance == "Q": - object_qt = self.object_qt + 1 - for i, sp_points in enumerate(Gpoints): - dx = 0.0 # Length of initial calculation of section - dist = lengs[i] / object_qt - last_point = sp_points[0] - j = 0 - for point in sp_points[1:]: - vetorx = point - last_point # Vector spline section - # Tracking the selected objects - quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z') - quat = quat.to_matrix().to_4x4() - - v_len = vetorx.length - if v_len > 0.0: - # Defined length calculation equal total length of the spline section - dx += v_len - v_norm = vetorx / v_len - while dx > dist: - object = G_Objeto[j % len(G_Objeto)] - j += 1 - dx -= dist # Calculating the remaining length of the section - obj = object.copy() - context.collection.objects.link(obj) - obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix - # Placing in the correct position - obj.matrix_world.translation = point - v_norm * dx - obj.scale *= self.scale - last_point = point - - else: - dist = self.distance - offset2 = 2 * self.offset - for i, sp_points in enumerate(Gpoints): - leng = lengs[i] - offset2 - rest = leng % dist - offset = offset2 + rest - leng -= rest - offset /= 2 - last_point = sp_points[0] - - dx = dist - offset # Length of initial calculation of section - j = 0 - for point in sp_points[1:]: - vetorx = point - last_point # Vector spline section - # Tracking the selected objects - quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z') - quat = quat.to_matrix().to_4x4() - - v_len = vetorx.length - if v_len > 0.0: - dx += v_len - v_norm = vetorx / v_len - while dx >= dist and leng >= 0.0: - leng -= dist - dx -= dist # Calculating the remaining length of the section - object = G_Objeto[j % len(G_Objeto)] - j += 1 - obj = object.copy() - context.collection.objects.link(obj) - obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix - # Placing in the correct position - obj.matrix_world.translation = point - v_norm * dx - obj.scale *= self.scale - last_point = point - - return {"FINISHED"} - - -def register(): - bpy.utils.register_class(PanelDupliCurve) - bpy.utils.register_class(DupliCurve) - - -def unregister(): - bpy.utils.unregister_class(PanelDupliCurve) - bpy.utils.unregister_class(DupliCurve) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/circle_array.py b/add_advanced_objects_menu/circle_array.py deleted file mode 100644 index 6d4a945939d8d650e1100544a497043065dbbb1c..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/circle_array.py +++ /dev/null @@ -1,166 +0,0 @@ -# gpl author: Antonis Karvelas - -# -*- coding: utf-8 -*- - -bl_info = { - "name": "Circle Array", - "author": "Antonis Karvelas", - "version": (1, 0, 1), - "blender": (2, 67, 0), - "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": "", - "category": "Mesh" - } - - -import bpy -from bpy.types import Operator -from math import radians - - -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 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): - return context.active_object is not None - - def check_empty_name(self, context): - new_name, def_name = "", "EMPTY_C_Array" - suffix = 1 - try: - # first slap a simple linear count + 1 for numeric suffix, if it fails - # harvest for the rightmost numbers and append the max value - list_obj = [] - obj_all = context.scene.objects - list_obj = [obj.name for obj in obj_all if obj.name.startswith(def_name)] - new_name = "{}_{}".format(def_name, len(list_obj) + 1) - - if new_name in list_obj: - from re import findall - test_num = [findall("\d+", words) for words in list_obj] - suffix += max([int(l[-1]) for l in test_num]) - new_name = "{}_{}".format(def_name, suffix) - return new_name - except: - return None - - def execute(self, context): - is_allowed = True - try: - allowed_obj = ['MESH', 'CURVE', 'SURFACE', 'FONT'] - for obj in context.selected_objects: - if obj.type not in allowed_obj: - is_allowed = False - break - - if not is_allowed: - self.report( - {"WARNING"}, - "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(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.view_layer.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_set(False) - bpy.context.view_layer.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 - empty_name.name = default_name - active.modifiers[0].offset_object = empty_name - else: - empty_name = active.modifiers[0].offset_object - - bpy.context.view_layer.objects.active = active - num = active.modifiers["Array"].count - rotate_num = 360 / num - active.select_set(True) - bpy.ops.object.transform_apply(location=False, rotation=True, scale=True) - empty_name.rotation_euler = (0, 0, radians(rotate_num)) - empty_name.select_set(False) - active.select_set(True) - bpy.ops.object.origin_set(type="ORIGIN_CURSOR") - - return {'FINISHED'} - else: - active = context.active_object - 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 - empty_name.name = default_name - active.modifiers[0].offset_object = empty_name - else: - empty_name = active.modifiers[0].offset_object - - bpy.context.view_layer.objects.active = active - num = active.modifiers["Array"].count - rotate_num = 360 / num - active.select_set(True) - bpy.ops.object.transform_apply(location=False, rotation=True, scale=True) - empty_name.rotation_euler = (0, 0, radians(rotate_num)) - empty_name.select_set(False) - active.select_set(True) - - return {'FINISHED'} - - except Exception as e: - self.report({'WARNING'}, - "Circle Array operator could not be executed (See the console for more info)") - print("\n[objects.circle_array_operator]\nError: {}\n".format(e)) - - return {'CANCELLED'} - - -# Register -def register(): - bpy.utils.register_class(Circle_Array) - - -def unregister(): - bpy.utils.unregister_class(Circle_Array) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/copy2.py b/add_advanced_objects_menu/copy2.py deleted file mode 100644 index 5b1bceb1453f42887271f084e1b7ad9768ed2fa1..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/copy2.py +++ /dev/null @@ -1,339 +0,0 @@ -# ##### 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 -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 -# 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, see http://www.gnu.org/licenses/ -# or write to the Free Software Foundation, Inc., 51 Franklin Street, -# Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -bl_info = { - "name": "Copy2 Vertices, Edges or Faces", - "author": "Eleanor Howick (elfnor.com)", - "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", - "warning": "", - "category": "Object" -} - -import bpy -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 - - def obj_list_cb(self, context): - return Copy2.obj_list - - def sec_axes_list_cb(self, context): - if self.priaxes == 'X': - sec_list = [('Y', "Y", "Secondary axis Y"), - ('Z', "Z", "Secondary axis Z")] - - if self.priaxes == 'Y': - sec_list = [('X', "X", "Secondary axis X"), - ('Z', "Z", "Secondary axis Z")] - - if self.priaxes == 'Z': - sec_list = [('X', "X", "Secondary axis X"), - ('Y', "Y", "Secondary axis Y")] - return sec_list - - 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.label(text="to:") - layout.prop(self, "copytype", expand=True) - layout.label(text="Primary axis:") - layout.prop(self, "priaxes", expand=True) - layout.label(text="Secondary axis:") - layout.prop(self, "secaxes", expand=True) - if self.copytype == "E": - layout.prop(self, "edgescale") - if self.edgescale: - layout.prop(self, "scale") - return - - def execute(self, context): - copytoobject = context.active_object.name - axes = self.priaxes + self.secaxes - - # 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.collection, - 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"} - - -def copy_to_from(collection, to_obj, from_obj, copymode, axes, edgescale, scale): - if copymode == 'V': - vertex_copy(collection, to_obj, from_obj, axes) - - if copymode == 'E': - # don't pass edgescalling to object types that cannot be scaled - if from_obj.type in ["CAMERA", "LIGHT", "EMPTY", "ARMATURE", "SPEAKER", "META"]: - edgescale = False - edge_copy(collection, to_obj, from_obj, axes, edgescale, scale) - - if copymode == 'F': - face_copy(collection, to_obj, from_obj, axes) - - -axes_dict = {'XY': (1, 2, 0), - 'XZ': (2, 1, 0), - 'YX': (0, 2, 1), - 'YZ': (2, 0, 1), - 'ZX': (0, 1, 2), - 'ZY': (1, 0, 2)} - - -def copyto(collection, 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_obj = source_obj.copy() - collection.objects.link(copy_obj) - - xdir = xdir.normalized() - zdir = zdir.normalized() - # rotation first - z_axis = zdir - x_axis = xdir - y_axis = z_axis.cross(x_axis) - # use axes_dict to assign the axis as chosen in panel - A, B, C = axes_dict[axes] - rot_mat = Matrix() - rot_mat[A].xyz = x_axis - rot_mat[B].xyz = y_axis - rot_mat[C].xyz = z_axis - rot_mat.transpose() - - # rotate object - copy_obj.matrix_world = rot_mat - - # move object into position - copy_obj.location = pos - - # scale object - if scale is not None: - copy_obj.scale = scale - - return copy_obj - - -def vertex_copy(collection, obj, source_obj, axes): - # vertex select mode - sel_verts = [] - copy_list = [] - - for v in obj.data.vertices: - if v.select is True: - sel_verts.append(v) - - # make a set for each vertex. The set contains all the connected vertices - # use sets so the list is unique - vert_con = [set() for i in range(len(obj.data.vertices))] - for e in obj.data.edges: - vert_con[e.vertices[0]].add(e.vertices[1]) - vert_con[e.vertices[1]].add(e.vertices[0]) - - for v in sel_verts: - pos = v.co * obj.matrix_world.transposed() - xco = obj.data.vertices[list(vert_con[v.index])[0]].co * obj.matrix_world.transposed() - - zdir = (v.co + v.normal) * obj.matrix_world.transposed() - pos - zdir = zdir.normalized() - - edir = pos - xco - - # edir is nor perpendicular to z dir - # want xdir to be projection of edir onto plane through pos with direction zdir - xdir = edir - edir.dot(zdir) * zdir - xdir = -xdir.normalized() - - copy = copyto(collection, source_obj, pos, xdir, zdir, axes) - copy_list.append(copy) - - # select all copied objects - for copy in copy_list: - copy.select_set(True) - obj.select_set(False) - - -def edge_copy(collection, obj, source_obj, axes, es, scale): - # edge select mode - sel_edges = [] - copy_list = [] - - for e in obj.data.edges: - 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() - v1 = obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed() - pos = (v0 + v1) / 2 - # xdir is along edge - xdir = v0 - v1 - 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 - vn0p = vn0 - vn0.dot(xdir) * xdir - vn1p = vn1 - vn1.dot(xdir) * xdir - # the mean of the two projected normals is the zdir - zdir = vn0p + vn1p - zdir = zdir.normalized() - escale = None - if es: - escale = Vector([1.0, 1.0, 1.0]) - i = list('XYZ').index(axes[1]) - escale[i] = scale * xlen / source_obj.dimensions[i] - - copy = copyto(collection, source_obj, pos, xdir, zdir, axes, scale=escale) - copy_list.append(copy) - - # select all copied objects - for copy in copy_list: - copy.select_set(True) - obj.select_set(False) - - -def face_copy(collection, obj, source_obj, axes): - # face select mode - sel_faces = [] - copy_list = [] - - for f in obj.data.polygons: - 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 - vco = obj.data.vertices[f.vertices[0]].co * obj.matrix_world.transposed() - # get face normal of transformed object - fn = (f.center + f.normal) * obj.matrix_world.transposed() - fco - fn = fn.normalized() - - copy = copyto(collection, source_obj, fco, vco - fco, fn, axes) - copy_list.append(copy) - - # select all copied objects - for copy in copy_list: - copy.select_set(True) - obj.select_set(False) - - -def register(): - bpy.utils.register_class(Copy2) - - -def unregister(): - bpy.utils.unregister_class(Copy2) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/cubester.py b/add_advanced_objects_menu/cubester.py deleted file mode 100644 index 8925171aa071c49a411b7cab34ca78f68a3c722b..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/cubester.py +++ /dev/null @@ -1,957 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# 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, 2), - "blender": (2, 78, 0), - "location": "View 3D > Toolbar > CubeSter", - "description": "Takes image, image sequence, or audio file and converts it " - "into a height map based on pixel color and alpha values", - "category": "Add Mesh" -} - -import bpy -import bmesh -from bpy.types import ( - Operator, - Panel, -) - -import timeit -from random import uniform -from math import radians -from os import ( - path, - listdir, -) - - -# 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.advanced_objects.cubester_block_style == "size": - z = 0.0 - else: - z = h - h = 2 * hw - - p = len(verts) - verts += [(x - hw, y - hw, z), (x + hw, y - hw, z), (x + hw, y + hw, z), (x - hw, y + hw, z)] - verts += [(x - hw, y - hw, z + h), (x + hw, y - hw, z + h), - (x + hw, y + hw, z + h), (x - hw, y + hw, z + h)] - - faces += [(p, p + 1, p + 5, p + 4), (p + 1, p + 2, p + 6, p + 5), - (p + 2, p + 3, p + 7, p + 6), (p, p + 4, p + 7, p + 3), - (p + 4, p + 5, p + 6, p + 7), (p, p + 3, p + 2, p + 1)] - - -# go through all frames in len(frames), adjusting values at frames[x][y] -def create_f_curves(mesh, frames, frame_step_size, style): - # use data to animate mesh - action = bpy.data.actions.new("CubeSterAnimation") - - mesh.animation_data_create() - mesh.animation_data.action = action - - data_path = "vertices[%d].co" - - vert_index = 4 if style == "blocks" else 0 # index of first vertex - - # loop for every face height value - for frame_start_vert in range(len(frames[0])): - # only go once if plane, otherwise do all four vertices that are in top plane if blocks - end_point = frame_start_vert + 4 if style == "blocks" else frame_start_vert + 1 - - # loop through to get the four vertices that compose the face - for frame_vert in range(frame_start_vert, end_point): - # fcurves for x, y, z - fcurves = [action.fcurves.new(data_path % vert_index, i) for i in range(3)] - frame_counter = 0 # go through each frame and add position - temp_v = mesh.vertices[vert_index].co - - # loop through frames - for frame in frames: - # new x, y, z positions - vals = [temp_v[0], temp_v[1], frame[frame_start_vert]] - for i in range(3): # for each x, y, z set each corresponding fcurve - fcurves[i].keyframe_points.insert(frame_counter, vals[i], {'FAST'}) - - frame_counter += frame_step_size # skip frames for smoother animation - - vert_index += 1 - - # only skip vertices if made of blocks - if style == "blocks": - vert_index += 4 - - -# 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 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: - try: - image = bpy.data.images[adv_obj.cubester_image] - except: - pass - - if scene.render.engine == "CYCLES": - mat.use_nodes = True - nodes = mat.node_tree.nodes - - att = nodes.new("ShaderNodeAttribute") - att.attribute_name = "Col" - att.location = (-200, 300) - - att = nodes.new("ShaderNodeTexImage") - if image: - att.image = image - - if adv_obj.cubester_load_type == "multiple": - att.image.source = "SEQUENCE" - att.location = (-200, 700) - - att = nodes.new("ShaderNodeTexCoord") - att.location = (-450, 600) - - if adv_obj.cubester_materials == "image": - mat.node_tree.links.new( - nodes["Image Texture"].outputs[0], - nodes["Diffuse BSDF"].inputs[0] - ) - mat.node_tree.links.new( - nodes["Texture Coordinate"].outputs[2], - nodes["Image Texture"].inputs[0] - ) - else: - mat.node_tree.links.new( - nodes["Attribute"].outputs[0], - nodes["Diffuse BSDF"].inputs[0] - ) - else: - if adv_obj.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER": - tex = bpy.data.textures.new("CubeSter_" + name, "IMAGE") - if image: - tex.image = image - slot = mat.texture_slots.add() - slot.texture = tex - else: - mat.use_vertex_color_paint = True - - ob.data.materials.append(mat) - - -# generate mesh from audio -def create_mesh_from_audio(self, context, verts, faces): - scene = context.scene - view_layer = context.view_layer - 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 - # Note: used for compatibility with vertex colors changes - bl_version = bool(bpy.app.version >= (2, 79, 1)) - - # create all blocks - y = -(width / 2) * size + (size / 2) - for r in range(width): - x = -(length / 2) * size + (size / 2) - for c in range(length): - create_block(x, y, size / 2, 1, verts, faces) - - x += size - y += size - - # create object - mesh = bpy.data.meshes.new("cubed") - mesh.from_pydata(verts, [], faces) - ob = bpy.data.objects.new("cubed", mesh) - bpy.context.collection.objects.link(ob) - bpy.context.view_layer.objects.active = ob - ob.select_set(True) - - # initial vertex colors - 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 = [] - - skip_y = int(picture.size[1] / width) - skip_x = int(picture.size[0] / length) - - for row in range(0, picture.size[1], skip_y + 1): - # go through each column, step by appropriate amount - for column in range(0, picture.size[0] * 4, 4 + skip_x * 4): - r, g, b, a = get_pixel_values(picture, pixels, row, column) - get_colors = (r, g, b, a) if bl_version else (r, g, b) - vert_colors += [get_colors 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: - if i < vert_colors_size: - c.color = vert_colors[i] - i += 1 - - # image sequence handling - if adv_obj.cubester_load_type == "multiple": - images = find_sequence_images(self, bpy.context) - - frames_vert_colors = [] - - 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, adv_obj.cubester_skip_images): - filepath = images[0][image_index] - name = images[1][image_index] - picture = fetch_image(self, name, filepath) - pixels = list(picture.pixels) - - frame_colors = [] - - for row in range(0, picture.size[1], skip_y + 1): - for column in range(0, picture.size[0] * 4, 4 + skip_x * 4): - r, g, b, a = get_pixel_values(picture, pixels, row, column) - get_colors = (r, g, b, a) if bl_version else (r, g, b) - frame_colors += [get_colors for i in range(24)] - - frames_vert_colors.append(frame_colors) - - 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: - ob.data.materials.append(bpy.data.materials["CubeSter_" + "Vertex"]) - else: - create_material(scene, ob, "Vertex") - - # set keyframe for each object as initial point - frame = [1 for i in range(int(len(verts) / 8))] - frames = [frame] - - area = bpy.context.area - old_type = area.type - area.type = "GRAPH_EDITOR" - - scene.frame_current = 0 - - create_f_curves(mesh, frames, 1, "blocks") - - # deselect all fcurves - fcurves = ob.data.animation_data.action.fcurves.data.fcurves - for i in fcurves: - i.select = False - - 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 = adv_obj.cubester_audio_frame_offset - - # animate each block with a portion of the frequency - for c in range(length): - frame_off = 0 - for r in range(width): - if freq_frame == "frame": - scene.frame_current = frame_off - l = c * freq_step - h = (c + 1) * freq_step - frame_off += frame_step - else: - l = c * freq_step + (r * freq_sub_step) - h = c * freq_step + ((r + 1) * freq_sub_step) - - pos = c + (r * length) # block number - index = pos * 4 # first index for vertex - - # select curves - for i in range(index, index + 4): - curve = i * 3 + 2 # fcurve location - fcurves[curve].select = True - 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): - curve = i * 3 + 2 # fcurve location - fcurves[curve].select = False - - area.type = old_type - - # UV unwrap - create_uv_map(bpy.context, width, length) - - # if radial apply needed modifiers - 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 - # slope determined off of collected data - curve_size = (0.319 * (width * (size * 100)) - 0.0169) / 100 - curve.dimensions = (curve_size, curve_size, 0.0) - # correct for z height - curve.scale = (curve.scale[0], curve.scale[0], curve.scale[0]) - - ob.select_set(True) - curve.select_set(False) - view_layer.objects.active = ob - - # data was collected and then multi-variable regression was done in Excel - # influence of width and length - width_infl, length_infl, intercept = -0.159125, 0.49996, 0.007637 - x_offset = ((width * (size * 100) * width_infl) + - (length * (size * 100) * length_infl) + intercept) / 100 - ob.location = (ob.location[0] + x_offset, ob.location[1], ob.location[2]) - - ob.rotation_euler = (radians(-90), 0.0, 0.0) - bpy.ops.object.modifier_add(type="CURVE") - ob.modifiers["Curve"].object = curve - ob.modifiers["Curve"].deform_axis = "POS_Z" - - -# generate mesh from image(s) -def create_mesh_from_image(self, scene, verts, faces): - context = bpy.context - adv_obj = scene.advanced_objects - picture = bpy.data.images[adv_obj.cubester_image] - pixels = list(picture.pixels) - # Note: used for compatibility with vertex colors changes - bl_version = bool(bpy.app.version >= (2, 79, 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 * 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 - - y = -height / 2 + half_width - - vert_colors = [] - rows = 0 - - # 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 + adv_obj.cubester_skip_pixels * 4): - r, g, b, a = get_pixel_values(picture, pixels, row, column) - get_colors = (r, g, b, a) if bl_version else (r, g, b) - h = find_point_height(r, g, b, a, scene) - - # if not transparent - if h != -1: - if adv_obj.cubester_mesh_style == "blocks": - create_block(x, y, half_width, h, verts, faces) - vert_colors += [get_colors for i in range(24)] - else: - verts += [(x, y, h)] - vert_colors += [get_colors for i in range(4)] - - x += step - y += step - - # if plane not blocks, then remove last 4 items from vertex_colors - # as the faces have already wrapped around - 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 adv_obj.cubester_mesh_style == "plane": - off = int(len(verts) / rows) - for r in range(rows - 1): - for c in range(off - 1): - faces += [(r * off + c, r * off + c + 1, (r + 1) * off + c + 1, (r + 1) * off + c)] - - mesh = bpy.data.meshes.new("cubed") - mesh.from_pydata(verts, [], faces) - ob = bpy.data.objects.new("cubed", mesh) - context.collection.objects.link(ob) - context.view_layer.objects.active = ob - ob.select_set(True) - - # uv unwrap - 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 adv_obj.cubester_materials == "vertex": # vertex color - image_name = "Vertex" - 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 = adv_obj.cubester_image - - # either add material or create - if ("CubeSter_" + image_name) in bpy.data.materials: - ob.data.materials.append(bpy.data.materials["CubeSter_" + image_name]) - - # create material - else: - create_material(scene, ob, image_name) - - # vertex colors - bpy.ops.mesh.vertex_color_add() - i = 0 - for c in ob.data.vertex_colors[0].data: - c.color = vert_colors[i] - i += 1 - - frames = [] - # image sequence handling - if adv_obj.cubester_load_type == "multiple": - images = find_sequence_images(self, context) - frames_vert_colors = [] - - 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, adv_obj.cubester_skip_images): - filepath = images[0][image_index] - name = images[1][image_index] - picture = fetch_image(self, name, filepath) - pixels = list(picture.pixels) - - frame_heights = [] - frame_colors = [] - - 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) - get_colors = (r, g, b, a) if bl_version else (r, g, b) - h = find_point_height(r, g, b, a, scene) - - if h != -1: - frame_heights.append(h) - if adv_obj.cubester_mesh_style == "blocks": - frame_colors += [get_colors for i in range(24)] - else: - frame_colors += [get_colors for i in range(4)] - - 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 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: - adv_obj.cubester_vertex_colors[ob.name] = { - "type": "image", "frame_skip": adv_obj.cubester_frame_step, - "total_images": max_images - } - att = get_image_node(ob.data.materials[0]) - att.image_user.frame_duration = len(frames) * adv_obj.cubester_frame_step - - # animate mesh - 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() - bm.from_mesh(mesh) - - uv_layer = bm.loops.layers.uv[0] - bm.faces.ensure_lookup_table() - - x_scale = 1 / columns - y_scale = 1 / rows - - y_pos = 0.0 - x_pos = 0.0 - count = columns - 1 # hold current count to compare to if need to go to next row - - # if 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 - bm.faces[pos].loops[0][uv_layer].uv = (x_pos, y_pos) - bm.faces[pos].loops[1][uv_layer].uv = (x_pos + x_scale, y_pos) - bm.faces[pos].loops[2][uv_layer].uv = (x_pos + x_scale, y_pos + y_scale) - bm.faces[pos].loops[3][uv_layer].uv = (x_pos, y_pos + y_scale) - - x_pos += x_scale - - if fa >= count: - y_pos += y_scale - x_pos = 0.0 - count += columns - - # if planes - else: - for fa in range(len(bm.faces)): - bm.faces[fa].loops[0][uv_layer].uv = (x_pos, y_pos) - bm.faces[fa].loops[1][uv_layer].uv = (x_pos + x_scale, y_pos) - bm.faces[fa].loops[2][uv_layer].uv = (x_pos + x_scale, y_pos + y_scale) - bm.faces[fa].loops[3][uv_layer].uv = (x_pos, y_pos + y_scale) - - x_pos += x_scale - - if fa >= count: - y_pos += y_scale - x_pos = 0.0 - count += columns - - bm.to_mesh(mesh) - - -# if already loaded return image, else load and return -def fetch_image(self, name, load_path): - if name in bpy.data.images: - return bpy.data.images[name] - else: - try: - image = bpy.data.images.load(load_path) - return image - except RuntimeError: - self.report({"ERROR"}, "CubeSter: '{}' could not be loaded".format(load_path)) - return None - - -# 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 adv_obj.cubester_advanced: - composed = 0.25 * r + 0.25 * g + 0.25 * b + 0.25 * a - else: - # user defined weighting - 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 - else: - weights = [uniform(0.0, 1.0) for i in range(4)] - composed = weights[0] * r + weights[1] * g + weights[2] * b + weights[3] * a - total = weights[0] + weights[1] + weights[2] + weights[3] - normalize = 1 / total - - if adv_obj.cubester_invert: - h = (1 - composed) * adv_obj.cubester_height_scale * normalize - else: - h = composed * adv_obj.cubester_height_scale * normalize - - return h - else: - return -1 - - -# find all images that would belong to sequence -def find_sequence_images(self, context): - scene = context.scene - images = [[], []] - - 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 - length = len(main) - keep_going = True - for i in range(length - 1, -1, -1): - if main[i].isdigit() and keep_going: - length -= 1 - else: - keep_going = not keep_going - name = main[0:length] - - dir_name = path.dirname(bpy.path.abspath(image.filepath)) - - try: - for file in listdir(dir_name): - if path.isfile(path.join(dir_name, file)) and file.startswith(name): - images[0].append(path.join(dir_name, file)) - images[1].append(file) - except FileNotFoundError: - self.report({"ERROR"}, "CubeSter: '{}' directory not found".format(dir_name)) - - return images - - -# get image node -def get_image_node(mat): - nodes = mat.node_tree.nodes - att = nodes["Image Texture"] - - return att - - -# get the RGBA values from pixel -def get_pixel_values(picture, pixels, row, column): - # determine i position to start at based on row and column position - i = (row * picture.size[0] * 4) + column - pixs = pixels[i: i + 4] - r = pixs[0] - g = pixs[1] - b = pixs[2] - a = pixs[3] - - return r, g, b, a - - -# frame change handler for materials -def material_frame_handler(scene): - frame = scene.frame_current - adv_obj = scene.advanced_objects - - 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 = adv_obj.advanced_objects.cubester_vertex_colors[ob.name] - skip_frames = data["frame_skip"] - - # update materials using vertex colors - if data['type'] == "vertex": - colors = data["frames"] - - if frame % skip_frames == 0 and 0 <= frame < (data['total_images'] - 1) * skip_frames: - use_frame = int(frame / skip_frames) - color = colors[use_frame] - - i = 0 - for c in ob.data.vertex_colors[0].data: - c.color = color[i] - i += 1 - - else: - att = get_image_node(ob.data.materials[0]) - offset = frame - int(frame / skip_frames) - att.image_user.frame_offset = -offset - - # if the object is no longer in the scene then delete then entry - else: - del adv_obj.advanced_objects.cubester_vertex_colors[i] - - -class CubeSterPanel(Panel): - bl_idname = "OBJECT_PT_cubester" - bl_label = "CubeSter" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" - bl_category = "Create" - bl_options = {"DEFAULT_CLOSED"} - bl_context = "objectmode" - - 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(adv_obj, "cubester_audio_image") - - if adv_obj.cubester_audio_image == "image": - box = layout.box() - box.prop(adv_obj, "cubester_load_type") - box.label(text="Image To Convert:") - box.prop_search(adv_obj, "cubester_image", bpy.data, "images") - box.prop(adv_obj, "cubester_load_image") - - # find number of appropriate images if sequence - 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]) <= 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(adv_obj, "cubester_max_images") - box.prop(adv_obj, "cubester_skip_images") - box.prop(adv_obj, "cubester_frame_step") - - box = layout.box() - 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") - - box = layout.box() - box.prop(adv_obj, "cubester_mesh_style", icon="MESH_GRID") - - if adv_obj.cubester_mesh_style == "blocks": - box.prop(adv_obj, "cubester_block_style") - else: - # 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.separator() - box.prop(adv_obj, "cubester_audio_offset_type") - - 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() - - col = box.column(align=True) - col.prop(adv_obj, "cubester_audio_width_blocks") - col.prop(adv_obj, "cubester_audio_length_blocks") - - rows = adv_obj.cubester_audio_width_blocks - columns = adv_obj.cubester_audio_length_blocks - - col.prop(adv_obj, "cubester_size_per_hundred_pixels") - - # materials - box = layout.box() - box.prop(adv_obj, "cubester_materials", icon="MATERIAL") - - if adv_obj.cubester_materials == "image": - box.prop(adv_obj, "cubester_load_type") - - # find number of appropriate images if sequence - 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]) <= 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(adv_obj, "cubester_max_images") - box.prop(adv_obj, "cubester_skip_images") - box.prop(adv_obj, "cubester_frame_step") - - box.separator() - - if adv_obj.cubester_audio_image == "image": - box.prop(adv_obj, "cubester_use_image_color", icon="COLOR") - - if not adv_obj.cubester_use_image_color or adv_obj.cubester_audio_image == "audio": - box.label(text="Image To Use For Colors:") - box.prop_search(adv_obj, "cubester_color_image", bpy.data, "images") - box.prop(adv_obj, "cubester_load_color_image") - - 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)) - - box = layout.box() - - if adv_obj.cubester_mesh_style == "blocks": - box.label(text="Approximate Cube Count: " + str(rows * columns)) - box.label(text="Expected Verts/Faces: " + str(rows * columns * 8) + " / " + str(rows * columns * 6)) - else: - box.label(text="Approximate Point Count: " + str(rows * columns)) - box.label(text="Expected Verts/Faces: " + str(rows * columns) + " / " + str(rows * (columns - 1))) - - # blocks and plane generation time values - 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 - block_infl, frame_infl, intercept2 = 0.000619, 0.344636, -0.272759 - - # if creating image based mesh - points = rows * columns - 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 / adv_obj.cubester_skip_images * frame_infl) + intercept2 - - box.label(text="Images To Be Used: " + str(int(images_found / adv_obj.cubester_skip_images))) - else: - # audio based mesh - box.label(text="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) + (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 - time /= 60 - time_mod = "min" - time = round(time, 3) - - box.label(text="Expected Time: " + str(time) + " " + time_mod) - - # advanced - 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(adv_obj, "cubester_advanced", icon=icon_1) - - if adv_obj.cubester_advanced: - box.prop(adv_obj, "cubester_random_weights", icon="RNDCURVE") - - if not adv_obj.cubester_random_weights: - box.label(text="RGBA Channel Weights", icon="COLOR") - 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.operator("mesh.cubester", icon="OBJECT_DATA") - - -class CubeSter(Operator): - bl_idname = "mesh.cubester" - bl_label = "Generate CubeSter Mesh" - bl_description = "Generate a mesh from an Image or Sound File" - bl_options = {"REGISTER", "UNDO"} - - def execute(self, context): - - verts, faces = [], [] - - start = timeit.default_timer() - scene = bpy.context.scene - adv_obj = scene.advanced_objects - - 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: - 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, context, 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 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)), - str(created), - str(round(stop - start, 4))) - ) - else: - self.report( - {"INFO"}, - "CubeSter: {} points and {} frame(s) " - "in {}s" .format(str(len(verts)), - str(created), - str(round(stop - start, 4))) - ) - - return {"FINISHED"} - - -def register(): - bpy.utils.register_module(__name__) - bpy.app.handlers.frame_change_pre.append(material_frame_handler) - - -def unregister(): - bpy.utils.unregister_module(__name__) - bpy.app.handlers.frame_change_pre.remove(material_frame_handler) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/make_struts.py b/add_advanced_objects_menu/make_struts.py deleted file mode 100644 index 547fb2f558cbd5d38359e3c913b893df43f49e81..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/make_struts.py +++ /dev/null @@ -1,592 +0,0 @@ -# Copyright (C) 2012 Bill Currie <bill@taniwha.org> -# Date: 2012/2/20 - -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# <pep8 compliant> - -import bpy -import bmesh -from bpy.types import Operator -from bpy.props import ( - FloatProperty, - IntProperty, - BoolProperty, - ) -from mathutils import ( - Vector, - Matrix, - Quaternion, - ) -from math import ( - pi, cos, - sin, - ) - -cossin = [] - -# Initialize the cossin table based on the number of segments. -# -# @param n The number of segments into which the circle will be -# divided. -# @return None - - -def build_cossin(n): - global cossin - cossin = [] - for i in range(n): - a = 2 * pi * i / n - cossin.append((cos(a), sin(a))) - - -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: - up = Vector((0, 0, 1)) - return up - -# Make a single strut in non-manifold mode. -# -# The strut will be a "cylinder" with @a n sides. The vertices of the -# cylinder will be @a od / 2 from the center of the cylinder. Optionally, -# extra loops will be placed (@a od - @a id) / 2 from either end. The -# strut will be either a simple, open-ended single-surface "cylinder", or a -# double walled "pipe" with the outer wall vertices @a od / 2 from the center -# and the inner wall vertices @a id / 2 from the center. The two walls will -# be joined together at the ends with a face ring such that the entire strut -# is a manifold object. All faces of the strut will be quads. -# -# @param v1 Vertex representing one end of the strut's center-line. -# @param v2 Vertex representing the other end of the strut's -# center-line. -# @param id The diameter of the inner wall of a solid strut. Used for -# calculating the position of the extra loops irrespective -# of the solidity of the strut. -# @param od The diameter of the outer wall of a solid strut, or the -# diameter of a non-solid strut. -# @param solid If true, the strut will be made solid such that it has an -# inner wall (diameter @a id), an outer wall (diameter -# @a od), and face rings at either end of the strut such -# the strut is a manifold object. If false, the strut is -# a simple, open-ended "cylinder". -# @param loops If true, edge loops will be placed at either end of the -# strut, (@a od - @a id) / 2 from the end of the strut. The -# loops make subsurfed solid struts work nicely. -# @return A tuple containing a list of vertices and a list of faces. -# The face vertex indices are accurate only for the list of -# vertices for the created strut. - - -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 - ind) / 2, od / 2), - (axis.length - (od - ind) / 2, od / 2)] - pos += [(axis.length, od / 2)] - if solid: - pos += [(axis.length, ind / 2)] - if loops: - 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: - fps -= 1 - fw = axis.copy() - fw.normalize() - up = select_up(axis) - lf = up.cross(fw) - lf.normalize() - up = fw.cross(lf) - mat = Matrix((fw, lf, up)) - mat.transpose() - verts = [None] * n * vps - faces = [None] * n * fps - for i in range(n): - base = (i - 1) * vps - x = cossin[i][0] - y = cossin[i][1] - for j in range(vps): - p = Vector((pos[j][0], pos[j][1] * x, pos[j][1] * y)) - p = mat * p - verts[i * vps + j] = p + v1 - if i: - for j in range(fps): - f = (i - 1) * fps + j - faces[f] = [base + j, base + vps + j, - base + vps + (j + 1) % vps, base + (j + 1) % vps] - base = len(verts) - vps - i = n - for j in range(fps): - f = (i - 1) * fps + j - faces[f] = [base + j, j, (j + 1) % vps, base + (j + 1) % vps] - - return verts, faces - - -# Project a point along a vector onto a plane. -# -# Really, just find the intersection of the line represented by @a point -# and @a dir with the plane represented by @a norm and @a p. However, if -# the point is on or in front of the plane, or the line is parallel to -# the plane, the original point will be returned. -# -# @param point The point to be projected onto the plane. -# @param dir The vector along which the point will be projected. -# @param norm The normal of the plane onto which the point will be -# projected. -# @param p A point through which the plane passes. -# @return A vector representing the projected point, or the -# original point. - -def project_point(point, dir, norm, p): - d = (point - p).dot(norm) - if d >= 0: - # the point is already on or in front of the plane - return point - v = dir.dot(norm) - if v * v < 1e-8: - # the plane is unreachable - return point - return point - dir * d / v - - -# Make a simple strut for debugging. -# -# The strut is just a single quad representing the Z axis of the edge. -# -# @param mesh The base mesh. Used for finding the edge vertices. -# @param edge_num The number of the current edge. For the face vertex -# indices. -# @param edge The edge for which the strut will be built. -# @param od Twice the width of the strut. -# @return A tuple containing a list of vertices and a list of faces. -# The face vertex indices are pre-adjusted by the edge -# number. -# @fixme The face vertex indices should be accurate for the local -# vertices (consistency) - -def make_debug_strut(mesh, edge_num, edge, od): - v = [mesh.verts[edge.verts[0].index].co, - mesh.verts[edge.verts[1].index].co, - None, None] - v[2] = v[1] + edge.z * od / 2 - v[3] = v[0] + edge.z * od / 2 - f = [[edge_num * 4 + 0, edge_num * 4 + 1, - edge_num * 4 + 2, edge_num * 4 + 3]] - return v, f - - -# Make a cylinder with ends clipped to the end-planes of the edge. -# -# The strut is just a single quad representing the Z axis of the edge. -# -# @param mesh The base mesh. Used for finding the edge vertices. -# @param edge_num The number of the current edge. For the face vertex -# indices. -# @param edge The edge for which the strut will be built. -# @param od The diameter of the strut. -# @return A tuple containing a list of vertices and a list of faces. -# The face vertex indices are pre-adjusted by the edge -# number. -# @fixme The face vertex indices should be accurate for the local -# vertices (consistency) - -def make_clipped_cylinder(mesh, edge_num, edge, od): - n = len(cossin) - cyl = [None] * n - v0 = mesh.verts[edge.verts[0].index].co - c0 = v0 + od * edge.y - v1 = mesh.verts[edge.verts[1].index].co - c1 = v1 - od * edge.y - for i in range(n): - x = cossin[i][0] - y = cossin[i][1] - r = (edge.z * x - edge.x * y) * od / 2 - cyl[i] = [c0 + r, c1 + r] - for p in edge.verts[0].planes: - cyl[i][0] = project_point(cyl[i][0], edge.y, p, v0) - for p in edge.verts[1].planes: - cyl[i][1] = project_point(cyl[i][1], -edge.y, p, v1) - v = [None] * n * 2 - f = [None] * n - base = edge_num * n * 2 - for i in range(n): - v[i * 2 + 0] = cyl[i][1] - v[i * 2 + 1] = cyl[i][0] - f[i] = [None] * 4 - f[i][0] = base + i * 2 + 0 - f[i][1] = base + i * 2 + 1 - f[i][2] = base + (i * 2 + 3) % (n * 2) - f[i][3] = base + (i * 2 + 2) % (n * 2) - return v, f - - -# Represent a vertex in the base mesh, with additional information. -# -# These vertices are @b not shared between edges. -# -# @var index The index of the vert in the base mesh -# @var edge The edge to which this vertex is attached. -# @var edges A tuple of indicess of edges attached to this vert, not -# including the edge to which this vertex is attached. -# @var planes List of vectors representing the normals of the planes that -# bisect the angle between this vert's edge and each other -# adjacant edge. - -class SVert: - # Create a vertex holding additional information about the bmesh vertex. - # @param bmvert The bmesh vertex for which additional information is - # to be stored. - # @param bmedge The edge to which this vertex is attached. - - def __init__(self, bmvert, bmedge, edge): - self.index = bmvert.index - self.edge = edge - edges = bmvert.link_edges[:] - edges.remove(bmedge) - self.edges = tuple(map(lambda e: e.index, edges)) - self.planes = [] - - def calc_planes(self, edges): - for ed in self.edges: - self.planes.append(calc_plane_normal(self.edge, edges[ed])) - - -# Represent an edge in the base mesh, with additional information. -# -# Edges do not share vertices so that the edge is always on the front (back? -# must verify) side of all the planes attached to its vertices. If the -# vertices were shared, the edge could be on either side of the planes, and -# there would be planes attached to the vertex that are irrelevant to the -# edge. -# -# @var index The index of the edge in the base mesh. -# @var bmedge Cached reference to this edge's bmedge -# @var verts A tuple of 2 SVert vertices, one for each end of the -# edge. The vertices are @b not shared between edges. -# However, if two edges are connected via a vertex in the -# bmesh, their corresponding SVert vertices will have the -# the same index value. -# @var x The x axis of the edges local frame of reference. -# Initially invalid. -# @var y The y axis of the edges local frame of reference. -# Initialized such that the edge runs from verts[0] to -# verts[1] along the negative y axis. -# @var z The z axis of the edges local frame of reference. -# Initially invalid. - - -class SEdge: - - def __init__(self, bmesh, bmedge): - - self.index = bmedge.index - self.bmedge = bmedge - bmesh.verts.ensure_lookup_table() - self.verts = (SVert(bmedge.verts[0], bmedge, self), - SVert(bmedge.verts[1], bmedge, self)) - self.y = (bmesh.verts[self.verts[0].index].co - - bmesh.verts[self.verts[1].index].co) - self.y.normalize() - self.x = self.z = None - - def set_frame(self, up): - self.x = self.y.cross(up) - self.x.normalize() - self.z = self.x.cross(self.y) - - def calc_frame(self, base_edge): - baxis = base_edge.y - if (self.verts[0].index == base_edge.verts[0].index or - self.verts[1].index == base_edge.verts[1].index): - axis = -self.y - elif (self.verts[0].index == base_edge.verts[1].index or - self.verts[1].index == base_edge.verts[0].index): - axis = self.y - else: - raise ValueError("edges not connected") - if baxis.dot(axis) in (-1, 1): - # aligned axis have their up/z aligned - up = base_edge.z - else: - # Get the unit vector dividing the angle (theta) between baxis and - # axis in two equal parts - h = (baxis + axis) - h.normalize() - # (cos(theta/2), sin(theta/2) * n) where n is the unit vector of the - # axis rotating baxis onto axis - q = Quaternion([baxis.dot(h)] + list(baxis.cross(h))) - # rotate the base edge's up around the rotation axis (blender - # quaternion shortcut:) - up = q * base_edge.z - self.set_frame(up) - - def calc_vert_planes(self, edges): - for v in self.verts: - v.calc_planes(edges) - - def bisect_faces(self): - n1 = self.bmedge.link_faces[0].normal - if len(self.bmedge.link_faces) > 1: - n2 = self.bmedge.link_faces[1].normal - return (n1 + n2).normalized() - return n1 - - def calc_simple_frame(self): - return self.y.cross(select_up(self.y)).normalized() - - def find_edge_frame(self, sedges): - if self.bmedge.link_faces: - return self.bisect_faces() - if self.verts[0].edges or self.verts[1].edges: - edges = list(self.verts[0].edges + self.verts[1].edges) - for i in range(len(edges)): - edges[i] = sedges[edges[i]] - while edges and edges[-1].y.cross(self.y).length < 1e-3: - edges.pop() - if not edges: - return self.calc_simple_frame() - n1 = edges[-1].y.cross(self.y).normalized() - edges.pop() - while edges and edges[-1].y.cross(self.y).cross(n1).length < 1e-3: - edges.pop() - if not edges: - return n1 - n2 = edges[-1].y.cross(self.y).normalized() - return (n1 + n2).normalized() - return self.calc_simple_frame() - - -def calc_plane_normal(edge1, edge2): - if edge1.verts[0].index == edge2.verts[0].index: - axis1 = -edge1.y - axis2 = edge2.y - elif edge1.verts[1].index == edge2.verts[1].index: - axis1 = edge1.y - axis2 = -edge2.y - elif edge1.verts[0].index == edge2.verts[1].index: - axis1 = -edge1.y - axis2 = -edge2.y - elif edge1.verts[1].index == edge2.verts[0].index: - axis1 = edge1.y - axis2 = edge2.y - else: - raise ValueError("edges not connected") - # Both axis1 and axis2 are unit vectors, so this will produce a vector - # bisects the two, so long as they are not 180 degrees apart (in which - # there are infinite solutions). - return (axis1 + axis2).normalized() - - -def build_edge_frames(edges): - edge_set = set(edges) - while edge_set: - edge_queue = [edge_set.pop()] - edge_queue[0].set_frame(edge_queue[0].find_edge_frame(edges)) - while edge_queue: - current_edge = edge_queue.pop() - for i in (0, 1): - for e in current_edge.verts[i].edges: - edge = edges[e] - if edge.x is not None: # edge already processed - continue - edge_set.remove(edge) - edge_queue.append(edge) - edge.calc_frame(current_edge) - - -def make_manifold_struts(truss_obj, od, segments): - bpy.context.view_layer.objects.active = truss_obj - bpy.ops.object.editmode_toggle() - truss_mesh = bmesh.from_edit_mesh(truss_obj.data).copy() - bpy.ops.object.editmode_toggle() - edges = [None] * len(truss_mesh.edges) - for i, e in enumerate(truss_mesh.edges): - edges[i] = SEdge(truss_mesh, e) - build_edge_frames(edges) - verts = [] - faces = [] - for e, edge in enumerate(edges): - # v, f = make_debug_strut(truss_mesh, e, edge, od) - edge.calc_vert_planes(edges) - v, f = make_clipped_cylinder(truss_mesh, e, edge, od) - verts += v - faces += f - return verts, faces - - -def make_simple_struts(truss_mesh, ind, od, segments, solid, loops): - vps = 2 - if solid: - vps *= 2 - if loops: - vps *= 2 - fps = vps - if not solid: - fps -= 1 - - verts = [None] * len(truss_mesh.edges) * segments * vps - 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, ind, od, segments, solid, loops) - for fv in f: - for i in range(len(fv)): - fv[i] += vbase - for i in range(len(v)): - verts[vbase + i] = v[i] - for i in range(len(f)): - faces[fbase + i] = f[i] - # if not base % 12800: - # print (base * 100 / len(verts)) - vbase += vps * segments - fbase += fps * segments - - return verts, faces - - -def create_struts(self, context, ind, od, segments, solid, loops, manifold): - build_cossin(segments) - - for truss_obj in bpy.context.scene.objects: - if not truss_obj.select_get(): - continue - truss_obj.select_set(False) - truss_mesh = truss_obj.to_mesh(context.scene, True, 'PREVIEW') - if not truss_mesh.edges: - continue - if manifold: - verts, faces = make_manifold_struts(truss_obj, od, segments) - else: - verts, faces = make_simple_struts(truss_mesh, ind, od, segments, - solid, loops) - mesh = bpy.data.meshes.new("Struts") - mesh.from_pydata(verts, [], faces) - obj = bpy.data.objects.new("Struts", mesh) - bpy.context.collection.objects.link(obj) - obj.select_set(True) - obj.location = truss_obj.location - bpy.context.view_layer.objects.active = obj - mesh.update() - - -class Struts(Operator): - bl_idname = "mesh.generate_struts" - bl_label = "Struts" - 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'} - - 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.preferences.edit.use_global_undo - bpy.context.preferences.edit.use_global_undo = False - keywords = self.as_keywords() - - try: - create_struts(self, context, **keywords) - bpy.context.preferences.edit.use_global_undo = store_undo - - return {"FINISHED"} - - except Exception as e: - bpy.context.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__) - - -def unregister(): - bpy.utils.unregister_module(__name__) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/mesh_easylattice.py b/add_advanced_objects_menu/mesh_easylattice.py deleted file mode 100644 index 6c8fa49f5f76958255f0b07a7662c708839dd766..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/mesh_easylattice.py +++ /dev/null @@ -1,361 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -bl_info = { - "name": "Easy Lattice Object", - "author": "Kursad Karatas", - "version": (0, 6, 0), - "blender": (2, 66, 0), - "location": "View3D > Easy Lattice", - "description": "Create a lattice for shape editing", - "warning": "", - "wiki_url": "https://wiki.blender.org/index.php/Easy_Lattice_Editing_Addon", - "tracker_url": "https://bitbucket.org/kursad/blender_addons_easylattice/src", - "category": "Mesh", -} - - -import bpy -from mathutils import ( - Matrix, - Vector, -) -from bpy.types import Operator -from bpy.props import ( - EnumProperty, - FloatProperty, - IntProperty, -) - - -def createLattice(context, obj, props): - # Create lattice and object - lat = bpy.data.lattices.new('EasyLattice') - ob = bpy.data.objects.new('EasyLattice', lat) - - # Take into consideration any selected vertices (default: all vertices) - selectedVertices = createVertexGroup(obj) - - size, pos = findBBox(obj, selectedVertices) - loc, rot = getTransformations(obj) - - # the position comes from the bbox - ob.location = pos - - # the size from bbox * the incoming scale factor - ob.scale = size * props[3] - - # the rotation comes from the combined obj world - # matrix which was converted to euler pairs - ob.rotation_euler = buildRot_World(obj) - ob.show_in_front = True - - # Link object to scene - scn = context.scene - - # Take care of the local view - base = scn.objects.link(ob) - scn.objects.active = ob - - v3d = None - if context.space_data and context.space_data.type == 'VIEW_3D': - v3d = context.space_data - - if v3d and v3d.local_view: - base.layers_from_view(v3d) - - scn.update() - - # Set lattice attributes - lat.points_u = props[0] - lat.points_v = props[1] - lat.points_w = props[2] - - lat.interpolation_type_u = props[4] - lat.interpolation_type_v = props[4] - lat.interpolation_type_w = props[4] - - lat.use_outside = False - - return ob - - -def createVertexGroup(obj): - vertices = obj.data.vertices - selverts = [] - - if obj.mode == "EDIT": - bpy.ops.object.editmode_toggle() - - group = obj.vertex_groups.new(name="easy_lattice_group") - - for vert in vertices: - if vert.select is True: - selverts.append(vert) - group.add([vert.index], 1.0, "REPLACE") - - # Default: use all vertices - if not selverts: - for vert in vertices: - selverts.append(vert) - group.add([vert.index], 1.0, "REPLACE") - - return selverts - - -def getTransformations(obj): - rot = obj.rotation_euler - loc = obj.location - - return [loc, rot] - - -def findBBox(obj, selvertsarray): - - mat = buildTrnScl_WorldMat(obj) - mat_world = obj.matrix_world - - minx, miny, minz = selvertsarray[0].co - maxx, maxy, maxz = selvertsarray[0].co - - c = 1 - - for c in range(len(selvertsarray)): - co = selvertsarray[c].co - - if co.x < minx: - minx = co.x - if co.y < miny: - miny = co.y - if co.z < minz: - minz = co.z - - if co.x > maxx: - maxx = co.x - if co.y > maxy: - maxy = co.y - if co.z > maxz: - maxz = co.z - c += 1 - - minpoint = Vector((minx, miny, minz)) - maxpoint = Vector((maxx, maxy, maxz)) - - # The middle position has to be calculated based on the real world matrix - pos = ((minpoint + maxpoint) / 2) - - minpoint = mat * minpoint # Calculate only based on loc/scale - maxpoint = mat * maxpoint # Calculate only based on loc/scale - pos = mat_world * pos # the middle position has to be calculated based on the real world matrix - - size = maxpoint - minpoint - size = Vector((max(0.1, abs(size.x)), max(0.1, abs(size.y)), max(0.1, abs(size.z)))) # Prevent zero size dimensions - - return [size, pos] - - -def buildTrnSclMat(obj): - # This function builds a local matrix that encodes translation - # and scale and it leaves out the rotation matrix - # The rotation is applied at object level if there is any - mat_trans = Matrix.Translation(obj.location) - mat_scale = Matrix.Scale(obj.scale[0], 4, (1, 0, 0)) - mat_scale *= Matrix.Scale(obj.scale[1], 4, (0, 1, 0)) - mat_scale *= Matrix.Scale(obj.scale[2], 4, (0, 0, 1)) - - mat_final = mat_trans * mat_scale - - return mat_final - - -def buildTrnScl_WorldMat(obj): - # This function builds a real world matrix that encodes translation - # and scale and it leaves out the rotation matrix - # The rotation is applied at object level if there is any - loc, rot, scl = obj.matrix_world.decompose() - mat_trans = Matrix.Translation(loc) - - mat_scale = Matrix.Scale(scl[0], 4, (1, 0, 0)) - mat_scale *= Matrix.Scale(scl[1], 4, (0, 1, 0)) - mat_scale *= Matrix.Scale(scl[2], 4, (0, 0, 1)) - - mat_final = mat_trans * mat_scale - - return mat_final - - -# Feature use -def buildRot_WorldMat(obj): - # This function builds a real world matrix that encodes rotation - # and it leaves out translation and scale matrices - loc, rot, scl = obj.matrix_world.decompose() - rot = rot.to_euler() - - mat_rot = Matrix.Rotation(rot[0], 4, 'X') - mat_rot *= Matrix.Rotation(rot[1], 4, 'Z') - mat_rot *= Matrix.Rotation(rot[2], 4, 'Y') - return mat_rot - - -def buildTrn_WorldMat(obj): - # This function builds a real world matrix that encodes translation - # and scale and it leaves out the rotation matrix - # The rotation is applied at object level if there is any - loc, rot, scl = obj.matrix_world.decompose() - mat_trans = Matrix.Translation(loc) - - return mat_trans - - -def buildScl_WorldMat(obj): - # This function builds a real world matrix that encodes translation - # and scale and it leaves out the rotation matrix - # The rotation is applied at object level if there is any - loc, rot, scl = obj.matrix_world.decompose() - - mat_scale = Matrix.Scale(scl[0], 4, (1, 0, 0)) - mat_scale *= Matrix.Scale(scl[1], 4, (0, 1, 0)) - mat_scale *= Matrix.Scale(scl[2], 4, (0, 0, 1)) - - return mat_scale - - -def buildRot_World(obj): - # This function builds a real world rotation values - loc, rot, scl = obj.matrix_world.decompose() - rot = rot.to_euler() - - return rot - - -def main(context, lat_props): - obj = context.object - - if obj.type == "MESH": - lat = createLattice(context, obj, lat_props) - - modif = obj.modifiers.new("EasyLattice", "LATTICE") - modif.object = lat - modif.vertex_group = "easy_lattice_group" - - bpy.ops.object.select_all(action='DESELECT') - bpy.ops.object.select_pattern(pattern=lat.name, extend=False) - context.view_layer.objects.active = lat - - context.scene.update() - - return - - -class EasyLattice(Operator): - bl_idname = "object.easy_lattice" - bl_label = "Easy Lattice Creator" - bl_description = ("Create a Lattice modifier ready to edit\n" - "Needs an existing Active Mesh Object\n") - - lat_u: IntProperty( - name="Lattice u", - description="Points in u direction", - default=3 - ) - lat_v: IntProperty( - name="Lattice v", - description="Points in v direction", - default=3 - ) - lat_w: IntProperty( - name="Lattice w", - description="Points in w direction", - default=3 - ) - lat_scale_factor: FloatProperty( - name="Lattice scale factor", - description="Adjustment to the lattice scale", - default=1, - min=0.1, - step=1, - precision=2 - ) - lat_types = (('KEY_LINEAR', "Linear", "Linear Interpolation type"), - ('KEY_CARDINAL', "Cardinal", "Cardinal Interpolation type"), - ('KEY_CATMULL_ROM', "Catmull-Rom", "Catmull-Rom Interpolation type"), - ('KEY_BSPLINE', "BSpline", "Key BSpline Interpolation Type") - ) - lat_type: EnumProperty( - name="Lattice Type", - description="Choose Lattice Type", - items=lat_types, - default='KEY_BSPLINE' - ) - - @classmethod - def poll(cls, context): - 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_v") - col.prop(self, "lat_w") - - layout.prop(self, "lat_scale_factor") - - layout.prop(self, "lat_type") - - def execute(self, context): - lat_u = self.lat_u - lat_v = self.lat_v - lat_w = self.lat_w - - lat_scale_factor = self.lat_scale_factor - - # enum property no need to complicate things - lat_type = self.lat_type - # XXX, should use keyword args - lat_props = [lat_u, lat_v, lat_w, lat_scale_factor, lat_type] - try: - main(context, lat_props) - - except Exception as ex: - print("\n[Add Advanced Objects]\nOperator:object.easy_lattice\n{}\n".format(ex)) - self.report( - {'WARNING'}, - "Easy Lattice Creator could not be completed (See Console for more info)" - ) - return {"CANCELLED"} - - return {"FINISHED"} - - def invoke(self, context, event): - wm = context.window_manager - return wm.invoke_props_dialog(self) - - -def register(): - bpy.utils.register_class(EasyLattice) - - -def unregister(): - bpy.utils.unregister_class(EasyLattice) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/object_add_chain.py b/add_advanced_objects_menu/object_add_chain.py deleted file mode 100644 index 86da80f8edc9e8facced48d241952f8065b3e070..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/object_add_chain.py +++ /dev/null @@ -1,179 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -bl_info = { - "name": "Add Chain", - "author": "Brian Hinton (Nichod)", - "version": (0, 1, 2), - "blender": (2, 71, 0), - "location": "Toolshelf > Create Tab", - "description": "Adds Chain with curve guide for easy creation", - "warning": "", - "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" - "Scripts/Object/Add_Chain", - "category": "Object", -} - -import bpy -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), - ) - - # Changes name of Empty to rot_link adds variable emp - emp = bpy.context.object - emp.name = "rot_link" - - # Rotate emp ~ 90 degrees - 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), - ) - - # 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, - ) - - # Positions Torus primitive to center of scene - bpy.context.active_object.location = 0.0, 0.0, 0.0 - - # Resetting Torus rotation in case of 'Align to view' option enabled - bpy.context.active_object.rotation_euler = 0.0, 0.0, 0.0 - - # Changes Torus name to chain adds variable tor - tor = bpy.context.object - tor.name = "chain" - - # Adds Array Modifier to tor - bpy.ops.object.modifier_add(type='ARRAY') - - # Adds subsurf modifier tor - bpy.ops.object.modifier_add(type='SUBSURF') - - # Smooths tor - bpy.ops.object.shade_smooth() - - # Select curv - sce = bpy.context.scene - sce.objects.active = curv - - # Toggle into editmode - bpy.ops.object.editmode_toggle() - - # TODO, may be better to move objects directly - # Translate curve object - bpy.ops.transform.translate( - value=(2, 0, 0), - constraint_axis=(True, False, False), - orient_type='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() - - # Select tor or chain - sce.objects.active = tor - - # Selects Array Modifier for editing - array = tor.modifiers['Array'] - - # Change Array Modifier Parameters - array.fit_type = 'FIT_CURVE' - array.curve = curv - array.offset_object = emp - array.use_object_offset = True - array.relative_offset_displace = 0.549, 0.0, 0.0 - - # Add curve modifier - bpy.ops.object.modifier_add(type='CURVE') - - # Selects Curve Modifier for editing - cur = tor.modifiers['Curve'] - - # Change Curve Modifier Parameters - cur.object = curv - - -class AddChain(Operator): - bl_idname = "mesh.primitive_chain_add" - bl_label = "Add Chain" - bl_description = ("Create a Chain segment with helper objects controlling modifiers:\n" - "1) A Curve Modifier Object (deform) for the length and shape,\n" - "Edit the Path to extend Chain Length\n" - "2) An Empty (rot_link) as an Array Offset for rotation") - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - 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_class(AddChain) - - -def unregister(): - bpy.utils.unregister_class(AddChain) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/oscurart_chain_maker.py b/add_advanced_objects_menu/oscurart_chain_maker.py deleted file mode 100644 index a553ae48b61eed325ae174de53834519ee3d326e..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/oscurart_chain_maker.py +++ /dev/null @@ -1,288 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# TODO: find English versions of created object names - -bl_info = { - "name": "Oscurart Chain Maker", - "author": "Oscurart", - "version": (1, 1), - "blender": (2, 56, 0), - "location": "Add > Mesh > Oscurart Chain", - "description": "Create chain links from armatures", - "warning": "", - "wiki_url": "oscurart.blogspot.com", - "category": "Object"} - - -import bpy -from bpy.props import ( - BoolProperty, - FloatProperty, - ) -from bpy.types import Operator - - -def makeChain(self, context, mult, curverig): - - if not context.active_object.type == 'ARMATURE': - self.report({'WARNING'}, "Active Object must be an Armature") - return False - - bpy.ops.object.mode_set(mode='OBJECT') - VAR_SWITCH = abs(1) - ARMATURE = bpy.context.active_object - - def creahuesocero(hueso): - # create data to link - mesh = bpy.data.meshes.new("objectData" + str(hueso.name)) - 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), - (-0.049861326813697815, -0.154555082321167, 0.17846627533435822), - (-0.049861326813697815, -0.15455523133277893, -0.17846614122390747), - (-0.04986133798956871, -0.03475356101989746, 0.25805795192718506), - (-0.04986133798956871, -0.03475397825241089, -0.25805795192718506), - (-0.049861278384923935, -0.8116106986999512, -0.2580576539039612), - (-0.049861278384923935, -0.8116104602813721, 0.25805822014808655), - (-0.04986128211021423, -0.7692053318023682, 2.6668965347198537e-07), - (-0.04986127093434334, -0.923523485660553, 2.7834033744511544e-07), - (-0.04986133426427841, -0.0771591067314148, 3.5627678585115063e-08), - (-0.04986134544014931, 0.0771591067314148, -3.5627678585115063e-08), - (0.04986133798956871, -0.03475397825241089, -0.25805795192718506), - (0.04986133053898811, 0.0771591067314148, -3.5627678585115063e-08), - (0.04986133798956871, -0.03475356101989746, 0.25805795192718506), - (0.04986134544014931, -0.15455523133277893, -0.17846614122390747), - (0.04986134544014931, -0.0771591067314148, 3.5627678585115063e-08), - (0.04986134544014931, -0.154555082321167, 0.17846627533435822), - (0.049861397594213486, -0.8116106986999512, -0.2580576539039612), - (0.04986140504479408, -0.923523485660553, 2.7834033744511544e-07), - (0.049861397594213486, -0.8116104602813721, 0.25805822014808655), - (0.04986139014363289, -0.6918091773986816, 0.17846640944480896), - (0.04986139014363289, -0.7692053318023682, 2.6668965347198537e-07), - (0.04986139014363289, -0.6918092370033264, -0.17846597731113434)], - [(1, 2), (0, 3), (3, 5), (2, 4), (0, 6), (5, 6), (1, 7), (4, 7), (0, 8), (1, 8), - (7, 9), (6, 9), (8, 9), (2, 10), (3, 10), (4, 11), (5, 11), (10, 11), (5, 12), - (12, 13), (11, 13), (13, 14), (4, 14), (10, 16), (15, 16), (3, 15), (2, 17), - (16, 17), (9, 19), (18, 19), (6, 18), (7, 20), (19, 20), (8, 22), (21, 22), - (1, 21), (0, 23), (22, 23), (14, 20), (12, 18), (15, 23), (17, 21), (12, 15), - (13, 16), (14, 17), (20, 21), (19, 22), (18, 23)], - [(6, 0, 3, 5), (1, 7, 4, 2), (0, 6, 9, 8), (8, 9, 7, 1), (2, 4, 11, 10), (10, 11, 5, 3), - (11, 13, 12, 5), (4, 14, 13, 11), (3, 15, 16, 10), (10, 16, 17, 2), (6, 18, 19, 9), - (9, 19, 20, 7), (1, 21, 22, 8), (23, 0, 8, 22), (7, 20, 14, 4), (5, 12, 18, 6), - (0, 23, 15, 3), (2, 17, 21, 1), (16, 15, 12, 13), (17, 16, 13, 14), (22, 21, 20, 19), - (23, 22, 19, 18), (21, 17, 14, 20), (15, 23, 18, 12)] - ) - mesh.validate() - bpy.context.collection.objects.link(object) - # scale to the bone - bpy.data.objects["HardLink" + str(hueso.name)].scale = (hueso.length * mult, - hueso.length * mult, - hueso.length * mult) - # Parent Objects - 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("NewLink" + str(hueso.name), mesh) - mesh.from_pydata( - [(0.1784660965204239, -0.6918091773986816, -0.049861203879117966), - (-0.1784662902355194, -0.6918091773986816, -0.04986126348376274), - (-0.17846627533435822, -0.1545550525188446, -0.04986134544014931), - (0.17846617102622986, -0.15455520153045654, -0.04986128583550453), - (-0.25805795192718506, -0.03475359082221985, -0.049861375242471695), - (0.25805795192718506, -0.034753888845443726, -0.04986129328608513), - (0.2580578327178955, -0.8116105794906616, -0.04986117407679558), - (-0.2580580413341522, -0.8116105198860168, -0.049861256033182144), - (-9.672299938756623e-08, -0.7692052721977234, -0.04986122250556946), - (-8.99775329799013e-08, -0.923523485660553, -0.04986120015382767), - (-7.764004550381287e-09, -0.07715904712677002, -0.049861326813697815), - (4.509517737005808e-08, 0.0771591067314148, -0.049861349165439606), - (0.25805795192718506, -0.034753888845443726, 0.049861375242471695), - (-2.2038317837314025e-08, 0.0771591067314148, 0.049861326813697815), - (-0.25805795192718506, -0.03475359082221985, 0.04986129328608513), - (0.17846617102622986, -0.15455520153045654, 0.04986138269305229), - (-1.529285498236277e-08, -0.07715907692909241, 0.049861352890729904), - (-0.17846627533435822, -0.1545550525188446, 0.049861323088407516), - (0.2580578029155731, -0.8116105794906616, 0.049861494451761246), - (-1.5711103173998708e-07, -0.923523485660553, 0.04986147582530975), - (-0.2580580711364746, -0.8116105198860168, 0.04986141249537468), - (-0.1784663051366806, -0.6918091773986816, 0.049861419945955276), - (-1.340541757599567e-07, -0.7692052721977234, 0.049861449748277664), - (0.1784660816192627, -0.6918091773986816, 0.04986146464943886)], - [(1, 2), (0, 3), (3, 5), (2, 4), (0, 6), (5, 6), (1, 7), (4, 7), (0, 8), - (1, 8), (7, 9), (6, 9), (8, 9), (2, 10), (3, 10), (4, 11), (5, 11), (10, 11), - (5, 12), (12, 13), (11, 13), (13, 14), (4, 14), (10, 16), (15, 16), (3, 15), - (2, 17), (16, 17), (9, 19), (18, 19), (6, 18), (7, 20), (19, 20), (8, 22), - (21, 22), (1, 21), (0, 23), (22, 23), (14, 20), (12, 18), (15, 23), (17, 21), - (12, 15), (13, 16), (14, 17), (20, 21), (19, 22), (18, 23)], - [(6, 0, 3, 5), (1, 7, 4, 2), (0, 6, 9, 8), (8, 9, 7, 1), (2, 4, 11, 10), - (10, 11, 5, 3), (11, 13, 12, 5), (4, 14, 13, 11), (3, 15, 16, 10), (10, 16, 17, 2), - (6, 18, 19, 9), (9, 19, 20, 7), (1, 21, 22, 8), (23, 0, 8, 22), (7, 20, 14, 4), - (5, 12, 18, 6), (0, 23, 15, 3), (2, 17, 21, 1), (16, 15, 12, 13), (17, 16, 13, 14), - (22, 21, 20, 19), (23, 22, 19, 18), (21, 17, 14, 20), (15, 23, 18, 12)] - ) - mesh.validate() - bpy.context.collection.objects.link(object) - # scale to the bone - bpy.data.objects["NewLink" + str(hueso.name)].scale = (hueso.length * mult, - hueso.length * mult, - hueso.length * mult) - # Parent objects - 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: - creahuesocero(hueso) - else: - creahuesonoventa(hueso) - if VAR_SWITCH == 1: - VAR_SWITCH = 0 - else: - VAR_SWITCH = 1 - - # if curve rig is activated - if curverig is True: - # variables - LISTA_POINTC = [] - ACTARM = bpy.context.active_object - - # create data and link the object to the scene - crv = bpy.data.curves.new("CurvaCable", "CURVE") - obCable = bpy.data.objects.new("Cable", crv) - bpy.context.collection.objects.link(obCable) - - # set the attributes - crv.dimensions = "3D" - crv.resolution_u = 10 - crv.resolution_v = 10 - crv.twist_mode = "MINIMUM" - - # 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 - )) - - for hueso in ACTARM.data.bones: - LISTA_POINTC.append(( - hueso.tail_local[0], - hueso.tail_local[1], - hueso.tail_local[2], 1 - )) - - # create the Spline - spline = crv.splines.new("NURBS") - lencoord = len(LISTA_POINTC) - rango = range(lencoord) - spline.points.add(lencoord - 1) - - for punto in rango: - spline.points[punto].co = LISTA_POINTC[punto] - - # set the endpoint - bpy.data.objects['Cable'].data.splines[0].use_endpoint_u = True - # select the curve - bpy.ops.object.select_all(action='DESELECT') - bpy.data.objects['Cable'].select = 1 - bpy.context.view_layer.objects.active = bpy.data.objects['Cable'] - # switch to Edit mode - bpy.ops.object.mode_set(mode='EDIT') - - # create hooks - POINTSTEP = 0 - for POINT in bpy.data.objects['Cable'].data.splines[0].points: - bpy.ops.curve.select_all(action="DESELECT") - bpy.data.objects['Cable'].data.splines[0].points[POINTSTEP].select = 1 - bpy.ops.object.hook_add_newob() - POINTSTEP += 1 - - # Objects selection step - bpy.ops.object.mode_set(mode='OBJECT') - bpy.ops.object.select_all(action='DESELECT') - ACTARM.select = 1 - bpy.context.view_layer.objects.active = bpy.data.objects['Armature'] - bpy.ops.object.mode_set(mode='POSE') - bpy.ops.pose.select_all(action='DESELECT') - ACTARM.data.bones[-1].select = 1 - ACTARM.data.bones.active = ACTARM.data.bones[-1] - - # set IK Spline - bpy.ops.pose.constraint_add_with_targets(type='SPLINE_IK') - ACTARM.pose.bones[-1].constraints['Spline IK'].target = bpy.data.objects['Cable'] - ACTARM.pose.bones[-1].constraints['Spline IK'].chain_count = 100 - bpy.context.active_object.pose.bones[-1].constraints['Spline IK'].use_y_stretch = False - # return to Object mode - bpy.ops.object.mode_set(mode='OBJECT') - - -class MESH_OT_primitive_oscurart_chain_add(Operator): - bl_idname = "mesh.primitive_oscurart_chain_add" - bl_label = "Chain to Bones" - bl_description = ("Add Chain Parented to an Existing Armature\n" - "The Active/Last Selected Object must be an Armature") - bl_options = {'REGISTER', 'UNDO'} - - curverig: BoolProperty( - name="Curve Rig", - default=False - ) - multiplier: FloatProperty( - name="Scale", - default=1, - min=0.01, max=100.0 - ) - - @classmethod - def poll(cls, context): - obj = context.active_object - return (obj is not None and obj.type == "ARMATURE") - - def execute(self, context): - 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'} - - -def register(): - bpy.utils.register_class(MESH_OT_primitive_oscurart_chain_add) - - -def unregister(): - bpy.utils.unregister_class(MESH_OT_primitive_oscurart_chain_add) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/pixelate_3d.py b/add_advanced_objects_menu/pixelate_3d.py deleted file mode 100644 index ea7c0e8296e8b104d016e937ac554796d258fa9f..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/pixelate_3d.py +++ /dev/null @@ -1,137 +0,0 @@ -# gpl author: liero -# very simple 'pixelization' or 'voxelization' engine # - -bl_info = { - "name": "3D Pixelate", - "author": "liero", - "version": (0, 5, 3), - "blender": (2, 74, 0), - "location": "View3D > Tool Shelf", - "description": "Creates a 3d pixelated version of the object", - "category": "Object"} - -# Note: winmgr properties are moved to the operator - - -import bpy -from bpy.types import Operator -from bpy.props import ( - FloatProperty, - IntProperty, - ) - - -def pix(self, obj): - sce = bpy.context.scene - obj.hide = obj.hide_render = True - mes = obj.to_mesh(sce, True, 'RENDER') - mes.transform(obj.matrix_world) - dup = bpy.data.objects.new('dup', mes) - sce.objects.link(dup) - dup.instance_type = 'VERTS' - sce.objects.active = dup - bpy.ops.object.mode_set() - ver = mes.vertices - - for i in range(250): - fin = True - for i in dup.data.edges: - d = ver[i.vertices[0]].co - ver[i.vertices[1]].co - if d.length > self.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=self.smooth) - bpy.ops.mesh.select_all(action='DESELECT') - bpy.ops.object.editmode_toggle() - if fin: - break - - for i in ver: - for n in range(3): - i.co[n] -= (.001 + i.co[n]) % self.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 = self.size * (100 - self.gap) * .005 - bpy.ops.mesh.primitive_cube_add(layers=[True] + [False] * 19) - bpy.ops.transform.resize(value=[sca] * 3) - bpy.context.view_layer.objects.active = dup - bpy.ops.object.parent_set(type='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'} - - size: FloatProperty( - name="Size", - min=.05, max=5, - default=.25, - description="Size of the cube / grid \n" - "Small values (below 0.1) can create a high polygon count" - ) - gap: IntProperty( - name="Gap", - min=0, max=90, - default=10, - subtype='PERCENTAGE', - description="Separation - percent of size" - ) - smooth: FloatProperty( - name="Smooth", - min=0, max=1, - default=.0, - description="Smooth factor when subdividing mesh" - ) - - @classmethod - def poll(cls, context): - return (context.active_object and - context.active_object.type == 'MESH' and - context.mode == 'OBJECT') - - def draw(self, context): - layout = self.layout - - col = layout.column(align=True) - col.prop(self, "size") - col.prop(self, "gap") - layout.prop(self, "smooth") - - def execute(self, context): - objeto = bpy.context.object - try: - pix(self, 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'} - - -def register(): - bpy.utils.register_class(Pixelate) - - -def unregister(): - bpy.utils.unregister_class(Pixelate) - - -if __name__ == '__main__': - register() diff --git a/add_advanced_objects_menu/random_box_structure.py b/add_advanced_objects_menu/random_box_structure.py deleted file mode 100644 index 819dd5ece3c0ea40c7dd7069c66e65202d2275de..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/random_box_structure.py +++ /dev/null @@ -1,201 +0,0 @@ -# gpl: author Dannyboy - -bl_info = { - "name": "Add Random Box Structure", - "author": "Dannyboy", - "version": (1, 0, 1), - "location": "View3D > Add > Make Box Structure", - "description": "Fill selected box shaped meshes with randomly sized cubes", - "warning": "", - "wiki_url": "", - "tracker_url": "dannyboypython.blogspot.com", - "category": "Object"} - -import bpy -import random -from bpy.types import Operator -from bpy.props import ( - BoolProperty, - FloatProperty, - FloatVectorProperty, - IntProperty, - ) - - -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(es)", - default=True - ) - wh: BoolProperty( - name="Stay Within Bounds", - description="Keeps cubes from exceeding base mesh bounds", - default=True - ) - uf: BoolProperty( - name="Uniform Cube Quantity", - default=False - ) - qn: IntProperty( - name="Cube Quantity", - default=10, - min=1, max=1500 - ) - mn: FloatVectorProperty( - 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' - ) - lo: FloatVectorProperty( - name="XYZ Offset", - default=(0.0, 0.0, 0.0), - subtype='XYZ' - ) - rsd: FloatProperty( - 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 - oblst = [] - uvyes = 0 - bpy.ops.collection.create(name='Cubagrouper') - bpy.ops.collection.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.view_layer.objects.active = obj - if obj.data.uv_layers[:] != []: - uvyes = 1 - else: - uvyes = 0 - bpy.ops.object.collection_link(group='Cubagrouper') - dim = obj.dimensions - rot = obj.rotation_euler - if self.uf is True: - 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 - 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 - pmn.x = 0.0 - if 0.0 > pmn.y: - pmn.y = 0.0 - if 0.0 > pmn.z: - pmn.z = 0.0 - 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 - 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 - elif self.wh is False: - ex = (random.random() * dim.x) - (dim.x / 2) + obj.location.x - wy = (random.random() * dim.y) - (dim.y / 2) + obj.location.y - ze = (random.random() * dim.z) - (dim.z / 2) + obj.location.z - bpy.ops.mesh.primitive_cube_add( - radius=0.5, location=(ex + self.lo.x, wy + self.lo.y, ze + self.lo.z) - ) - 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), - orient_type='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() - bpy.ops.object.collection_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), - orient_type='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), - orient_type='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), - orient_type='GLOBAL', mirror=False, proportional='DISABLED', - proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True - ) - bpy.context.view_layer.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.collection.objects_remove() - bpy.context.view_layer.objects.active = select - - if self.dc is True: - bpy.context.collection.objects.unlink(obj) - - return {'FINISHED'} - - -def register(): - bpy.utils.register_class(makestructure) - - -def unregister(): - bpy.utils.unregister_class(makestructure) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/rope_alpha.py b/add_advanced_objects_menu/rope_alpha.py deleted file mode 100644 index 7c9c5129c94709ddca5682106a99e6b4954a924b..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/rope_alpha.py +++ /dev/null @@ -1,832 +0,0 @@ -# Copyright (c) 2012 Jorge Hernandez - Melendez - -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# 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, 2), - "blender": (2, 73, 0), - "location": "Left Toolbar > ClothRope", - "warning": "", - "wiki_url": "", - "category": "Add Mesh" -} - - -import bpy -from bpy.types import Operator -from bpy.props import ( - BoolProperty, - FloatProperty, - IntProperty, - ) - - -def desocultar(quien): - if quien == "todo": - for ob in bpy.data.objects: - ob.hide = False - else: - bpy.data.objects[quien].hide = False - - -def deseleccionar_todo(): - bpy.ops.object.select_all(action='DESELECT') - - -def seleccionar_todo(): - bpy.ops.object.select_all(action='SELECT') - - -def salir_de_editmode(): - if bpy.context.mode in ["EDIT", "EDIT_MESH", "EDIT_CURVE"]: - bpy.ops.object.mode_set(mode='OBJECT') - - -# Clear scene: -def reset_scene(): - desocultar("todo") - # playback to the start - bpy.ops.screen.frame_jump(end=False) - try: - salir_de_editmode() - except: - pass - 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(): - if bpy.context.mode == "OBJECT": - bpy.ops.object.mode_set(mode='EDIT') - - -def select_all_in_edit_mode(ob): - if ob.mode != 'EDIT': - entrar_en_editmode() - 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 not v.select: - v.select = True - entrar_en_editmode() - - -def deselect_all_in_edit_mode(ob): - if ob.mode != 'EDIT': - entrar_en_editmode() - 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 not v.select: - v.select = False - entrar_en_editmode() - - -def which_vertex_are_selected(ob): - for v in ob.data.vertices: - if v.select: - print(str(v.index)) - print("Vertex " + str(v.index) + " is selected") - - -def seleccionar_por_nombre(nombre): - scn = bpy.context.scene - bpy.data.objects[nombre].select_set(True) - - scn.objects.active = bpy.data.objects[nombre] - - -def deseleccionar_por_nombre(nombre): - bpy.data.objects[nombre].select_set(False) - - -def crear_vertices(ob): - ob.data.vertices.add(1) - ob.data.update - - -def borrar_elementos_seleccionados(tipo): - if tipo == "vertices": - bpy.ops.mesh.delete(type='VERT') - - -def obtener_coords_vertex_seleccionados(): - coordenadas_de_vertices = [] - for ob in bpy.context.selected_objects: - if ob.type == 'MESH': - for v in ob.data.vertices: - if v.select: - coordenadas_de_vertices.append([v.co[0], v.co[1], v.co[2]]) - return coordenadas_de_vertices[0] - - -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) - ) - - -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), - "orient_type": '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): - for i in range(len(bc.data.splines[0].points)): - bc.data.splines[0].points[i].select = True - - -def deselect_all_vertex_in_curve_bezier(bc): - for i in range(len(bc.data.splines[0].points)): - bc.data.splines[0].points[i].select = False - - -def ocultar_relationships(): - for area in bpy.context.screen.areas: - if area.type == 'VIEW_3D': - area.spaces[0].show_relationship_lines = False - - -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") - - ropelength: IntProperty( - name="Rope Length", - description="Length of the generated Rope", - default=5 - ) - ropesegments: IntProperty( - name="Rope Segments", - description="Number of the Rope Segments", - default=5 - ) - qcr: IntProperty( - name="Collision Quality", - description="Rope's Cloth modifier collsion quality", - min=1, max=20, - default=20 - ) - substeps: IntProperty( - name="Rope Substeps", - description="Rope's Cloth modifier quality", - min=4, max=80, - default=50 - ) - resrope: IntProperty( - name="Rope Resolution", - description="Rope's Bevel resolution", - default=5 - ) - radiusrope: FloatProperty( - name="Radius", - description="Rope's Radius", - min=0.04, max=1, - default=0.04 - ) - hide_emptys: BoolProperty( - name="Hide Empties", - description="Hide Helper Objects", - default=False - ) - - def execute(self, context): - # add a new scene - bpy.ops.scene.new(type="NEW") - scene = bpy.context.scene - scene.name = "Test Rope" - seleccionar_todo() - longitud = self.ropelength - - # 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() - # 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) - ) - ob = bpy.context.selected_objects[0] - ob.name = "Rope" - # .001 and friends - rope_name = ob.name - deseleccionar_todo() - - # 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) - ) - ob = bpy.context.selected_objects[0] - # rename: - ob.name = "cuerda" - # .001 and friends - cuerda_1_name = ob.name - - entrar_en_editmode() # enter edit mode - select_all_in_edit_mode(ob) - - borrar_elementos_seleccionados("vertices") - 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() # assign it - - salir_de_editmode() # leave edit mode - ob.vertex_groups[0].name = "Pin" - deseleccionar_todo() - seleccionar_por_nombre(cuerda_1_name) - - # extrude vertices: - for i in range(cuantos_segmentos): - entrar_en_editmode() - extruir_vertices(longitud, cuantos_segmentos) - # delete the PIN group - bpy.ops.object.vertex_group_remove_from() - # get the direction to create the locator on it's position - pos = obtener_coords_vertex_seleccionados() - - salir_de_editmode() # leave edit mode - # create locator at position - crear_locator(pos) - deseleccionar_todo() - seleccionar_por_nombre(cuerda_1_name) - deseleccionar_todo() - - seleccionar_por_nombre(cuerda_1_name) # select the rope - entrar_en_editmode() - - pos = obtener_coords_vertex_seleccionados() # get their positions - salir_de_editmode() - # create the last locator - crear_locator(pos) - deseleccionar_todo() - 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() # and assign it - ob.vertex_groups[1].name = "Guide_rope" - - # extrude the Curve so it has a minimum 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), - "orient_type": '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() - bpy.ops.object.modifier_add(type='CLOTH') - bpy.context.object.modifiers["Cloth"].settings.use_pin_cloth = True - 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 - - # 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() - - # 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), - "orient_type": '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() - - 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") - # convert the Curve: - bpy.ops.object.convert(target='CURVE') - - # all Empties that are not previously present - emptys = [] - for eo in bpy.data.objects: - if eo.type == 'EMPTY' and eo.name not in empties_prev: - if eo.name != rope_name: - emptys.append(eo) - - # select and deselect: - bc = bpy.data.objects[cuerda_2_name] - n = 0 - - for e in emptys: - deseleccionar_todo() - seleccionar_por_nombre(e.name) - seleccionar_por_nombre(bc.name) - entrar_en_editmode() - deselect_all_vertex_in_curve_bezier(bc) - bc.data.splines[0].points[n].select = True - bpy.ops.object.hook_add_selob(use_bone=False) - salir_de_editmode() - n = n + 1 - - ob = bpy.data.objects[cuerda_1_name] - n = 0 - - for e in emptys: - deseleccionar_todo() - seleccionar_por_nombre(e.name) - seleccionar_por_nombre(ob.name) - entrar_en_editmode() - 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() - - salir_de_editmode() - n = n + 1 - - # hide the Empties: - deseleccionar_todo() - - # 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() - - # do not display the relations - ocultar_relationships() - 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 - - return {'FINISHED'} - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width=350) - - def draw(self, context): - layout = self.layout - box = layout.box() - col = box.column(align=True) - - col.label(text="Rope settings:") - rowsub0 = col.row() - rowsub0.prop(self, "ropelength", text="Length") - rowsub0.prop(self, "ropesegments", text="Segments") - rowsub0.prop(self, "radiusrope", text="Radius") - - col.label(text="Quality Settings:") - 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 = "Wrecking Ball" - bl_description = ("Create a new Scene with a Rigid Body simulation of\n" - "Wrecking Ball on a rope") - - # defaults rope ball - ropelength2: IntProperty( - name="Rope Length", - description="Length of the Wrecking Ball rope", - default=10 - ) - ropesegments2: IntProperty( - name="Rope Segments", - description="Number of the Wrecking Ball rope segments", - min=0, max=999, - default=6 - ) - radiuscubes: FloatProperty( - name="Cube Radius", - description="Size of the Linked Cubes helpers", - default=0.5 - ) - radiusrope: FloatProperty( - name="Rope Radius", - description="Radius of the Rope", - default=0.4 - ) - worldsteps: IntProperty( - name="World Steps", - description="Rigid Body Solver world steps per second (update)", - min=60, max=1000, - default=250 - ) - solveriterations: IntProperty( - name="Solver Iterations", - description="How many times the Rigid Body Solver should run", - min=10, max=100, - default=50 - ) - massball: IntProperty( - name="Ball Mass", - description="Mass of the Wrecking Ball", - default=1 - ) - resrope: IntProperty( - name="Resolution", - description="Rope resolution", - default=4 - ) - grados: FloatProperty( - name="Degrees", - description="Angle of the Wrecking Ball compared to the Ground Plane", - default=45 - ) - separacion: FloatProperty( - name="Link Cubes Gap", - description="Space between the Rope's Linked Cubes", - default=0.1 - ) - hidecubes: BoolProperty( - 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.ropelength2 - - # 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) - radio = self.radiuscubes - radiorope = self.radiusrope - masa = self.massball - resolucion = self.resrope - 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" - - # 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) - ) - 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') - - # create the first cube: - cuboslink = [] - n = 0 - for i in range(segmentos): - # if 0 start from 1 - if i == 0: - i = offset_del_suelo - 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 # 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) - ) - bpy.ops.rigidbody.objects_add(type='ACTIVE') - bpy.context.object.name = "CubeLink" - if n != 0: - bpy.context.object.display_type = 'WIRE' - bpy.context.object.hide_render = True - n += 1 - bpy.context.object.scale.z = (longitud * 2) / (segmentos * 2) - separation - 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: - nombre1 = cuboslink[i] - nombre2 = cuboslink[i + 1] - seleccionar_por_nombre(nombre1.name) - seleccionar_por_nombre(nombre2.name) - bpy.ops.rigidbody.connect() - - # 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() - - # 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) - ) - 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(real_name) - x = cuboslink[i].location[0] - y = cuboslink[i].location[1] - z = cuboslink[i].location[2] - - # if it is 0 make it start from 1 as the offset from the ground... - if i == 0: - i = offset_del_suelo - 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() - - if i == 1: - # select all the vertices and delete them - select_all_vertex_in_curve_bezier(bpy.data.objects[real_name]) - bpy.ops.curve.delete(type='VERT') - # create the first vertex: - bpy.ops.curve.vertex_add(location=(x, y, z)) - else: - # 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), - "orient_type": '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() - - # 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) - ) - bpy.ops.transform.translate( - value=(0, 0, -z + 2), constraint_axis=(False, False, True), - orient_type='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), - orient_type='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') - - # move it all up a bit more: - seleccionar_todo() - deseleccionar_por_nombre(groundplane_name) - bpy.ops.transform.translate( - value=(0, 0, offset_del_suelo_real), - constraint_axis=(False, False, True), - orient_type='GLOBAL', mirror=False, - proportional='DISABLED', proportional_edit_falloff='SMOOTH', - proportional_size=1 - ) - - deseleccionar_todo() - seleccionar_por_nombre(cuboslink[-1].name) - bpy.ops.rigidbody.objects_add(type='PASSIVE') - - bpy.context.scene.rigidbody_world.steps_per_second = world_steps - bpy.context.scene.rigidbody_world.solver_iterations = solver_iterations - - # 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_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), - orient_type='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(real_name) - bpy.context.object.data.fill_mode = 'FULL' - bpy.context.object.data.bevel_depth = radiorope - for ob in bpy.data.objects: - if ob.name != cuboslink[0].name: - if ob.name.find("CubeLink") >= 0: - deseleccionar_todo() - seleccionar_por_nombre(ob.name) - if hidecubeslinks: - bpy.context.object.hide = True - ocultar_relationships() - deseleccionar_todo() - return {'FINISHED'} - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width=350) - - def draw(self, context): - layout = self.layout - box = layout.box() - col = box.column(align=True) - - col.label(text="Rope settings:") - rowsub0 = col.row() - rowsub0.prop(self, "hidecubes", text="Hide Link Cubes") - - rowsub1 = col.row(align=True) - rowsub1.prop(self, "ropelength2", 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(text="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") - - -# Register - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_menu/scene_objects_bi.py b/add_advanced_objects_menu/scene_objects_bi.py deleted file mode 100644 index 199289dd6aec3571563ee19e46331e5acefe9754..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/scene_objects_bi.py +++ /dev/null @@ -1,195 +0,0 @@ -# gpl: author meta-androcto - -import bpy -from bpy.types import Operator - - -class add_BI_scene(Operator): - bl_idname = "bi.add_scene" - bl_label = "Create test scene" - bl_description = "Blender Internal renderer Scene with Objects" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - 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.display_size = 0.1 - bpy.ops.view3d.viewnumpad(type='CAMERA') - - # add point lamp - bpy.ops.object.light_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.light_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), - orient_type='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_menu/scene_objects_cycles.py b/add_advanced_objects_menu/scene_objects_cycles.py deleted file mode 100644 index 7158b02f18472de4638008efa977f333c2122ccf..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/scene_objects_cycles.py +++ /dev/null @@ -1,142 +0,0 @@ -# gpl: author meta-androcto - -import bpy -from bpy.types import Operator - - -class add_cycles_scene(Operator): - bl_idname = "objects_cycles.add_scene" - bl_label = "Create test scene" - bl_description = "Cycles renderer Scene with Objects" - bl_options = {'REGISTER'} - - def execute(self, context): - 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.display_size = 0.1 - bpy.ops.view3d.viewnumpad(type='CAMERA') - - # add point lamp - bpy.ops.object.light_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.light_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_menu/scene_texture_render.py b/add_advanced_objects_menu/scene_texture_render.py deleted file mode 100644 index 0f9ccc2b329321bc40a1525b454b394061d9388f..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/scene_texture_render.py +++ /dev/null @@ -1,78 +0,0 @@ -# gpl: author meta-androcto - -import bpy -from bpy.types import Operator - - -class add_texture_scene(Operator): - bl_idname = "objects_texture.add_scene" - bl_label = "Create test scene" - bl_description = "Cycles renderer Scene: Camera aligned to a plane" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - 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" - - # 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 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.display_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 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'} - - return {'FINISHED'} diff --git a/add_advanced_objects_menu/trilighting.py b/add_advanced_objects_menu/trilighting.py deleted file mode 100644 index 8fee580be5e9932c94a7d6f04c1dabf7d038cb7c..0000000000000000000000000000000000000000 --- a/add_advanced_objects_menu/trilighting.py +++ /dev/null @@ -1,239 +0,0 @@ -# gpl: author Daniel Schalla - -import bpy -from bpy.types import Operator -from bpy.props import ( - EnumProperty, - FloatProperty, - IntProperty, - ) -from math import ( - sin, cos, - radians, - sqrt, - ) - - -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 existing Active Object") - bl_options = {'REGISTER', 'UNDO'} - - height: FloatProperty( - name="Height", - default=5 - ) - distance: FloatProperty( - name="Distance", - default=5, - min=0.1, - subtype="DISTANCE" - ) - energy: IntProperty( - name="Base Energy", - default=3, - min=1 - ) - contrast: IntProperty( - name="Contrast", - default=50, - min=-100, max=100, - subtype="PERCENTAGE" - ) - leftangle: IntProperty( - name="Left Angle", - default=26, - min=1, max=90, - subtype="ANGLE" - ) - rightangle: IntProperty( - 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") - ] - primarytype: EnumProperty( - 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 types of secondary Lights you would like", - items=Light_Type_List, - default="POINT" - ) - - @classmethod - def poll(cls, context): - return context.active_object is not None - - def draw(self, context): - layout = self.layout - - layout.label(text="Position:") - col = layout.column(align=True) - col.prop(self, "height") - col.prop(self, "distance") - - layout.label(text="Light:") - col = layout.column(align=True) - col.prop(self, "energy") - col.prop(self, "contrast") - - layout.label(text="Orientation:") - col = layout.column(align=True) - col.prop(self, "leftangle") - col.prop(self, "rightangle") - col.prop(self, "backangle") - - col = layout.column() - col.label(text="Key Light Type:") - col.prop(self, "primarytype", text="") - col.label(text="Fill + Back Type:") - col.prop(self, "secondarytype", text="") - - def execute(self, context): - try: - collection = context.collection - 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) - collection.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.view_layer.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.lights.new(name="TriLamp-Back", type=self.secondarytype) - backData.energy = backEnergy - - backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData) - collection.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.lights.new(name="TriLamp-Fill", type=self.secondarytype) - rightData.energy = fillEnergy - rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData) - collection.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.lights.new(name="TriLamp-Key", type=self.primarytype) - leftData.energy = keyEnergy - - leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData) - collection.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)) - - return {'CANCELLED'} - - return {'FINISHED'} diff --git a/add_advanced_objects_panels/DelaunayVoronoi.py b/add_advanced_objects_panels/DelaunayVoronoi.py deleted file mode 100644 index d291d7009a4f5323f73fa2e87987ce1a7b262e68..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/DelaunayVoronoi.py +++ /dev/null @@ -1,998 +0,0 @@ -# -*- coding: utf-8 -*- - -# Voronoi diagram calculator/ Delaunay triangulator -# -# - Voronoi Diagram Sweepline algorithm and C code by Steven Fortune, -# 1987, http://ect.bell-labs.com/who/sjf/ -# - Python translation to file voronoi.py by Bill Simons, 2005, http://www.oxfish.com/ -# - Additional changes for QGIS by Carson Farmer added November 2010 -# - 2012 Ported to Python 3 and additional clip functions by domlysz at gmail.com -# -# Calculate Delaunay triangulation or the Voronoi polygons for a set of -# 2D input points. -# -# Derived from code bearing the following notice: -# -# The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T -# Bell Laboratories. -# Permission to use, copy, modify, and distribute this software for any -# purpose without fee is hereby granted, provided that this entire notice -# is included in all copies of any software which is or includes a copy -# or modification of this software and in all copies of the supporting -# documentation for such software. -# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED -# WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY -# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY -# OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. -# -# Comments were incorporated from Shane O'Sullivan's translation of the -# original code into C++ (http://mapviewer.skynet.ie/voronoi.html) -# -# Steve Fortune's homepage: http://netlib.bell-labs.com/cm/cs/who/sjf/index.html -# -# For programmatic use, two functions are available: -# -# computeVoronoiDiagram(points, xBuff, yBuff, polygonsOutput=False, formatOutput=False): -# Takes : -# - a list of point objects (which must have x and y fields). -# - x and y buffer values which are the expansion percentages of the -# bounding box rectangle including all input points. -# Returns : -# - With default options : -# A list of 2-tuples, representing the two points of each Voronoi diagram edge. -# Each point contains 2-tuples which are the x,y coordinates of point. -# if formatOutput is True, returns : -# - a list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices. -# - and a list of 2-tuples (v1, v2) representing edges of the Voronoi diagram. -# v1 and v2 are the indices of the vertices at the end of the edge. -# - If polygonsOutput option is True, returns : -# A dictionary of polygons, keys are the indices of the input points, -# values contains n-tuples representing the n points of each Voronoi diagram polygon. -# Each point contains 2-tuples which are the x,y coordinates of point. -# if formatOutput is True, returns : -# - A list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices. -# - and a dictionary of input points indices. Values contains n-tuples representing -# the n points of each Voronoi diagram polygon. -# Each tuple contains the vertex indices of the polygon vertices. -# -# computeDelaunayTriangulation(points): -# Takes a list of point objects (which must have x and y fields). -# Returns a list of 3-tuples: the indices of the points that form a Delaunay triangle. - -import bpy -import math - -# Globals -TOLERANCE = 1e-9 -BIG_FLOAT = 1e38 - - -class Context(object): - - def __init__(self): - self.doPrint = 0 - self.debug = 0 - - # tuple (xmin, xmax, ymin, ymax) - self.extent = () - self.triangulate = False - # list of vertex 2-tuples: (x,y) - self.vertices = [] - # equation of line 3-tuple (a b c), for the equation of the line a*x+b*y = c - self.lines = [] - - # edge 3-tuple: (line index, vertex 1 index, vertex 2 index) - # if either vertex index is -1, the edge extends to infinity - self.edges = [] - # 3-tuple of vertex indices - self.triangles = [] - # a dict of site:[edges] pairs - self.polygons = {} - - -# Clip functions # - def getClipEdges(self): - xmin, xmax, ymin, ymax = self.extent - clipEdges = [] - for edge in self.edges: - equation = self.lines[edge[0]] # line equation - if edge[1] != -1 and edge[2] != -1: # finite line - x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1] - x2, y2 = self.vertices[edge[2]][0], self.vertices[edge[2]][1] - pt1, pt2 = (x1, y1), (x2, y2) - inExtentP1, inExtentP2 = self.inExtent(x1, y1), self.inExtent(x2, y2) - if inExtentP1 and inExtentP2: - clipEdges.append((pt1, pt2)) - elif inExtentP1 and not inExtentP2: - pt2 = self.clipLine(x1, y1, equation, leftDir=False) - clipEdges.append((pt1, pt2)) - elif not inExtentP1 and inExtentP2: - pt1 = self.clipLine(x2, y2, equation, leftDir=True) - clipEdges.append((pt1, pt2)) - else: # infinite line - if edge[1] != -1: - x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1] - leftDir = False - else: - x1, y1 = self.vertices[edge[2]][0], self.vertices[edge[2]][1] - leftDir = True - if self.inExtent(x1, y1): - pt1 = (x1, y1) - pt2 = self.clipLine(x1, y1, equation, leftDir) - clipEdges.append((pt1, pt2)) - return clipEdges - - def getClipPolygons(self, closePoly): - xmin, xmax, ymin, ymax = self.extent - poly = {} - for inPtsIdx, edges in self.polygons.items(): - clipEdges = [] - for edge in edges: - equation = self.lines[edge[0]] # line equation - if edge[1] != -1 and edge[2] != -1: # finite line - x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1] - x2, y2 = self.vertices[edge[2]][0], self.vertices[edge[2]][1] - pt1, pt2 = (x1, y1), (x2, y2) - inExtentP1, inExtentP2 = self.inExtent(x1, y1), self.inExtent(x2, y2) - if inExtentP1 and inExtentP2: - clipEdges.append((pt1, pt2)) - elif inExtentP1 and not inExtentP2: - pt2 = self.clipLine(x1, y1, equation, leftDir=False) - clipEdges.append((pt1, pt2)) - elif not inExtentP1 and inExtentP2: - pt1 = self.clipLine(x2, y2, equation, leftDir=True) - clipEdges.append((pt1, pt2)) - else: # infinite line - if edge[1] != -1: - x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1] - leftDir = False - else: - x1, y1 = self.vertices[edge[2]][0], self.vertices[edge[2]][1] - leftDir = True - if self.inExtent(x1, y1): - pt1 = (x1, y1) - pt2 = self.clipLine(x1, y1, equation, leftDir) - clipEdges.append((pt1, pt2)) - # create polygon definition from edges and check if polygon is completely closed - polyPts, complete = self.orderPts(clipEdges) - if not complete: - startPt = polyPts[0] - endPt = polyPts[-1] - # if start & end points are collinear then they are along an extent border - if startPt[0] == endPt[0] or startPt[1] == endPt[1]: - polyPts.append(polyPts[0]) # simple close - else: # close at extent corner - # upper left - if (startPt[0] == xmin and endPt[1] == ymax) or (endPt[0] == xmin and startPt[1] == ymax): - polyPts.append((xmin, ymax)) # corner point - polyPts.append(polyPts[0]) # close polygon - # upper right - if (startPt[0] == xmax and endPt[1] == ymax) or (endPt[0] == xmax and startPt[1] == ymax): - polyPts.append((xmax, ymax)) - polyPts.append(polyPts[0]) - # bottom right - if (startPt[0] == xmax and endPt[1] == ymin) or (endPt[0] == xmax and startPt[1] == ymin): - polyPts.append((xmax, ymin)) - polyPts.append(polyPts[0]) - # bottom left - if (startPt[0] == xmin and endPt[1] == ymin) or (endPt[0] == xmin and startPt[1] == ymin): - polyPts.append((xmin, ymin)) - polyPts.append(polyPts[0]) - if not closePoly: # unclose polygon - polyPts = polyPts[:-1] - poly[inPtsIdx] = polyPts - return poly - - def clipLine(self, x1, y1, equation, leftDir): - xmin, xmax, ymin, ymax = self.extent - a, b, c = equation - if b == 0: # vertical line - if leftDir: # left is bottom of vertical line - return (x1, ymax) - else: - return (x1, ymin) - elif a == 0: # horizontal line - if leftDir: - return (xmin, y1) - else: - return (xmax, y1) - else: - y2_at_xmin = (c - a * xmin) / b - y2_at_xmax = (c - a * xmax) / b - x2_at_ymin = (c - b * ymin) / a - x2_at_ymax = (c - b * ymax) / a - intersectPts = [] - if ymin <= y2_at_xmin <= ymax: # valid intersect point - intersectPts.append((xmin, y2_at_xmin)) - if ymin <= y2_at_xmax <= ymax: - intersectPts.append((xmax, y2_at_xmax)) - if xmin <= x2_at_ymin <= xmax: - intersectPts.append((x2_at_ymin, ymin)) - if xmin <= x2_at_ymax <= xmax: - intersectPts.append((x2_at_ymax, ymax)) - # delete duplicate (happens if intersect point is at extent corner) - intersectPts = set(intersectPts) - # choose target intersect point - if leftDir: - pt = min(intersectPts) # smaller x value - else: - pt = max(intersectPts) - return pt - - def inExtent(self, x, y): - xmin, xmax, ymin, ymax = self.extent - return x >= xmin and x <= xmax and y >= ymin and y <= ymax - - def orderPts(self, edges): - poly = [] # returned polygon points list [pt1, pt2, pt3, pt4 ....] - pts = [] - # get points list - for edge in edges: - pts.extend([pt for pt in edge]) - # try to get start & end point - try: - startPt, endPt = [pt for pt in pts if pts.count(pt) < 2] # start and end point aren't duplicate - except: # all points are duplicate --> polygon is complete --> append some or other edge points - complete = True - firstIdx = 0 - poly.append(edges[0][0]) - poly.append(edges[0][1]) - else: # incomplete --> append the first edge points - complete = False - # search first edge - for i, edge in enumerate(edges): - if startPt in edge: # find - firstIdx = i - break - poly.append(edges[firstIdx][0]) - poly.append(edges[firstIdx][1]) - if poly[0] != startPt: - poly.reverse() - # append next points in list - del edges[firstIdx] - while edges: # all points will be treated when edges list will be empty - currentPt = poly[-1] # last item - for i, edge in enumerate(edges): - if currentPt == edge[0]: - poly.append(edge[1]) - break - elif currentPt == edge[1]: - poly.append(edge[0]) - break - del edges[i] - return poly, complete - - def setClipBuffer(self, xpourcent, ypourcent): - xmin, xmax, ymin, ymax = self.extent - width = xmax - xmin - height = ymax - ymin - xmin = xmin - width * xpourcent / 100 - xmax = xmax + width * xpourcent / 100 - ymin = ymin - height * ypourcent / 100 - ymax = ymax + height * ypourcent / 100 - self.extent = xmin, xmax, ymin, ymax - - # End clip functions # - - def outSite(self, s): - if(self.debug): - print("site (%d) at %f %f" % (s.sitenum, s.x, s.y)) - elif(self.triangulate): - pass - elif(self.doPrint): - print("s %f %f" % (s.x, s.y)) - - def outVertex(self, s): - self.vertices.append((s.x, s.y)) - if(self.debug): - print("vertex(%d) at %f %f" % (s.sitenum, s.x, s.y)) - elif(self.triangulate): - pass - elif(self.doPrint): - print("v %f %f" % (s.x, s.y)) - - def outTriple(self, s1, s2, s3): - self.triangles.append((s1.sitenum, s2.sitenum, s3.sitenum)) - if (self.debug): - print("circle through left=%d right=%d bottom=%d" % (s1.sitenum, s2.sitenum, s3.sitenum)) - 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): - 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) - ) - elif(self.doPrint): - print("l %f %f %f" % (edge.a, edge.b, edge.c)) - - def outEdge(self, edge): - sitenumL = -1 - if edge.ep[Edge.LE] is not None: - sitenumL = edge.ep[Edge.LE].sitenum - sitenumR = -1 - if edge.ep[Edge.RE] is not None: - sitenumR = edge.ep[Edge.RE].sitenum - - # polygons dict add by CF - if edge.reg[0].sitenum not in self.polygons: - self.polygons[edge.reg[0].sitenum] = [] - if edge.reg[1].sitenum not in self.polygons: - self.polygons[edge.reg[1].sitenum] = [] - self.polygons[edge.reg[0].sitenum].append((edge.edgenum, sitenumL, sitenumR)) - self.polygons[edge.reg[1].sitenum].append((edge.edgenum, sitenumL, sitenumR)) - - self.edges.append((edge.edgenum, sitenumL, sitenumR)) - - if (not self.triangulate): - if (self.doPrint): - print("e %d" % edge.edgenum) - print(" %d " % sitenumL) - print("%d" % sitenumR) - - -def voronoi(siteList, context): - context.extent = siteList.extent - edgeList = EdgeList(siteList.xmin, siteList.xmax, len(siteList)) - priorityQ = PriorityQueue(siteList.ymin, siteList.ymax, len(siteList)) - siteIter = siteList.iterator() - - bottomsite = siteIter.next() - context.outSite(bottomsite) - newsite = siteIter.next() - minpt = Site(-BIG_FLOAT, -BIG_FLOAT) - while True: - if not priorityQ.isEmpty(): - minpt = priorityQ.getMinPt() - - if (newsite and (priorityQ.isEmpty() or newsite < minpt)): - # newsite is smallest - this is a site event - context.outSite(newsite) - - # get first Halfedge to the LEFT and RIGHT of the new site - lbnd = edgeList.leftbnd(newsite) - rbnd = lbnd.right - - # if this halfedge has no edge, bot = bottom site (whatever that is) - # create a new edge that bisects - bot = lbnd.rightreg(bottomsite) - edge = Edge.bisect(bot, newsite) - context.outBisector(edge) - - # create a new Halfedge, setting its pm field to 0 and insert - # this new bisector edge between the left and right vectors in - # a linked list - bisector = Halfedge(edge, Edge.LE) - edgeList.insert(lbnd, bisector) - - # if the new bisector intersects with the left edge, remove - # the left edge's vertex, and put in the new one - p = lbnd.intersect(bisector) - if p is not None: - priorityQ.delete(lbnd) - priorityQ.insert(lbnd, p, newsite.distance(p)) - - # create a new Halfedge, setting its pm field to 1 - # insert the new Halfedge to the right of the original bisector - lbnd = bisector - bisector = Halfedge(edge, Edge.RE) - edgeList.insert(lbnd, bisector) - - # if this new bisector intersects with the right Halfedge - p = bisector.intersect(rbnd) - if p is not None: - # push the Halfedge into the ordered linked list of vertices - priorityQ.insert(bisector, p, newsite.distance(p)) - - newsite = siteIter.next() - - elif not priorityQ.isEmpty(): - # intersection is smallest - this is a vector (circle) event - # pop the Halfedge with the lowest vector off the ordered list of - # vectors. Get the Halfedge to the left and right of the above HE - # and also the Halfedge to the right of the right HE - lbnd = priorityQ.popMinHalfedge() - llbnd = lbnd.left - rbnd = lbnd.right - rrbnd = rbnd.right - - # get the Site to the left of the left HE and to the right of - # the right HE which it bisects - bot = lbnd.leftreg(bottomsite) - top = rbnd.rightreg(bottomsite) - - # output the triple of sites, stating that a circle goes through them - mid = lbnd.rightreg(bottomsite) - context.outTriple(bot, top, mid) - - # get the vertex that caused this event and set the vertex number - # couldn't do this earlier since we didn't know when it would be processed - v = lbnd.vertex - siteList.setSiteNumber(v) - context.outVertex(v) - - # set the endpoint of the left and right Halfedge to be this vector - if lbnd.edge.setEndpoint(lbnd.pm, v): - context.outEdge(lbnd.edge) - - if rbnd.edge.setEndpoint(rbnd.pm, v): - context.outEdge(rbnd.edge) - - # delete the lowest HE, remove all vertex events to do with the - # right HE and delete the right HE - edgeList.delete(lbnd) - priorityQ.delete(rbnd) - edgeList.delete(rbnd) - - # if the site to the left of the event is higher than the Site - # to the right of it, then swap them and set 'pm' to RIGHT - pm = Edge.LE - if bot.y > top.y: - bot, top = top, bot - pm = Edge.RE - - # Create an Edge (or line) that is between the two Sites. This - # creates the formula of the line, and assigns a line number to it - edge = Edge.bisect(bot, top) - context.outBisector(edge) - - # create a HE from the edge - bisector = Halfedge(edge, pm) - - # insert the new bisector to the right of the left HE - # set one endpoint to the new edge to be the vector point 'v' - # If the site to the left of this bisector is higher than the right - # Site, then this endpoint is put in position 0; otherwise in pos 1 - edgeList.insert(llbnd, bisector) - if edge.setEndpoint(Edge.RE - pm, v): - context.outEdge(edge) - - # if left HE and the new bisector don't intersect, then delete - # the left HE, and reinsert it - p = llbnd.intersect(bisector) - if p is not None: - priorityQ.delete(llbnd) - priorityQ.insert(llbnd, p, bot.distance(p)) - - # if right HE and the new bisector don't intersect, then reinsert it - p = bisector.intersect(rrbnd) - if p is not None: - priorityQ.insert(bisector, p, bot.distance(p)) - else: - break - - he = edgeList.leftend.right - while he is not edgeList.rightend: - context.outEdge(he.edge) - he = he.right - Edge.EDGE_NUM = 0 # CF - - -def isEqual(a, b, relativeError=TOLERANCE): - # is nearly equal to within the allowed relative error - norm = max(abs(a), abs(b)) - return (norm < relativeError) or (abs(a - b) < (relativeError * norm)) - - -class Site(object): - - def __init__(self, x=0.0, y=0.0, sitenum=0): - self.x = x - self.y = y - self.sitenum = sitenum - - def dump(self): - print("Site #%d (%g, %g)" % (self.sitenum, self.x, self.y)) - - def __lt__(self, other): - if self.y < other.y: - return True - elif self.y > other.y: - return False - elif self.x < other.x: - return True - elif self.x > other.x: - return False - else: - return False - - def __eq__(self, other): - if self.y == other.y and self.x == other.x: - return True - - def distance(self, other): - dx = self.x - other.x - dy = self.y - other.y - return math.sqrt(dx * dx + dy * dy) - - -class Edge(object): - LE = 0 # left end indice --> edge.ep[Edge.LE] - RE = 1 # right end indice - EDGE_NUM = 0 - DELETED = {} # marker value - - def __init__(self): - self.a = 0.0 # equation of the line a*x+b*y = c - self.b = 0.0 - self.c = 0.0 - self.ep = [None, None] # end point (2 tuples of site) - self.reg = [None, None] - self.edgenum = 0 - - def dump(self): - print("(#%d a=%g, b=%g, c=%g)" % (self.edgenum, self.a, self.b, self.c)) - print("ep", self.ep) - print("reg", self.reg) - - def setEndpoint(self, lrFlag, site): - self.ep[lrFlag] = site - if self.ep[Edge.RE - lrFlag] is None: - return False - return True - - @staticmethod - def bisect(s1, s2): - newedge = Edge() - newedge.reg[0] = s1 # store the sites that this edge is bisecting - newedge.reg[1] = s2 - - # to begin with, there are no endpoints on the bisector - it goes to infinity - # ep[0] and ep[1] are None - - # get the difference in x dist between the sites - dx = float(s2.x - s1.x) - dy = float(s2.y - s1.y) - adx = abs(dx) # make sure that the difference in positive - ady = abs(dy) - - # get the slope of the line - newedge.c = float(s1.x * dx + s1.y * dy + (dx * dx + dy * dy) * 0.5) - if adx > ady: - # set formula of line, with x fixed to 1 - newedge.a = 1.0 - newedge.b = dy / dx - newedge.c /= dx - else: - # set formula of line, with y fixed to 1 - newedge.b = 1.0 - newedge.a = dx / dy - newedge.c /= dy - - newedge.edgenum = Edge.EDGE_NUM - Edge.EDGE_NUM += 1 - return newedge - - -class Halfedge(object): - - def __init__(self, edge=None, pm=Edge.LE): - self.left = None # left Halfedge in the edge list - self.right = None # right Halfedge in the edge list - self.qnext = None # priority queue linked list pointer - self.edge = edge # edge list Edge - self.pm = pm - self.vertex = None # Site() - self.ystar = BIG_FLOAT - - def dump(self): - print("Halfedge--------------------------") - print("left: ", self.left) - print("right: ", self.right) - print("edge: ", self.edge) - print("pm: ", self.pm) - print("vertex: "), - if self.vertex: - self.vertex.dump() - else: - print("None") - print("ystar: ", self.ystar) - - def __lt__(self, other): - if self.ystar < other.ystar: - return True - elif self.ystar > other.ystar: - return False - elif self.vertex.x < other.vertex.x: - return True - elif self.vertex.x > other.vertex.x: - return False - else: - return False - - def __eq__(self, other): - if self.ystar == other.ystar and self.vertex.x == other.vertex.x: - return True - - def leftreg(self, default): - if not self.edge: - return default - elif self.pm == Edge.LE: - return self.edge.reg[Edge.LE] - else: - return self.edge.reg[Edge.RE] - - def rightreg(self, default): - if not self.edge: - return default - elif self.pm == Edge.LE: - return self.edge.reg[Edge.RE] - else: - return self.edge.reg[Edge.LE] - - # returns True if p is to right of halfedge self - def isPointRightOf(self, pt): - e = self.edge - topsite = e.reg[1] - right_of_site = pt.x > topsite.x - - if(right_of_site and self.pm == Edge.LE): - return True - - if(not right_of_site and self.pm == Edge.RE): - return False - - if(e.a == 1.0): - dyp = pt.y - topsite.y - dxp = pt.x - topsite.x - fast = 0 - if ((not right_of_site and e.b < 0.0) or (right_of_site and e.b >= 0.0)): - above = dyp >= e.b * dxp - fast = above - else: - above = pt.x + pt.y * e.b > e.c - if(e.b < 0.0): - above = not above - if (not above): - fast = 1 - if (not fast): - dxs = topsite.x - (e.reg[0]).x - above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.b * e.b) - if(e.b < 0.0): - above = not above - else: # e.b == 1.0 - yl = e.c - e.a * pt.x - t1 = pt.y - yl - t2 = pt.x - topsite.x - t3 = yl - topsite.y - above = t1 * t1 > t2 * t2 + t3 * t3 - - if(self.pm == Edge.LE): - return above - else: - return not above - - # create a new site where the Halfedges el1 and el2 intersect - def intersect(self, other): - e1 = self.edge - e2 = other.edge - if (e1 is None) or (e2 is None): - return None - - # if the two edges bisect the same parent return None - if e1.reg[1] is e2.reg[1]: - return None - - d = e1.a * e2.b - e1.b * e2.a - if isEqual(d, 0.0): - return None - - xint = (e1.c * e2.b - e2.c * e1.b) / d - yint = (e2.c * e1.a - e1.c * e2.a) / d - if e1.reg[1] < e2.reg[1]: - he = self - e = e1 - else: - he = other - e = e2 - - rightOfSite = xint >= e.reg[1].x - if((rightOfSite and he.pm == Edge.LE) or - (not rightOfSite and he.pm == Edge.RE)): - return None - - # create a new site at the point of intersection - this is a new - # vector event waiting to happen - return Site(xint, yint) - - -class EdgeList(object): - - def __init__(self, xmin, xmax, nsites): - if xmin > xmax: - xmin, xmax = xmax, xmin - self.hashsize = int(2 * math.sqrt(nsites + 4)) - - self.xmin = xmin - self.deltax = float(xmax - xmin) - self.hash = [None] * self.hashsize - - self.leftend = Halfedge() - self.rightend = Halfedge() - self.leftend.right = self.rightend - self.rightend.left = self.leftend - self.hash[0] = self.leftend - self.hash[-1] = self.rightend - - def insert(self, left, he): - he.left = left - he.right = left.right - left.right.left = he - left.right = he - - def delete(self, he): - he.left.right = he.right - he.right.left = he.left - he.edge = Edge.DELETED - - # Get entry from hash table, pruning any deleted nodes - def gethash(self, b): - if(b < 0 or b >= self.hashsize): - return None - he = self.hash[b] - if he is None or he.edge is not Edge.DELETED: - return he - - # Hash table points to deleted half edge. Patch as necessary. - self.hash[b] = None - return None - - def leftbnd(self, pt): - # Use hash table to get close to desired halfedge - bucket = int(((pt.x - self.xmin) / self.deltax * self.hashsize)) - - if(bucket < 0): - bucket = 0 - - if(bucket >= self.hashsize): - bucket = self.hashsize - 1 - - he = self.gethash(bucket) - if(he is None): - i = 1 - while True: - he = self.gethash(bucket - i) - if (he is not None): - break - he = self.gethash(bucket + i) - if (he is not None): - break - i += 1 - - # Now search linear list of halfedges for the correct one - if (he is self.leftend) or (he is not self.rightend and he.isPointRightOf(pt)): - he = he.right - while he is not self.rightend and he.isPointRightOf(pt): - he = he.right - he = he.left - else: - he = he.left - while (he is not self.leftend and not he.isPointRightOf(pt)): - he = he.left - - # Update hash table and reference counts - if(bucket > 0 and bucket < self.hashsize - 1): - self.hash[bucket] = he - return he - - -class PriorityQueue(object): - - def __init__(self, ymin, ymax, nsites): - self.ymin = ymin - self.deltay = ymax - ymin - self.hashsize = int(4 * math.sqrt(nsites)) - self.count = 0 - self.minidx = 0 - self.hash = [] - for i in range(self.hashsize): - self.hash.append(Halfedge()) - - def __len__(self): - return self.count - - def isEmpty(self): - return self.count == 0 - - def insert(self, he, site, offset): - he.vertex = site - he.ystar = site.y + offset - last = self.hash[self.getBucket(he)] - next = last.qnext - while((next is not None) and he > next): - last = next - next = last.qnext - he.qnext = last.qnext - last.qnext = he - self.count += 1 - - def delete(self, he): - if (he.vertex is not None): - last = self.hash[self.getBucket(he)] - while last.qnext is not he: - last = last.qnext - last.qnext = he.qnext - self.count -= 1 - he.vertex = None - - def getBucket(self, he): - bucket = int(((he.ystar - self.ymin) / self.deltay) * self.hashsize) - if bucket < 0: - bucket = 0 - if bucket >= self.hashsize: - bucket = self.hashsize - 1 - if bucket < self.minidx: - self.minidx = bucket - return bucket - - def getMinPt(self): - while(self.hash[self.minidx].qnext is None): - self.minidx += 1 - he = self.hash[self.minidx].qnext - x = he.vertex.x - y = he.ystar - return Site(x, y) - - def popMinHalfedge(self): - curr = self.hash[self.minidx].qnext - self.hash[self.minidx].qnext = curr.qnext - self.count -= 1 - return curr - - -class SiteList(object): - - def __init__(self, pointList): - self.__sites = [] - self.__sitenum = 0 - - self.__xmin = min([pt.x for pt in pointList]) - self.__ymin = min([pt.y for pt in pointList]) - self.__xmax = max([pt.x for pt in pointList]) - self.__ymax = max([pt.y for pt in pointList]) - self.__extent = (self.__xmin, self.__xmax, self.__ymin, self.__ymax) - - for i, pt in enumerate(pointList): - self.__sites.append(Site(pt.x, pt.y, i)) - self.__sites.sort() - - def setSiteNumber(self, site): - site.sitenum = self.__sitenum - self.__sitenum += 1 - - class Iterator(object): - - def __init__(this, lst): - this.generator = (s for s in lst) - - def __iter__(this): - return this - - def next(this): - try: - # Note: Blender is Python 3.x so no need for 2.x checks - return this.generator.__next__() - except StopIteration: - return None - - def iterator(self): - return SiteList.Iterator(self.__sites) - - def __iter__(self): - return SiteList.Iterator(self.__sites) - - def __len__(self): - return len(self.__sites) - - def _getxmin(self): - return self.__xmin - - def _getymin(self): - return self.__ymin - - def _getxmax(self): - return self.__xmax - - def _getymax(self): - return self.__ymax - - def _getextent(self): - return self.__extent - - xmin = property(_getxmin) - ymin = property(_getymin) - xmax = property(_getxmax) - ymax = property(_getymax) - extent = property(_getextent) - - -def computeVoronoiDiagram(points, xBuff=0, yBuff=0, polygonsOutput=False, - formatOutput=False, closePoly=True): - """ - Takes : - - a list of point objects (which must have x and y fields). - - x and y buffer values which are the expansion percentages of the bounding box - rectangle including all input points. - Returns : - - With default options : - A list of 2-tuples, representing the two points of each Voronoi diagram edge. - Each point contains 2-tuples which are the x,y coordinates of point. - if formatOutput is True, returns : - - a list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices. - - and a list of 2-tuples (v1, v2) representing edges of the Voronoi diagram. - v1 and v2 are the indices of the vertices at the end of the edge. - - If polygonsOutput option is True, returns : - A dictionary of polygons, keys are the indices of the input points, - values contains n-tuples representing the n points of each Voronoi diagram polygon. - Each point contains 2-tuples which are the x,y coordinates of point. - if formatOutput is True, returns : - - A list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices. - - and a dictionary of input points indices. Values contains n-tuples representing - the n points of each Voronoi diagram polygon. - Each tuple contains the vertex indices of the polygon vertices. - - if closePoly is True then, in the list of points of a polygon, last point will be the same of first point - """ - siteList = SiteList(points) - context = Context() - voronoi(siteList, context) - context.setClipBuffer(xBuff, yBuff) - if not polygonsOutput: - clipEdges = context.getClipEdges() - if formatOutput: - vertices, edgesIdx = formatEdgesOutput(clipEdges) - return vertices, edgesIdx - else: - return clipEdges - else: - clipPolygons = context.getClipPolygons(closePoly) - if formatOutput: - vertices, polyIdx = formatPolygonsOutput(clipPolygons) - return vertices, polyIdx - else: - return clipPolygons - - -def formatEdgesOutput(edges): - # get list of points - pts = [] - for edge in edges: - pts.extend(edge) - # get unique values - pts = set(pts) # unique values (tuples are hashable) - # get dict {values:index} - valuesIdxDict = dict(zip(pts, range(len(pts)))) - # get edges index reference - edgesIdx = [] - for edge in edges: - edgesIdx.append([valuesIdxDict[pt] for pt in edge]) - return list(pts), edgesIdx - - -def formatPolygonsOutput(polygons): - # get list of points - pts = [] - for poly in polygons.values(): - pts.extend(poly) - # get unique values - pts = set(pts) # unique values (tuples are hashable) - # get dict {values:index} - valuesIdxDict = dict(zip(pts, range(len(pts)))) - # get polygons index reference - polygonsIdx = {} - for inPtsIdx, poly in polygons.items(): - polygonsIdx[inPtsIdx] = [valuesIdxDict[pt] for pt in poly] - return list(pts), polygonsIdx - - -def computeDelaunayTriangulation(points): - """ Takes a list of point objects (which must have x and y fields). - Returns a list of 3-tuples: the indices of the points that form a - Delaunay triangle. - """ - siteList = SiteList(points) - context = Context() - context.triangulate = True - voronoi(siteList, context) - return context.triangles diff --git a/add_advanced_objects_panels/__init__.py b/add_advanced_objects_panels/__init__.py deleted file mode 100644 index 9e2cb0304bc23f94db234b3a31ca23ac954f4d76..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/__init__.py +++ /dev/null @@ -1,515 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# Contributed to by: -# 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 # -# Atom, Dannyboy, Mano-Wii, Kursad Karatas, teldredge, Phil Cote # - -bl_info = { - "name": "Add Advanced Object Panels", - "author": "meta-androcto", - "version": (1, 1, 5), - "blender": (2, 77, 0), - "description": "Individual Create Panel Activation List", - "location": "Addons Preferences", - "warning": "", - "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6" - "/Py/Scripts/Object/Add_Advanced", - "category": "Object" - } - -import bpy -from bpy.types import ( - AddonPreferences, - PropertyGroup, - ) -from bpy.props import ( - BoolProperty, - BoolVectorProperty, - EnumProperty, - FloatProperty, - FloatVectorProperty, - IntProperty, - StringProperty, - PointerProperty, - ) - -sub_modules_names = ( - "drop_to_ground", - "object_laplace_lightning", - "object_mangle_tools", - "unfold_transition", - "delaunay_voronoi", - "oscurart_constellation", - ) - - -sub_modules = [__import__(__package__ + "." + submod, {}, {}, submod) for submod in sub_modules_names] -sub_modules.sort(key=lambda mod: (mod.bl_info['category'], mod.bl_info['name'])) - - -# Add-ons Preferences -def _get_pref_class(mod): - import inspect - - for obj in vars(mod).values(): - if inspect.isclass(obj) and issubclass(obj, PropertyGroup): - if hasattr(obj, 'bl_idname') and obj.bl_idname == mod.__name__: - return obj - - -def get_addon_preferences(name=''): - """Acquisition and registration""" - addons = bpy.context.preferences.addons - if __name__ not in addons: # wm.read_factory_settings() - return None - addon_prefs = addons[__name__].preferences - if name: - if not hasattr(addon_prefs, name): - for mod in sub_modules: - if mod.__name__.split('.')[-1] == name: - cls = _get_pref_class(mod) - if cls: - prop = PointerProperty(type=cls) - setattr(AdvancedObjPreferences1, name, prop) - bpy.utils.unregister_class(AdvancedObjPreferences1) - bpy.utils.register_class(AdvancedObjPreferences1) - return getattr(addon_prefs, name, None) - else: - return addon_prefs - - -def register_submodule(mod): - if not hasattr(mod, '__addon_enabled__'): - mod.__addon_enabled__ = False - if not mod.__addon_enabled__: - mod.register() - mod.__addon_enabled__ = True - - -def unregister_submodule(mod): - if mod.__addon_enabled__: - mod.unregister() - mod.__addon_enabled__ = False - - prefs = get_addon_preferences() - name = mod.__name__.split('.')[-1] - if hasattr(AdvancedObjPreferences1, name): - delattr(AdvancedObjPreferences1, name) - if prefs: - bpy.utils.unregister_class(AdvancedObjPreferences1) - bpy.utils.register_class(AdvancedObjPreferences1) - if name in prefs: - del prefs[name] - - -def enable_all_modules(self, context): - for mod in sub_modules: - mod_name = mod.__name__.split('.')[-1] - setattr(self, 'use_' + mod_name, False) - if not mod.__addon_enabled__: - setattr(self, 'use_' + mod_name, True) - mod.__addon_enabled__ = True - - return None - - -def disable_all_modules(self, context): - for mod in sub_modules: - mod_name = mod.__name__.split('.')[-1] - - if mod.__addon_enabled__: - setattr(self, 'use_' + mod_name, False) - mod.__addon_enabled__ = False - - return None - - -class AdvancedObjPreferences1(AddonPreferences): - bl_idname = __name__ - - enable_all: BoolProperty( - name="Enable all", - description="Enable all Advanced Objects' Panels", - default=False, - update=enable_all_modules - ) - disable_all: BoolProperty( - name="Disable all", - description="Disable all Advanced Objects' Panels", - default=False, - update=disable_all_modules - ) - - def draw(self, context): - layout = self.layout - split = layout.split(percentage=0.5, align=True) - row = split.row() - row.alignment = "LEFT" - sub_box = row.box() - sub_box.prop(self, "enable_all", emboss=False, - icon="VISIBLE_IPO_ON", icon_only=True) - row.label(text="Enable All") - - row = split.row() - row.alignment = "RIGHT" - row.label(text="Disable All") - sub_box = row.box() - sub_box.prop(self, "disable_all", emboss=False, - icon="VISIBLE_IPO_OFF", icon_only=True) - - for mod in sub_modules: - mod_name = mod.__name__.split('.')[-1] - info = mod.bl_info - column = layout.column() - box = column.box() - - # first stage - expand = getattr(self, 'show_expanded_' + mod_name) - icon = 'TRIA_DOWN' if expand else 'TRIA_RIGHT' - col = box.column() - row = col.row() - sub = row.row() - sub.context_pointer_set('addon_prefs', self) - op = sub.operator('wm.context_toggle', text='', icon=icon, - emboss=False) - op.data_path = 'addon_prefs.show_expanded_' + mod_name - sub.label(text='{}: {}'.format(info['category'], info['name'])) - sub = row.row() - sub.alignment = 'RIGHT' - if info.get('warning'): - sub.label(text='', icon='ERROR') - sub.prop(self, 'use_' + mod_name, text='') - - # The second stage - if expand: - if info.get('description'): - split = col.row().split(percentage=0.15) - split.label(text='Description:') - split.label(text=info['description']) - if info.get('location'): - split = col.row().split(percentage=0.15) - split.label(text='Location:') - split.label(text=info['location']) - if info.get('author'): - split = col.row().split(percentage=0.15) - split.label(text='Author:') - split.label(text=info['author']) - if info.get('version'): - split = col.row().split(percentage=0.15) - split.label(text='Version:') - split.label(text='.'.join(str(x) for x in info['version']), - translate=False) - if info.get('warning'): - split = col.row().split(percentage=0.15) - split.label(text='Warning:') - split.label(text=' ' + info['warning'], icon='ERROR') - - tot_row = int(bool(info.get('wiki_url'))) - if tot_row: - split = col.row().split(percentage=0.15) - split.label(text='Internet:') - if info.get('wiki_url'): - op = split.operator('wm.url_open', - text='Documentation', icon='HELP') - op.url = info.get('wiki_url') - for i in range(4 - tot_row): - split.separator() - - # Details and settings - if getattr(self, 'use_' + mod_name): - prefs = get_addon_preferences(mod_name) - - if prefs and hasattr(prefs, 'draw'): - box = box.column() - prefs.layout = box - try: - prefs.draw(context) - except: - import traceback - traceback.print_exc() - box.label(text="Error (see console)", icon="ERROR") - del prefs.layout - - row = layout.row() - row.label(text="End of Advanced Object Panels Activations", - icon="FILE_PARENT") - - -for mod in sub_modules: - info = mod.bl_info - mod_name = mod.__name__.split('.')[-1] - - def gen_update(mod): - def update(self, context): - if getattr(self, 'use_' + mod.__name__.split('.')[-1]): - if not mod.__addon_enabled__: - register_submodule(mod) - else: - if mod.__addon_enabled__: - unregister_submodule(mod) - return update - - prop = BoolProperty( - name=info['name'], - description=info.get('description', ''), - update=gen_update(mod), - ) - setattr(AdvancedObjPreferences1, 'use_' + mod_name, prop) - prop = BoolProperty() - setattr(AdvancedObjPreferences1, 'show_expanded_' + mod_name, prop) - - -class AdvancedObjProperties1(PropertyGroup): - - # 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="Initial 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 - ) - - -# Class list -classes = ( - AdvancedObjPreferences1, - AdvancedObjProperties1, - ) - - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - bpy.types.Scene.advanced_objects1 = PointerProperty( - type=AdvancedObjProperties1 - ) - - prefs = get_addon_preferences() - for mod in sub_modules: - if not hasattr(mod, '__addon_enabled__'): - mod.__addon_enabled__ = False - name = mod.__name__.split('.')[-1] - if getattr(prefs, 'use_' + name): - register_submodule(mod) - - -def unregister(): - for mod in sub_modules: - if mod.__addon_enabled__: - unregister_submodule(mod) - del bpy.types.Scene.advanced_objects1 - - for cls in reversed(classes): - bpy.utils.unregister_class(cls) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_panels/delaunay_voronoi.py b/add_advanced_objects_panels/delaunay_voronoi.py deleted file mode 100644 index fdb54a1729dc157cd2d4ce0e81dc4b12af1e7883..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/delaunay_voronoi.py +++ /dev/null @@ -1,344 +0,0 @@ -# -*- coding:utf-8 -*- - -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -bl_info = { - "name": "Delaunay Voronoi", - "description": "Points cloud Delaunay triangulation in 2.5D " - "(suitable for terrain modelling) or Voronoi diagram in 2D", - "author": "Domlysz, Oscurart", - "version": (1, 3), - "blender": (2, 70, 0), - "location": "3D View > Toolshelf > Create > Delaunay Voronoi", - "warning": "", - "wiki_url": "https://github.com/domlysz/BlenderGIS/wiki", - "category": "Add Mesh" - } - -import bpy -from .DelaunayVoronoi import ( - computeVoronoiDiagram, - computeDelaunayTriangulation, - ) -from bpy.types import ( - Operator, - Panel, - ) -from bpy.props import EnumProperty - -try: - from scipy.spatial import Delaunay - import bmesh - import numpy as np - HAS_SCIPY = True -except: - HAS_SCIPY = False - pass - - -# 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 - - -def unique(L): - """Return a list of unhashable elements in s, but without duplicates. - [[1, 2], [2, 3], [1, 2]] >>> [[1, 2], [2, 3]]""" - # For unhashable objects, you can sort the sequence and - # then scan from the end of the list, deleting duplicates as you go - nDupli = 0 - nZcolinear = 0 - # sort() brings the equal elements together; then duplicates - # are easy to weed out in a single pass - L.sort() - last = L[-1] - for i in range(len(L) - 2, -1, -1): - if last[:2] == L[i][:2]: # XY coordinates compararison - if last[2] == L[i][2]: # Z coordinates compararison - nDupli += 1 # duplicates vertices - else: # Z colinear - nZcolinear += 1 - del L[i] - else: - last = L[i] - # list data type is mutable, input list will automatically update - # and doesn't need to be returned - return (nDupli, nZcolinear) - - -def checkEqual(lst): - return lst[1:] == lst[:-1] - - -class ToolsPanelDelaunay(Panel): - bl_category = "Create" - bl_label = "Delaunay Voronoi" - bl_space_type = "VIEW_3D" - bl_context = "objectmode" - bl_region_type = "TOOLS" - bl_options = {"DEFAULT_CLOSED"} - - def draw(self, context): - layout = self.layout - - box = layout.box() - col = box.column(align=True) - col.label(text="Point Cloud:") - col.operator("delaunay.triangulation") - col.operator("voronoi.tesselation") - - -class OBJECT_OT_TriangulateButton(Operator): - bl_idname = "delaunay.triangulation" - bl_label = "Triangulation" - bl_description = ("Terrain points cloud Delaunay triangulation in 2.5D\n" - "Needs an existing Active Mesh Object") - bl_options = {"REGISTER", "UNDO"} - - @classmethod - def poll(cls, context): - obj = context.active_object - return (obj is not None and obj.type == "MESH") - - def execute(self, context): - # move the check into the poll - obj = context.active_object - - if HAS_SCIPY: - # Use scipy when present (~18 x faster) - bpy.ops.object.mode_set(mode='EDIT') - bm = bmesh.from_edit_mesh(obj.data) - points_3D = [list(v.co) for v in bm.verts] - points_2D = np.array([[v[0], v[1]] for v in points_3D]) - print("Triangulate " + str(len(points_3D)) + " points...") - # Triangulate - tri = Delaunay(points_2D) - faces = tri.simplices.tolist() - # Create new mesh structure - print("Create mesh...") - bpy.ops.object.mode_set(mode='OBJECT') - mesh = bpy.data.meshes.new("TIN") - mesh.from_pydata(points_3D, [], faces) - mesh.update(calc_edges=True) - my = bpy.data.objects.new("TIN", mesh) - context.collection.objects.link(my) - my.matrix_world = obj.matrix_world.copy() - obj.select_set(False) - my.select_set(True) - context.view_layer.objects.active = my - self.report({'INFO'}, "Mesh created (" + str(len(faces)) + " triangles)") - print("Total :%s faces %s verts" % (len(faces), len(points_3D))) - return {'FINISHED'} - - # Get points coordinates - 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) - - debug_prints(text=str(nDupli) + " duplicate points ignored") - debug_prints(str(nZcolinear) + " z colinear points excluded") - - if nVerts < 3: - 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'} - - # Triangulate - 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] - - debug_prints(text=str(len(triangles)) + " triangles") - - # Create new mesh structure - 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.collection.objects.link(tinObj) # Link object to collection - bpy.context.view_layer.objects.active = tinObj - tinObj.select_set(True) - obj.select_set(False) - - 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\n" - "Needs an existing Active Mesh Object") - bl_options = {"REGISTER", "UNDO"} - - meshType: EnumProperty( - 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" - ) - - @classmethod - def poll(cls, context): - obj = context.active_object - return (obj is not None and obj.type == "MESH") - - def execute(self, context): - # move the check into the poll - obj = context.active_object - - # Get points coordinates - 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) - - debug_prints(text=str(nDupli) + " duplicates points ignored") - debug_prints(text=str(nZcolinear) + " z colinear points excluded") - - if nVerts < 3: - 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({"WARNING"}, - "Points are colinear. Operation Cancelled") - - return {"CANCELLED"} - - # Create diagram - 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 - ) - else: - pts, polyIdx = computeVoronoiDiagram( - vertsPts, xbuff, ybuff, polygonsOutput=True, - formatOutput=True, closePoly=False - ) - - pts = [[pt[0], pt[1], zPosition] for pt in pts] - - # Create new mesh structure - voronoiDiagram = bpy.data.meshes.new("VoronoiDiagram") # create a new mesh - - if self.meshType == "Edges": - # Fill the mesh with triangles - voronoiDiagram.from_pydata(pts, edgesIdx, []) - else: - # Fill the mesh with triangles - voronoiDiagram.from_pydata(pts, [], list(polyIdx.values())) - - voronoiDiagram.update(calc_edges=True) # Update mesh with new data - # create an object with that mesh - voronoiObj = bpy.data.objects.new("VoronoiDiagram", voronoiDiagram) - # place object - voronoiObj.location = obj.location.copy() - voronoiObj.rotation_euler = r - voronoiObj.scale = s - - # update scene - bpy.context.collection.objects.link(voronoiObj) # Link object to collection - bpy.context.view_layer.objects.active = voronoiObj - voronoiObj.select_set(True) - obj.select_set(False) - - # Report - if self.meshType == "Edges": - self.report({"INFO"}, "Mesh created (" + str(len(edgesIdx)) + " edges)") - else: - self.report({"INFO"}, "Mesh created (" + str(len(polyIdx)) + " polygons)") - - return {'FINISHED'} - - -# Register -def register(): - bpy.utils.register_class(OBJECT_OT_VoronoiButton) - bpy.utils.register_class(OBJECT_OT_TriangulateButton) - bpy.utils.register_class(ToolsPanelDelaunay) - - -def unregister(): - bpy.utils.unregister_class(OBJECT_OT_VoronoiButton) - bpy.utils.unregister_class(OBJECT_OT_TriangulateButton) - bpy.utils.unregister_class(ToolsPanelDelaunay) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_panels/drop_to_ground.py b/add_advanced_objects_panels/drop_to_ground.py deleted file mode 100644 index c9d9a7b4130bfc0d52804874322ba122d3b731e6..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/drop_to_ground.py +++ /dev/null @@ -1,378 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -bl_info = { - "name": "Drop to Ground1", - "author": "Unnikrishnan(kodemax), Florian Meyer(testscreenings)", - "blender": (2, 71, 0), - "location": "3D View > Toolshelf > Create > Drop To Ground", - "description": "Drop selected objects on active object", - "warning": "", - "category": "Object"} - - -import bpy -import bmesh -from mathutils import ( - Vector, - Matrix, - ) -from bpy.types import ( - Operator, - Panel, - ) -from bpy.props import BoolProperty - - -def test_ground_object(ground): - if ground.type in {'MESH', 'FONT', 'META', 'CURVE', 'SURFACE'}: - return True - return False - - -def get_align_matrix(location, normal): - up = Vector((0, 0, 1)) - angle = normal.angle(up) - axis = up.cross(normal) - mat_rot = Matrix.Rotation(angle, 4, axis) - mat_loc = Matrix.Translation(location) - mat_align = mat_rot * mat_loc - return mat_align - - -def transform_ground_to_world(sc, ground): - tmpMesh = ground.to_mesh(sc, True, 'PREVIEW') - tmpMesh.transform(ground.matrix_world) - tmp_ground = bpy.data.objects.new('tmpGround', tmpMesh) - sc.objects.link(tmp_ground) - sc.update() - - return tmp_ground - - -def get_lowest_world_co_from_mesh(ob, mat_parent=None): - bme = bmesh.new() - bme.from_mesh(ob.data) - mat_to_world = ob.matrix_world.copy() - if mat_parent: - mat_to_world = mat_parent * mat_to_world - lowest = None - for v in bme.verts: - if not lowest: - lowest = v - if (mat_to_world * v.co).z < (mat_to_world * lowest.co).z: - lowest = v - lowest_co = mat_to_world * lowest.co - bme.free() - - return lowest_co - - -def get_lowest_world_co(context, ob, mat_parent=None): - if ob.type == 'MESH': - return get_lowest_world_co_from_mesh(ob) - - elif ob.type == 'EMPTY' and ob.instance_type == 'COLLECTION': - if not ob.instance_collection: - return None - - else: - lowest_co = None - for ob_l in ob.instance_collection.objects: - if ob_l.type == 'MESH': - lowest_ob_l = get_lowest_world_co_from_mesh(ob_l, ob.matrix_world) - if not lowest_co: - lowest_co = lowest_ob_l - if lowest_ob_l.z < lowest_co.z: - lowest_co = lowest_ob_l - - return lowest_co - - -def drop_objectsall(self, context): - ground = context.active_object - name = ground.name - - for obs in bpy.context.scene.objects: - obs.select_set(True) - if obs.name == name: - obs.select_set(False) - - obs2 = context.selected_objects - - tmp_ground = transform_ground_to_world(context.scene, ground) - down = Vector((0, 0, -10000)) - - for ob in obs2: - if self.use_origin: - lowest_world_co = ob.location - else: - lowest_world_co = get_lowest_world_co(context, ob) - - if not lowest_world_co: - message = "Object {} is of type {} works only with Use Center option " \ - "checked".format(ob.name, ob.type) - 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: - message = ob.name + " did not hit the Ground" - self.reported.append(message) - continue - - # simple drop down - to_ground_vec = hit_location - lowest_world_co - ob.location += to_ground_vec - - # drop with align to hit normal - if self.align: - to_center_vec = ob.location - hit_location # vec: hit_loc to origin - # rotate object to align with face normal - mat_normal = get_align_matrix(hit_location, hit_normal) - rot_euler = mat_normal.to_euler() - mat_ob_tmp = ob.matrix_world.copy().to_3x3() - mat_ob_tmp.rotate(rot_euler) - mat_ob_tmp = mat_ob_tmp.to_4x4() - ob.matrix_world = mat_ob_tmp - # move_object to hit_location - ob.location = hit_location - # move object above surface again - to_center_vec.rotate(rot_euler) - ob.location += to_center_vec - - # cleanup - bpy.ops.object.select_all(action='DESELECT') - tmp_ground.select_set(True) - bpy.ops.object.delete('EXEC_DEFAULT') - for ob in obs2: - ob.select_set(True) - ground.select_set(True) - - -def drop_objects(self, context): - ground = context.active_object - - obs = context.selected_objects - if ground in obs: - obs.remove(ground) - - tmp_ground = transform_ground_to_world(context.scene, ground) - down = Vector((0, 0, -10000)) - - for ob in obs: - if self.use_origin: - lowest_world_co = ob.location - else: - lowest_world_co = get_lowest_world_co(context, ob) - - if not lowest_world_co: - message = "Object {} is of type {} works only with Use Center option " \ - "checked".format(ob.name, ob.type) - 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: - message = ob.name + " did not hit the Active Object" - self.reported.append(message) - continue - - # simple drop down - to_ground_vec = hit_location - lowest_world_co - ob.location += to_ground_vec - - # drop with align to hit normal - if self.align: - to_center_vec = ob.location - hit_location # vec: hit_loc to origin - # rotate object to align with face normal - mat_normal = get_align_matrix(hit_location, hit_normal) - rot_euler = mat_normal.to_euler() - mat_ob_tmp = ob.matrix_world.copy().to_3x3() - mat_ob_tmp.rotate(rot_euler) - mat_ob_tmp = mat_ob_tmp.to_4x4() - ob.matrix_world = mat_ob_tmp - # move_object to hit_location - ob.location = hit_location - # move object above surface again - to_center_vec.rotate(rot_euler) - ob.location += to_center_vec - - # cleanup - bpy.ops.object.select_all(action='DESELECT') - tmp_ground.select_set(True) - bpy.ops.object.delete('EXEC_DEFAULT') - for ob in obs: - ob.select_set(True) - ground.select_set(True) - - -# define base dummy class for inheritance -class DropBaseAtributes: - align: BoolProperty( - name="Align to ground", - description="Aligns the objects' rotation to the ground", - default=True) - use_origin: BoolProperty( - name="Use Origins", - description="Drop to objects' origins\n" - "Use this option for dropping all types of Objects", - default=False) - - -class OBJECT_OT_drop_to_ground(Operator, DropBaseAtributes): - bl_idname = "object.drop_on_active" - bl_label = "Drop to Ground" - bl_description = ("Drop selected objects on the Active object\n" - "Active Object has to be of following the types:\n" - "Mesh, Font, Metaball, Curve, Surface") - bl_options = {'REGISTER', 'UNDO'} - - reported = [] - - @classmethod - def poll(cls, context): - act_obj = context.active_object - return (len(context.selected_objects) >= 2 and - act_obj and test_ground_object(act_obj)) - - def execute(self, context): - drop_objects(self, context) - - if self.reported: - self.report({"INFO"}, - "Some objects could not be dropped (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, DropBaseAtributes): - bl_idname = "object.drop_all_active" - bl_label = "Drop All to Ground (Active Object)" - bl_description = ("Drop all other objects onto Active Object\n" - "Active Object has to be of following the types:\n" - "Mesh, Font, Metaball, Curve, Surface") - bl_options = {'REGISTER', 'UNDO'} - - reported = [] - - @classmethod - def poll(cls, context): - act_obj = context.active_object - return act_obj and test_ground_object(act_obj) - - def execute(self, context): - drop_objectsall(self, context) - - if self.reported: - self.report({"INFO"}, - "Some objects could not be dropped (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): - bl_idname = "help.drop" - bl_label = "Drop to Ground Help" - bl_description = "Clik for some information about Drop to Ground" - bl_options = {"REGISTER", "INTERNAL"} - - is_all: BoolProperty( - default=True, - options={"HIDDEN"} - ) - - def draw(self, context): - layout = self.layout - - layout.label(text="General Info:") - layout.label(text="The Active Object has to be of a Mesh, Font,") - layout.label(text="Metaball, Curve or Surface type and") - layout.label(text="be at the lowest Z location") - layout.label(text="The option Use Origins must be enabled to drop") - layout.label(text="objects that are not of a Mesh or DupliGroup type") - layout.label(text="The Active Object has to be big enough to catch them") - layout.label(text="To check that, use the Orthographic Top View") - layout.separator() - - layout.label(text="To use:") - - if self.is_all is False: - layout.label(text="Select objects to drop") - layout.label(text="Then Shift Select the object to be the ground") - layout.label(text="Drops Selected Object to the Active one") - else: - layout.label(text="Select the ground Mesh and press Drop all") - layout.label(text="The unselected Objects will be moved straight") - layout.label(text="down the Z axis, so they have to be above") - layout.label(text="the Selected / Active one to fall") - - def execute(self, context): - return {'FINISHED'} - - def invoke(self, context, event): - return context.window_manager.invoke_popup(self, width=300) - - -class Drop_Operator_Panel(Panel): - bl_label = "Drop To Ground" - bl_region_type = "TOOLS" - bl_space_type = "VIEW_3D" - bl_options = {'DEFAULT_CLOSED'} - bl_context = "objectmode" - bl_category = "Create" - - def draw(self, context): - layout = self.layout - - row = layout.split(percentage=0.8, align=True) - row.operator(OBJECT_OT_drop_to_ground.bl_idname, - text="Drop Selected") - row.operator("help.drop", text="", 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", text="", icon="LAYER_USED").is_all = True - - -# Register -def register(): - 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_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__": - register() diff --git a/add_advanced_objects_panels/object_laplace_lightning.py b/add_advanced_objects_panels/object_laplace_lightning.py deleted file mode 100644 index ece0640dd44445d2d0247883bd11df3b33ebf9ce..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/object_laplace_lightning.py +++ /dev/null @@ -1,1446 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -# NOTE: moved the winmgr properties to __init__ and scene -# search for context.scene.advanced_objects1 - -bl_info = { - "name": "Laplacian Lightning", - "author": "teldredge", - "blender": (2, 78, 0), - "location": "3D View > Toolshelf > Create > Laplacian Lightning", - "description": "Lightning mesh generator using laplacian growth algorithm", - "warning": "", - "category": "Object"} - -# BLENDER LAPLACIAN LIGHTNING -# teldredge -# www.funkboxing.com -# https://developer.blender.org/T27189 - -# using algorithm from -# FAST SIMULATION OF LAPLACIAN GROWTH (FSLG) -# http://gamma.cs.unc.edu/FRAC/ - -# and a few ideas ideas from -# FAST ANIMATION OF LIGHTNING USING AN ADAPTIVE MESH (FALUAM) -# http://gamma.cs.unc.edu/FAST_LIGHTNING/ - - -""" ------ RELEASE LOG/NOTES/PONTIFICATIONS ----- -v0.1.0 - 04.11.11 - basic generate functions and UI - object creation report (Custom Properties: FSLG_REPORT) -v0.2.0 - 04.15.11 - started spelling laplacian right. - add curve function (not in UI) ...twisting problem - classify stroke by MAIN path, h-ORDER paths, TIP paths - jitter cells for mesh creation - add materials if present -v0.2.1 - 04.16.11 - mesh classification speedup -v0.2.2 - 04.21.11 - fxns to write/read array to file - restrict growth to insulator cells (object bounding box) - origin/ground defineable by object - gridunit more like 'resolution' -v0.2.3 - 04.24.11 - cloud attractor object (termintates loop if hit) - secondary path orders (hOrder) disabled in UI (set to 1) -v0.2.4 - 04.26.11 - fixed object selection in UI - will not run if required object not selected - moved to view 3d > toolbox -v0.2.5 - 05.08.11 - testing for 2.57b - single mesh output (for build modifier) - speedups (dist fxn) -v0.2.6 - 06.20.11 - scale/pos on 'write to cubes' works now - if origin obj is mesh, uses all verts as initial charges - semi-helpful tooltips - speedups, faster dedupe fxn, faster classification - use any shape mesh obj as insulator mesh - must have rot=0, scale=1, origin set to geometry - often fails to block bolt with curved/complex shapes - separate single and multi mesh creation -v0.2.7 - 01.05.13 - fixed the issue that prevented enabling the add-on - fixed makeMeshCube fxn - disabled visualization for voxels - -v0.x - - -prevent create_setup_objects from generating duplicates - -fix vis fxn to only buildCPGraph once for VM or VS - -improve list fxns (rid of ((x,y,z),w) and use (x,y,z,w)), use 'sets' - -create python cmodule for a few of most costly fxns - i have pretty much no idea how to do this yet - -cloud and insulator can be groups of MESH objs - -text output, possibly to save on interrupt, allow continue from text - -?hook modifiers from tips->sides->main, weight w/ vert groups - -user defined 'attractor' path - -fix add curve function - -animated arcs via. ionization path - -environment map boundary conditions - requires Eqn. 15 from FSLG. - -assign wattage at each segment for HDRI - -?default settings for -lightning, -teslacoil, -spark/arc - -fix hOrder functionality - -multiple 'MAIN' brances for non-lightning discharges - -n-symmetry option, create mirror images, snowflakes, etc... -""" - -import bpy -import time -import random -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 -# 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) - - -def within(x, y, d): - # CHECK IF x - d <= y <= x + d - if x - d <= y and x + d >= y: - return True - else: - return False - - -def dist(ax, ay, az, bx, by, bz): - dv = Vector((ax, ay, az)) - Vector((bx, by, bz)) - d = dv.length - return d - - -def splitList(aList, idx): - ll = [] - for x in aList: - ll.append(x[idx]) - return ll - - -def splitListCo(aList): - ll = [] - for p in aList: - ll.append((p[0], p[1], p[2])) - return ll - - -def getLowHigh(aList): - tLow = aList[0] - tHigh = aList[0] - for a in aList: - if a < tLow: - tLow = a - if a > tHigh: - tHigh = a - return tLow, tHigh - - -def weightedRandomChoice(aList): - tL = [] - tweight = 0 - for a in range(len(aList)): - idex = a - weight = aList[a] - if weight > 0.0: - tweight += weight - tL.append((tweight, idex)) - i = bisect.bisect(tL, (random.uniform(0, tweight), None)) - r = tL[i][1] - return r - - -def getStencil3D_26(x, y, z): - nL = [] - for xT in range(x - 1, x + 2): - for yT in range(y - 1, y + 2): - for zT in range(z - 1, z + 2): - nL.append((xT, yT, zT)) - nL.remove((x, y, z)) - return nL - - -def jitterCells(aList, jit): - j = jit / 2 - bList = [] - for a in aList: - ax = a[0] + random.uniform(-j, j) - ay = a[1] + random.uniform(-j, j) - az = a[2] + random.uniform(-j, j) - bList.append((ax, ay, az)) - return bList - - -def deDupe(seq, idfun=None): - # Thanks to this guy - http://www.peterbe.com/plog/uniqifiers-benchmark - if idfun is None: - def idfun(x): - return x - seen = {} - result = [] - for item in seq: - marker = idfun(item) - if marker in seen: - continue - seen[marker] = 1 - result.append(item) - return result - - -# Visulization functions - -def writeArrayToVoxel(arr, filename): - gridS = 64 - half = int(gridS / 2) - bitOn = 255 - aGrid = [[[0 for z in range(gridS)] for y in range(gridS)] for x in range(gridS)] - for a in arr: - try: - aGrid[a[0] + half][a[1] + half][a[2] + half] = bitOn - except: - debug_prints(func="writeArrayToVoxel", text="Particle beyond voxel domain") - - file = open(filename, "wb") - for z in range(gridS): - for y in range(gridS): - for x in range(gridS): - file.write(struct.pack('B', aGrid[x][y][z])) - file.flush() - file.close() - - -def writeArrayToFile(arr, filename): - file = open(filename, "w") - for a in arr: - tstr = str(a[0]) + ',' + str(a[1]) + ',' + str(a[2]) + '\n' - file.write(tstr) - file.close - - -def readArrayFromFile(filename): - file = open(filename, "r") - arr = [] - for f in file: - pt = f[0:-1].split(',') - arr.append((int(pt[0]), int(pt[1]), int(pt[2]))) - return arr - - -def makeMeshCube_OLD(msize): - msize = msize / 2 - mmesh = bpy.data.meshes.new('q') - mmesh.vertices.add(8) - mmesh.vertices[0].co = [-msize, -msize, -msize] - mmesh.vertices[1].co = [-msize, msize, -msize] - mmesh.vertices[2].co = [msize, msize, -msize] - mmesh.vertices[3].co = [msize, -msize, -msize] - mmesh.vertices[4].co = [-msize, -msize, msize] - mmesh.vertices[5].co = [-msize, msize, msize] - mmesh.vertices[6].co = [msize, msize, msize] - mmesh.vertices[7].co = [msize, -msize, msize] - mmesh.faces.add(6) - mmesh.faces[0].vertices_raw = [0, 1, 2, 3] - mmesh.faces[1].vertices_raw = [0, 4, 5, 1] - mmesh.faces[2].vertices_raw = [2, 1, 5, 6] - mmesh.faces[3].vertices_raw = [3, 2, 6, 7] - mmesh.faces[4].vertices_raw = [0, 3, 7, 4] - mmesh.faces[5].vertices_raw = [5, 4, 7, 6] - mmesh.update(calc_edges=True) - - return(mmesh) - - -def makeMeshCube(msize): - m2 = msize / 2 - # 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) - ] - # Define mesh and object - mmesh = bpy.data.meshes.new("Cube") - - # Create mesh - mmesh.from_pydata(verts, [], faces) - mmesh.update(calc_edges=True) - return(mmesh) - - -def writeArrayToCubes(arr, gridBU, orig, cBOOL=False, jBOOL=True): - for a in arr: - x = a[0] - y = a[1] - z = a[2] - me = makeMeshCube(gridBU) - ob = bpy.data.objects.new('xCUBE', me) - 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 - col = (1.0, 1.0, 1.0, 1.0) - if a[3] == 0: - col = (0.0, 0.0, 0.0, 1.0) - if a[3] < 0: - col = (-a[3], 0.0, 0.0, 1.0) - if a[3] > 0: - col = (0.0, 0.0, a[3], 1.0) - ob.color = col - bpy.context.collection.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... - for q in bpy.context.scene.objects: - q.select_set(False) - if q.name[0:5] == 'xCUBE': - q.select_set(True) - bpy.context.view_layer.objects.active = q - - -def addVert(ob, pt, conni=-1): - mmesh = ob.data - mmesh.vertices.add(1) - vcounti = len(mmesh.vertices) - 1 - mmesh.vertices[vcounti].co = [pt[0], pt[1], pt[2]] - if conni > -1: - mmesh.edges.add(1) - ecounti = len(mmesh.edges) - 1 - mmesh.edges[ecounti].vertices = [conni, vcounti] - mmesh.update() - - -def addEdge(ob, va, vb): - mmesh = ob.data - mmesh.edges.add(1) - ecounti = len(mmesh.edges) - 1 - mmesh.edges[ecounti].vertices = [va, vb] - mmesh.update() - - -def newMesh(mname): - mmesh = bpy.data.meshes.new(mname) - omesh = bpy.data.objects.new(mname, mmesh) - bpy.context.collection.objects.link(omesh) - return omesh - - -def writeArrayToMesh(mname, arr, gridBU, rpt=None): - mob = newMesh(mname) - mob.scale = (gridBU, gridBU, gridBU) - if rpt: - addReportProp(mob, rpt) - addVert(mob, arr[0], -1) - for ai in range(1, len(arr)): - a = arr[ai] - addVert(mob, a, ai - 1) - return mob - - -# 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 - cur.use_fill_back = False - cur.bevel_depth = bd - cur.bevel_resolution = 2 - cob = bpy.data.objects.new(cname, cur) - cob.scale = (gridBU, gridBU, gridBU) - - if rpt: - addReportProp(cob, rpt) - bpy.context.collection.objects.link(cob) - cur.splines.new('BEZIER') - cspline = cur.splines[0] - 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] - if a - 1 < 0: - hL = arr[a] - else: - hx = arr[a][0] - ((arr[a][0] - arr[a - 1][0]) / div) - hy = arr[a][1] - ((arr[a][1] - arr[a - 1][1]) / div) - hz = arr[a][2] - ((arr[a][2] - arr[a - 1][2]) / div) - hL = (hx, hy, hz) - - if a + 1 > len(arr) - 1: - hR = arr[a] - else: - hx = arr[a][0] + ((arr[a + 1][0] - arr[a][0]) / div) - hy = arr[a][1] + ((arr[a + 1][1] - arr[a][1]) / div) - hz = arr[a][2] + ((arr[a + 1][2] - arr[a][2]) / div) - hR = (hx, hy, hz) - bp.co = arr[a] - bp.handle_left = hL - bp.handle_right = hR - - -def addArrayToMesh(mob, arr): - addVert(mob, arr[0], -1) - mmesh = mob.data - vcounti = len(mmesh.vertices) - 1 - for ai in range(1, len(arr)): - a = arr[ai] - addVert(mob, a, len(mmesh.vertices) - 1) - - -def addMaterial(ob, matname): - mat = bpy.data.materials[matname] - ob.active_material = mat - - -def writeStokeToMesh(arr, jarr, MAINi, HORDERi, TIPSi, orig, gs, rpt=None): - # 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 - for hOi in range(len(HORDERi)): - debug_prints(func="writeStokeToMesh", text="Writing order", var=hOi) - hO = HORDERi[hOi] - hob = newMesh('la1H' + str(hOi)) - - for y in hO: - llHO = [] - for x in y: - llHO.append(jarr[x]) - addArrayToMesh(hob, llHO) - hob.scale = (gs, gs, gs) - hob.location = orig - - # tips - debug_prints(func="writeStokeToMesh", text="Writing tip paths") - tob = newMesh('la2TIPS') - for y in TIPSi: - llt = [] - for x in y: - llt.append(jarr[x]) - addArrayToMesh(tob, llt) - tob.scale = (gs, gs, gs) - tob.location = orig - - # add materials to objects (if they exist) - try: - addMaterial(mob, 'edgeMAT-h0') - addMaterial(hob, 'edgeMAT-h1') - addMaterial(tob, 'edgeMAT-h2') - debug_prints(func="writeStokeToMesh", text="Added materials") - - except: - debug_prints(func="writeStokeToMesh", text="Materials not found") - - # add generation report to all meshes - if rpt: - addReportProp(mob, rpt) - addReportProp(hob, rpt) - addReportProp(tob, rpt) - - -def writeStokeToSingleMesh(arr, jarr, orig, gs, mct, rpt=None): - sgarr = buildCPGraph(arr, mct) - llALL = [] - - Aob = newMesh('laALL') - for pt in jarr: - addVert(Aob, pt) - for cpi in range(len(sgarr)): - ci = sgarr[cpi][0] - pi = sgarr[cpi][1] - addEdge(Aob, pi, ci) - Aob.location = orig - Aob.scale = ((gs, gs, gs)) - - if rpt: - addReportProp(Aob, rpt) - - -def visualizeArray(cg, oob, gs, vm, vs, vc, vv, rst): - winmgr = bpy.context.scene.advanced_objects1 - # IN: (cellgrid, origin, gridscale, - # mulimesh, single mesh, cubes, voxels, report string) - origin = oob.location - - # deal with vert multi-origins - oct = 2 - if oob.type == 'MESH': - oct = len(oob.data.vertices) - - # jitter cells - if vm or vs: - cjarr = jitterCells(cg, 1) - - if vm: # write array to multi mesh - - aMi, aHi, aTi = classifyStroke(cg, oct, winmgr.HORDER) - debug_prints(func="visualizeArray", text="Writing to multi-mesh") - writeStokeToMesh(cg, cjarr, aMi, aHi, aTi, origin, gs, rst) - debug_prints(func="visualizeArray", text="Multi-mesh written") - - if vs: # write to single mesh - debug_prints(func="visualizeArray", text="Writing to single mesh") - writeStokeToSingleMesh(cg, cjarr, origin, gs, oct, rst) - debug_prints(func="visualizeArray", text="Single mesh written") - - if vc: # write array to cube objects - debug_prints(func="visualizeArray", text="Writing to cubes") - writeArrayToCubes(cg, gs, origin) - debug_prints(func="visualizeArray", text="Cubes written") - - 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) - - 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) - # writeArrayToCurves('laMAIN', llmain, .10, .25) - - -# 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 - sgarr = [] - sgarr.append((1, 0)) - - for ai in range(sti, len(arr)): - cs = arr[ai] - cpts = arr[0:ai] - cslap = getStencil3D_26(cs[0], cs[1], cs[2]) - - for nc in cslap: - ct = cpts.count(nc) - 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 - sgarr = [] - sgarr.append((1, 0)) - ctix = 0 - for ai in range(sti, len(arr)): - cs = arr[ai] - # cpts = arr[0:ai] - cpts = arr[ctix:ai] - cslap = getStencil3D_26(cs[0], cs[1], cs[2]) - for nc in cslap: - ct = cpts.count(nc) - if ct > 0: - # cti = cpts.index(nc) - cti = ctix + cpts.index(nc) - 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 encountered - cList = splitList(ngraph, 0) - pList = splitList(ngraph, 1) - aRi = [] - cNODE = fc - for x in range(len(ngraph)): - pNODE = pList[cList.index(cNODE)] - aRi.append(cNODE) - cNODE = pNODE - npNODECOUNT = cList.count(pNODE) - 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 [] - aRi.append(cNODE) - return aRi - else: - return [] - - -def findTips(arr): - lt = [] - for ai in arr[0: len(arr) - 1]: - a = ai[0] - cCOUNT = 0 - for bi in arr: - b = bi[1] - if a == b: - cCOUNT += 1 - if cCOUNT == 0: - lt.append(a) - - return lt - - -def findChannelRoots(path, ngraph, restrict=[]): - roots = [] - for ai in range(len(ngraph)): - chi = ngraph[ai][0] - par = ngraph[ai][1] - if par in path and chi not in path and chi not in restrict: - roots.append(par) - droots = deDupe(roots) - - return droots - - -def findChannels(roots, tips, ngraph, restrict): - cPATHS = [] - for ri in range(len(roots)): - r = roots[ri] - sL = 1 - sPATHi = [] - for ti in range(len(tips)): - t = tips[ti] - if t < r: - continue - tPATHi = findChargePath(r, t, ngraph, restrict, False) - tL = len(tPATHi) - if tL > sL: - if countChildrenOnPath(tPATHi, ngraph) > 1: - sL = tL - sPATHi = tPATHi - tTEMP = t - tiTEMP = ti - if len(sPATHi) > 0: - 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 - - -def findChannels_WORKINPROGRESS(roots, ttips, ngraph, restrict): - cPATHS = [] - tips = list(ttips) - for ri in range(len(roots)): - r = roots[ri] - sL = 1 - sPATHi = [] - tipREMOVE = [] # checked tip indexes, to be removed for next loop - for ti in range(len(tips)): - t = tips[ti] - if ti < ri: - continue - - tPATHi = findChargePath(r, t, ngraph, restrict, False) - tL = len(tPATHi) - if tL > sL: - if countChildrenOnPath(tPATHi, ngraph) > 1: - sL = tL - sPATHi = tPATHi - tTEMP = t - tiTEMP = ti - if tL > 0: - tipREMOVE.append(t) - if len(sPATHi) > 0: - 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) - - return cPATHS - - -def countChildrenOnPath(aPath, ngraph, quick=True): - # 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' -def classifyStroke(sarr, mct, hORDER=1): - debug_prints(func="classifyStroke", text="Classifying stroke") - # build child/parent graph (indexes of sarr) - sgarr = buildCPGraph(sarr, mct) - - # 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 - debug_prints(func="classifyStroke", text="Finding TIPS") - aTIPSi = findTips(sgarr) - - # find horder channel roots - # hcount = orders between 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 - p = curPATHSi[pi] - # get roots for this path - aHROOTSi = findChannelRoots(p, sgarr, hRESTRICT) - 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: - 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 - for hri in aHPATHSi: - hRESTRICT += hri - curPATHSi = aHPATHSi - - # side branches, final order of hierarchy - # 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: - for o in oH: - aDRAWNi += o - aTPATHSi = [] - for a in aTIPSi: - if a not in aDRAWNi: - aPATHi = findChargePath(oCharge, a, sgarr, aDRAWNi) - aDRAWNi += aPATHi - aTPATHSi.append(aPATHi) - - return aMAINi, allHPATHSi, aTPATHSi - - -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 - 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 - bbox = ob.bound_box - bbxL = bbox[0][0] - bbxR = bbox[4][0] - bbyL = bbox[0][1] - bbyR = bbox[2][1] - bbzL = bbox[0][2] - bbzR = bbox[1][2] - xct = int((bbxR - bbxL) / gs) - yct = int((bbyR - bbyL) / gs) - zct = int((bbzR - bbzL) / gs) - xs = int(xct / 2) - ys = int(yct / 2) - zs = int(zct / 2) - - 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 - debug_prints(func="voxelByRays", text="Raycasting top/bottom") - - for x in range(xct): - for y in range(yct): - xco = bbxL + (x * gs) - yco = bbyL + (y * gs) - v1 = ((xco, yco, rc)) - v2 = ((xco, yco, -rc)) - vz1 = ob.ray_cast(v1, v2) - vz2 = ob.ray_cast(v2, v1) - - 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) - zco = bbzL + (z * gs) - v1 = ((xco, rc, zco)) - v2 = ((xco, -rc, zco)) - vy1 = ob.ray_cast(v1, v2) - vy2 = ob.ray_cast(v2, v1) - 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) - zco = bbzL + (z * gs) - v1 = ((rc, yco, zco)) - v2 = ((-rc, yco, zco)) - vx1 = ob.ray_cast(v1, v2) - vx2 = ob.ray_cast(v2, v1) - 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 through - nlist = [] - for l in ll: - nl = getStencil3D_26(l[0], l[1], l[2]) - nlist += nl - - # 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... - od = Vector( - ((ob.location[0] - orig[0]) / gs, - (ob.location[1] - orig[1]) / gs, - (ob.location[2] - orig[2]) / gs) - ) - for r in rlist: - qlist.append((r[0] + int(od[0]), r[1] + int(od[1]), r[2] + int(od[2]))) - - return qlist - - -def fakeGroundChargePlane(z, charge): - eCL = [] - xy = abs(z) / 2 - eCL += [(0, 0, z, charge)] - eCL += [(xy, 0, 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)] - clist = [] - for l in ll: - clist.append((l[0], l[1], l[2], charge)) - return clist - - -# 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] - cList = splitList(aList, 0) - oList = splitList(aList, 1) - Omin, Omax = getLowHigh(oList) - if Omin == Omax: - Omax += notZero - Omin -= notZero - PdL = [] - E = 0 - E = notZero # divisor for (fslg - eqn. 12) - - for o in oList: - 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) - PdINT = Pd * 100 - PdL.append(Pd) - - return PdL - - -# work in progress, trying to speed these up -def fslg_e13(x, min, max, u): - return pow((x - min) / (max - min), u) - - -def addit(x, y): - return x + y - - -def fslg_e12(x, min, max, u, e): - return (fslg_e13(x, min, max, u) / e) * 100 - - -def getGrowthProbability(uN, aList): - # 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))] - maxL = [Omax for q in range(len(oList))] - uNL = [uN for q in range(len(oList))] - 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 - r1 = 1 / 2 # (FSLG - Eqn. 10) - nOiL = [] - - for oi in range(len(cList)): - o = cList[oi][1] - c = cList[oi][0] - iOe = 0 - rit = dist(c[0], c[1], c[2], p[0], p[1], p[2]) - 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 - r1 = 1 / 2 # (FSLG - Eqn. 10) - npList = [] - - for p in pList: - npList.append(((p[0], p[1], p[2]), 1.0)) - - for e in eList: - npList.append(((e[0], e[1], e[2]), e[3])) - - OiL = [] - for i in cList: - Oi = 0 - 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 - 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)] - cList = [] - for c in aList: - tempList = getStencil3D_26(c[0], c[1], c[2]) - for t in tempList: - if t not in aList and t not in iList: - cList.append(t) - ncList = deDupe(cList) - - return ncList - - -# Setup functions - -def setupObjects(): - winmgr = bpy.context.scene.advanced_objects1 - oOB = bpy.data.objects.new('ELorigin', None) - oOB.location = ((0, 0, 10)) - bpy.context.collection.objects.link(oOB) - - gOB = bpy.data.objects.new('ELground', None) - gOB.empty_display_type = 'ARROWS' - bpy.context.collection.objects.link(gOB) - - cME = makeMeshCube(1) - cOB = bpy.data.objects.new('ELcloud', cME) - cOB.location = ((-2, 8, 12)) - cOB.hide_render = True - bpy.context.collection.objects.link(cOB) - - iME = makeMeshCube(1) - for v in iME.vertices: - xyl = 6.5 - zl = .5 - v.co[0] = v.co[0] * xyl - v.co[1] = v.co[1] * xyl - v.co[2] = v.co[2] * zl - iOB = bpy.data.objects.new('ELinsulator', iME) - iOB.location = ((0, 0, 5)) - iOB.hide_render = True - bpy.context.collection.objects.link(iOB) - - try: - winmgr.OOB = 'ELorigin' - winmgr.GOB = 'ELground' - winmgr.COB = 'ELcloud' - winmgr.IOB = 'ELinsulator' - except: - pass - - -def checkSettings(): - check = True - winmgr = bpy.context.scene.advanced_objects1 - message = "" - if winmgr.OOB == "": - message = "Error: no origin object selected" - check = False - - if winmgr.GROUNDBOOL and winmgr.GOB == "": - message = "Error: no ground object selected" - check = False - - if winmgr.CLOUDBOOL and winmgr.COB == "": - message = "Error: no cloud object selected" - check = False - - if winmgr.IBOOL and winmgr.IOB == "": - message = "Error: no insulator object selected" - check = False - - if check is False: - debug_prints(func="checkSettings", text=message) - - # return state and the message for the operator report - return check, message - - -# Main - -def FSLG(): - winmgr = bpy.context.scene.advanced_objects1 - # fast simulation of laplacian growth - debug_prints(func="FSLG", - text="Go go gadget: fast simulation of laplacian growth") - tc1 = time.clock() - TSTEPS = winmgr.TSTEPS - - obORIGIN = bpy.context.scene.objects[winmgr.OOB] - obGROUND = bpy.context.scene.objects[winmgr.GOB] - winmgr.ORIGIN = obORIGIN.location - winmgr.GROUNDZ = int((obGROUND.location[2] - winmgr.ORIGIN[2]) / winmgr.GSCALE) - - # 1) insert initial charge(s) point (uses verts if mesh) - cgrid = [(0, 0, 0)] - - if obORIGIN.type == 'MESH': - debug_prints( - func="FSLG", - text="Origin object is mesh, 'voxelizing' initial charges from verts" - ) - cgrid = voxelByVertex(obORIGIN, winmgr.GSCALE) - - if winmgr.VMMESH: - 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) - eChargeList = [] - icList = [] - if winmgr.GROUNDBOOL: - eChargeList = fakeGroundChargePlane(winmgr.GROUNDZ, winmgr.GROUNDC) - - if winmgr.CLOUDBOOL: - 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) - debug_prints( - func="FSLG", - text="cloud object cell count", var=len(eChargeList) - ) - - if winmgr.IBOOL: - 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) - - 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) - cSites = initialPointCharges(cgrid, cSites, eChargeList) - - ts = 1 - while ts <= TSTEPS: - # 1) select new growth site (eqn. 12) - # get probabilities at candidate sites - gProbs = getGrowthProbability(winmgr.BIGVAR, cSites) - # 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 - cgrid.append(gsite) - # remove new growth cell from candidate sites - cSites.remove(cSites[gSitei]) - - # 3) update potential at candidate sites (eqn. 11) - cSites = updatePointCharges(gsite, cSites, eChargeList) - - # 4) add new candidates surrounding growth site - # get candidate 'stencil' - ncSitesT = getCandidateSites([gsite], icList) - # 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) - ncSplit = splitList(ncSites, 0) - ncSites = initialPointCharges(cgrid, ncSplit, eChargeList) - - # add new candidate sites to candidate list - for ncs in ncSites: - cSites.append(ncs) - - # iteration complete - istr1 = ':::T-STEP: ' + str(ts) + '/' + str(TSTEPS) - istr12 = ' | GROUNDZ: ' + str(winmgr.GROUNDZ) + ' | ' - istr2 = 'CANDS: ' + str(len(cSites)) + ' | ' - istr3 = 'GSITE: ' + str(gsite) - debug_prints( - func="FSLG", - text="Iteration complete", - var=istr1 + istr12 + istr2 + istr3 - ) - ts += 1 - - # early termination for ground/cloud strike - if winmgr.GROUNDBOOL: - if gsite[2] == winmgr.GROUNDZ: - ts = TSTEPS + 1 - debug_prints( - func="FSLG", - text="Early termination due to groundstrike" - ) - continue - - if winmgr.CLOUDBOOL: - if gsite in splitListCo(eChargeList): - ts = TSTEPS + 1 - debug_prints( - func="FSLG", - text="Early termination due to cloudstrike" - ) - continue - - tc2 = time.clock() - tcRUN = tc2 - tc1 - 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 - ) - - debug_prints(func="FSLG", text="COMPLETE") - - -# GUI # - -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): - # tuple - state, report text - is_conditions, message = checkSettings() - - if is_conditions: - FSLG() - else: - self.report({'WARNING'}, message + " Operation Cancelled") - - return {'CANCELLED'} - - return {'FINISHED'} - - -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(Panel): - bl_label = "Laplacian Lightning" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" - bl_context = "objectmode" - bl_category = "Create" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - winmgr = context.scene.advanced_objects1 - - 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(text="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_objects1 - 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)) - return rSTRING1 + rSTRING2 + rSTRING3 - - -def addReportProp(ob, str): - bpy.types.Object.FSLG_REPORT = bpy.props.StringProperty( - name='fslg_report', default='') - ob.FSLG_REPORT = str - - -def register(): - bpy.utils.register_class(runFSLGLoopOperator) - bpy.utils.register_class(setupObjectsOperator) - bpy.utils.register_class(OBJECT_PT_fslg) - - -def unregister(): - bpy.utils.unregister_class(runFSLGLoopOperator) - bpy.utils.unregister_class(setupObjectsOperator) - bpy.utils.unregister_class(OBJECT_PT_fslg) - - -if __name__ == "__main__": - register() - pass - - -# Benchmarks Function - -def BENCH(): - debug_prints(func="BENCH", text="BEGIN BENCHMARK") - bt0 = time.clock() - # make a big list - tsize = 25 - tlist = [] - for x in range(tsize): - for y in range(tsize): - for z in range(tsize): - tlist.append((x, y, z)) - tlist.append((x, y, z)) - - # function to test - bt1 = time.clock() - bt2 = time.clock() - btRUNb = bt2 - bt1 - btRUNa = bt1 - bt0 - - 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_panels/object_mangle_tools.py b/add_advanced_objects_panels/object_mangle_tools.py deleted file mode 100644 index 4631f82dc9ddfbe7f1f9276d76c2d761867e8b4b..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/object_mangle_tools.py +++ /dev/null @@ -1,208 +0,0 @@ -# mangle_tools.py (c) 2011 Phil Cote (cotejrp1) - -# ###### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 ###### - -# Note: properties are moved into __init__ - -bl_info = { - "name": "Mangle Tools", - "author": "Phil Cote", - "blender": (2, 71, 0), - "location": "3D View > Toolshelf > Create > Mangle Tools", - "description": "Set of tools to mangle curves, meshes, and shape keys", - "warning": "", - "wiki_url": "", - "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): - advanced_objects = context.scene.advanced_objects1 - xyz_const = advanced_objects.mangle_constraint_vector - random.seed(time.time()) - multiplier = 1 - - # For curves, we base the multiplier on the circumference formula. - # This helps make curve changes more noticeable. - if is_curve: - multiplier = 2 * pi - random_mag = advanced_objects.mangle_random_magnitude - if xyz_const[0]: - co.x += .01 * random.randrange(-random_mag, random_mag) * multiplier - if xyz_const[1]: - co.y += .01 * random.randrange(-random_mag, random_mag) * multiplier - if xyz_const[2]: - co.z += .01 * random.randrange(-random_mag, random_mag) * multiplier - - -class MeshManglerOperator(Operator): - bl_idname = "ba.mesh_mangler" - bl_label = "Mangle Mesh" - 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 is not None and ob.type == 'MESH' - - def execute(self, context): - mesh = context.active_object.data - bm = bmesh.new() - bm.from_mesh(mesh) - verts = bm.verts - advanced_objects = context.scene.advanced_objects1 - 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) - - 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(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 is not None and ob.type in ['MESH', 'CURVE'] - - def execute(self, context): - scn = context.scene.advanced_objects1 - mangleName = scn.mangle_name - ob = context.object - shapeKey = ob.shape_key_add(name=mangleName) - verts = shapeKey.data - - for vert in verts: - move_coordinate(context, vert.co, is_curve=ob.type == 'CURVE') - - return {'FINISHED'} - - -class CurveManglerOp(Operator): - bl_idname = "ba.curve_mangler" - bl_label = "Mangle Curve" - 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 is not None and ob.type == "CURVE" - - def execute(self, context): - ob = context.active_object - 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: - if spline.type == 'BEZIER': - points = spline.bezier_points - elif spline.type in {'POLY', 'NURBS'}: - points = spline.points - - for point in points: - move_coordinate(context, point.co, is_curve=True) - - return {'FINISHED'} - - -class MangleToolsPanel(Panel): - bl_label = "Mangle Tools" - bl_space_type = "VIEW_3D" - bl_context = "objectmode" - bl_region_type = "TOOLS" - bl_category = "Create" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - scn = context.scene.advanced_objects1 - obj = context.object - - 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, "mangle_random_magnitude") - col.operator("ba.mesh_mangler") - col.separator() - - col.prop(scn, "mangle_name") - col.operator("ba.ani_mangler") - else: - layout = self.layout - layout.label(text="Please select a Mesh Object", icon="INFO") - - -def register(): - bpy.utils.register_class(AnimanglerOperator) - bpy.utils.register_class(MeshManglerOperator) - bpy.utils.register_class(CurveManglerOp) - bpy.utils.register_class(MangleToolsPanel) - - -def unregister(): - bpy.utils.unregister_class(AnimanglerOperator) - bpy.utils.unregister_class(MeshManglerOperator) - bpy.utils.unregister_class(MangleToolsPanel) - bpy.utils.unregister_class(CurveManglerOp) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_panels/oscurart_constellation.py b/add_advanced_objects_panels/oscurart_constellation.py deleted file mode 100644 index a07f78f2e0886d31c52db795ee3419211c239165..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/oscurart_constellation.py +++ /dev/null @@ -1,145 +0,0 @@ -# ##### 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 the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# 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 -# 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 LICENSE BLOCK ##### - -bl_info = { - "name": "Constellation", - "author": "Oscurart", - "blender": (2, 67, 0), - "location": "3D View > Toolshelf > Create > Constellation", - "description": "Create a new Mesh From Selected", - "warning": "", - "wiki_url": "", - "category": "Add Mesh"} - -# Note the setting is moved to __init__ search for -# the adv_obj and advanced_objects patterns - -import bpy -from bpy.props import FloatProperty -from math import sqrt -from bpy.types import ( - Operator, - Panel, - ) - - -def VertDis(a, b): - dst = sqrt(pow(a.co.x - b.co.x, 2) + - pow(a.co.y - b.co.y, 2) + - pow(a.co.z - b.co.z, 2)) - return(dst) - - -def OscConstellation(limit): - actobj = bpy.context.object - vertlist = [] - edgelist = [] - edgei = 0 - - for ind, verta in enumerate(actobj.data.vertices[:]): - for vertb in actobj.data.vertices[ind:]: - if VertDis(verta, vertb) <= limit: - vertlist.append(verta.co[:]) - vertlist.append(vertb.co[:]) - edgelist.append((edgei, edgei + 1)) - edgei += 2 - - mesh = bpy.data.meshes.new("rsdata") - obj = bpy.data.objects.new("rsObject", mesh) - bpy.context.collection.objects.link(obj) - mesh.from_pydata(vertlist, edgelist, []) - - -class Oscurart_Constellation(Operator): - bl_idname = "mesh.constellation" - bl_label = "Constellation" - 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="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): - obj = context.active_object - return (obj and obj.type == "MESH") - - def invoke(self, context, event): - adv_obj = context.scene.advanced_objects1 - 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): - try: - OscConstellation(self.limit) - except Exception as e: - print("\n[Add Advanced Objects]\nOperator: mesh.constellation\n{}".format(e)) - - self.report({"WARNING"}, - "Constellation Operation could not be Completed (See Console for more Info)") - - return {"CANCELLED"} - - return {'FINISHED'} - - -class Constellation_Operator_Panel(Panel): - bl_label = "Constellation" - bl_region_type = "TOOLS" - bl_space_type = "VIEW_3D" - bl_options = {'DEFAULT_CLOSED'} - bl_context = "objectmode" - bl_category = "Create" - - def draw(self, context): - layout = self.layout - adv_obj = context.scene.advanced_objects1 - - box = layout.box() - col = box.column(align=True) - col.label(text="Constellation:") - col.operator("mesh.constellation", text="Cross Section") - col.prop(adv_obj, "constellation_limit") - - -# Register -def register(): - bpy.utils.register_class(Oscurart_Constellation) - bpy.utils.register_class(Constellation_Operator_Panel) - - -def unregister(): - bpy.utils.unregister_class(Oscurart_Constellation) - bpy.utils.unregister_class(Constellation_Operator_Panel) - - -if __name__ == "__main__": - register() diff --git a/add_advanced_objects_panels/unfold_transition.py b/add_advanced_objects_panels/unfold_transition.py deleted file mode 100644 index 726a49429e3340fca422e4f5b11048346607c546..0000000000000000000000000000000000000000 --- a/add_advanced_objects_panels/unfold_transition.py +++ /dev/null @@ -1,348 +0,0 @@ -# gpl: authors Liero, Atom - -bl_info = { - "name": "Unfold transition", - "author": "Liero, Atom", - "location": "3D View > Toolshelf > Create > Unfold Transition", - "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.types import ( - Operator, - Panel, - ) -from random import ( - randint, - uniform, - ) -from mathutils import Vector -from mathutils.geometry import intersect_point_line - - -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\n" - "Needs an existing Active Mesh Object") - bl_options = {"REGISTER", "UNDO"} - - @classmethod - def poll(cls, context): - obj = context.active_object - return (obj is not None and obj.type == "MESH") - - def execute(self, context): - bpy.ops.object.mode_set() - scn = bpy.context.scene - adv_obj = scn.advanced_objects1 - obj = bpy.context.object - dat = obj.data - fac = dat.polygons - ver = dat.vertices - - # try to cleanup traces of previous actions - bpy.ops.object.mode_set(mode="EDIT") - bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=True) - bpy.ops.object.mode_set() - 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 - try: - scn.objects.unlink(arm) - bpy.data.objects.remove(arm) - bpy.data.armatures.remove(rig) - except: - pass - obj.modifiers.remove(obj.modifiers["UnFold"]) - - # try to obtain the face sequence from the vertex weights - if adv_obj.unfold_modo == "weight": - if len(obj.vertex_groups): - i = obj.vertex_groups.active.index - W = [] - for f in fac: - v_data = [] - for v in f.vertices: - try: - w = ver[v].groups[i].weight - v_data.append((w, v)) - except: - v_data.append((0, v)) - v_data.sort(reverse=True) - v1 = ver[v_data[0][1]].co - v2 = ver[v_data[1][1]].co - cen = Vector(f.center) - its = intersect_point_line(cen, v2, v1) - head = v2.lerp(v1, its[1]) - peso = sum([x[0] for x in v_data]) - W.append((peso, f.index, cen, head)) - W.sort(reverse=True) - 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 - bpy.ops.object.mode_set(mode="EDIT") - bpy.ops.mesh.select_all(action="SELECT") - bpy.ops.mesh.edge_split() - bpy.ops.mesh.select_all(action="SELECT") - - 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.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 adv_obj.unfold_modo != "weight": - S = [] - for f in fac: - E = list(f.edge_keys) - E.sort() - v1 = ver[E[0][0]].co - v2 = ver[E[0][1]].co - cen = Vector(f.center) - its = intersect_point_line(cen, v2, v1) - head = v2.lerp(v1, its[1]) - S.append((f.index, f.center, head)) - - # 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 - bpy.ops.object.mode_set(mode="EDIT") - arm.display_type = "WIRE" - rig.show_in_front = True - mod = obj.modifiers.new("UnFold", "ARMATURE") - mod.show_in_editmode = True - mod.object = rig - - # create bones and vertex groups - root = arm.edit_bones.new("bone.000") - root.tail = (0, 0, 0) - root.head = (0, 0, 1) - root.select = True - vis = [False, True] + [False] * 30 - - for fb in S: - f = fac[fb[0]] - b = arm.edit_bones.new("bone.000") - 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_set(False) - b.layers = vis - b.parent = root - vg = obj.vertex_groups.new(name=b.name) - vg.add(f.vertices, 1, "ADD") - - bpy.ops.object.mode_set() - - if adv_obj.unfold_modo == "weight": - obj.vertex_groups.active_index = 0 - scn.objects.active = rig - obj.select_set(False) - - return {"FINISHED"} - - -class Animate_Fold(Operator): - bl_idname = "object.animate_fold" - bl_label = "Animate Unfold" - 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_objects1 - - 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 - scn = bpy.context.scene - adv_obj = scn.advanced_objects1 - - 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_objects1 - fra = scn.frame_current - 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 adv_obj.unfold_flip: - rot = -3.141592 - else: - 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, adv_obj.unfold_fold_noise) - - if adv_obj.unfold_flip: - b.scale = (1, 1, 1) - elif adv_obj.unfold_from_point: - b.scale = (0, 0, 0) - else: - b.scale = (1, 0, 0) - - if not adv_obj.unfold_flip: - b.keyframe_insert("scale", frame=t) - b.scale = (1, 1, 1) - b.keyframe_insert("scale", frame=t + adv_obj.unfold_sca_time) - - if adv_obj.unfold_rot_max: - b.rotation_mode = "XYZ" - 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 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 + 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 + 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 + adv_obj.unfold_rot_time + .75 * extra - ) - - b.rotation_euler = (0, 0, 0) - b.keyframe_insert( - "rotation_euler", frame=t + adv_obj.unfold_rot_time + extra - ) - self.is_not_undo = False - - return {"FINISHED"} - - -class PanelFOLD(Panel): - bl_label = "Unfold Transition" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" - bl_category = "Create" - bl_context = "objectmode" - bl_options = {"DEFAULT_CLOSED"} - - def draw(self, context): - layout = self.layout - adv_obj = context.scene.advanced_objects1 - - box = layout.box() - col = box.column() - col.operator("object.set_up_fold", text="1. Set Up Unfold") - col.separator() - col.label(text="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") - - -classes = ( - Set_Up_Fold, - Animate_Fold, - PanelFOLD, - ) - - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - -def unregister(): - for cls in classes: - bpy.utils.unregister_class(cls) - - -if __name__ == "__main__": - register()