Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2017-06-15 15:06:00 +0300
committermeta-androcto <meta.androcto1@gmail.com>2017-06-15 15:06:00 +0300
commitc6676127556e5756e4c94f39784d27149f2eb86d (patch)
treea97b100b59224a3c3b8954b891968c4afa9bea79 /add_advanced_objects_menu
parent17d293687324e86b2e94e6ca3574e294f3da3667 (diff)
add advanced objects: split to 2 folders menu and panel
Diffstat (limited to 'add_advanced_objects_menu')
-rw-r--r--add_advanced_objects_menu/__init__.py573
-rw-r--r--add_advanced_objects_menu/add_light_template.py145
-rw-r--r--add_advanced_objects_menu/add_mesh_aggregate.py338
-rw-r--r--add_advanced_objects_menu/arrange_on_curve.py355
-rw-r--r--add_advanced_objects_menu/circle_array.py166
-rw-r--r--add_advanced_objects_menu/copy2.py339
-rw-r--r--add_advanced_objects_menu/cubester.py944
-rw-r--r--add_advanced_objects_menu/make_struts.py592
-rw-r--r--add_advanced_objects_menu/mesh_easylattice.py401
-rw-r--r--add_advanced_objects_menu/object_add_chain.py179
-rw-r--r--add_advanced_objects_menu/oscurart_chain_maker.py288
-rw-r--r--add_advanced_objects_menu/pixelate_3d.py114
-rw-r--r--add_advanced_objects_menu/random_box_structure.py201
-rw-r--r--add_advanced_objects_menu/rope_alpha.py832
-rw-r--r--add_advanced_objects_menu/scene_objects_bi.py195
-rw-r--r--add_advanced_objects_menu/scene_objects_cycles.py142
-rw-r--r--add_advanced_objects_menu/scene_texture_render.py78
-rw-r--r--add_advanced_objects_menu/trilighting.py238
18 files changed, 6120 insertions, 0 deletions
diff --git a/add_advanced_objects_menu/__init__.py b/add_advanced_objects_menu/__init__.py
new file mode 100644
index 00000000..b1d86454
--- /dev/null
+++ b/add_advanced_objects_menu/__init__.py
@@ -0,0 +1,573 @@
+# ##### 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, 3),
+ "blender": (2, 78, 0),
+ "location": "View3D > Add ",
+ "description": "Add Object & Camera extras",
+ "warning": "",
+ "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
+ "/Py/Scripts/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(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 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 (
+ Menu,
+ AddonPreferences,
+ PropertyGroup,
+ )
+from bpy.props import (
+ BoolProperty,
+ BoolVectorProperty,
+ EnumProperty,
+ FloatProperty,
+ FloatVectorProperty,
+ IntProperty,
+ StringProperty,
+ PointerProperty,
+ )
+
+
+# Define the "Scenes" menu
+class INFO_MT_scene_elements_add(Menu):
+ bl_idname = "INFO_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 INFO_MT_mesh_lamps_add(Menu):
+ bl_idname = "INFO_MT_scene_lamps"
+ 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 INFO_MT_mesh_chain_add(Menu):
+ bl_idname = "INFO_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 INFO_MT_array_mods_add(Menu):
+ bl_idname = "INFO_MT_array_mods"
+ bl_label = "Array Mods"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'INVOKE_REGION_WIN'
+
+ layout.menu("INFO_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 INFO_MT_quick_blocks_add(Menu):
+ bl_idname = "INFO_MT_quick_tools"
+ bl_label = "Block Tools"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'INVOKE_REGION_WIN'
+
+ layout.operator("object.pixelate", icon="MESH_GRID")
+ layout.operator("mesh.generate_struts",
+ text="Struts", icon="GRID")
+ layout.operator("object.make_structure",
+ text="Random Boxes", icon="SEQ_SEQUENCER")
+ layout.operator("object.easy_lattice",
+ text="Easy Lattice", icon="MOD_LATTICE")
+
+
+# Define the "Phsysics Tools" Menu
+class INFO_MT_Physics_tools_add(Menu):
+ bl_idname = "INFO_MT_physics_tools"
+ bl_label = "Physics Tools"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'INVOKE_REGION_WIN'
+ layout.operator("object.drop_on_active",
+ text="Drop To Ground", icon="SORTSIZE")
+ 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("INFO_MT_scene_elements", icon="SCENE_DATA")
+ self.layout.menu("INFO_MT_scene_lamps", icon="LAMP_SPOT")
+ self.layout.separator()
+ self.layout.menu("INFO_MT_array_mods", icon="MOD_ARRAY")
+ self.layout.menu("INFO_MT_quick_tools", icon="MOD_BUILD")
+ self.layout.menu("INFO_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="Drop to Ground, Wrecking Ball and Cloth Rope", icon="LAYER_USED")
+
+
+ icon_2 = "TRIA_RIGHT" if not self.show_panel_list else "TRIA_DOWN"
+ box = layout.box()
+ box.prop(self, "show_panel_list", emboss=False, icon=icon_2)
+
+ if self.show_panel_list:
+ box.label(text="Panels located in 3D View Tools Region > Create",
+ icon="LAYER_ACTIVE")
+ box.label(text="CubeSter", icon="LAYER_USED")
+
+
+
+# Cubester update functions
+def find_audio_length(self, context):
+ adv_obj = context.scene.advanced_objects
+ audio_file = adv_obj.cubester_audio_path
+ length = 0
+
+ if audio_file != "":
+ # confirm that strip hasn't been loaded yet
+ get_sequence = getattr(context.scene.sequence_editor, "sequences_all", [])
+ for strip in get_sequence:
+ if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file:
+ length = strip.frame_final_duration
+
+ if length == 0:
+ area = context.area
+ old_type = area.type
+ area.type = "SEQUENCE_EDITOR"
+ try:
+ bpy.ops.sequencer.sound_strip_add(filepath=audio_file)
+ adv_obj.cubester_check_audio = True
+ except Exception as e:
+ print("\n[Add Advanced Objects]\n Function: "
+ "find_audio_length\n {}\n".format(e))
+ adv_obj.cubester_check_audio = False
+ pass
+
+ area.type = old_type
+
+ # find audio file
+ for strip in context.scene.sequence_editor.sequences_all:
+ if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file:
+ adv_obj.cubester_check_audio = True
+ length = strip.frame_final_duration
+
+ adv_obj.cubester_audio_file_length = length
+
+
+# load image if possible
+def adjust_selected_image(self, context):
+ scene = context.scene.advanced_objects
+ try:
+ image = bpy.data.images.load(scene.cubester_load_image)
+ scene.cubester_image = image.name
+ except Exception as e:
+ print("\n[Add Advanced Objects]\n Function: "
+ "adjust_selected_image\n {}\n".format(e))
+
+
+# load color image if possible
+def adjust_selected_color_image(self, context):
+ scene = context.scene.advanced_objects
+ try:
+ image = bpy.data.images.load(scene.cubester_load_color_image)
+ scene.cubester_color_image = image.name
+ except Exception as e:
+ print("\nAdd Advanced Objects]\n Function: "
+ "adjust_selected_color_image\n {}\n".format(e))
+
+
+class AdvancedObjProperties(PropertyGroup):
+ # cubester
+ # main properties
+ cubester_check_audio = BoolProperty(
+ name="",
+ default=False
+ )
+ cubester_audio_image = EnumProperty(
+ name="Input Type",
+ items=(("image", "Image",
+ "Use an Image as input for generating Geometry", "IMAGE_COL", 0),
+ ("audio", "Audio",
+ "Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1))
+ )
+ cubester_audio_file_length = IntProperty(
+ default=0
+ )
+ # audio
+ cubester_audio_path = StringProperty(
+ default="",
+ name="Audio File",
+ subtype="FILE_PATH",
+ update=find_audio_length
+ )
+ cubester_audio_min_freq = IntProperty(
+ name="Minimum Frequency",
+ min=20, max=100000,
+ default=20
+ )
+ cubester_audio_max_freq = IntProperty(
+ name="Maximum Frequency",
+ min=21, max=999999,
+ default=5000
+ )
+ cubester_audio_offset_type = EnumProperty(
+ name="Offset Type",
+ items=(("freq", "Frequency Offset", ""),
+ ("frame", "Frame Offset", "")),
+ description="Type of offset per row of mesh"
+ )
+ cubester_audio_frame_offset = IntProperty(
+ name="Frame Offset",
+ min=0, max=10,
+ default=2
+ )
+ cubester_audio_block_layout = EnumProperty(
+ name="Block Layout",
+ items=(("rectangle", "Rectangular", ""),
+ ("radial", "Radial", ""))
+ )
+ cubester_audio_width_blocks = IntProperty(
+ name="Width Block Count",
+ min=1, max=10000,
+ default=5
+ )
+ cubester_audio_length_blocks = IntProperty(
+ name="Length Block Count",
+ min=1, max=10000,
+ default=50
+ )
+ # image
+ cubester_load_type = EnumProperty(
+ name="Image Input Type",
+ items=(("single", "Single Image", ""),
+ ("multiple", "Image Sequence", ""))
+ )
+ cubester_image = StringProperty(
+ default="",
+ name=""
+ )
+ cubester_load_image = StringProperty(
+ default="",
+ name="Load Image",
+ subtype="FILE_PATH",
+ update=adjust_selected_image
+ )
+ cubester_skip_images = IntProperty(
+ name="Image Step",
+ min=1, max=30,
+ default=1,
+ description="Step from image to image by this number"
+ )
+ cubester_max_images = IntProperty(
+ name="Max Number Of Images",
+ min=2, max=1000,
+ default=10,
+ description="Maximum number of images to be used"
+ )
+ cubester_frame_step = IntProperty(
+ name="Frame Step Size",
+ min=1, max=10,
+ default=4,
+ description="The number of frames each picture is used"
+ )
+ cubester_skip_pixels = IntProperty(
+ name="Skip # Pixels",
+ min=0, max=256,
+ default=64,
+ description="Skip this number of pixels before placing the next"
+ )
+ cubester_mesh_style = EnumProperty(
+ name="Mesh Type",
+ items=(("blocks", "Blocks", ""),
+ ("plane", "Plane", "")),
+ description="Compose mesh of multiple blocks or of a single plane"
+ )
+ cubester_block_style = EnumProperty(
+ name="Block Style",
+ items=(("size", "Vary Size", ""),
+ ("position", "Vary Position", "")),
+ description="Vary Z-size of block, or vary Z-position"
+ )
+ cubester_height_scale = FloatProperty(
+ name="Height Scale",
+ subtype="DISTANCE",
+ min=0.1, max=2,
+ default=0.2
+ )
+ cubester_invert = BoolProperty(
+ name="Invert Height",
+ default=False
+ )
+ # general adjustments
+ cubester_size_per_hundred_pixels = FloatProperty(
+ name="Size Per 100 Blocks/Points",
+ subtype="DISTANCE",
+ min=0.001, max=5,
+ default=1
+ )
+ # material based stuff
+ cubester_materials = EnumProperty(
+ name="Material",
+ items=(("vertex", "Vertex Colors", ""),
+ ("image", "Image", "")),
+ description="Color with vertex colors, or uv unwrap and use an image"
+ )
+ cubester_use_image_color = BoolProperty(
+ name="Use Original Image Colors'?",
+ default=True,
+ description="Use original image colors, or replace with an another one"
+ )
+ cubester_color_image = StringProperty(
+ default="",
+ name=""
+ )
+ cubester_load_color_image = StringProperty(
+ default="",
+ name="Load Color Image",
+ subtype="FILE_PATH",
+ update=adjust_selected_color_image
+ )
+ cubester_vertex_colors = {}
+ # advanced
+ cubester_advanced = BoolProperty(
+ name="Advanced Options",
+ default=False
+ )
+ cubester_random_weights = BoolProperty(
+ name="Random Weights",
+ default=False
+ )
+ cubester_weight_r = FloatProperty(
+ name="Red",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
+ cubester_weight_g = FloatProperty(
+ name="Green",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
+ cubester_weight_b = FloatProperty(
+ name="Blue",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
+ cubester_weight_a = FloatProperty(
+ name="Alpha",
+ subtype="FACTOR",
+ min=0.01, max=1.0,
+ default=0.25
+ )
+
+ # pixelate_3d properties
+ pixelate_3d_size = FloatProperty(
+ name="Size",
+ min=.05, max=5,
+ default=.25,
+ description="Size of the cube / grid"
+ )
+ pixelate_3d_gap = IntProperty(
+ name="Gap",
+ min=0, max=90,
+ default=10,
+ subtype='PERCENTAGE',
+ description="Separation - percent of size"
+ )
+ pixelate_3d_smooth = FloatProperty(
+ name="Smooth",
+ min=0, max=1,
+ default=.0,
+ description="Smooth factor when subdividing mesh"
+ )
+ # arrange_on_curve
+ arrange_c_use_selected = BoolProperty(
+ name="Use Selected",
+ description="Use the selected objects to duplicate",
+ default=True,
+ )
+ arrange_c_obj_arranjar = StringProperty(
+ name=""
+ )
+ arrange_c_select_type = EnumProperty(
+ name="Type",
+ description="Select object or group",
+ items=[
+ ('O', "Object", "Make duplicates of a specific object"),
+ ('G', "Group", "Make duplicates of the objects in a group"),
+ ],
+ default='O',
+ )
+
+
+def register():
+ bpy.utils.register_module(__name__)
+
+ bpy.types.Scene.advanced_objects = PointerProperty(
+ type=AdvancedObjProperties
+ )
+
+ # Add "Extras" menu to the "Add" menu
+ bpy.types.INFO_MT_add.append(menu)
+ try:
+ bpy.types.VIEW3D_MT_AddMenu.append(menu)
+ except:
+ pass
+
+
+def unregister():
+ # Remove "Extras" menu from the "Add" menu.
+ bpy.types.INFO_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
+
+ # cleanup Easy Lattice Scene Property if it was created
+ if hasattr(bpy.types.Scene, "activelatticeobject"):
+ del bpy.types.Scene.activelatticeobject
+
+
+if __name__ == "__main__":
+ register()
diff --git a/add_advanced_objects_menu/add_light_template.py b/add_advanced_objects_menu/add_light_template.py
new file mode 100644
index 00000000..9e2c139f
--- /dev/null
+++ b/add_advanced_objects_menu/add_light_template.py
@@ -0,0 +1,145 @@
+# gpl: author Rebellion
+
+import bpy
+from bpy.types import Operator
+from bpy.props import BoolProperty
+
+
+def add_lamps(self, context):
+
+ if self.bKeyLight:
+ keyLight = bpy.data.lamps.new(name="Key_Light", type="SPOT")
+ ob = bpy.data.objects.new("Key_Light", keyLight)
+ constraint = ob.constraints.new(type='COPY_LOCATION')
+ constraint.use_offset = True
+ constraint.owner_space = 'LOCAL'
+ constraint.target = self.camera
+ constraint = ob.constraints.new(type='TRACK_TO')
+ constraint.target = self.target
+ constraint.track_axis = 'TRACK_NEGATIVE_Z'
+ constraint.up_axis = 'UP_X'
+ constraint.owner_space = 'LOCAL'
+ bpy.context.scene.objects.link(ob)
+ ob.rotation_euler[2] = -0.785398
+
+ if self.bFillLight:
+ fillLight = bpy.data.lamps.new(name="Fill_Light", type="SPOT")
+ ob = bpy.data.objects.new("Fill_Light", fillLight)
+ constraint = ob.constraints.new(type='COPY_LOCATION')
+ constraint.use_offset = True
+ constraint.owner_space = 'LOCAL'
+ constraint.target = self.camera
+ constraint = ob.constraints.new(type='TRACK_TO')
+ constraint.target = self.target
+ constraint.track_axis = 'TRACK_NEGATIVE_Z'
+ constraint.up_axis = 'UP_X'
+ constraint.owner_space = 'LOCAL'
+ bpy.context.scene.objects.link(ob)
+ ob.rotation_euler[2] = 0.785398
+ ob.data.energy = 0.3
+
+ if self.bBackLight:
+ backLight = bpy.data.lamps.new(name="Back_Light", type="SPOT")
+ ob = bpy.data.objects.new("Back_Light", backLight)
+ constraint = ob.constraints.new(type='COPY_LOCATION')
+ constraint.use_offset = True
+ constraint.owner_space = 'LOCAL'
+ constraint.target = self.camera
+ constraint = ob.constraints.new(type='TRACK_TO')
+ constraint.target = self.target
+ constraint.track_axis = 'TRACK_NEGATIVE_Z'
+ constraint.up_axis = 'UP_X'
+ constraint.owner_space = 'LOCAL'
+ bpy.context.scene.objects.link(ob)
+ ob.rotation_euler[2] = 3.14159
+ ob.data.energy = 0.2
+
+ if self.camera_constraint and self.camera is not None and \
+ self.camera.type == "CAMERA":
+
+ constraint = self.camera.constraints.new(type='TRACK_TO')
+ constraint.target = self.target
+ constraint.track_axis = 'TRACK_NEGATIVE_Z'
+ constraint.up_axis = 'UP_Y'
+
+
+class OBJECT_OT_add_light_template(Operator):
+ bl_idname = "object.add_light_template"
+ bl_label = "Add Light Template"
+ bl_description = ("Add Key, Fill 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_lamps(self, context)
+
+ except Exception as e:
+ self.report({'WARNING'},
+ "Some operations could not be performed (See Console for more info)")
+
+ print("\n[Add Advanced Objects]\nOperator: "
+ "object.add_light_template\nError: {}".format(e))
+
+ return {'FINISHED'}
+
+
+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
new file mode 100644
index 00000000..6072cb9c
--- /dev/null
+++ b/add_advanced_objects_menu/add_mesh_aggregate.py
@@ -0,0 +1,338 @@
+# ##### 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, 7, 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 = 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 = 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 = 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 = True
+
+ obj.select = 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
new file mode 100644
index 00000000..14017480
--- /dev/null
+++ b/add_advanced_objects_menu/arrange_on_curve.py
@@ -0,0 +1,355 @@
+# gpl author: Mano-Wii
+
+bl_info = {
+ "name": "Arrange on Curve",
+ "author": "Mano-Wii",
+ "version": (6, 3, 0),
+ "blender": (2, 7, 7),
+ "location": "View3D > TOOLS",
+ "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_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ bl_context = "objectmode"
+ bl_category = "Create"
+ bl_label = "Duplicate 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, "groups"
+ )
+ if context.object.type == 'CURVE':
+ layout.operator("object.arranjar_numa_curva", text="Arrange Objects")
+
+
+class DupliCurve(Operator):
+ bl_idname = "object.arranjar_numa_curva"
+ bl_label = "Arrange Objects 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.groups[adv_obj.arrange_c_obj_arranjar].objects
+
+ yawMatrix = mathutils.Matrix.Rotation(self.Yaw, 4, 'X')
+ pitchMatrix = mathutils.Matrix.Rotation(self.Pitch, 4, 'Y')
+ rollMatrix = mathutils.Matrix.Rotation(self.Roll, 4, 'Z')
+
+ max_angle = self.max_angle # 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.scene.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.scene.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.scene.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
new file mode 100644
index 00000000..af5a6a0a
--- /dev/null
+++ b/add_advanced_objects_menu/circle_array.py
@@ -0,0 +1,166 @@
+# gpl author: Antonis Karvelas
+
+# -*- coding: utf-8 -*-
+
+bl_info = {
+ "name": "Circle Array",
+ "author": "Antonis Karvelas",
+ "version": (1, 0, 1),
+ "blender": (2, 6, 7),
+ "location": "View3D > Object > Circle_Array",
+ "description": "Uses an existing array and creates an empty, "
+ "rotates it properly and makes a Circle Array",
+ "warning": "",
+ "wiki_url": "",
+ "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.scene.objects.active = active
+ bpy.ops.object.modifier_add(type='ARRAY')
+ active.modifiers[0].use_object_offset = True
+ active.modifiers[0].use_relative_offset = False
+
+ active.modifiers[0].use_object_offset = True
+ active.modifiers[0].use_relative_offset = False
+ active.select = False
+ bpy.context.scene.objects.active = 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.scene.objects.active = active
+ num = active.modifiers["Array"].count
+ rotate_num = 360 / num
+ active.select = True
+ bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
+ empty_name.rotation_euler = (0, 0, radians(rotate_num))
+ empty_name.select = False
+ active.select = 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.scene.objects.active = active
+ num = active.modifiers["Array"].count
+ rotate_num = 360 / num
+ active.select = True
+ bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
+ empty_name.rotation_euler = (0, 0, radians(rotate_num))
+ empty_name.select = False
+ active.select = True
+
+ return {'FINISHED'}
+
+ except Exception as e:
+ self.report({'WARNING'},
+ "Circle Array operator could not be executed (See the console for more info)")
+ 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
new file mode 100644
index 00000000..489f6dee
--- /dev/null
+++ b/add_advanced_objects_menu/copy2.py
@@ -0,0 +1,339 @@
+# ##### 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("to:")
+ layout.prop(self, "copytype", expand=True)
+ layout.label("Primary axis:")
+ layout.prop(self, "priaxes", expand=True)
+ layout.label("Secondary axis:")
+ layout.prop(self, "secaxes", expand=True)
+ if self.copytype == "E":
+ layout.prop(self, "edgescale")
+ 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.scene,
+ copy_to_object,
+ copy_from_object,
+ self.copytype,
+ axes,
+ self.edgescale,
+ self.scale
+ )
+ except Exception as e:
+ self.report({"WARNING"},
+ "Copy2 could not be completed (Check the Console for more info)")
+ print("\n[Add Advanced Objects]\nOperator: mesh.copy2\n{}\n".format(e))
+
+ return {"CANCELLED"}
+
+ return {"FINISHED"}
+
+ def invoke(self, context, event):
+ Copy2.obj_list = [(obj.name, obj.name, obj.name) for obj in bpy.data.objects]
+
+ return {"FINISHED"}
+
+
+def copy_to_from(scene, to_obj, from_obj, copymode, axes, edgescale, scale):
+ if copymode == 'V':
+ vertex_copy(scene, 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", "LAMP", "EMPTY", "ARMATURE", "SPEAKER", "META"]:
+ edgescale = False
+ edge_copy(scene, to_obj, from_obj, axes, edgescale, scale)
+
+ if copymode == 'F':
+ face_copy(scene, 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(scene, source_obj, pos, xdir, zdir, axes, scale=None):
+ """
+ copy the source_obj to pos, so its primary axis points in zdir and its
+ secondary axis points in xdir
+ """
+ copy_obj = source_obj.copy()
+ scene.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(scene, 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(scene, source_obj, pos, xdir, zdir, axes)
+ copy_list.append(copy)
+
+ # select all copied objects
+ for copy in copy_list:
+ copy.select = True
+ obj.select = False
+
+
+def edge_copy(scene, obj, source_obj, axes, es, scale):
+ # edge select mode
+ sel_edges = []
+ copy_list = []
+
+ for e in obj.data.edges:
+ if e.select 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(scene, source_obj, pos, xdir, zdir, axes, scale=escale)
+ copy_list.append(copy)
+
+ # select all copied objects
+ for copy in copy_list:
+ copy.select = True
+ obj.select = False
+
+
+def face_copy(scene, 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(scene, source_obj, fco, vco - fco, fn, axes)
+ copy_list.append(copy)
+
+ # select all copied objects
+ for copy in copy_list:
+ copy.select = True
+ obj.select = False
+
+
+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
new file mode 100644
index 00000000..1a516bd0
--- /dev/null
+++ b/add_advanced_objects_menu/cubester.py
@@ -0,0 +1,944 @@
+# ##### 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, 1),
+ "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, scene, verts, faces):
+ 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
+
+ # 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.scene.objects.link(ob)
+ bpy.context.scene.objects.active = ob
+ ob.select = True
+
+ # inital 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)
+ vert_colors += [(r, g, b) for i in range(24)]
+
+ bpy.ops.mesh.vertex_color_add()
+
+ i = 0
+ vert_colors_size = len(vert_colors)
+ for c in ob.data.vertex_colors[0].data:
+ 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)
+ frame_colors += [(r, g, b) 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 = True
+ curve.select = False
+ scene.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)
+
+ 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 = []
+ weights = [uniform(0.0, 1.0) for i in range(4)] # random weights
+ 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)
+ 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 += [(r, g, b) for i in range(24)]
+ else:
+ verts += [(x, y, h)]
+ vert_colors += [(r, g, b) 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.scene.objects.link(ob)
+ context.scene.objects.active = ob
+ ob.select = 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)
+ 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 += [(r, g, b) for i in range(24)]
+ else:
+ frame_colors += [(r, g, b) 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": scene.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("Image To Convert:")
+ box.prop_search(adv_obj, "cubester_image", bpy.data, "images")
+ box.prop(adv_obj, "cubester_load_image")
+
+ # find number of approriate images if sequence
+ if 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 approriate 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("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("Approximate Cube Count: " + str(rows * columns))
+ box.label("Expected Verts/Faces: " + str(rows * columns * 8) + " / " + str(rows * columns * 6))
+ else:
+ box.label("Approximate Point Count: " + str(rows * columns))
+ box.label("Expected Verts/Faces: " + str(rows * columns) + " / " + str(rows * (columns - 1)))
+
+ # 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("Images To Be Used: " + str(int(images_found / adv_obj.cubester_skip_images)))
+ else:
+ # audio based mesh
+ box.label("Audio Track Length: " + str(adv_obj.cubester_audio_file_length) + " frames")
+
+ block_infl, frame_infl, intercept = 0.0948, 0.0687566, -25.85985
+ time = (points * block_infl) + (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("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("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 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, scene, verts, faces)
+ created = adv_obj.cubester_audio_file_length
+ else:
+ self.report({'WARNING'},
+ "Please add an Sound File for Object generation. Operation Cancelled")
+ return {"CANCELLED"}
+
+ stop = timeit.default_timer()
+
+ if 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
new file mode 100644
index 00000000..58e149ab
--- /dev/null
+++ b/add_advanced_objects_menu/make_struts.py
@@ -0,0 +1,592 @@
+# 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.scene.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:
+ continue
+ truss_obj.select = 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.scene.objects.link(obj)
+ obj.select = True
+ obj.location = truss_obj.location
+ bpy.context.scene.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.user_preferences.edit.use_global_undo
+ bpy.context.user_preferences.edit.use_global_undo = False
+ keywords = self.as_keywords()
+
+ try:
+ create_struts(self, context, **keywords)
+ bpy.context.user_preferences.edit.use_global_undo = store_undo
+
+ return {"FINISHED"}
+
+ except Exception as e:
+ bpy.context.user_preferences.edit.use_global_undo = store_undo
+ self.report({"WARNING"},
+ "Make Struts could not be performed. Operation Cancelled")
+ print("\n[mesh.generate_struts]\n{}".format(e))
+ return {"CANCELLED"}
+
+
+def register():
+ bpy.utils.register_module(__name__)
+
+
+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
new file mode 100644
index 00000000..91a167dc
--- /dev/null
+++ b/add_advanced_objects_menu/mesh_easylattice.py
@@ -0,0 +1,401 @@
+# ##### 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 a better solution for allowing more than one lattice per scene
+
+bl_info = {
+ "name": "Easy Lattice Object",
+ "author": "Kursad Karatas",
+ "version": (0, 5, 1),
+ "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,
+ IntProperty,
+ StringProperty,
+ )
+
+
+# Cleanup
+def modifiersDelete(obj):
+ for mod in obj.modifiers:
+ if mod.name == "latticeeasytemp":
+ try:
+ if mod.object == bpy.data.objects['LatticeEasytTemp']:
+ bpy.ops.object.modifier_apply(apply_as='DATA', modifier=mod.name)
+ except:
+ bpy.ops.object.modifier_remove(modifier=mod.name)
+
+
+def modifiersApplyRemove(obj):
+ bpy.ops.object.select_all(action='DESELECT')
+ bpy.ops.object.select_pattern(pattern=obj.name, extend=False)
+ bpy.context.scene.objects.active = obj
+
+ for mod in obj.modifiers:
+ if mod.name == "latticeeasytemp":
+ if mod.object == bpy.data.objects['LatticeEasytTemp']:
+ bpy.ops.object.modifier_apply(apply_as='DATA', modifier=mod.name)
+
+
+def latticeDelete(obj):
+ bpy.ops.object.select_all(action='DESELECT')
+ for ob in bpy.context.scene.objects:
+ if "LatticeEasytTemp" in ob.name:
+ ob.select = True
+ bpy.ops.object.delete(use_global=False)
+
+ obj.select = True
+
+
+def createLattice(obj, size, pos, props):
+ # Create lattice and object
+ lat = bpy.data.lattices.new('LatticeEasytTemp')
+ ob = bpy.data.objects.new('LatticeEasytTemp', lat)
+
+ loc, rot, scl = getTransformations(obj)
+
+ # the position comes from the bbox
+ ob.location = pos
+
+ # the size from bbox
+ ob.scale = size
+
+ # the rotation comes from the combined obj world
+ # matrix which was converted to euler pairs
+ ob.rotation_euler = buildRot_World(obj)
+
+ ob.show_x_ray = True
+ # Link object to scene
+ scn = bpy.context.scene
+ scn.objects.link(ob)
+ scn.objects.active = ob
+ scn.update()
+
+ # Set lattice attributes
+ lat.interpolation_type_u = props[3]
+ lat.interpolation_type_v = props[3]
+ lat.interpolation_type_w = props[3]
+
+ lat.use_outside = False
+
+ lat.points_u = props[0]
+ lat.points_v = props[1]
+ lat.points_w = props[2]
+
+ return ob
+
+
+def selectedVerts_Grp(obj):
+ vertices = obj.data.vertices
+ selverts = []
+
+ if obj.mode == "EDIT":
+ bpy.ops.object.editmode_toggle()
+
+ for grp in obj.vertex_groups:
+ if "templatticegrp" in grp.name:
+ bpy.ops.object.vertex_group_set_active(group=grp.name)
+ bpy.ops.object.vertex_group_remove()
+
+ tempgroup = obj.vertex_groups.new("templatticegrp")
+
+ for vert in vertices:
+ if vert.select is True:
+ selverts.append(vert)
+ tempgroup.add([vert.index], 1.0, "REPLACE")
+
+ return selverts
+
+
+def getTransformations(obj):
+ rot = obj.rotation_euler
+ loc = obj.location
+ size = obj.scale
+
+ return [loc, rot, size]
+
+
+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))
+
+ # middle point has to be calculated based on the real world matrix
+ middle = ((minpoint + maxpoint) / 2)
+
+ minpoint = mat * minpoint # Calculate only based on loc/scale
+ maxpoint = mat * maxpoint # Calculate only based on loc/scale
+ middle = mat_world * middle # the middle has to be calculated based on the real world matrix
+
+ size = maxpoint - minpoint
+ size = Vector((abs(size.x), abs(size.y), abs(size.z)))
+
+ return [minpoint, maxpoint, size, middle]
+
+
+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 obejct 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 obejct 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 obejct 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 obejct 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 run(lat_props):
+ obj = bpy.context.object
+
+ if obj.type == "MESH":
+ # set global property for the currently active latticed object
+ # removed in __init__ on unregister if created
+ bpy.types.Scene.activelatticeobject = StringProperty(
+ name="currentlatticeobject",
+ default=""
+ )
+ bpy.types.Scene.activelatticeobject = obj.name
+
+ modifiersDelete(obj)
+ selvertsarray = selectedVerts_Grp(obj)
+ bbox = findBBox(obj, selvertsarray)
+
+ size = bbox[2]
+ pos = bbox[3]
+
+ latticeDelete(obj)
+ lat = createLattice(obj, size, pos, lat_props)
+
+ modif = obj.modifiers.new("latticeeasytemp", "LATTICE")
+ modif.object = lat
+ modif.vertex_group = "templatticegrp"
+
+ bpy.ops.object.select_all(action='DESELECT')
+ bpy.ops.object.select_pattern(pattern=lat.name, extend=False)
+ bpy.context.scene.objects.active = lat
+
+ bpy.context.scene.update()
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ if obj.type == "LATTICE":
+ if bpy.types.Scene.activelatticeobject:
+ name = bpy.types.Scene.activelatticeobject
+
+ # Are we in edit lattice mode? If so move on to object mode
+ if obj.mode == "EDIT":
+ bpy.ops.object.editmode_toggle()
+
+ for ob in bpy.context.scene.objects:
+ if ob.name == name: # found the object with the lattice mod
+ object = ob
+ modifiersApplyRemove(object)
+ latticeDelete(obj)
+
+ return
+
+
+def main(context, latticeprops):
+ run(latticeprops)
+
+
+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"
+ "Note: Works only with one lattice per scene")
+
+ lat_u = IntProperty(
+ name="Lattice u",
+ description="Points in u direction",
+ default=3
+ )
+ lat_w = IntProperty(
+ name="Lattice w",
+ description="Points in w direction",
+ default=3
+ )
+ lat_m = IntProperty(
+ name="Lattice m",
+ description="Points in m direction",
+ default=3
+ )
+ lat_types = (('KEY_LINEAR', "Linear", "Linear Interpolation type"),
+ ('KEY_CARDINAL', "Cardinal", "Cardinal Interpolation type"),
+ ('KEY_BSPLINE', "BSpline", "Key BSpline Interpolation Type")
+ )
+ lat_type = EnumProperty(
+ name="Lattice Type",
+ description="Choose Lattice Type",
+ items=lat_types,
+ default='KEY_LINEAR'
+ )
+
+ @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_w")
+ col.prop(self, "lat_m")
+
+ layout.prop(self, "lat_type")
+
+ def execute(self, context):
+ lat_u = self.lat_u
+ lat_w = self.lat_w
+ lat_m = self.lat_m
+
+ # enum property no need to complicate things
+ lat_type = self.lat_type
+ lat_props = [lat_u, lat_w, lat_m, lat_type]
+ try:
+ main(context, lat_props)
+
+ except Exception as e:
+ print("\n[Add Advanced Objects]\nOperator:object.easy_lattice\n{}\n".format(e))
+ self.report({'WARNING'},
+ "Easy Lattice Creator could not be completed (See Console for more info)")
+
+ 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
new file mode 100644
index 00000000..8b182c82
--- /dev/null
+++ b/add_advanced_objects_menu/object_add_chain.py
@@ -0,0 +1,179 @@
+# ##### 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
+
+ # Reseting 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),
+ constraint_orientation='GLOBAL',
+ mirror=False,
+ proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH',
+ proportional_size=1,
+ snap=False,
+ snap_target='CLOSEST',
+ snap_point=(0, 0, 0),
+ snap_align=False,
+ snap_normal=(0, 0, 0),
+ release_confirm=False,
+ )
+
+ # Toggle into objectmode
+ bpy.ops.object.editmode_toggle()
+
+ # 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
new file mode 100644
index 00000000..6dcd6f80
--- /dev/null
+++ b/add_advanced_objects_menu/oscurart_chain_maker.py
@@ -0,0 +1,288 @@
+# ##### 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.scene.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.scene.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.scene.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.scene.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.scene.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
new file mode 100644
index 00000000..d2b28971
--- /dev/null
+++ b/add_advanced_objects_menu/pixelate_3d.py
@@ -0,0 +1,114 @@
+# gpl author: liero
+# very simple 'pixelization' or 'voxelization' engine #
+
+bl_info = {
+ "name": "3D Pixelate",
+ "author": "liero",
+ "version": (0, 5, 2),
+ "blender": (2, 74, 0),
+ "location": "View3D > Tool Shelf",
+ "description": "Creates a 3d pixelated version of the object",
+ "category": "Object"}
+
+# Note: winmgr properties are moved into __init__
+# search for patterns advanced_objects and adv_obj
+
+import bpy
+from bpy.types import Operator
+
+
+def pix(obj):
+ sce = bpy.context.scene
+ props = sce.advanced_objects
+ 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.dupli_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 > props.pixelate_3d_size:
+ ver[i.vertices[0]].select = True
+ ver[i.vertices[1]].select = True
+ fin = False
+ bpy.ops.object.editmode_toggle()
+ bpy.ops.mesh.subdivide(number_cuts=1, smoothness=props.pixelate_3d_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]) % props.pixelate_3d_size
+
+ bpy.ops.object.mode_set(mode='EDIT', toggle=False)
+ bpy.ops.mesh.select_all(action='SELECT')
+ bpy.ops.mesh.remove_doubles(threshold=0.0001)
+ bpy.ops.mesh.delete(type='EDGE_FACE')
+ bpy.ops.object.mode_set()
+ sca = props.pixelate_3d_size * (100 - props.pixelate_3d_gap) * .005
+ bpy.ops.mesh.primitive_cube_add(layers=[True] + [False] * 19)
+ bpy.ops.transform.resize(value=[sca] * 3)
+ bpy.context.scene.objects.active = dup
+ bpy.ops.object.parent_set(type='OBJECT')
+
+
+class Pixelate(Operator):
+ bl_idname = "object.pixelate"
+ bl_label = "Pixelate Object"
+ bl_description = ("Create a 3d pixelated version of the object\n"
+ "using a Duplivert Box around each copied vertex\n"
+ "With high poly objects, it can take some time\n"
+ "Needs an existing Active Mesh Object")
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return (context.active_object and
+ context.active_object.type == 'MESH' and
+ context.mode == 'OBJECT')
+
+ def draw(self, context):
+ layout = self.layout
+ adv_obj = context.scene.advanced_objects
+
+ col = layout.column(align=True)
+ col.prop(adv_obj, "pixelate_size")
+ col.prop(adv_obj, "pixelate_gap")
+ layout.prop(adv_obj, "pixelate_smooth")
+
+ def execute(self, context):
+ objeto = context.active_object
+ try:
+ pix(objeto)
+
+ except Exception as e:
+ self.report({'WARNING'},
+ "Some operations could not be performed (See Console for more info)")
+
+ print("\n[Add Advanced Objects]\nOperator: "
+ "object.pixelate\nError: {}".format(e))
+
+ return {'CANCELLED'}
+
+ return {'FINISHED'}
+
+
+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
new file mode 100644
index 00000000..fa4b6497
--- /dev/null
+++ b/add_advanced_objects_menu/random_box_structure.py
@@ -0,0 +1,201 @@
+# 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.group.create(name='Cubagrouper')
+ bpy.ops.group.objects_remove()
+
+ for ob in bpy.context.selected_objects:
+ oblst.append(ob)
+
+ for obj in oblst:
+ bpy.ops.object.select_pattern(pattern=obj.name) # Select base mesh
+ bpy.context.scene.objects.active = obj
+ if obj.data.uv_layers[:] != []:
+ uvyes = 1
+ else:
+ uvyes = 0
+ bpy.ops.object.group_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),
+ constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
+ )
+ bpy.ops.object.mode_set(mode='OBJECT')
+ select = bpy.context.object # This is used to keep something selected for poll()
+ bpy.ops.object.group_link(group='Cubagrouper')
+ rsdchange += 3
+ bpy.ops.object.select_grouped(type='GROUP')
+ bpy.ops.transform.rotate(
+ value=rot[0], axis=(1, 0, 0), constraint_axis=(False, False, False),
+ constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
+ )
+ bpy.ops.transform.rotate(
+ value=rot[1], axis=(0, 1, 0), constraint_axis=(False, False, False),
+ constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
+ )
+ bpy.ops.transform.rotate(
+ value=rot[2], axis=(0, 0, 1), constraint_axis=(False, False, False),
+ constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
+ )
+ bpy.context.scene.objects.active = obj # Again needed to avoid poll() taking me down
+ bpy.ops.object.make_links_data(type='MODIFIERS')
+ bpy.ops.object.make_links_data(type='MATERIAL')
+
+ if uvyes == 1:
+ bpy.ops.object.join_uvs()
+
+ bpy.ops.group.objects_remove()
+ bpy.context.scene.objects.active = select
+
+ if self.dc is True:
+ bpy.context.scene.objects.unlink(obj)
+
+ return {'FINISHED'}
+
+
+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
new file mode 100644
index 00000000..904168a1
--- /dev/null
+++ b/add_advanced_objects_menu/rope_alpha.py
@@ -0,0 +1,832 @@
+# 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, 7, 3),
+ "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 = True
+
+ scn.objects.active = bpy.data.objects[nombre]
+
+
+def deseleccionar_por_nombre(nombre):
+ bpy.data.objects[nombre].select = 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),
+ "constraint_orientation": 'GLOBAL', "mirror": False,
+ "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
+ "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
+ "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
+ "gpencil_strokes": False, "texture_space": False,
+ "remove_on_cancel": False, "release_confirm": False
+ }
+ )
+
+
+def select_all_vertex_in_curve_bezier(bc):
+ 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")
+
+ ropelenght = 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.ropelenght
+
+ # 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 assing it
+ ob.vertex_groups[1].name = "Guide_rope"
+
+ # extrude the Curve so it has a minumum thickness for collide
+ bpy.ops.mesh.extrude_region_move(
+ MESH_OT_extrude_region={"mirror": False},
+ TRANSFORM_OT_translate={
+ "value": (0, 0.005, 0), "constraint_axis": (False, True, False),
+ "constraint_orientation": 'GLOBAL', "mirror": False,
+ "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
+ "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
+ "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
+ "gpencil_strokes": False, "texture_space": False,
+ "remove_on_cancel": False, "release_confirm": False
+ }
+ )
+ 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),
+ "constraint_orientation": 'GLOBAL', "mirror": False,
+ "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
+ "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
+ "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
+ "gpencil_strokes": False, "texture_space": False,
+ "remove_on_cancel": False, "release_confirm": False
+ }
+ )
+ # separate the selections:
+ bpy.ops.mesh.separate(type='SELECTED')
+ salir_de_editmode()
+ deseleccionar_todo()
+
+ 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("Rope settings:")
+ rowsub0 = col.row()
+ rowsub0.prop(self, "ropelenght", text="Length")
+ rowsub0.prop(self, "ropesegments", text="Segments")
+ rowsub0.prop(self, "radiusrope", text="Radius")
+
+ col.label("Quality Settings:")
+ col.prop(self, "resrope", text="Resolution curve")
+ col.prop(self, "qcr", text="Quality Collision")
+ col.prop(self, "substeps", text="Substeps")
+
+
+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
+ ropelenght2 = 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.ropelenght2
+
+ # 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.draw_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),
+ "constraint_orientation": 'GLOBAL', "mirror": False,
+ "proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
+ "proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
+ "snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
+ "gpencil_strokes": False, "texture_space": False,
+ "remove_on_cancel": False, "release_confirm": False
+ }
+ )
+ bpy.ops.object.hook_add_selob(use_bone=False)
+ salir_de_editmode()
+ bpy.context.object.data.bevel_resolution = resolucion
+ deseleccionar_todo()
+
+ # 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),
+ constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1
+ )
+ bpy.ops.transform.resize(
+ value=(longitud / 2, longitud / 2, longitud / 2),
+ constraint_axis=(False, False, False),
+ constraint_orientation='GLOBAL',
+ mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1
+ )
+ 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),
+ constraint_orientation='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),
+ constraint_orientation='GLOBAL',
+ mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH',
+ proportional_size=1
+ )
+ bpy.context.space_data.pivot_point = 'MEDIAN_POINT'
+ deseleccionar_todo()
+
+ seleccionar_por_nombre(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("Rope settings:")
+ rowsub0 = col.row()
+ rowsub0.prop(self, "hidecubes", text="Hide Link Cubes")
+
+ rowsub1 = col.row(align=True)
+ rowsub1.prop(self, "ropelenght2", text="Length")
+ rowsub1.prop(self, "ropesegments2", text="Segments")
+
+ rowsub2 = col.row(align=True)
+ rowsub2.prop(self, "radiuscubes", text="Radius Link Cubes")
+ rowsub2.prop(self, "radiusrope", text="Radius Rope")
+
+ rowsub3 = col.row(align=True)
+ rowsub3.prop(self, "grados", text="Degrees")
+ rowsub3.prop(self, "separacion", text="Separation Link Cubes")
+
+ col.label("Quality Settings:")
+ col.prop(self, "resrope", text="Resolution Rope")
+ col.prop(self, "massball", text="Ball Mass")
+ col.prop(self, "worldsteps", text="World Steps")
+ col.prop(self, "solveriterations", text="Solver Iterarions")
+
+
+# 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
new file mode 100644
index 00000000..f189bb11
--- /dev/null
+++ b/add_advanced_objects_menu/scene_objects_bi.py
@@ -0,0 +1,195 @@
+# 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.draw_size = 0.1
+ bpy.ops.view3d.viewnumpad(type='CAMERA')
+
+ # add point lamp
+ bpy.ops.object.lamp_add(
+ type="POINT", location=(4.07625, 1.00545, 5.90386),
+ rotation=(0.650328, 0.055217, 1.866391)
+ )
+ lamp1 = bpy.context.active_object.data
+ lamp1.name = "Point_Right"
+ lamp1.energy = 1.0
+ lamp1.distance = 30.0
+ lamp1.shadow_method = "RAY_SHADOW"
+ lamp1.use_sphere = True
+
+ # add point lamp2
+ bpy.ops.object.lamp_add(
+ type="POINT", location=(-0.57101, -4.24586, 5.53674),
+ rotation=(1.571, 0, 0.785)
+ )
+ lamp2 = bpy.context.active_object.data
+ lamp2.name = "Point_Left"
+ lamp2.energy = 1.0
+ lamp2.distance = 30.0
+
+ # Add cube
+ bpy.ops.mesh.primitive_cube_add()
+ bpy.ops.object.editmode_toggle()
+ bpy.ops.mesh.subdivide(number_cuts=2)
+ bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
+ bpy.ops.object.editmode_toggle()
+
+ cube = bpy.context.active_object
+ # add new material
+ cubeMaterial = blend_data.materials.new("Cube_Material")
+ bpy.ops.object.material_slot_add()
+ cube.material_slots[0].material = cubeMaterial
+ # Diffuse
+ cubeMaterial.preview_render_type = "CUBE"
+ cubeMaterial.diffuse_color = (1.000, 0.373, 0.00)
+ cubeMaterial.diffuse_shader = 'OREN_NAYAR'
+ cubeMaterial.diffuse_intensity = 1.0
+ cubeMaterial.roughness = 0.09002
+ # Specular
+ cubeMaterial.specular_color = (1.000, 0.800, 0.136)
+ cubeMaterial.specular_shader = "PHONG"
+ cubeMaterial.specular_intensity = 1.0
+ cubeMaterial.specular_hardness = 511.0
+ # Shading
+ cubeMaterial.ambient = 1.00
+ cubeMaterial.use_cubic = False
+ # Transparency
+ cubeMaterial.use_transparency = False
+ cubeMaterial.alpha = 0
+ # Mirror
+ cubeMaterial.raytrace_mirror.use = True
+ cubeMaterial.mirror_color = (1.000, 0.793, 0.0)
+ cubeMaterial.raytrace_mirror.reflect_factor = 0.394
+ cubeMaterial.raytrace_mirror.fresnel = 2.0
+ cubeMaterial.raytrace_mirror.fresnel_factor = 1.641
+ cubeMaterial.raytrace_mirror.fade_to = "FADE_TO_SKY"
+ cubeMaterial.raytrace_mirror.gloss_anisotropic = 1.0
+ # Shadow
+ cubeMaterial.use_transparent_shadows = True
+
+ # Add a texture
+ cubetex = blend_data.textures.new("CloudTex", type='CLOUDS')
+ cubetex.noise_type = 'SOFT_NOISE'
+ cubetex.noise_scale = 0.25
+ mtex = cubeMaterial.texture_slots.add()
+ mtex.texture = cubetex
+ mtex.texture_coords = 'ORCO'
+ mtex.scale = (0.800, 0.800, 0.800)
+ mtex.use_map_mirror = True
+ mtex.mirror_factor = 0.156
+ mtex.use_map_color_diffuse = True
+ mtex.diffuse_color_factor = 0.156
+ mtex.use_map_normal = True
+ mtex.normal_factor = 0.010
+ mtex.blend_type = "ADD"
+ mtex.use_rgb_to_intensity = True
+ mtex.color = (1.000, 0.207, 0.000)
+
+ # Add monkey
+ bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505))
+ bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1))
+ bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0))
+ bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0))
+ bpy.ops.object.modifier_add(type='SUBSURF')
+ bpy.ops.object.shade_smooth()
+ monkey = bpy.context.active_object
+ # add new material
+ monkeyMaterial = blend_data.materials.new("Monkey_Material")
+ bpy.ops.object.material_slot_add()
+ monkey.material_slots[0].material = monkeyMaterial
+ # Material settings
+ monkeyMaterial.preview_render_type = "MONKEY"
+ monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288)
+ monkeyMaterial.specular_color = (0.604, 0.465, 0.136)
+ monkeyMaterial.diffuse_shader = 'LAMBERT'
+ monkeyMaterial.diffuse_intensity = 1.0
+ monkeyMaterial.specular_intensity = 0.3
+ monkeyMaterial.ambient = 0
+ monkeyMaterial.type = 'SURFACE'
+ monkeyMaterial.use_cubic = True
+ monkeyMaterial.use_transparency = False
+ monkeyMaterial.alpha = 0
+ monkeyMaterial.use_transparent_shadows = True
+ monkeyMaterial.raytrace_mirror.use = True
+ monkeyMaterial.raytrace_mirror.reflect_factor = 0.65
+ monkeyMaterial.raytrace_mirror.fade_to = "FADE_TO_MATERIAL"
+
+ # Add plane
+ bpy.ops.mesh.primitive_plane_add(
+ radius=50, view_align=False, enter_editmode=False, location=(0, 0, -1)
+ )
+ bpy.ops.object.editmode_toggle()
+ bpy.ops.transform.rotate(
+ value=-0.8, axis=(0, 0, 1), constraint_axis=(False, False, True),
+ constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
+ proportional_edit_falloff='SMOOTH', proportional_size=1
+ )
+ bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
+ bpy.ops.object.editmode_toggle()
+ plane = bpy.context.active_object
+ # add new material
+ planeMaterial = blend_data.materials.new("Plane_Material")
+ bpy.ops.object.material_slot_add()
+ plane.material_slots[0].material = planeMaterial
+ # Material settings
+ planeMaterial.preview_render_type = "CUBE"
+ planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
+ planeMaterial.specular_color = (0.604, 0.465, 0.136)
+ planeMaterial.specular_intensity = 0.3
+ planeMaterial.ambient = 0
+ planeMaterial.use_cubic = True
+ planeMaterial.use_transparency = False
+ planeMaterial.alpha = 0
+ planeMaterial.use_transparent_shadows = True
+
+ except Exception as e:
+ self.report({'WARNING'},
+ "Some operations could not be performed (See Console for more info)")
+
+ print("\n[Add Advanced Objects]\nOperator: "
+ "bi.add_scene\nError: {}".format(e))
+
+ return {'CANCELLED'}
+
+ return {"FINISHED"}
diff --git a/add_advanced_objects_menu/scene_objects_cycles.py b/add_advanced_objects_menu/scene_objects_cycles.py
new file mode 100644
index 00000000..85e85867
--- /dev/null
+++ b/add_advanced_objects_menu/scene_objects_cycles.py
@@ -0,0 +1,142 @@
+# 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.draw_size = 0.1
+ bpy.ops.view3d.viewnumpad(type='CAMERA')
+
+ # add point lamp
+ bpy.ops.object.lamp_add(
+ type="POINT", location=(4.07625, 1.00545, 5.90386),
+ rotation=(0.650328, 0.055217, 1.866391)
+ )
+ lamp1 = bpy.context.active_object.data
+ lamp1.name = "Point_Right"
+ lamp1.energy = 1.0
+ lamp1.distance = 30.0
+ lamp1.shadow_method = "RAY_SHADOW"
+ lamp1.use_sphere = True
+
+ # add point lamp2
+ bpy.ops.object.lamp_add(
+ type="POINT", location=(-0.57101, -4.24586, 5.53674),
+ rotation=(1.571, 0, 0.785)
+ )
+ lamp2 = bpy.context.active_object.data
+ lamp2.name = "Point_Left"
+ lamp2.energy = 1.0
+ lamp2.distance = 30.0
+
+ # Add cube
+ bpy.ops.mesh.primitive_cube_add()
+ bpy.ops.object.editmode_toggle()
+ bpy.ops.mesh.subdivide(number_cuts=2)
+ bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
+ bpy.ops.object.editmode_toggle()
+ cube = bpy.context.active_object
+
+ # add cube material
+ cubeMaterial = blend_data.materials.new("Cycles_Cube_Material")
+ bpy.ops.object.material_slot_add()
+ cube.material_slots[0].material = cubeMaterial
+ # Diffuse
+ cubeMaterial.preview_render_type = "CUBE"
+ cubeMaterial.diffuse_color = (1.000, 0.373, 0.00)
+ # Cycles
+ cubeMaterial.use_nodes = True
+
+ # Add monkey
+ bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505))
+ bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1))
+ bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0))
+ bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0))
+
+ bpy.ops.object.modifier_add(type='SUBSURF')
+ bpy.ops.object.shade_smooth()
+ monkey = bpy.context.active_object
+
+ # add monkey material
+ monkeyMaterial = blend_data.materials.new("Cycles_Monkey_Material")
+ bpy.ops.object.material_slot_add()
+ monkey.material_slots[0].material = monkeyMaterial
+ # Diffuse
+ monkeyMaterial.preview_render_type = "MONKEY"
+ monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288)
+ # Cycles
+ monkeyMaterial.use_nodes = True
+
+ # Add plane
+ bpy.ops.mesh.primitive_plane_add(
+ radius=50, view_align=False,
+ enter_editmode=False, location=(0, 0, -1)
+ )
+ bpy.ops.object.editmode_toggle()
+ bpy.ops.transform.rotate(
+ value=-0.8, axis=(0, 0, 1),
+ constraint_axis=(False, False, True)
+ )
+ bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
+ bpy.ops.object.editmode_toggle()
+ plane = bpy.context.active_object
+
+ # add plane material
+ planeMaterial = blend_data.materials.new("Cycles_Plane_Material")
+ bpy.ops.object.material_slot_add()
+ plane.material_slots[0].material = planeMaterial
+ # Diffuse
+ planeMaterial.preview_render_type = "FLAT"
+ planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
+ # Cycles
+ planeMaterial.use_nodes = True
+
+ except Exception as e:
+ self.report({'WARNING'},
+ "Some operations could not be performed (See Console for more info)")
+
+ print("\n[Add Advanced Objects]\nOperator: "
+ "objects_cycles.add_scene\nError: {}".format(e))
+
+ return {'CANCELLED'}
+
+ return {'FINISHED'}
diff --git a/add_advanced_objects_menu/scene_texture_render.py b/add_advanced_objects_menu/scene_texture_render.py
new file mode 100644
index 00000000..02d6490b
--- /dev/null
+++ b/add_advanced_objects_menu/scene_texture_render.py
@@ -0,0 +1,78 @@
+# 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.draw_size = 0.1
+
+ # add plane
+ bpy.ops.mesh.primitive_plane_add(enter_editmode=True, location=(0, 0, 0))
+ bpy.ops.mesh.subdivide(number_cuts=10, smoothness=0)
+ bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
+ bpy.ops.object.editmode_toggle()
+ plane = bpy.context.active_object
+
+ # add plane 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
new file mode 100644
index 00000000..e0068e66
--- /dev/null
+++ b/add_advanced_objects_menu/trilighting.py
@@ -0,0 +1,238 @@
+# 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("Position:")
+ col = layout.column(align=True)
+ col.prop(self, "height")
+ col.prop(self, "distance")
+
+ layout.label("Light:")
+ col = layout.column(align=True)
+ col.prop(self, "energy")
+ col.prop(self, "contrast")
+
+ layout.label("Orientation:")
+ col = layout.column(align=True)
+ col.prop(self, "leftangle")
+ col.prop(self, "rightangle")
+ col.prop(self, "backangle")
+
+ col = layout.column()
+ col.label("Key Light Type:")
+ col.prop(self, "primarytype", text="")
+ col.label("Fill + Back Type:")
+ col.prop(self, "secondarytype", text="")
+
+ def execute(self, context):
+ try:
+ scene = context.scene
+ view = context.space_data
+ if view.type == 'VIEW_3D' and not view.lock_camera_and_layers:
+ camera = view.camera
+ else:
+ camera = scene.camera
+
+ if (camera is None):
+ cam_data = bpy.data.cameras.new(name='Camera')
+ cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data)
+ scene.objects.link(cam_obj)
+ scene.camera = cam_obj
+ bpy.ops.view3d.camera_to_view()
+ camera = cam_obj
+ bpy.ops.view3d.viewnumpad(type='TOP')
+
+ obj = bpy.context.scene.objects.active
+
+ # Calculate Energy for each Lamp
+ if(self.contrast > 0):
+ keyEnergy = self.energy
+ backEnergy = (self.energy / 100) * abs(self.contrast)
+ fillEnergy = (self.energy / 100) * abs(self.contrast)
+ else:
+ keyEnergy = (self.energy / 100) * abs(self.contrast)
+ backEnergy = self.energy
+ fillEnergy = self.energy
+
+ # Calculate Direction for each Lamp
+
+ # Calculate current Distance and get Delta
+ obj_position = obj.location
+ cam_position = camera.location
+
+ delta_position = cam_position - obj_position
+ vector_length = sqrt(
+ (pow(delta_position.x, 2) +
+ pow(delta_position.y, 2) +
+ pow(delta_position.z, 2))
+ )
+ if not vector_length:
+ # division by zero most likely
+ self.report({'WARNING'},
+ "Operation Cancelled. No viable object in the scene")
+
+ return {'CANCELLED'}
+
+ single_vector = (1 / vector_length) * delta_position
+
+ # Calc back position
+ singleback_vector = single_vector.copy()
+ singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \
+ (-sin(radians(self.backangle)) * single_vector.y)
+
+ singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \
+ (cos(radians(self.backangle)) * single_vector.y)
+
+ backx = obj_position.x + self.distance * singleback_vector.x
+ backy = obj_position.y + self.distance * singleback_vector.y
+
+ backData = bpy.data.lamps.new(name="TriLamp-Back", type=self.secondarytype)
+ backData.energy = backEnergy
+
+ backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData)
+ scene.objects.link(backLamp)
+ backLamp.location = (backx, backy, self.height)
+
+ trackToBack = backLamp.constraints.new(type="TRACK_TO")
+ trackToBack.target = obj
+ trackToBack.track_axis = "TRACK_NEGATIVE_Z"
+ trackToBack.up_axis = "UP_Y"
+
+ # Calc right position
+ singleright_vector = single_vector.copy()
+ singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \
+ (-sin(radians(self.rightangle)) * single_vector.y)
+
+ singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \
+ (cos(radians(self.rightangle)) * single_vector.y)
+
+ rightx = obj_position.x + self.distance * singleright_vector.x
+ righty = obj_position.y + self.distance * singleright_vector.y
+
+ rightData = bpy.data.lamps.new(name="TriLamp-Fill", type=self.secondarytype)
+ rightData.energy = fillEnergy
+ rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData)
+ scene.objects.link(rightLamp)
+ rightLamp.location = (rightx, righty, self.height)
+ trackToRight = rightLamp.constraints.new(type="TRACK_TO")
+ trackToRight.target = obj
+ trackToRight.track_axis = "TRACK_NEGATIVE_Z"
+ trackToRight.up_axis = "UP_Y"
+
+ # Calc left position
+ singleleft_vector = single_vector.copy()
+ singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \
+ (-sin(radians(-self.leftangle)) * single_vector.y)
+ singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \
+ (cos(radians(-self.leftangle)) * single_vector.y)
+ leftx = obj_position.x + self.distance * singleleft_vector.x
+ lefty = obj_position.y + self.distance * singleleft_vector.y
+
+ leftData = bpy.data.lamps.new(name="TriLamp-Key", type=self.primarytype)
+ leftData.energy = keyEnergy
+
+ leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData)
+ scene.objects.link(leftLamp)
+ leftLamp.location = (leftx, lefty, self.height)
+ trackToLeft = leftLamp.constraints.new(type="TRACK_TO")
+ trackToLeft.target = obj
+ trackToLeft.track_axis = "TRACK_NEGATIVE_Z"
+ trackToLeft.up_axis = "UP_Y"
+
+ except Exception as e:
+ self.report({'WARNING'},
+ "Some operations could not be performed (See Console for more info)")
+
+ print("\n[Add Advanced Objects]\nOperator: "
+ "object.trilighting\nError: {}".format(e))
+
+ return {'CANCELLED'}
+
+ return {'FINISHED'}