Remove to contrib: add advanced objects series: T63750

This commit is contained in:
Brendon Murphy 2019-04-28 11:56:49 +10:00
parent 654d27c6ca
commit 713fa9b2c4
Notes: blender-bot 2023-02-14 18:07:15 +01:00
Referenced by commit f3d58200: lighting_tri_lights: Initial commit to release (standalone) T65823 713fa9b2c4
Referenced by commit f3d58200, lighting_tri_lights: Initial commit to release (standalone) T65823 713fa9b2c4
26 changed files with 0 additions and 10474 deletions

View File

@ -1,547 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Contributed to by:
# meta-androcto, Bill Currie, Jorge Hernandez - Melenedez Jacob Morris, Oscurart #
# Rebellion, Antonis Karvelas, Eleanor Howick, lijenstina, Daniel Schalla, Domlysz #
# Unnikrishnan(kodemax), Florian Meyer, Omar ahmed, Brian Hinton (Nichod), liero #
# Atom, Dannyboy, Mano-Wii, Kursad Karatas, teldredge, Phil Cote #
bl_info = {
"name": "Add Advanced Objects",
"author": "Meta Androcto",
"version": (0, 1, 6),
"blender": (2, 78, 0),
"location": "View3D > Add ",
"description": "Add Object & Camera extras",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
"/Py/Scripts/Object/Add_Advanced",
"category": "Object"}
if "bpy" in locals():
import importlib
importlib.reload(add_light_template)
importlib.reload(scene_objects_bi)
importlib.reload(scene_objects_cycles)
importlib.reload(scene_texture_render)
importlib.reload(trilighting)
importlib.reload(pixelate_3d)
importlib.reload(object_add_chain)
importlib.reload(oscurart_chain_maker)
importlib.reload(circle_array)
importlib.reload(copy2)
importlib.reload(make_struts)
importlib.reload(random_box_structure)
importlib.reload(cubester)
importlib.reload(rope_alpha)
importlib.reload(add_mesh_aggregate)
importlib.reload(arrange_on_curve)
importlib.reload(mesh_easylattice)
else:
from . import add_light_template
from . import scene_objects_bi
from . import scene_objects_cycles
from . import scene_texture_render
from . import trilighting
from . import pixelate_3d
from . import object_add_chain
from . import oscurart_chain_maker
from . import circle_array
from . import copy2
from . import make_struts
from . import random_box_structure
from . import cubester
from . import rope_alpha
from . import add_mesh_aggregate
from . import arrange_on_curve
from . import mesh_easylattice
import bpy
from bpy.types import (
AddonPreferences,
Menu,
PropertyGroup,
)
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
IntProperty,
StringProperty,
PointerProperty,
)
# Define the "Scenes" menu
class VIEW3D_MT_scene_elements_add(Menu):
bl_idname = "VIEW3D_MT_scene_elements"
bl_label = "Test Scenes"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("bi.add_scene",
text="Scene_Objects_BI")
layout.operator("objects_cycles.add_scene",
text="Scene_Objects_Cycles")
layout.operator("objects_texture.add_scene",
text="Scene_Textures_Cycles")
# Define the "Lights" menu
class VIEW3D_MT_mesh_lights_add(Menu):
bl_idname = "VIEW3D_MT_scene_lights"
bl_label = "Lighting Sets"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("object.add_light_template",
text="Add Light Template")
layout.operator("object.trilighting",
text="Add Tri Lighting")
# Define the "Chains" menu
class VIEW3D_MT_mesh_chain_add(Menu):
bl_idname = "VIEW3D_MT_mesh_chain"
bl_label = "Chains"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.primitive_chain_add", icon="LINKED")
layout.operator("mesh.primitive_oscurart_chain_add", icon="LINKED")
# Define the "Array" Menu
class VIEW3D_MT_array_mods_add(Menu):
bl_idname = "VIEW3D_MT_array_mods"
bl_label = "Array Mods"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.menu("VIEW3D_MT_mesh_chain", icon="LINKED")
layout.operator("objects.circle_array_operator",
text="Circle Array", icon="MOD_ARRAY")
layout.operator("object.agregate_mesh",
text="Aggregate Mesh", icon="MOD_ARRAY")
layout.operator("mesh.copy2",
text="Copy To Vert/Edge", icon="MOD_ARRAY")
# Define the "Blocks" Menu
class VIEW3D_MT_quick_blocks_add(Menu):
bl_idname = "VIEW3D_MT_quick_tools"
bl_label = "Block Tools"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("object.pixelate", icon="MESH_GRID")
layout.operator("mesh.generate_struts",
text="Struts", icon="GRID")
layout.operator("object.make_structure",
text="Random Boxes", icon="SEQ_SEQUENCER")
layout.operator("object.easy_lattice",
text="Easy Lattice", icon="MOD_LATTICE")
# Define the "Phsysics Tools" Menu
class VIEW3D_MT_Physics_tools_add(Menu):
bl_idname = "VIEW3D_MT_physics_tools"
bl_label = "Physics Tools"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("ball.rope",
text="Wrecking Ball", icon='PHYSICS')
layout.operator("clot.rope",
text="Cloth Rope", icon='PHYSICS')
# Define "Extras" menu
def menu(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
self.layout.separator()
self.layout.menu("VIEW3D_MT_scene_elements", icon="SCENE_DATA")
self.layout.menu("VIEW3D_MT_scene_lights", icon="LIGHT_SPOT")
self.layout.separator()
self.layout.menu("VIEW3D_MT_array_mods", icon="MOD_ARRAY")
self.layout.menu("VIEW3D_MT_quick_tools", icon="MOD_BUILD")
self.layout.menu("VIEW3D_MT_physics_tools", icon="PHYSICS")
# Addons Preferences
class AdvancedObjPreferences(AddonPreferences):
bl_idname = __name__
show_menu_list: BoolProperty(
name="Menu List",
description="Show/Hide the Add Menu items",
default=False
)
show_panel_list: BoolProperty(
name="Panels List",
description="Show/Hide the Panel items",
default=False
)
def draw(self, context):
layout = self.layout
icon_1 = "TRIA_RIGHT" if not self.show_menu_list else "TRIA_DOWN"
box = layout.box()
box.prop(self, "show_menu_list", emboss=False, icon=icon_1)
if self.show_menu_list:
box.label(text="Items located in the Add Menu (default shortcut Ctrl + A):",
icon="LAYER_USED")
box.label(text="Test Scenes:", icon="LAYER_ACTIVE")
box.label(text="Scene Objects BI, Scene Objects Cycles, Scene Textures Cycles",
icon="LAYER_USED")
box.label(text="Lighting Sets:", icon="LAYER_ACTIVE")
box.label(text="Add Light Template, Add Tri Lighting", icon="LAYER_USED")
box.label(text="Array Mods:", icon="LAYER_ACTIVE")
box.label(text="Circle Array, Chains submenu, Copy Vert/Edge and Aggregate Mesh",
icon="LAYER_ACTIVE")
box.label(text="Chains Submenu - Add Chain, Chain to Bones",
icon="LAYER_ACTIVE")
box.label(text="Block Tools:", icon="LAYER_ACTIVE")
box.label(text="Pixelate Object, Struts, Random Boxes, Easy Lattice",
icon="LAYER_USED")
box.label(text="Physics Tools:", icon="LAYER_ACTIVE")
box.label(text="Wrecking Ball and Cloth Rope", icon="LAYER_USED")
icon_2 = "TRIA_RIGHT" if not self.show_panel_list else "TRIA_DOWN"
box = layout.box()
box.prop(self, "show_panel_list", emboss=False, icon=icon_2)
if self.show_panel_list:
box.label(text="Panels located in 3D View Tools Region > Create",
icon="LAYER_ACTIVE")
box.label(text="CubeSter", icon="LAYER_USED")
box.label(text="Arrange on Curve (Shown if an Active Curve Object is it the 3D View)",
icon="LAYER_USED")
# Cubester update functions
def find_audio_length(self, context):
adv_obj = context.scene.advanced_objects
audio_file = adv_obj.cubester_audio_path
length = 0
if audio_file != "":
# confirm that strip hasn't been loaded yet
get_sequence = getattr(context.scene.sequence_editor, "sequences_all", [])
for strip in get_sequence:
if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file:
length = strip.frame_final_duration
if length == 0:
area = context.area
old_type = area.type
area.type = "SEQUENCE_EDITOR"
try:
bpy.ops.sequencer.sound_strip_add(filepath=audio_file)
adv_obj.cubester_check_audio = True
except Exception as e:
print("\n[Add Advanced Objects]\n Function: "
"find_audio_length\n {}\n".format(e))
adv_obj.cubester_check_audio = False
pass
area.type = old_type
# find audio file
for strip in context.scene.sequence_editor.sequences_all:
if type(strip) == bpy.types.SoundSequence and strip.sound.filepath == audio_file:
adv_obj.cubester_check_audio = True
length = strip.frame_final_duration
adv_obj.cubester_audio_file_length = length
# load image if possible
def adjust_selected_image(self, context):
scene = context.scene.advanced_objects
try:
image = bpy.data.images.load(scene.cubester_load_image)
scene.cubester_image = image.name
except Exception as e:
print("\n[Add Advanced Objects]\n Function: "
"adjust_selected_image\n {}\n".format(e))
# load color image if possible
def adjust_selected_color_image(self, context):
scene = context.scene.advanced_objects
try:
image = bpy.data.images.load(scene.cubester_load_color_image)
scene.cubester_color_image = image.name
except Exception as e:
print("\nAdd Advanced Objects]\n Function: "
"adjust_selected_color_image\n {}\n".format(e))
class AdvancedObjProperties(PropertyGroup):
# cubester
# main properties
cubester_check_audio: BoolProperty(
name="",
default=False
)
cubester_audio_image: EnumProperty(
name="Input Type",
items=(("image", "Image",
"Use an Image as input for generating Geometry", "IMAGE_COL", 0),
("audio", "Audio",
"Use a Sound Strip as input for generating Geometry", "FILE_SOUND", 1))
)
cubester_audio_file_length: IntProperty(
default=0
)
# audio
cubester_audio_path: StringProperty(
default="",
name="Audio File",
subtype="FILE_PATH",
update=find_audio_length
)
cubester_audio_min_freq: IntProperty(
name="Minimum Frequency",
min=20, max=100000,
default=20
)
cubester_audio_max_freq: IntProperty(
name="Maximum Frequency",
min=21, max=999999,
default=5000
)
cubester_audio_offset_type: EnumProperty(
name="Offset Type",
items=(("freq", "Frequency Offset", ""),
("frame", "Frame Offset", "")),
description="Type of offset per row of mesh"
)
cubester_audio_frame_offset: IntProperty(
name="Frame Offset",
min=0, max=10,
default=2
)
cubester_audio_block_layout: EnumProperty(
name="Block Layout",
items=(("rectangle", "Rectangular", ""),
("radial", "Radial", ""))
)
cubester_audio_width_blocks: IntProperty(
name="Width Block Count",
min=1, max=10000,
default=5
)
cubester_audio_length_blocks: IntProperty(
name="Length Block Count",
min=1, max=10000,
default=50
)
# image
cubester_load_type: EnumProperty(
name="Image Input Type",
items=(("single", "Single Image", ""),
("multiple", "Image Sequence", ""))
)
cubester_image: StringProperty(
default="",
name=""
)
cubester_load_image: StringProperty(
default="",
name="Load Image",
subtype="FILE_PATH",
update=adjust_selected_image
)
cubester_skip_images: IntProperty(
name="Image Step",
min=1, max=30,
default=1,
description="Step from image to image by this number"
)
cubester_max_images: IntProperty(
name="Max Number Of Images",
min=2, max=1000,
default=10,
description="Maximum number of images to be used"
)
cubester_frame_step: IntProperty(
name="Frame Step Size",
min=1, max=10,
default=4,
description="The number of frames each picture is used"
)
cubester_skip_pixels: IntProperty(
name="Skip # Pixels",
min=0, max=256,
default=64,
description="Skip this number of pixels before placing the next"
)
cubester_mesh_style: EnumProperty(
name="Mesh Type",
items=(("blocks", "Blocks", ""),
("plane", "Plane", "")),
description="Compose mesh of multiple blocks or of a single plane"
)
cubester_block_style: EnumProperty(
name="Block Style",
items=(("size", "Vary Size", ""),
("position", "Vary Position", "")),
description="Vary Z-size of block, or vary Z-position"
)
cubester_height_scale: FloatProperty(
name="Height Scale",
subtype="DISTANCE",
min=0.1, max=2,
default=0.2
)
cubester_invert: BoolProperty(
name="Invert Height",
default=False
)
# general adjustments
cubester_size_per_hundred_pixels: FloatProperty(
name="Size Per 100 Blocks/Points",
subtype="DISTANCE",
min=0.001, max=5,
default=1
)
# material based stuff
cubester_materials: EnumProperty(
name="Material",
items=(("vertex", "Vertex Colors", ""),
("image", "Image", "")),
description="Color with vertex colors, or uv unwrap and use an image"
)
cubester_use_image_color: BoolProperty(
name="Use Original Image Colors'?",
default=True,
description="Use original image colors, or replace with an another one"
)
cubester_color_image: StringProperty(
default="",
name=""
)
cubester_load_color_image: StringProperty(
default="",
name="Load Color Image",
subtype="FILE_PATH",
update=adjust_selected_color_image
)
cubester_vertex_colors = {}
# advanced
cubester_advanced: BoolProperty(
name="Advanced Options",
default=False
)
cubester_random_weights: BoolProperty(
name="Random Weights",
default=False
)
cubester_weight_r: FloatProperty(
name="Red",
subtype="FACTOR",
min=0.01, max=1.0,
default=0.25
)
cubester_weight_g: FloatProperty(
name="Green",
subtype="FACTOR",
min=0.01, max=1.0,
default=0.25
)
cubester_weight_b: FloatProperty(
name="Blue",
subtype="FACTOR",
min=0.01, max=1.0,
default=0.25
)
cubester_weight_a: FloatProperty(
name="Alpha",
subtype="FACTOR",
min=0.01, max=1.0,
default=0.25
)
# arrange_on_curve
arrange_c_use_selected: BoolProperty(
name="Use Selected",
description="Use the selected objects to duplicate",
default=True,
)
arrange_c_obj_arranjar: StringProperty(
name=""
)
arrange_c_select_type: EnumProperty(
name="Type",
description="Select object or group",
items=[
('O', "Object", "Make duplicates of a specific object"),
('G', "Group", "Make duplicates of the objects in a group"),
],
default='O',
)
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.advanced_objects = PointerProperty(
type=AdvancedObjProperties
)
# Add "Extras" menu to the "Add" menu
bpy.types.VIEW3D_MT_add.append(menu)
try:
bpy.types.VIEW3D_MT_AddMenu.append(menu)
except:
pass
def unregister():
# Remove "Extras" menu from the "Add" menu.
bpy.types.VIEW3D_MT_add.remove(menu)
try:
bpy.types.VIEW3D_MT_AddMenu.remove(menu)
except:
pass
bpy.utils.unregister_module(__name__)
del bpy.types.Scene.advanced_objects
if __name__ == "__main__":
register()

View File

@ -1,145 +0,0 @@
# gpl: author Rebellion
import bpy
from bpy.types import Operator
from bpy.props import BoolProperty
def add_lights(self, context):
if self.bKeyLight:
keyLight = bpy.data.lights.new(name="Key_Light", type="SPOT")
ob = bpy.data.objects.new("Key_Light", keyLight)
constraint = ob.constraints.new(type='COPY_LOCATION')
constraint.use_offset = True
constraint.owner_space = 'LOCAL'
constraint.target = self.camera
constraint = ob.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_X'
constraint.owner_space = 'LOCAL'
bpy.context.collection.objects.link(ob)
ob.rotation_euler[2] = -0.785398
if self.bFillLight:
fillLight = bpy.data.lights.new(name="Fill_Light", type="SPOT")
ob = bpy.data.objects.new("Fill_Light", fillLight)
constraint = ob.constraints.new(type='COPY_LOCATION')
constraint.use_offset = True
constraint.owner_space = 'LOCAL'
constraint.target = self.camera
constraint = ob.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_X'
constraint.owner_space = 'LOCAL'
bpy.context.collection.objects.link(ob)
ob.rotation_euler[2] = 0.785398
ob.data.energy = 0.3
if self.bBackLight:
backLight = bpy.data.lights.new(name="Back_Light", type="SPOT")
ob = bpy.data.objects.new("Back_Light", backLight)
constraint = ob.constraints.new(type='COPY_LOCATION')
constraint.use_offset = True
constraint.owner_space = 'LOCAL'
constraint.target = self.camera
constraint = ob.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_X'
constraint.owner_space = 'LOCAL'
bpy.context.collection.objects.link(ob)
ob.rotation_euler[2] = 3.14159
ob.data.energy = 0.2
if self.camera_constraint and self.camera is not None and \
self.camera.type == "CAMERA":
constraint = self.camera.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_Y'
class OBJECT_OT_add_light_template(Operator):
bl_idname = "object.add_light_template"
bl_label = "Add Light Template"
bl_description = ("Add Key, Fill and Back Lights to the Scene\n"
"Needs an existing Active Object")
bl_options = {'REGISTER', 'UNDO'}
camera = None
target = None
bKeyLight: BoolProperty(
name="Key Light",
description="Enable Key Light in the Scene",
default=True
)
bFillLight: BoolProperty(
name="Fill Light",
description="Enable Fill Light in the Scene",
default=True
)
bBackLight: BoolProperty(
name="Back Light",
description="Enable Back Light in the Scene",
default=True
)
camera_constraint: BoolProperty(
name="Camera Constraint",
description="Add a Constraint to the Camera Object",
default=False
)
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
try:
objects = context.selected_objects
if len(objects) == 2:
for ob in objects:
if ob.type == 'CAMERA':
self.camera = ob
else:
self.target = ob
elif len(objects) == 1:
if objects[0].type == 'CAMERA':
self.camera = objects[0]
bpy.ops.object.empty_add()
self.target = context.active_object
else:
self.camera = context.scene.camera
self.target = context.active_object
elif len(objects) == 0:
bpy.ops.object.empty_add()
self.target = context.active_object
self.camera = context.scene.camera
add_lights(self, context)
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"object.add_light_template\nError: {}".format(e))
return {'FINISHED'}
def register():
bpy.utils.register_class(OBJECT_OT_add_light_template)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_add_light_template)
if __name__ == "__main__":
register()

View File

@ -1,338 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Simple aggregate of particles / meshes
# Copy the selected objects on the active object
# Based on the position of the cursor and a defined volume
# Allows to control growth by using a Build modifier
bl_info = {
"name": "Aggregate Mesh",
"author": "liero",
"version": (0, 0, 5),
"blender": (2, 70, 0),
"location": "View3D > Tool Shelf",
"description": "Adds geometry to a mesh like in DLA aggregators",
"category": "Object"}
import bpy
import bmesh
from random import (
choice,
gauss,
seed,
)
from mathutils import Matrix
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
)
from bpy.types import Operator
def use_random_seed(self):
seed(self.rSeed)
return
def rg(n):
return (round(gauss(0, n), 2))
def remover(sel=False):
bpy.ops.object.editmode_toggle()
if sel:
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.object.mode_set()
class OBJECT_OT_agregate_mesh(Operator):
bl_idname = "object.agregate_mesh"
bl_label = "Aggregate"
bl_description = ("Adds geometry to a mesh like in DLA aggregators\n"
"Needs at least two selected Mesh objects")
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
volX: FloatProperty(
name="Volume X",
min=0.1, max=25,
default=3,
description="The cloud around cursor"
)
volY: FloatProperty(
name="Volume Y",
min=0.1, max=25,
default=3,
description="The cloud around cursor"
)
volZ: FloatProperty(
name="Volume Z",
min=0.1, max=25,
default=3,
description="The cloud around cursor"
)
baseSca: FloatProperty(
name="Scale",
min=0.01, max=5,
default=.25,
description="Particle Scale"
)
varSca: FloatProperty(
name="Var",
min=0, max=1,
default=0,
description="Particle Scale Variation"
)
rotX: FloatProperty(
name="Rot Var X",
min=0, max=2,
default=0,
description="X Rotation Variation"
)
rotY: FloatProperty(
name="Rot Var Y",
min=0, max=2,
default=0,
description="Y Rotation Variation"
)
rotZ: FloatProperty(
name="Rot Var Z",
min=0, max=2,
default=1,
description="Z Rotation Variation"
)
rSeed: IntProperty(
name="Random seed",
min=0, max=999999,
default=1,
description="Seed to feed random values"
)
numP: IntProperty(
name="Number",
min=1,
max=9999, soft_max=500,
default=50,
description="Number of particles"
)
nor: BoolProperty(
name="Normal Oriented",
default=False,
description="Align Z axis with Faces normals"
)
cent: BoolProperty(
name="Use Face Center",
default=False,
description="Center on Faces"
)
track: BoolProperty(
name="Cursor Follows",
default=False,
description="Cursor moves as structure grows / more compact results"
)
anim: BoolProperty(
name="Animatable",
default=False,
description="Sort faces so you can regrow with Build Modifier, materials are lost"
)
refresh: BoolProperty(
name="Update",
default=False
)
auto_refresh: BoolProperty(
name="Auto",
description="Auto update spline",
default=False
)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
row = col.row(align=True)
if self.auto_refresh is False:
self.refresh = False
elif self.auto_refresh is True:
self.refresh = True
row.prop(self, "auto_refresh", toggle=True, icon="AUTO")
row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH")
col = layout.column(align=True)
col.separator()
col = layout.column(align=True)
col.prop(self, "volX", slider=True)
col.prop(self, "volY", slider=True)
col.prop(self, "volZ", slider=True)
layout.label(text="Particles:")
col = layout.column(align=True)
col.prop(self, "baseSca", slider=True)
col.prop(self, "varSca", slider=True)
col = layout.column(align=True)
col.prop(self, "rotX", slider=True)
col.prop(self, "rotY", slider=True)
col.prop(self, "rotZ", slider=True)
col = layout.column(align=True)
col.prop(self, "rSeed", slider=False)
col.prop(self, "numP")
row = layout.row(align=True)
row.prop(self, "nor")
row.prop(self, "cent")
row = layout.row(align=True)
row.prop(self, "track")
row.prop(self, "anim")
@classmethod
def poll(cls, context):
return (len(bpy.context.selected_objects) > 1 and
bpy.context.object.type == 'MESH')
def invoke(self, context, event):
self.refresh = True
return self.execute(context)
def execute(self, context):
if not self.refresh:
return {'PASS_THROUGH'}
scn = bpy.context.scene
obj = bpy.context.active_object
use_random_seed(self)
mat = Matrix((
(1, 0, 0, 0),
(0, 1, 0, 0),
(0, 0, 1, 0),
(0, 0, 0, 1))
)
if obj.matrix_world != mat:
self.report({'WARNING'},
"Please, Apply transformations to Active Object first")
return{'FINISHED'}
par = [o for o in bpy.context.selected_objects if o.type == 'MESH' and o != obj]
if not par:
return{'FINISHED'}
bpy.ops.object.mode_set()
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
msv = []
for i in range(len(obj.modifiers)):
msv.append(obj.modifiers[i].show_viewport)
obj.modifiers[i].show_viewport = False
cur = scn.cursor.location
for i in range(self.numP):
mes = choice(par).data
newobj = bpy.data.objects.new('nuevo', mes)
scn.objects.link(newobj)
origen = (rg(self.volX) + cur[0], rg(self.volY) + cur[1], rg(self.volZ) + cur[2])
cpom = obj.closest_point_on_mesh(origen)
if self.cent:
bm = bmesh.new()
bm.from_mesh(obj.data)
if hasattr(bm.verts, "ensure_lookup_table"):
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
newobj.location = bm.faces[cpom[3]].calc_center_median()
bm.free()
else:
newobj.location = cpom[1]
if self.nor:
newobj.rotation_mode = 'QUATERNION'
newobj.rotation_quaternion = cpom[1].to_track_quat('Z', 'Y')
newobj.rotation_mode = 'XYZ'
newobj.rotation_euler[0] += rg(self.rotX)
newobj.rotation_euler[1] += rg(self.rotY)
newobj.rotation_euler[2] += rg(self.rotZ)
else:
newobj.rotation_euler = (rg(self.rotX), rg(self.rotY), rg(self.rotZ))
newobj.scale = [self.baseSca + self.baseSca * rg(self.varSca)] * 3
if self.anim:
newobj.select_set(True)
bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', obdata=True)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bme = bmesh.new()
bme.from_mesh(obj.data)
tmp = bmesh.new()
tmp.from_mesh(newobj.data)
for f in tmp.faces:
# z = len(bme.verts)
for v in f.verts:
bme.verts.new(list(v.co))
bme.faces.new(bme.verts[-len(f.verts):])
bme.to_mesh(obj.data)
remover(True)
# Note: foo.user_clear() is deprecated use do_unlink=True instead
bpy.data.meshes.remove(newobj.data, do_unlink=True)
else:
scn.objects.active = obj
newobj.select_set(True)
bpy.ops.object.join()
if self.track:
cur = scn.cursor.location = cpom[1]
for i in range(len(msv)):
obj.modifiers[i].show_viewport = msv[i]
for o in par:
o.select_set(True)
obj.select_set(True)
if self.auto_refresh is False:
self.refresh = False
return{'FINISHED'}
def register():
bpy.utils.register_class(OBJECT_OT_agregate_mesh)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_agregate_mesh)
if __name__ == '__main__':
register()

View File

@ -1,356 +0,0 @@
# gpl author: Mano-Wii
bl_info = {
"name": "Arrange on Curve",
"author": "Mano-Wii",
"version": (6, 3, 0),
"blender": (2, 77, 0),
"location": "3D View > Toolshelf > Create > Arrange on Curve",
"description": "Arrange objects along a curve",
"warning": "Select curve",
"wiki_url": "",
"category": "3D View"
}
# Note: scene properties are moved into __init__
# search for patterns advanced_objects and adv_obj
import bpy
import mathutils
from bpy.types import (
Operator,
Panel,
)
from bpy.props import (
EnumProperty,
FloatProperty,
IntProperty,
)
FLT_MIN = 0.004
class PanelDupliCurve(Panel):
bl_idname = "VIEW3D_PT_arranjar_numa_curva"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_context = "objectmode"
bl_category = "Create"
bl_label = "Arrange on Curve"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return context.object and context.mode == 'OBJECT' and context.object.type == 'CURVE'
def draw(self, context):
layout = self.layout
adv_obj = context.scene.advanced_objects
layout.prop(adv_obj, "arrange_c_use_selected")
if not adv_obj.arrange_c_use_selected:
layout.prop(adv_obj, "arrange_c_select_type", expand=True)
if adv_obj.arrange_c_select_type == 'O':
layout.column(align=True).prop_search(
adv_obj, "arrange_c_obj_arranjar",
bpy.data, "objects"
)
elif adv_obj.arrange_c_select_type == 'G':
layout.column(align=True).prop_search(
adv_obj, "arrange_c_obj_arranjar",
bpy.data, "collections"
)
if context.object.type == 'CURVE':
layout.operator("object.arranjar_numa_curva", text="Arrange Objects")
class DupliCurve(Operator):
bl_idname = "object.arranjar_numa_curva"
bl_label = "Arrange Objects along a Curve"
bl_description = "Arange chosen / selected objects along the Active Curve"
bl_options = {'REGISTER', 'UNDO'}
use_distance: EnumProperty(
name="Arrangement",
items=[
("D", "Distance", "Objects are arranged depending on the distance", 0),
("Q", "Quantity", "Objects are arranged depending on the quantity", 1),
("R", "Range", "Objects are arranged uniformly between the corners", 2)
]
)
distance: FloatProperty(
name="Distance",
description="Distance between Objects",
default=1.0,
min=FLT_MIN,
soft_min=0.1,
unit='LENGTH',
)
object_qt: IntProperty(
name="Quantity",
description="Object amount",
default=2,
min=0,
)
scale: FloatProperty(
name="Scale",
description="Object Scale",
default=1.0,
min=FLT_MIN,
unit='LENGTH',
)
Yaw: FloatProperty(
name="X",
description="Rotate around the X axis (Yaw)",
default=0.0,
unit='ROTATION'
)
Pitch: FloatProperty(
default=0.0,
description="Rotate around the Y axis (Pitch)",
name="Y",
unit='ROTATION'
)
Roll: FloatProperty(
default=0.0,
description="Rotate around the Z axis (Roll)",
name="Z",
unit='ROTATION'
)
max_angle: FloatProperty(
default=1.57079,
max=3.141592,
name="Angle",
unit='ROTATION'
)
offset: FloatProperty(
default=0.0,
name="Offset",
unit='LENGTH'
)
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
def draw(self, context):
layout = self.layout
col = layout.column()
col.prop(self, "use_distance", text="")
col = layout.column(align=True)
if self.use_distance == "D":
col.prop(self, "distance")
elif self.use_distance == "Q":
col.prop(self, "object_qt")
else:
col.prop(self, "distance")
col.prop(self, "max_angle")
col.prop(self, "offset")
col = layout.column(align=True)
col.prop(self, "scale")
col.prop(self, "Yaw")
col.prop(self, "Pitch")
col.prop(self, "Roll")
def Glpoints(self, curve):
Gpoints = []
for i, spline in enumerate(curve.data.splines):
segments = len(spline.bezier_points)
if segments >= 2:
r = spline.resolution_u + 1
points = []
for j in range(segments):
bp1 = spline.bezier_points[j]
inext = (j + 1)
if inext == segments:
if not spline.use_cyclic_u:
break
inext = 0
bp2 = spline.bezier_points[inext]
if bp1.handle_right_type == bp2.handle_left_type == 'VECTOR':
_points = (bp1.co, bp2.co) if j == 0 else (bp2.co,)
else:
knot1 = bp1.co
handle1 = bp1.handle_right
handle2 = bp2.handle_left
knot2 = bp2.co
_points = mathutils.geometry.interpolate_bezier(knot1, handle1, handle2, knot2, r)
points.extend(_points)
Gpoints.append(tuple((curve.matrix_world * p for p in points)))
elif len(spline.points) >= 2:
l = [curve.matrix_world * p.co.xyz for p in spline.points]
if spline.use_cyclic_u:
l.append(l[0])
Gpoints.append(tuple(l))
if self.use_distance == "R":
max_angle = self.max_angle
tmp_Gpoints = []
sp = Gpoints[i]
sp2 = [sp[0], sp[1]]
lp = sp[1]
v1 = lp - sp[0]
for p in sp[2:]:
v2 = p - lp
try:
if (3.14158 - v1.angle(v2)) < max_angle:
tmp_Gpoints.append(tuple(sp2))
sp2 = [lp]
except Exception as e:
print("\n[Add Advanced Objects]\nOperator: "
"object.arranjar_numa_curva\nError: {}".format(e))
pass
sp2.append(p)
v1 = v2
lp = p
tmp_Gpoints.append(tuple(sp2))
Gpoints = Gpoints[:i] + tmp_Gpoints
lengths = []
if self.use_distance != "D":
for sp in Gpoints:
lp = sp[1]
leng = (lp - sp[0]).length
for p in sp[2:]:
leng += (p - lp).length
lp = p
lengths.append(leng)
return Gpoints, lengths
def execute(self, context):
if context.object.type != 'CURVE':
return {'CANCELLED'}
curve = context.active_object
Gpoints, lengs = self.Glpoints(curve)
adv_obj = context.scene.advanced_objects
if adv_obj.arrange_c_use_selected:
G_Objeto = context.selected_objects
G_Objeto.remove(curve)
if not G_Objeto:
return {'CANCELLED'}
elif adv_obj.arrange_c_select_type == 'O':
G_Objeto = bpy.data.objects[adv_obj.arrange_c_obj_arranjar],
elif adv_obj.arrange_c_select_type == 'G':
G_Objeto = bpy.data.collections[adv_obj.arrange_c_obj_arranjar].objects
yawMatrix = mathutils.Matrix.Rotation(self.Yaw, 4, 'X')
pitchMatrix = mathutils.Matrix.Rotation(self.Pitch, 4, 'Y')
rollMatrix = mathutils.Matrix.Rotation(self.Roll, 4, 'Z')
max_angle = self.max_angle # max_angle is called in Glpoints
if self.use_distance == "D":
dist = self.distance
for sp_points in Gpoints:
dx = 0.0 # Length of initial calculation of section
last_point = sp_points[0]
j = 0
for point in sp_points[1:]:
vetorx = point - last_point # Vector spline section
quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z') # Tracking the selected objects
quat = quat.to_matrix().to_4x4()
v_len = vetorx.length
if v_len > 0.0:
dx += v_len # Defined length calculation equal total length of the spline section
v_norm = vetorx / v_len
while dx > dist:
object = G_Objeto[j % len(G_Objeto)]
j += 1
dx -= dist # Calculating the remaining length of the section
obj = object.copy()
context.collection.objects.link(obj)
obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
# Placing in the correct position
obj.matrix_world.translation = point - v_norm * dx
obj.scale *= self.scale
last_point = point
elif self.use_distance == "Q":
object_qt = self.object_qt + 1
for i, sp_points in enumerate(Gpoints):
dx = 0.0 # Length of initial calculation of section
dist = lengs[i] / object_qt
last_point = sp_points[0]
j = 0
for point in sp_points[1:]:
vetorx = point - last_point # Vector spline section
# Tracking the selected objects
quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
quat = quat.to_matrix().to_4x4()
v_len = vetorx.length
if v_len > 0.0:
# Defined length calculation equal total length of the spline section
dx += v_len
v_norm = vetorx / v_len
while dx > dist:
object = G_Objeto[j % len(G_Objeto)]
j += 1
dx -= dist # Calculating the remaining length of the section
obj = object.copy()
context.collection.objects.link(obj)
obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
# Placing in the correct position
obj.matrix_world.translation = point - v_norm * dx
obj.scale *= self.scale
last_point = point
else:
dist = self.distance
offset2 = 2 * self.offset
for i, sp_points in enumerate(Gpoints):
leng = lengs[i] - offset2
rest = leng % dist
offset = offset2 + rest
leng -= rest
offset /= 2
last_point = sp_points[0]
dx = dist - offset # Length of initial calculation of section
j = 0
for point in sp_points[1:]:
vetorx = point - last_point # Vector spline section
# Tracking the selected objects
quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
quat = quat.to_matrix().to_4x4()
v_len = vetorx.length
if v_len > 0.0:
dx += v_len
v_norm = vetorx / v_len
while dx >= dist and leng >= 0.0:
leng -= dist
dx -= dist # Calculating the remaining length of the section
object = G_Objeto[j % len(G_Objeto)]
j += 1
obj = object.copy()
context.collection.objects.link(obj)
obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
# Placing in the correct position
obj.matrix_world.translation = point - v_norm * dx
obj.scale *= self.scale
last_point = point
return {"FINISHED"}
def register():
bpy.utils.register_class(PanelDupliCurve)
bpy.utils.register_class(DupliCurve)
def unregister():
bpy.utils.unregister_class(PanelDupliCurve)
bpy.utils.unregister_class(DupliCurve)
if __name__ == "__main__":
register()

View File

@ -1,166 +0,0 @@
# gpl author: Antonis Karvelas
# -*- coding: utf-8 -*-
bl_info = {
"name": "Circle Array",
"author": "Antonis Karvelas",
"version": (1, 0, 1),
"blender": (2, 67, 0),
"location": "View3D > Object > Circle_Array",
"description": "Uses an existing array and creates an empty, "
"rotates it properly and makes a Circle Array",
"warning": "",
"wiki_url": "",
"category": "Mesh"
}
import bpy
from bpy.types import Operator
from math import radians
class Circle_Array(Operator):
bl_label = "Circle Array"
bl_idname = "objects.circle_array_operator"
bl_description = ("Creates an Array Modifier with offset empty object\n"
"Works with Mesh, Curve, Text and Surface\n"
"Use an object with an existing Array modifier\n"
"or rotate the newly created Empty with the name pattern\n"
"EMPTY_C_Array_ if the Array doesn't exist (angle: 360/Count)")
@classmethod
def poll(cls, context):
return context.active_object is not None
def check_empty_name(self, context):
new_name, def_name = "", "EMPTY_C_Array"
suffix = 1
try:
# first slap a simple linear count + 1 for numeric suffix, if it fails
# harvest for the rightmost numbers and append the max value
list_obj = []
obj_all = context.scene.objects
list_obj = [obj.name for obj in obj_all if obj.name.startswith(def_name)]
new_name = "{}_{}".format(def_name, len(list_obj) + 1)
if new_name in list_obj:
from re import findall
test_num = [findall("\d+", words) for words in list_obj]
suffix += max([int(l[-1]) for l in test_num])
new_name = "{}_{}".format(def_name, suffix)
return new_name
except:
return None
def execute(self, context):
is_allowed = True
try:
allowed_obj = ['MESH', 'CURVE', 'SURFACE', 'FONT']
for obj in context.selected_objects:
if obj.type not in allowed_obj:
is_allowed = False
break
if not is_allowed:
self.report(
{"WARNING"},
"The Active/Selected objects are not of "
"Mesh, Curve, Surface or Font type. Operation Cancelled"
)
return {'CANCELLED'}
default_name = self.check_empty_name(context) or "EMPTY_C_Array"
bpy.ops.object.modifier_add(type='ARRAY')
if len(context.selected_objects) == 2:
selected = context.selected_objects
lists = [obj for obj in selected if obj != context.active_object]
active = lists[0]
# check if the list object has a modifier
check_mod = None
for mod in active.modifiers[:]:
if mod.type == "ARRAY":
check_mod = mod
break
if check_mod:
check_mod.use_object_offset = True
check_mod.use_relative_offset = False
else:
# fallback
bpy.context.view_layer.objects.active = active
bpy.ops.object.modifier_add(type='ARRAY')
active.modifiers[0].use_object_offset = True
active.modifiers[0].use_relative_offset = False
active.modifiers[0].use_object_offset = True
active.modifiers[0].use_relative_offset = False
active.select_set(False)
bpy.context.view_layer.objects.active = context.active_object
bpy.ops.view3d.snap_cursor_to_selected()
if active.modifiers[0].offset_object is None:
bpy.ops.object.add(type='EMPTY')
empty_name = bpy.context.active_object
empty_name.name = default_name
active.modifiers[0].offset_object = empty_name
else:
empty_name = active.modifiers[0].offset_object
bpy.context.view_layer.objects.active = active
num = active.modifiers["Array"].count
rotate_num = 360 / num
active.select_set(True)
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
empty_name.rotation_euler = (0, 0, radians(rotate_num))
empty_name.select_set(False)
active.select_set(True)
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
return {'FINISHED'}
else:
active = context.active_object
active.modifiers[0].use_object_offset = True
active.modifiers[0].use_relative_offset = False
bpy.ops.view3d.snap_cursor_to_selected()
if active.modifiers[0].offset_object is None:
bpy.ops.object.add(type='EMPTY')
empty_name = bpy.context.active_object
empty_name.name = default_name
active.modifiers[0].offset_object = empty_name
else:
empty_name = active.modifiers[0].offset_object
bpy.context.view_layer.objects.active = active
num = active.modifiers["Array"].count
rotate_num = 360 / num
active.select_set(True)
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
empty_name.rotation_euler = (0, 0, radians(rotate_num))
empty_name.select_set(False)
active.select_set(True)
return {'FINISHED'}
except Exception as e:
self.report({'WARNING'},
"Circle Array operator could not be executed (See the console for more info)")
print("\n[objects.circle_array_operator]\nError: {}\n".format(e))
return {'CANCELLED'}
# Register
def register():
bpy.utils.register_class(Circle_Array)
def unregister():
bpy.utils.unregister_class(Circle_Array)
if __name__ == "__main__":
register()

View File

@ -1,339 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/
# or write to the Free Software Foundation, Inc., 51 Franklin Street,
# Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Copy2 Vertices, Edges or Faces",
"author": "Eleanor Howick (elfnor.com)",
"version": (0, 1, 1),
"blender": (2, 71, 0),
"location": "3D View > Object > Copy 2",
"description": "Copy one object to the selected vertices, edges or faces of another object",
"warning": "",
"category": "Object"
}
import bpy
from bpy.types import Operator
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
)
from mathutils import (
Vector,
Matrix,
)
class Copy2(Operator):
bl_idname = "mesh.copy2"
bl_label = "Copy 2"
bl_description = ("Copy Vertices, Edges or Faces to the Selected object\n"
"Needs an existing Active Mesh Object")
bl_options = {"REGISTER", "UNDO"}
obj_list = None
def obj_list_cb(self, context):
return Copy2.obj_list
def sec_axes_list_cb(self, context):
if self.priaxes == 'X':
sec_list = [('Y', "Y", "Secondary axis Y"),
('Z', "Z", "Secondary axis Z")]
if self.priaxes == 'Y':
sec_list = [('X', "X", "Secondary axis X"),
('Z', "Z", "Secondary axis Z")]
if self.priaxes == 'Z':
sec_list = [('X', "X", "Secondary axis X"),
('Y', "Y", "Secondary axis Y")]
return sec_list
copytype: EnumProperty(
items=(('V', "Vertex",
"Paste the Copied Geometry to Vertices of the Active Object", 'VERTEXSEL', 0),
('E', "Edge",
"Paste the Copied Geometry to Edges of the Active Object", 'EDGESEL', 1),
('F', "Face",
"Paste the Copied Geometry to Faces of the Active Object", 'FACESEL', 2)),
)
copyfromobject: EnumProperty(
name="Copy from",
description="Copy an Object from the list",
items=obj_list_cb
)
priaxes: EnumProperty(
description="Primary axes used for Copied Object orientation",
items=(('X', "X", "Along X"),
('Y', "Y", "Along Y"),
('Z', "Z", "Along Z")),
)
edgescale: BoolProperty(
name="Scale to fill edge",
default=False
)
secaxes: EnumProperty(
name="Secondary Axis",
description="Secondary axis used for Copied Object orientation",
items=sec_axes_list_cb
)
scale: FloatProperty(
name="Scale",
default=1.0,
min=0.0,
)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == "MESH"
def draw(self, context):
layout = self.layout
layout.prop(self, "copyfromobject")
layout.label(text="to:")
layout.prop(self, "copytype", expand=True)
layout.label(text="Primary axis:")
layout.prop(self, "priaxes", expand=True)
layout.label(text="Secondary axis:")
layout.prop(self, "secaxes", expand=True)
if self.copytype == "E":
layout.prop(self, "edgescale")
if self.edgescale:
layout.prop(self, "scale")
return
def execute(self, context):
copytoobject = context.active_object.name
axes = self.priaxes + self.secaxes
# check if there is a problem with the strings related to some chars
copy_to_object = bpy.data.objects[copytoobject] if \
copytoobject in bpy.data.objects else None
copy_from_object = bpy.data.objects[self.copyfromobject] if \
self.copyfromobject in bpy.data.objects else None
if copy_to_object is None or copy_from_object is None:
self.report({"WARNING"},
"There was a problem with retrieving Object data. Operation Cancelled")
return {"CANCELLED"}
try:
copy_to_from(
context.collection,
copy_to_object,
copy_from_object,
self.copytype,
axes,
self.edgescale,
self.scale
)
except Exception as e:
self.report({"WARNING"},
"Copy2 could not be completed (Check the Console for more info)")
print("\n[Add Advanced Objects]\nOperator: mesh.copy2\n{}\n".format(e))
return {"CANCELLED"}
return {"FINISHED"}
def invoke(self, context, event):
Copy2.obj_list = [(obj.name, obj.name, obj.name) for obj in bpy.data.objects]
return {"FINISHED"}
def copy_to_from(collection, to_obj, from_obj, copymode, axes, edgescale, scale):
if copymode == 'V':
vertex_copy(collection, to_obj, from_obj, axes)
if copymode == 'E':
# don't pass edgescalling to object types that cannot be scaled
if from_obj.type in ["CAMERA", "LIGHT", "EMPTY", "ARMATURE", "SPEAKER", "META"]:
edgescale = False
edge_copy(collection, to_obj, from_obj, axes, edgescale, scale)
if copymode == 'F':
face_copy(collection, to_obj, from_obj, axes)
axes_dict = {'XY': (1, 2, 0),
'XZ': (2, 1, 0),
'YX': (0, 2, 1),
'YZ': (2, 0, 1),
'ZX': (0, 1, 2),
'ZY': (1, 0, 2)}
def copyto(collection, source_obj, pos, xdir, zdir, axes, scale=None):
"""
copy the source_obj to pos, so its primary axis points in zdir and its
secondary axis points in xdir
"""
copy_obj = source_obj.copy()
collection.objects.link(copy_obj)
xdir = xdir.normalized()
zdir = zdir.normalized()
# rotation first
z_axis = zdir
x_axis = xdir
y_axis = z_axis.cross(x_axis)
# use axes_dict to assign the axis as chosen in panel
A, B, C = axes_dict[axes]
rot_mat = Matrix()
rot_mat[A].xyz = x_axis
rot_mat[B].xyz = y_axis
rot_mat[C].xyz = z_axis
rot_mat.transpose()
# rotate object
copy_obj.matrix_world = rot_mat
# move object into position
copy_obj.location = pos
# scale object
if scale is not None:
copy_obj.scale = scale
return copy_obj
def vertex_copy(collection, obj, source_obj, axes):
# vertex select mode
sel_verts = []
copy_list = []
for v in obj.data.vertices:
if v.select is True:
sel_verts.append(v)
# make a set for each vertex. The set contains all the connected vertices
# use sets so the list is unique
vert_con = [set() for i in range(len(obj.data.vertices))]
for e in obj.data.edges:
vert_con[e.vertices[0]].add(e.vertices[1])
vert_con[e.vertices[1]].add(e.vertices[0])
for v in sel_verts:
pos = v.co * obj.matrix_world.transposed()
xco = obj.data.vertices[list(vert_con[v.index])[0]].co * obj.matrix_world.transposed()
zdir = (v.co + v.normal) * obj.matrix_world.transposed() - pos
zdir = zdir.normalized()
edir = pos - xco
# edir is nor perpendicular to z dir
# want xdir to be projection of edir onto plane through pos with direction zdir
xdir = edir - edir.dot(zdir) * zdir
xdir = -xdir.normalized()
copy = copyto(collection, source_obj, pos, xdir, zdir, axes)
copy_list.append(copy)
# select all copied objects
for copy in copy_list:
copy.select_set(True)
obj.select_set(False)
def edge_copy(collection, obj, source_obj, axes, es, scale):
# edge select mode
sel_edges = []
copy_list = []
for e in obj.data.edges:
if e.select is True:
sel_edges.append(e)
for e in sel_edges:
# pos is average of two edge vertexs
v0 = obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed()
v1 = obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed()
pos = (v0 + v1) / 2
# xdir is along edge
xdir = v0 - v1
xlen = xdir.magnitude
xdir = xdir.normalized()
# project each edge vertex normal onto plane normal to xdir
vn0 = (obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed() +
obj.data.vertices[e.vertices[0]].normal) - v0
vn1 = (obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed() +
obj.data.vertices[e.vertices[1]].normal) - v1
vn0p = vn0 - vn0.dot(xdir) * xdir
vn1p = vn1 - vn1.dot(xdir) * xdir
# the mean of the two projected normals is the zdir
zdir = vn0p + vn1p
zdir = zdir.normalized()
escale = None
if es:
escale = Vector([1.0, 1.0, 1.0])
i = list('XYZ').index(axes[1])
escale[i] = scale * xlen / source_obj.dimensions[i]
copy = copyto(collection, source_obj, pos, xdir, zdir, axes, scale=escale)
copy_list.append(copy)
# select all copied objects
for copy in copy_list:
copy.select_set(True)
obj.select_set(False)
def face_copy(collection, obj, source_obj, axes):
# face select mode
sel_faces = []
copy_list = []
for f in obj.data.polygons:
if f.select is True:
sel_faces.append(f)
for f in sel_faces:
fco = f.center * obj.matrix_world.transposed()
# get first vertex corner of transformed object
vco = obj.data.vertices[f.vertices[0]].co * obj.matrix_world.transposed()
# get face normal of transformed object
fn = (f.center + f.normal) * obj.matrix_world.transposed() - fco
fn = fn.normalized()
copy = copyto(collection, source_obj, fco, vco - fco, fn, axes)
copy_list.append(copy)
# select all copied objects
for copy in copy_list:
copy.select_set(True)
obj.select_set(False)
def register():
bpy.utils.register_class(Copy2)
def unregister():
bpy.utils.unregister_class(Copy2)
if __name__ == "__main__":
register()

View File

@ -1,957 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Original Author = Jacob Morris
# URL = blendingjacob.blogspot.com
# Note: scene properties are moved into __init__ together with the 3 update functions
# for properties search for the name patterns adv_obj and advanced_objects
bl_info = {
"name": "CubeSter",
"author": "Jacob Morris",
"version": (0, 7, 2),
"blender": (2, 78, 0),
"location": "View 3D > Toolbar > CubeSter",
"description": "Takes image, image sequence, or audio file and converts it "
"into a height map based on pixel color and alpha values",
"category": "Add Mesh"
}
import bpy
import bmesh
from bpy.types import (
Operator,
Panel,
)
import timeit
from random import uniform
from math import radians
from os import (
path,
listdir,
)
# create block at center position x, y with block width 2 * hx and 2 * hy and height of h
def create_block(x, y, hw, h, verts: list, faces: list):
if bpy.context.scene.advanced_objects.cubester_block_style == "size":
z = 0.0
else:
z = h
h = 2 * hw
p = len(verts)
verts += [(x - hw, y - hw, z), (x + hw, y - hw, z), (x + hw, y + hw, z), (x - hw, y + hw, z)]
verts += [(x - hw, y - hw, z + h), (x + hw, y - hw, z + h),
(x + hw, y + hw, z + h), (x - hw, y + hw, z + h)]
faces += [(p, p + 1, p + 5, p + 4), (p + 1, p + 2, p + 6, p + 5),
(p + 2, p + 3, p + 7, p + 6), (p, p + 4, p + 7, p + 3),
(p + 4, p + 5, p + 6, p + 7), (p, p + 3, p + 2, p + 1)]
# go through all frames in len(frames), adjusting values at frames[x][y]
def create_f_curves(mesh, frames, frame_step_size, style):
# use data to animate mesh
action = bpy.data.actions.new("CubeSterAnimation")
mesh.animation_data_create()
mesh.animation_data.action = action
data_path = "vertices[%d].co"
vert_index = 4 if style == "blocks" else 0 # index of first vertex
# loop for every face height value
for frame_start_vert in range(len(frames[0])):
# only go once if plane, otherwise do all four vertices that are in top plane if blocks
end_point = frame_start_vert + 4 if style == "blocks" else frame_start_vert + 1
# loop through to get the four vertices that compose the face
for frame_vert in range(frame_start_vert, end_point):
# fcurves for x, y, z
fcurves = [action.fcurves.new(data_path % vert_index, i) for i in range(3)]
frame_counter = 0 # go through each frame and add position
temp_v = mesh.vertices[vert_index].co
# loop through frames
for frame in frames:
# new x, y, z positions
vals = [temp_v[0], temp_v[1], frame[frame_start_vert]]
for i in range(3): # for each x, y, z set each corresponding fcurve
fcurves[i].keyframe_points.insert(frame_counter, vals[i], {'FAST'})
frame_counter += frame_step_size # skip frames for smoother animation
vert_index += 1
# only skip vertices if made of blocks
if style == "blocks":
vert_index += 4
# create material with given name, apply to object
def create_material(scene, ob, name):
mat = bpy.data.materials.new("CubeSter_" + name)
adv_obj = scene.advanced_objects
image = None
# image
if not adv_obj.cubester_use_image_color and adv_obj.cubester_color_image in bpy.data.images:
try:
image = bpy.data.images[adv_obj.cubester_color_image]
except:
pass
else:
try:
image = bpy.data.images[adv_obj.cubester_image]
except:
pass
if scene.render.engine == "CYCLES":
mat.use_nodes = True
nodes = mat.node_tree.nodes
att = nodes.new("ShaderNodeAttribute")
att.attribute_name = "Col"
att.location = (-200, 300)
att = nodes.new("ShaderNodeTexImage")
if image:
att.image = image
if adv_obj.cubester_load_type == "multiple":
att.image.source = "SEQUENCE"
att.location = (-200, 700)
att = nodes.new("ShaderNodeTexCoord")
att.location = (-450, 600)
if adv_obj.cubester_materials == "image":
mat.node_tree.links.new(
nodes["Image Texture"].outputs[0],
nodes["Diffuse BSDF"].inputs[0]
)
mat.node_tree.links.new(
nodes["Texture Coordinate"].outputs[2],
nodes["Image Texture"].inputs[0]
)
else:
mat.node_tree.links.new(
nodes["Attribute"].outputs[0],
nodes["Diffuse BSDF"].inputs[0]
)
else:
if adv_obj.cubester_materials == "image" or scene.render.engine != "BLENDER_RENDER":
tex = bpy.data.textures.new("CubeSter_" + name, "IMAGE")
if image:
tex.image = image
slot = mat.texture_slots.add()
slot.texture = tex
else:
mat.use_vertex_color_paint = True
ob.data.materials.append(mat)
# generate mesh from audio
def create_mesh_from_audio(self, context, verts, faces):
scene = context.scene
view_layer = context.view_layer
adv_obj = scene.advanced_objects
audio_filepath = adv_obj.cubester_audio_path
width = adv_obj.cubester_audio_width_blocks
length = adv_obj.cubester_audio_length_blocks
size_per_hundred = adv_obj.cubester_size_per_hundred_pixels
size = size_per_hundred / 100
# Note: used for compatibility with vertex colors changes
bl_version = bool(bpy.app.version >= (2, 79, 1))
# create all blocks
y = -(width / 2) * size + (size / 2)
for r in range(width):
x = -(length / 2) * size + (size / 2)
for c in range(length):
create_block(x, y, size / 2, 1, verts, faces)
x += size
y += size
# create object
mesh = bpy.data.meshes.new("cubed")
mesh.from_pydata(verts, [], faces)
ob = bpy.data.objects.new("cubed", mesh)
bpy.context.collection.objects.link(ob)
bpy.context.view_layer.objects.active = ob
ob.select_set(True)
# initial vertex colors
if adv_obj.cubester_materials == "image" and adv_obj.cubester_color_image != "":
picture = bpy.data.images[adv_obj.cubester_color_image]
pixels = list(picture.pixels)
vert_colors = []
skip_y = int(picture.size[1] / width)
skip_x = int(picture.size[0] / length)
for row in range(0, picture.size[1], skip_y + 1):
# go through each column, step by appropriate amount
for column in range(0, picture.size[0] * 4, 4 + skip_x * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
get_colors = (r, g, b, a) if bl_version else (r, g, b)
vert_colors += [get_colors for i in range(24)]
bpy.ops.mesh.vertex_color_add()
i = 0
vert_colors_size = len(vert_colors)
for c in ob.data.vertex_colors[0].data:
if i < vert_colors_size:
c.color = vert_colors[i]
i += 1
# image sequence handling
if adv_obj.cubester_load_type == "multiple":
images = find_sequence_images(self, bpy.context)
frames_vert_colors = []
max_images = adv_obj.cubester_max_images + 1 if \
len(images[0]) > adv_obj.cubester_max_images else len(images[0])
# goes through and for each image for each block finds new height
for image_index in range(0, max_images, adv_obj.cubester_skip_images):
filepath = images[0][image_index]
name = images[1][image_index]
picture = fetch_image(self, name, filepath)
pixels = list(picture.pixels)
frame_colors = []
for row in range(0, picture.size[1], skip_y + 1):
for column in range(0, picture.size[0] * 4, 4 + skip_x * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
get_colors = (r, g, b, a) if bl_version else (r, g, b)
frame_colors += [get_colors for i in range(24)]
frames_vert_colors.append(frame_colors)
adv_obj.cubester_vertex_colors[ob.name] = \
{"type": "vertex", "frames": frames_vert_colors,
"frame_skip": adv_obj.cubester_frame_step,
"total_images": max_images}
# either add material or create
if ("CubeSter_" + "Vertex") in bpy.data.materials:
ob.data.materials.append(bpy.data.materials["CubeSter_" + "Vertex"])
else:
create_material(scene, ob, "Vertex")
# set keyframe for each object as initial point
frame = [1 for i in range(int(len(verts) / 8))]
frames = [frame]
area = bpy.context.area
old_type = area.type
area.type = "GRAPH_EDITOR"
scene.frame_current = 0
create_f_curves(mesh, frames, 1, "blocks")
# deselect all fcurves
fcurves = ob.data.animation_data.action.fcurves.data.fcurves
for i in fcurves:
i.select = False
max_images = adv_obj.cubester_audio_max_freq
min_freq = adv_obj.cubester_audio_min_freq
freq_frame = adv_obj.cubester_audio_offset_type
freq_step = (max_images - min_freq) / length
freq_sub_step = freq_step / width
frame_step = adv_obj.cubester_audio_frame_offset
# animate each block with a portion of the frequency
for c in range(length):
frame_off = 0
for r in range(width):
if freq_frame == "frame":
scene.frame_current = frame_off
l = c * freq_step
h = (c + 1) * freq_step
frame_off += frame_step
else:
l = c * freq_step + (r * freq_sub_step)
h = c * freq_step + ((r + 1) * freq_sub_step)
pos = c + (r * length) # block number
index = pos * 4 # first index for vertex
# select curves
for i in range(index, index + 4):
curve = i * 3 + 2 # fcurve location
fcurves[curve].select = True
try:
bpy.ops.graph.sound_bake(filepath=bpy.path.abspath(audio_filepath), low=l, high=h)
except:
pass
# deselect curves
for i in range(index, index + 4):
curve = i * 3 + 2 # fcurve location
fcurves[curve].select = False
area.type = old_type
# UV unwrap
create_uv_map(bpy.context, width, length)
# if radial apply needed modifiers
if adv_obj.cubester_audio_block_layout == "radial":
# add bezier curve of correct width
bpy.ops.curve.primitive_bezier_circle_add()
curve = bpy.context.object
# slope determined off of collected data
curve_size = (0.319 * (width * (size * 100)) - 0.0169) / 100
curve.dimensions = (curve_size, curve_size, 0.0)
# correct for z height
curve.scale = (curve.scale[0], curve.scale[0], curve.scale[0])
ob.select_set(True)
curve.select_set(False)
view_layer.objects.active = ob
# data was collected and then multi-variable regression was done in Excel
# influence of width and length
width_infl, length_infl, intercept = -0.159125, 0.49996, 0.007637
x_offset = ((width * (size * 100) * width_infl) +
(length * (size * 100) * length_infl) + intercept) / 100
ob.location = (ob.location[0] + x_offset, ob.location[1], ob.location[2])
ob.rotation_euler = (radians(-90), 0.0, 0.0)
bpy.ops.object.modifier_add(type="CURVE")
ob.modifiers["Curve"].object = curve
ob.modifiers["Curve"].deform_axis = "POS_Z"
# generate mesh from image(s)
def create_mesh_from_image(self, scene, verts, faces):
context = bpy.context
adv_obj = scene.advanced_objects
picture = bpy.data.images[adv_obj.cubester_image]
pixels = list(picture.pixels)
# Note: used for compatibility with vertex colors changes
bl_version = bool(bpy.app.version >= (2, 79, 1))
x_pixels = picture.size[0] / (adv_obj.cubester_skip_pixels + 1)
y_pixels = picture.size[1] / (adv_obj.cubester_skip_pixels + 1)
width = x_pixels / 100 * adv_obj.cubester_size_per_hundred_pixels
height = y_pixels / 100 * adv_obj.cubester_size_per_hundred_pixels
step = width / x_pixels
half_width = step / 2
y = -height / 2 + half_width
vert_colors = []
rows = 0
# go through each row of pixels stepping by adv_obj.cubester_skip_pixels + 1
for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1):
rows += 1
x = -width / 2 + half_width # reset to left edge of mesh
# go through each column, step by appropriate amount
for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
get_colors = (r, g, b, a) if bl_version else (r, g, b)
h = find_point_height(r, g, b, a, scene)
# if not transparent
if h != -1:
if adv_obj.cubester_mesh_style == "blocks":
create_block(x, y, half_width, h, verts, faces)
vert_colors += [get_colors for i in range(24)]
else:
verts += [(x, y, h)]
vert_colors += [get_colors for i in range(4)]
x += step
y += step
# if plane not blocks, then remove last 4 items from vertex_colors
# as the faces have already wrapped around
if adv_obj.cubester_mesh_style == "plane":
del vert_colors[len(vert_colors) - 4:len(vert_colors)]
# create faces if plane based and not block based
if adv_obj.cubester_mesh_style == "plane":
off = int(len(verts) / rows)
for r in range(rows - 1):
for c in range(off - 1):
faces += [(r * off + c, r * off + c + 1, (r + 1) * off + c + 1, (r + 1) * off + c)]
mesh = bpy.data.meshes.new("cubed")
mesh.from_pydata(verts, [], faces)
ob = bpy.data.objects.new("cubed", mesh)
context.collection.objects.link(ob)
context.view_layer.objects.active = ob
ob.select_set(True)
# uv unwrap
if adv_obj.cubester_mesh_style == "blocks":
create_uv_map(context, rows, int(len(faces) / 6 / rows))
else:
create_uv_map(context, rows - 1, int(len(faces) / (rows - 1)))
# material
# determine name and if already created
if adv_obj.cubester_materials == "vertex": # vertex color
image_name = "Vertex"
elif not adv_obj.cubester_use_image_color and \
adv_obj.cubester_color_image in bpy.data.images and \
adv_obj.cubester_materials == "image": # replaced image
image_name = adv_obj.cubester_color_image
else: # normal image
image_name = adv_obj.cubester_image
# either add material or create
if ("CubeSter_" + image_name) in bpy.data.materials:
ob.data.materials.append(bpy.data.materials["CubeSter_" + image_name])
# create material
else:
create_material(scene, ob, image_name)
# vertex colors
bpy.ops.mesh.vertex_color_add()
i = 0
for c in ob.data.vertex_colors[0].data:
c.color = vert_colors[i]
i += 1
frames = []
# image sequence handling
if adv_obj.cubester_load_type == "multiple":
images = find_sequence_images(self, context)
frames_vert_colors = []
max_images = adv_obj.cubester_max_images + 1 if \
len(images[0]) > adv_obj.cubester_max_images else len(images[0])
# goes through and for each image for each block finds new height
for image_index in range(0, max_images, adv_obj.cubester_skip_images):
filepath = images[0][image_index]
name = images[1][image_index]
picture = fetch_image(self, name, filepath)
pixels = list(picture.pixels)
frame_heights = []
frame_colors = []
for row in range(0, picture.size[1], adv_obj.cubester_skip_pixels + 1):
for column in range(0, picture.size[0] * 4, 4 + adv_obj.cubester_skip_pixels * 4):
r, g, b, a = get_pixel_values(picture, pixels, row, column)
get_colors = (r, g, b, a) if bl_version else (r, g, b)
h = find_point_height(r, g, b, a, scene)
if h != -1:
frame_heights.append(h)
if adv_obj.cubester_mesh_style == "blocks":
frame_colors += [get_colors for i in range(24)]
else:
frame_colors += [get_colors for i in range(4)]
if adv_obj.cubester_mesh_style == "plane":
del vert_colors[len(vert_colors) - 4:len(vert_colors)]
frames.append(frame_heights)
frames_vert_colors.append(frame_colors)
# determine what data to use
if adv_obj.cubester_materials == "vertex" or scene.render.engine == "BLENDER_ENGINE":
adv_obj.cubester_vertex_colors[ob.name] = {
"type": "vertex", "frames": frames_vert_colors,
"frame_skip": adv_obj.cubester_frame_step,
"total_images": max_images
}
else:
adv_obj.cubester_vertex_colors[ob.name] = {
"type": "image", "frame_skip": adv_obj.cubester_frame_step,
"total_images": max_images
}
att = get_image_node(ob.data.materials[0])
att.image_user.frame_duration = len(frames) * adv_obj.cubester_frame_step
# animate mesh
create_f_curves(
mesh, frames,
adv_obj.cubester_frame_step,
adv_obj.cubester_mesh_style
)
# generate uv map for object
def create_uv_map(context, rows, columns):
adv_obj = context.scene.advanced_objects
mesh = context.object.data
mesh.uv_textures.new("cubester")
bm = bmesh.new()
bm.from_mesh(mesh)
uv_layer = bm.loops.layers.uv[0]
bm.faces.ensure_lookup_table()
x_scale = 1 / columns
y_scale = 1 / rows
y_pos = 0.0
x_pos = 0.0
count = columns - 1 # hold current count to compare to if need to go to next row
# if blocks
if adv_obj.cubester_mesh_style == "blocks":
for fa in range(int(len(bm.faces) / 6)):
for i in range(6):
pos = (fa * 6) + i
bm.faces[pos].loops[0][uv_layer].uv = (x_pos, y_pos)
bm.faces[pos].loops[1][uv_layer].uv = (x_pos + x_scale, y_pos)
bm.faces[pos].loops[2][uv_layer].uv = (x_pos + x_scale, y_pos + y_scale)
bm.faces[pos].loops[3][uv_layer].uv = (x_pos, y_pos + y_scale)
x_pos += x_scale
if fa >= count:
y_pos += y_scale
x_pos = 0.0
count += columns
# if planes
else:
for fa in range(len(bm.faces)):
bm.faces[fa].loops[0][uv_layer].uv = (x_pos, y_pos)
bm.faces[fa].loops[1][uv_layer].uv = (x_pos + x_scale, y_pos)
bm.faces[fa].loops[2][uv_layer].uv = (x_pos + x_scale, y_pos + y_scale)
bm.faces[fa].loops[3][uv_layer].uv = (x_pos, y_pos + y_scale)
x_pos += x_scale
if fa >= count:
y_pos += y_scale
x_pos = 0.0
count += columns
bm.to_mesh(mesh)
# if already loaded return image, else load and return
def fetch_image(self, name, load_path):
if name in bpy.data.images:
return bpy.data.images[name]
else:
try:
image = bpy.data.images.load(load_path)
return image
except RuntimeError:
self.report({"ERROR"}, "CubeSter: '{}' could not be loaded".format(load_path))
return None
# find height for point
def find_point_height(r, g, b, a, scene):
adv_obj = scene.advanced_objects
if a: # if not completely transparent
normalize = 1
# channel weighting
if not adv_obj.cubester_advanced:
composed = 0.25 * r + 0.25 * g + 0.25 * b + 0.25 * a
else:
# user defined weighting
if not adv_obj.cubester_random_weights:
composed = adv_obj.cubester_weight_r * r + adv_obj.cubester_weight_g * g + \
adv_obj.cubester_weight_b * b + adv_obj.cubester_weight_a * a
total = adv_obj.cubester_weight_r + adv_obj.cubester_weight_g + adv_obj.cubester_weight_b + \
adv_obj.cubester_weight_a
normalize = 1 / total
# random weighting
else:
weights = [uniform(0.0, 1.0) for i in range(4)]
composed = weights[0] * r + weights[1] * g + weights[2] * b + weights[3] * a
total = weights[0] + weights[1] + weights[2] + weights[3]
normalize = 1 / total
if adv_obj.cubester_invert:
h = (1 - composed) * adv_obj.cubester_height_scale * normalize
else:
h = composed * adv_obj.cubester_height_scale * normalize
return h
else:
return -1
# find all images that would belong to sequence
def find_sequence_images(self, context):
scene = context.scene
images = [[], []]
if scene.advanced_objects.cubester_image in bpy.data.images:
image = bpy.data.images[scene.advanced_objects.cubester_image]
main = image.name.split(".")[0]
# first part of name to check against other files
length = len(main)
keep_going = True
for i in range(length - 1, -1, -1):
if main[i].isdigit() and keep_going:
length -= 1
else:
keep_going = not keep_going
name = main[0:length]
dir_name = path.dirname(bpy.path.abspath(image.filepath))
try:
for file in listdir(dir_name):
if path.isfile(path.join(dir_name, file)) and file.startswith(name):
images[0].append(path.join(dir_name, file))
images[1].append(file)
except FileNotFoundError:
self.report({"ERROR"}, "CubeSter: '{}' directory not found".format(dir_name))
return images
# get image node
def get_image_node(mat):
nodes = mat.node_tree.nodes
att = nodes["Image Texture"]
return att
# get the RGBA values from pixel
def get_pixel_values(picture, pixels, row, column):
# determine i position to start at based on row and column position
i = (row * picture.size[0] * 4) + column
pixs = pixels[i: i + 4]
r = pixs[0]
g = pixs[1]
b = pixs[2]
a = pixs[3]
return r, g, b, a
# frame change handler for materials
def material_frame_handler(scene):
frame = scene.frame_current
adv_obj = scene.advanced_objects
keys = list(adv_obj.cubester_vertex_colors.keys())
# get keys and see if object is still in scene
for i in keys:
# if object is in scene then update information
if i in bpy.data.objects:
ob = bpy.data.objects[i]
data = adv_obj.advanced_objects.cubester_vertex_colors[ob.name]
skip_frames = data["frame_skip"]
# update materials using vertex colors
if data['type'] == "vertex":
colors = data["frames"]
if frame % skip_frames == 0 and 0 <= frame < (data['total_images'] - 1) * skip_frames:
use_frame = int(frame / skip_frames)
color = colors[use_frame]
i = 0
for c in ob.data.vertex_colors[0].data:
c.color = color[i]
i += 1
else:
att = get_image_node(ob.data.materials[0])
offset = frame - int(frame / skip_frames)
att.image_user.frame_offset = -offset
# if the object is no longer in the scene then delete then entry
else:
del adv_obj.advanced_objects.cubester_vertex_colors[i]
class CubeSterPanel(Panel):
bl_idname = "OBJECT_PT_cubester"
bl_label = "CubeSter"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Create"
bl_options = {"DEFAULT_CLOSED"}
bl_context = "objectmode"
def draw(self, context):
layout = self.layout.box()
scene = bpy.context.scene
adv_obj = scene.advanced_objects
images_found = 0
rows = 0
columns = 0
layout.prop(adv_obj, "cubester_audio_image")
if adv_obj.cubester_audio_image == "image":
box = layout.box()
box.prop(adv_obj, "cubester_load_type")
box.label(text="Image To Convert:")
box.prop_search(adv_obj, "cubester_image", bpy.data, "images")
box.prop(adv_obj, "cubester_load_image")
# find number of appropriate images if sequence
if adv_obj.cubester_load_type == "multiple":
box = layout.box()
# display number of images found there
images = find_sequence_images(self, context)
images_found = len(images[0]) if len(images[0]) <= adv_obj.cubester_max_images \
else adv_obj.cubester_max_images
if len(images[0]):
box.label(str(len(images[0])) + " Images Found", icon="PACKAGE")
box.prop(adv_obj, "cubester_max_images")
box.prop(adv_obj, "cubester_skip_images")
box.prop(adv_obj, "cubester_frame_step")
box = layout.box()
col = box.column(align=True)
col.prop(adv_obj, "cubester_skip_pixels")
col.prop(adv_obj, "cubester_size_per_hundred_pixels")
col.prop(adv_obj, "cubester_height_scale")
box.prop(adv_obj, "cubester_invert", icon="FILE_REFRESH")
box = layout.box()
box.prop(adv_obj, "cubester_mesh_style", icon="MESH_GRID")
if adv_obj.cubester_mesh_style == "blocks":
box.prop(adv_obj, "cubester_block_style")
else:
# audio file
layout.prop(adv_obj, "cubester_audio_path")
box = layout.box()
col = box.column(align=True)
col.prop(adv_obj, "cubester_audio_min_freq")
col.prop(adv_obj, "cubester_audio_max_freq")
box.separator()
box.prop(adv_obj, "cubester_audio_offset_type")
if adv_obj.cubester_audio_offset_type == "frame":
box.prop(adv_obj, "cubester_audio_frame_offset")
box.prop(adv_obj, "cubester_audio_block_layout")
box.separator()
col = box.column(align=True)
col.prop(adv_obj, "cubester_audio_width_blocks")
col.prop(adv_obj, "cubester_audio_length_blocks")
rows = adv_obj.cubester_audio_width_blocks
columns = adv_obj.cubester_audio_length_blocks
col.prop(adv_obj, "cubester_size_per_hundred_pixels")
# materials
box = layout.box()
box.prop(adv_obj, "cubester_materials", icon="MATERIAL")
if adv_obj.cubester_materials == "image":
box.prop(adv_obj, "cubester_load_type")
# find number of appropriate images if sequence
if adv_obj.cubester_load_type == "multiple":
# display number of images found there
images = find_sequence_images(self, context)
images_found = len(images[0]) if len(images[0]) <= adv_obj.cubester_max_images \
else adv_obj.cubester_max_images
if len(images[0]):
box.label(str(len(images[0])) + " Images Found", icon="PACKAGE")
box.prop(adv_obj, "cubester_max_images")
box.prop(adv_obj, "cubester_skip_images")
box.prop(adv_obj, "cubester_frame_step")
box.separator()
if adv_obj.cubester_audio_image == "image":
box.prop(adv_obj, "cubester_use_image_color", icon="COLOR")
if not adv_obj.cubester_use_image_color or adv_obj.cubester_audio_image == "audio":
box.label(text="Image To Use For Colors:")
box.prop_search(adv_obj, "cubester_color_image", bpy.data, "images")
box.prop(adv_obj, "cubester_load_color_image")
if adv_obj.cubester_image in bpy.data.images:
rows = int(bpy.data.images[adv_obj.cubester_image].size[1] /
(adv_obj.cubester_skip_pixels + 1))
columns = int(bpy.data.images[adv_obj.cubester_image].size[0] /
(adv_obj.cubester_skip_pixels + 1))
box = layout.box()
if adv_obj.cubester_mesh_style == "blocks":
box.label(text="Approximate Cube Count: " + str(rows * columns))
box.label(text="Expected Verts/Faces: " + str(rows * columns * 8) + " / " + str(rows * columns * 6))
else:
box.label(text="Approximate Point Count: " + str(rows * columns))
box.label(text="Expected Verts/Faces: " + str(rows * columns) + " / " + str(rows * (columns - 1)))
# blocks and plane generation time values
if adv_obj.cubester_mesh_style == "blocks":
slope = 0.0000876958
intercept = 0.02501
block_infl, frame_infl, intercept2 = 0.0025934, 0.38507, -0.5840189
else:
slope = 0.000017753
intercept = 0.04201
block_infl, frame_infl, intercept2 = 0.000619, 0.344636, -0.272759
# if creating image based mesh
points = rows * columns
if adv_obj.cubester_audio_image == "image":
if adv_obj.cubester_load_type == "single":
time = rows * columns * slope + intercept # approximate time count for mesh
else:
time = (points * slope) + intercept + (points * block_infl) + \
(images_found / adv_obj.cubester_skip_images * frame_infl) + intercept2
box.label(text="Images To Be Used: " + str(int(images_found / adv_obj.cubester_skip_images)))
else:
# audio based mesh
box.label(text="Audio Track Length: " + str(adv_obj.cubester_audio_file_length) + " frames")
block_infl, frame_infl, intercept = 0.0948, 0.0687566, -25.85985
time = (points * block_infl) + (adv_obj.cubester_audio_file_length * frame_infl) + intercept
if time < 0.0: # usually no audio loaded
time = 0.0
time_mod = "s"
if time > 60: # convert to minutes if needed
time /= 60
time_mod = "min"
time = round(time, 3)
box.label(text="Expected Time: " + str(time) + " " + time_mod)
# advanced
if adv_obj.cubester_audio_image == "image":
icon_1 = "TRIA_DOWN" if adv_obj.cubester_advanced else "TRIA_RIGHT"
# layout.separator()
box = layout.box()
box.prop(adv_obj, "cubester_advanced", icon=icon_1)
if adv_obj.cubester_advanced:
box.prop(adv_obj, "cubester_random_weights", icon="RNDCURVE")
if not adv_obj.cubester_random_weights:
box.label(text="RGBA Channel Weights", icon="COLOR")
col = box.column(align=True)
col.prop(adv_obj, "cubester_weight_r")
col.prop(adv_obj, "cubester_weight_g")
col.prop(adv_obj, "cubester_weight_b")
col.prop(adv_obj, "cubester_weight_a")
# generate mesh
layout.operator("mesh.cubester", icon="OBJECT_DATA")
class CubeSter(Operator):
bl_idname = "mesh.cubester"
bl_label = "Generate CubeSter Mesh"
bl_description = "Generate a mesh from an Image or Sound File"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
verts, faces = [], []
start = timeit.default_timer()
scene = bpy.context.scene
adv_obj = scene.advanced_objects
if adv_obj.cubester_audio_image == "image":
if adv_obj.cubester_image != "":
create_mesh_from_image(self, scene, verts, faces)
frames = find_sequence_images(self, context)
created = len(frames[0])
else:
self.report({'WARNING'},
"Please add an Image for Object generation. Operation Cancelled")
return {"CANCELLED"}
else:
if (adv_obj.cubester_audio_path != "" and
path.isfile(adv_obj.cubester_audio_path) and
adv_obj.cubester_check_audio is True):
create_mesh_from_audio(self, context, verts, faces)
created = adv_obj.cubester_audio_file_length
else:
self.report({'WARNING'},
"Please add an Sound File for Object generation. Operation Cancelled")
return {"CANCELLED"}
stop = timeit.default_timer()
if adv_obj.cubester_mesh_style == "blocks" or adv_obj.cubester_audio_image == "audio":
self.report(
{"INFO"},
"CubeSter: {} blocks and {} frame(s) "
"in {}s".format(str(int(len(verts) / 8)),
str(created),
str(round(stop - start, 4)))
)
else:
self.report(
{"INFO"},
"CubeSter: {} points and {} frame(s) "
"in {}s" .format(str(len(verts)),
str(created),
str(round(stop - start, 4)))
)
return {"FINISHED"}
def register():
bpy.utils.register_module(__name__)
bpy.app.handlers.frame_change_pre.append(material_frame_handler)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.app.handlers.frame_change_pre.remove(material_frame_handler)
if __name__ == "__main__":
register()

View File

@ -1,592 +0,0 @@
# Copyright (C) 2012 Bill Currie <bill@taniwha.org>
# Date: 2012/2/20
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
import bmesh
from bpy.types import Operator
from bpy.props import (
FloatProperty,
IntProperty,
BoolProperty,
)
from mathutils import (
Vector,
Matrix,
Quaternion,
)
from math import (
pi, cos,
sin,
)
cossin = []
# Initialize the cossin table based on the number of segments.
#
# @param n The number of segments into which the circle will be
# divided.
# @return None
def build_cossin(n):
global cossin
cossin = []
for i in range(n):
a = 2 * pi * i / n
cossin.append((cos(a), sin(a)))
def select_up(axis):
# if axis.length != 0 and (abs(axis[0] / axis.length) < 1e-5 and abs(axis[1] / axis.length) < 1e-5):
if (abs(axis[0] / axis.length) < 1e-5 and abs(axis[1] / axis.length) < 1e-5):
up = Vector((-1, 0, 0))
else:
up = Vector((0, 0, 1))
return up
# Make a single strut in non-manifold mode.
#
# The strut will be a "cylinder" with @a n sides. The vertices of the
# cylinder will be @a od / 2 from the center of the cylinder. Optionally,
# extra loops will be placed (@a od - @a id) / 2 from either end. The
# strut will be either a simple, open-ended single-surface "cylinder", or a
# double walled "pipe" with the outer wall vertices @a od / 2 from the center
# and the inner wall vertices @a id / 2 from the center. The two walls will
# be joined together at the ends with a face ring such that the entire strut
# is a manifold object. All faces of the strut will be quads.
#
# @param v1 Vertex representing one end of the strut's center-line.
# @param v2 Vertex representing the other end of the strut's
# center-line.
# @param id The diameter of the inner wall of a solid strut. Used for
# calculating the position of the extra loops irrespective
# of the solidity of the strut.
# @param od The diameter of the outer wall of a solid strut, or the
# diameter of a non-solid strut.
# @param solid If true, the strut will be made solid such that it has an
# inner wall (diameter @a id), an outer wall (diameter
# @a od), and face rings at either end of the strut such
# the strut is a manifold object. If false, the strut is
# a simple, open-ended "cylinder".
# @param loops If true, edge loops will be placed at either end of the
# strut, (@a od - @a id) / 2 from the end of the strut. The
# loops make subsurfed solid struts work nicely.
# @return A tuple containing a list of vertices and a list of faces.
# The face vertex indices are accurate only for the list of
# vertices for the created strut.
def make_strut(v1, v2, ind, od, n, solid, loops):
v1 = Vector(v1)
v2 = Vector(v2)
axis = v2 - v1
pos = [(0, od / 2)]
if loops:
pos += [((od - ind) / 2, od / 2),
(axis.length - (od - ind) / 2, od / 2)]
pos += [(axis.length, od / 2)]
if solid:
pos += [(axis.length, ind / 2)]
if loops:
pos += [(axis.length - (od - ind) / 2, ind / 2),
((od - ind) / 2, ind / 2)]
pos += [(0, ind / 2)]
vps = len(pos)
fps = vps
if not solid:
fps -= 1
fw = axis.copy()
fw.normalize()
up = select_up(axis)
lf = up.cross(fw)
lf.normalize()
up = fw.cross(lf)
mat = Matrix((fw, lf, up))
mat.transpose()
verts = [None] * n * vps
faces = [None] * n * fps
for i in range(n):
base = (i - 1) * vps
x = cossin[i][0]
y = cossin[i][1]
for j in range(vps):
p = Vector((pos[j][0], pos[j][1] * x, pos[j][1] * y))
p = mat * p
verts[i * vps + j] = p + v1
if i:
for j in range(fps):
f = (i - 1) * fps + j
faces[f] = [base + j, base + vps + j,
base + vps + (j + 1) % vps, base + (j + 1) % vps]
base = len(verts) - vps
i = n
for j in range(fps):
f = (i - 1) * fps + j
faces[f] = [base + j, j, (j + 1) % vps, base + (j + 1) % vps]
return verts, faces
# Project a point along a vector onto a plane.
#
# Really, just find the intersection of the line represented by @a point
# and @a dir with the plane represented by @a norm and @a p. However, if
# the point is on or in front of the plane, or the line is parallel to
# the plane, the original point will be returned.
#
# @param point The point to be projected onto the plane.
# @param dir The vector along which the point will be projected.
# @param norm The normal of the plane onto which the point will be
# projected.
# @param p A point through which the plane passes.
# @return A vector representing the projected point, or the
# original point.
def project_point(point, dir, norm, p):
d = (point - p).dot(norm)
if d >= 0:
# the point is already on or in front of the plane
return point
v = dir.dot(norm)
if v * v < 1e-8:
# the plane is unreachable
return point
return point - dir * d / v
# Make a simple strut for debugging.
#
# The strut is just a single quad representing the Z axis of the edge.
#
# @param mesh The base mesh. Used for finding the edge vertices.
# @param edge_num The number of the current edge. For the face vertex
# indices.
# @param edge The edge for which the strut will be built.
# @param od Twice the width of the strut.
# @return A tuple containing a list of vertices and a list of faces.
# The face vertex indices are pre-adjusted by the edge
# number.
# @fixme The face vertex indices should be accurate for the local
# vertices (consistency)
def make_debug_strut(mesh, edge_num, edge, od):
v = [mesh.verts[edge.verts[0].index].co,
mesh.verts[edge.verts[1].index].co,
None, None]
v[2] = v[1] + edge.z * od / 2
v[3] = v[0] + edge.z * od / 2
f = [[edge_num * 4 + 0, edge_num * 4 + 1,
edge_num * 4 + 2, edge_num * 4 + 3]]
return v, f
# Make a cylinder with ends clipped to the end-planes of the edge.
#
# The strut is just a single quad representing the Z axis of the edge.
#
# @param mesh The base mesh. Used for finding the edge vertices.
# @param edge_num The number of the current edge. For the face vertex
# indices.
# @param edge The edge for which the strut will be built.
# @param od The diameter of the strut.
# @return A tuple containing a list of vertices and a list of faces.
# The face vertex indices are pre-adjusted by the edge
# number.
# @fixme The face vertex indices should be accurate for the local
# vertices (consistency)
def make_clipped_cylinder(mesh, edge_num, edge, od):
n = len(cossin)
cyl = [None] * n
v0 = mesh.verts[edge.verts[0].index].co
c0 = v0 + od * edge.y
v1 = mesh.verts[edge.verts[1].index].co
c1 = v1 - od * edge.y
for i in range(n):
x = cossin[i][0]
y = cossin[i][1]
r = (edge.z * x - edge.x * y) * od / 2
cyl[i] = [c0 + r, c1 + r]
for p in edge.verts[0].planes:
cyl[i][0] = project_point(cyl[i][0], edge.y, p, v0)
for p in edge.verts[1].planes:
cyl[i][1] = project_point(cyl[i][1], -edge.y, p, v1)
v = [None] * n * 2
f = [None] * n
base = edge_num * n * 2
for i in range(n):
v[i * 2 + 0] = cyl[i][1]
v[i * 2 + 1] = cyl[i][0]
f[i] = [None] * 4
f[i][0] = base + i * 2 + 0
f[i][1] = base + i * 2 + 1
f[i][2] = base + (i * 2 + 3) % (n * 2)
f[i][3] = base + (i * 2 + 2) % (n * 2)
return v, f
# Represent a vertex in the base mesh, with additional information.
#
# These vertices are @b not shared between edges.
#
# @var index The index of the vert in the base mesh
# @var edge The edge to which this vertex is attached.
# @var edges A tuple of indicess of edges attached to this vert, not
# including the edge to which this vertex is attached.
# @var planes List of vectors representing the normals of the planes that
# bisect the angle between this vert's edge and each other
# adjacant edge.
class SVert:
# Create a vertex holding additional information about the bmesh vertex.
# @param bmvert The bmesh vertex for which additional information is
# to be stored.
# @param bmedge The edge to which this vertex is attached.
def __init__(self, bmvert, bmedge, edge):
self.index = bmvert.index
self.edge = edge
edges = bmvert.link_edges[:]
edges.remove(bmedge)
self.edges = tuple(map(lambda e: e.index, edges))
self.planes = []
def calc_planes(self, edges):
for ed in self.edges:
self.planes.append(calc_plane_normal(self.edge, edges[ed]))
# Represent an edge in the base mesh, with additional information.
#
# Edges do not share vertices so that the edge is always on the front (back?
# must verify) side of all the planes attached to its vertices. If the
# vertices were shared, the edge could be on either side of the planes, and
# there would be planes attached to the vertex that are irrelevant to the
# edge.
#
# @var index The index of the edge in the base mesh.
# @var bmedge Cached reference to this edge's bmedge
# @var verts A tuple of 2 SVert vertices, one for each end of the
# edge. The vertices are @b not shared between edges.
# However, if two edges are connected via a vertex in the
# bmesh, their corresponding SVert vertices will have the
# the same index value.
# @var x The x axis of the edges local frame of reference.
# Initially invalid.
# @var y The y axis of the edges local frame of reference.
# Initialized such that the edge runs from verts[0] to
# verts[1] along the negative y axis.
# @var z The z axis of the edges local frame of reference.
# Initially invalid.
class SEdge:
def __init__(self, bmesh, bmedge):
self.index = bmedge.index
self.bmedge = bmedge
bmesh.verts.ensure_lookup_table()
self.verts = (SVert(bmedge.verts[0], bmedge, self),
SVert(bmedge.verts[1], bmedge, self))
self.y = (bmesh.verts[self.verts[0].index].co -
bmesh.verts[self.verts[1].index].co)
self.y.normalize()
self.x = self.z = None
def set_frame(self, up):
self.x = self.y.cross(up)
self.x.normalize()
self.z = self.x.cross(self.y)
def calc_frame(self, base_edge):
baxis = base_edge.y
if (self.verts[0].index == base_edge.verts[0].index or
self.verts[1].index == base_edge.verts[1].index):
axis = -self.y
elif (self.verts[0].index == base_edge.verts[1].index or
self.verts[1].index == base_edge.verts[0].index):
axis = self.y
else:
raise ValueError("edges not connected")
if baxis.dot(axis) in (-1, 1):
# aligned axis have their up/z aligned
up = base_edge.z
else:
# Get the unit vector dividing the angle (theta) between baxis and
# axis in two equal parts
h = (baxis + axis)
h.normalize()
# (cos(theta/2), sin(theta/2) * n) where n is the unit vector of the
# axis rotating baxis onto axis
q = Quaternion([baxis.dot(h)] + list(baxis.cross(h)))
# rotate the base edge's up around the rotation axis (blender
# quaternion shortcut:)
up = q * base_edge.z
self.set_frame(up)
def calc_vert_planes(self, edges):
for v in self.verts:
v.calc_planes(edges)
def bisect_faces(self):
n1 = self.bmedge.link_faces[0].normal
if len(self.bmedge.link_faces) > 1:
n2 = self.bmedge.link_faces[1].normal
return (n1 + n2).normalized()
return n1
def calc_simple_frame(self):
return self.y.cross(select_up(self.y)).normalized()
def find_edge_frame(self, sedges):
if self.bmedge.link_faces:
return self.bisect_faces()
if self.verts[0].edges or self.verts[1].edges:
edges = list(self.verts[0].edges + self.verts[1].edges)
for i in range(len(edges)):
edges[i] = sedges[edges[i]]
while edges and edges[-1].y.cross(self.y).length < 1e-3:
edges.pop()
if not edges:
return self.calc_simple_frame()
n1 = edges[-1].y.cross(self.y).normalized()
edges.pop()
while edges and edges[-1].y.cross(self.y).cross(n1).length < 1e-3:
edges.pop()
if not edges:
return n1
n2 = edges[-1].y.cross(self.y).normalized()
return (n1 + n2).normalized()
return self.calc_simple_frame()
def calc_plane_normal(edge1, edge2):
if edge1.verts[0].index == edge2.verts[0].index:
axis1 = -edge1.y
axis2 = edge2.y
elif edge1.verts[1].index == edge2.verts[1].index:
axis1 = edge1.y
axis2 = -edge2.y
elif edge1.verts[0].index == edge2.verts[1].index:
axis1 = -edge1.y
axis2 = -edge2.y
elif edge1.verts[1].index == edge2.verts[0].index:
axis1 = edge1.y
axis2 = edge2.y
else:
raise ValueError("edges not connected")
# Both axis1 and axis2 are unit vectors, so this will produce a vector
# bisects the two, so long as they are not 180 degrees apart (in which
# there are infinite solutions).
return (axis1 + axis2).normalized()
def build_edge_frames(edges):
edge_set = set(edges)
while edge_set:
edge_queue = [edge_set.pop()]
edge_queue[0].set_frame(edge_queue[0].find_edge_frame(edges))
while edge_queue:
current_edge = edge_queue.pop()
for i in (0, 1):
for e in current_edge.verts[i].edges:
edge = edges[e]
if edge.x is not None: # edge already processed
continue
edge_set.remove(edge)
edge_queue.append(edge)
edge.calc_frame(current_edge)
def make_manifold_struts(truss_obj, od, segments):
bpy.context.view_layer.objects.active = truss_obj
bpy.ops.object.editmode_toggle()
truss_mesh = bmesh.from_edit_mesh(truss_obj.data).copy()
bpy.ops.object.editmode_toggle()
edges = [None] * len(truss_mesh.edges)
for i, e in enumerate(truss_mesh.edges):
edges[i] = SEdge(truss_mesh, e)
build_edge_frames(edges)
verts = []
faces = []
for e, edge in enumerate(edges):
# v, f = make_debug_strut(truss_mesh, e, edge, od)
edge.calc_vert_planes(edges)
v, f = make_clipped_cylinder(truss_mesh, e, edge, od)
verts += v
faces += f
return verts, faces
def make_simple_struts(truss_mesh, ind, od, segments, solid, loops):
vps = 2
if solid:
vps *= 2
if loops:
vps *= 2
fps = vps
if not solid:
fps -= 1
verts = [None] * len(truss_mesh.edges) * segments * vps
faces = [None] * len(truss_mesh.edges) * segments * fps
vbase = 0
fbase = 0
for e in truss_mesh.edges:
v1 = truss_mesh.vertices[e.vertices[0]]
v2 = truss_mesh.vertices[e.vertices[1]]
v, f = make_strut(v1.co, v2.co, ind, od, segments, solid, loops)
for fv in f:
for i in range(len(fv)):
fv[i] += vbase
for i in range(len(v)):
verts[vbase + i] = v[i]
for i in range(len(f)):
faces[fbase + i] = f[i]
# if not base % 12800:
# print (base * 100 / len(verts))
vbase += vps * segments
fbase += fps * segments
return verts, faces
def create_struts(self, context, ind, od, segments, solid, loops, manifold):
build_cossin(segments)
for truss_obj in bpy.context.scene.objects:
if not truss_obj.select_get():
continue
truss_obj.select_set(False)
truss_mesh = truss_obj.to_mesh(context.scene, True, 'PREVIEW')
if not truss_mesh.edges:
continue
if manifold:
verts, faces = make_manifold_struts(truss_obj, od, segments)
else:
verts, faces = make_simple_struts(truss_mesh, ind, od, segments,
solid, loops)
mesh = bpy.data.meshes.new("Struts")
mesh.from_pydata(verts, [], faces)
obj = bpy.data.objects.new("Struts", mesh)
bpy.context.collection.objects.link(obj)
obj.select_set(True)
obj.location = truss_obj.location
bpy.context.view_layer.objects.active = obj
mesh.update()
class Struts(Operator):
bl_idname = "mesh.generate_struts"
bl_label = "Struts"
bl_description = ("Add one or more struts meshes based on selected truss meshes \n"
"Note: can get very high poly\n"
"Needs an existing Active Mesh Object")
bl_options = {'REGISTER', 'UNDO'}
ind: FloatProperty(
name="Inside Diameter",
description="Diameter of inner surface",
min=0.0, soft_min=0.0,
max=100, soft_max=100,
default=0.04
)
od: FloatProperty(
name="Outside Diameter",
description="Diameter of outer surface",
min=0.001, soft_min=0.001,
max=100, soft_max=100,
default=0.05
)
manifold: BoolProperty(
name="Manifold",
description="Connect struts to form a single solid",
default=False
)
solid: BoolProperty(
name="Solid",
description="Create inner surface",
default=False
)
loops: BoolProperty(
name="Loops",
description="Create sub-surf friendly loops",
default=False
)
segments: IntProperty(
name="Segments",
description="Number of segments around strut",
min=3, soft_min=3,
max=64, soft_max=64,
default=12
)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.prop(self, "ind")
col.prop(self, "od")
col.prop(self, "segments")
col.separator()
col.prop(self, "manifold")
col.prop(self, "solid")
col.prop(self, "loops")
@classmethod
def poll(cls, context):
obj = context.active_object
return obj is not None and obj.type == "MESH"
def execute(self, context):
store_undo = bpy.context.preferences.edit.use_global_undo
bpy.context.preferences.edit.use_global_undo = False
keywords = self.as_keywords()
try:
create_struts(self, context, **keywords)
bpy.context.preferences.edit.use_global_undo = store_undo
return {"FINISHED"}
except Exception as e:
bpy.context.preferences.edit.use_global_undo = store_undo
self.report({"WARNING"},
"Make Struts could not be performed. Operation Cancelled")
print("\n[mesh.generate_struts]\n{}".format(e))
return {"CANCELLED"}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@ -1,361 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Easy Lattice Object",
"author": "Kursad Karatas",
"version": (0, 6, 0),
"blender": (2, 66, 0),
"location": "View3D > Easy Lattice",
"description": "Create a lattice for shape editing",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Easy_Lattice_Editing_Addon",
"tracker_url": "https://bitbucket.org/kursad/blender_addons_easylattice/src",
"category": "Mesh",
}
import bpy
from mathutils import (
Matrix,
Vector,
)
from bpy.types import Operator
from bpy.props import (
EnumProperty,
FloatProperty,
IntProperty,
)
def createLattice(context, obj, props):
# Create lattice and object
lat = bpy.data.lattices.new('EasyLattice')
ob = bpy.data.objects.new('EasyLattice', lat)
# Take into consideration any selected vertices (default: all vertices)
selectedVertices = createVertexGroup(obj)
size, pos = findBBox(obj, selectedVertices)
loc, rot = getTransformations(obj)
# the position comes from the bbox
ob.location = pos
# the size from bbox * the incoming scale factor
ob.scale = size * props[3]
# the rotation comes from the combined obj world
# matrix which was converted to euler pairs
ob.rotation_euler = buildRot_World(obj)
ob.show_in_front = True
# Link object to scene
scn = context.scene
# Take care of the local view
base = scn.objects.link(ob)
scn.objects.active = ob
v3d = None
if context.space_data and context.space_data.type == 'VIEW_3D':
v3d = context.space_data
if v3d and v3d.local_view:
base.layers_from_view(v3d)
scn.update()
# Set lattice attributes
lat.points_u = props[0]
lat.points_v = props[1]
lat.points_w = props[2]
lat.interpolation_type_u = props[4]
lat.interpolation_type_v = props[4]
lat.interpolation_type_w = props[4]
lat.use_outside = False
return ob
def createVertexGroup(obj):
vertices = obj.data.vertices
selverts = []
if obj.mode == "EDIT":
bpy.ops.object.editmode_toggle()
group = obj.vertex_groups.new(name="easy_lattice_group")
for vert in vertices:
if vert.select is True:
selverts.append(vert)
group.add([vert.index], 1.0, "REPLACE")
# Default: use all vertices
if not selverts:
for vert in vertices:
selverts.append(vert)
group.add([vert.index], 1.0, "REPLACE")
return selverts
def getTransformations(obj):
rot = obj.rotation_euler
loc = obj.location
return [loc, rot]
def findBBox(obj, selvertsarray):
mat = buildTrnScl_WorldMat(obj)
mat_world = obj.matrix_world
minx, miny, minz = selvertsarray[0].co
maxx, maxy, maxz = selvertsarray[0].co
c = 1
for c in range(len(selvertsarray)):
co = selvertsarray[c].co
if co.x < minx:
minx = co.x
if co.y < miny:
miny = co.y
if co.z < minz:
minz = co.z
if co.x > maxx:
maxx = co.x
if co.y > maxy:
maxy = co.y
if co.z > maxz:
maxz = co.z
c += 1
minpoint = Vector((minx, miny, minz))
maxpoint = Vector((maxx, maxy, maxz))
# The middle position has to be calculated based on the real world matrix
pos = ((minpoint + maxpoint) / 2)
minpoint = mat * minpoint # Calculate only based on loc/scale
maxpoint = mat * maxpoint # Calculate only based on loc/scale
pos = mat_world * pos # the middle position has to be calculated based on the real world matrix
size = maxpoint - minpoint
size = Vector((max(0.1, abs(size.x)), max(0.1, abs(size.y)), max(0.1, abs(size.z)))) # Prevent zero size dimensions
return [size, pos]
def buildTrnSclMat(obj):
# This function builds a local matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at object level if there is any
mat_trans = Matrix.Translation(obj.location)
mat_scale = Matrix.Scale(obj.scale[0], 4, (1, 0, 0))
mat_scale *= Matrix.Scale(obj.scale[1], 4, (0, 1, 0))
mat_scale *= Matrix.Scale(obj.scale[2], 4, (0, 0, 1))
mat_final = mat_trans * mat_scale
return mat_final
def buildTrnScl_WorldMat(obj):
# This function builds a real world matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at object level if there is any
loc, rot, scl = obj.matrix_world.decompose()
mat_trans = Matrix.Translation(loc)
mat_scale = Matrix.Scale(scl[0], 4, (1, 0, 0))
mat_scale *= Matrix.Scale(scl[1], 4, (0, 1, 0))
mat_scale *= Matrix.Scale(scl[2], 4, (0, 0, 1))
mat_final = mat_trans * mat_scale
return mat_final
# Feature use
def buildRot_WorldMat(obj):
# This function builds a real world matrix that encodes rotation
# and it leaves out translation and scale matrices
loc, rot, scl = obj.matrix_world.decompose()
rot = rot.to_euler()
mat_rot = Matrix.Rotation(rot[0], 4, 'X')
mat_rot *= Matrix.Rotation(rot[1], 4, 'Z')
mat_rot *= Matrix.Rotation(rot[2], 4, 'Y')
return mat_rot
def buildTrn_WorldMat(obj):
# This function builds a real world matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at object level if there is any
loc, rot, scl = obj.matrix_world.decompose()
mat_trans = Matrix.Translation(loc)
return mat_trans
def buildScl_WorldMat(obj):
# This function builds a real world matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at object level if there is any
loc, rot, scl = obj.matrix_world.decompose()
mat_scale = Matrix.Scale(scl[0], 4, (1, 0, 0))
mat_scale *= Matrix.Scale(scl[1], 4, (0, 1, 0))
mat_scale *= Matrix.Scale(scl[2], 4, (0, 0, 1))
return mat_scale
def buildRot_World(obj):
# This function builds a real world rotation values
loc, rot, scl = obj.matrix_world.decompose()
rot = rot.to_euler()
return rot
def main(context, lat_props):
obj = context.object
if obj.type == "MESH":
lat = createLattice(context, obj, lat_props)
modif = obj.modifiers.new("EasyLattice", "LATTICE")
modif.object = lat
modif.vertex_group = "easy_lattice_group"
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_pattern(pattern=lat.name, extend=False)
context.view_layer.objects.active = lat
context.scene.update()
return
class EasyLattice(Operator):
bl_idname = "object.easy_lattice"
bl_label = "Easy Lattice Creator"
bl_description = ("Create a Lattice modifier ready to edit\n"
"Needs an existing Active Mesh Object\n")
lat_u: IntProperty(
name="Lattice u",
description="Points in u direction",
default=3
)
lat_v: IntProperty(
name="Lattice v",
description="Points in v direction",
default=3
)
lat_w: IntProperty(
name="Lattice w",
description="Points in w direction",
default=3
)
lat_scale_factor: FloatProperty(
name="Lattice scale factor",
description="Adjustment to the lattice scale",
default=1,
min=0.1,
step=1,
precision=2
)
lat_types = (('KEY_LINEAR', "Linear", "Linear Interpolation type"),
('KEY_CARDINAL', "Cardinal", "Cardinal Interpolation type"),
('KEY_CATMULL_ROM', "Catmull-Rom", "Catmull-Rom Interpolation type"),
('KEY_BSPLINE', "BSpline", "Key BSpline Interpolation Type")
)
lat_type: EnumProperty(
name="Lattice Type",
description="Choose Lattice Type",
items=lat_types,
default='KEY_BSPLINE'
)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj is not None and obj.type == "MESH"
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.prop(self, "lat_u")
col.prop(self, "lat_v")
col.prop(self, "lat_w")
layout.prop(self, "lat_scale_factor")
layout.prop(self, "lat_type")
def execute(self, context):
lat_u = self.lat_u
lat_v = self.lat_v
lat_w = self.lat_w
lat_scale_factor = self.lat_scale_factor
# enum property no need to complicate things
lat_type = self.lat_type
# XXX, should use keyword args
lat_props = [lat_u, lat_v, lat_w, lat_scale_factor, lat_type]
try:
main(context, lat_props)
except Exception as ex:
print("\n[Add Advanced Objects]\nOperator:object.easy_lattice\n{}\n".format(ex))
self.report(
{'WARNING'},
"Easy Lattice Creator could not be completed (See Console for more info)"
)
return {"CANCELLED"}
return {"FINISHED"}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def register():
bpy.utils.register_class(EasyLattice)
def unregister():
bpy.utils.unregister_class(EasyLattice)
if __name__ == "__main__":
register()

View File

@ -1,179 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Add Chain",
"author": "Brian Hinton (Nichod)",
"version": (0, 1, 2),
"blender": (2, 71, 0),
"location": "Toolshelf > Create Tab",
"description": "Adds Chain with curve guide for easy creation",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Object/Add_Chain",
"category": "Object",
}
import bpy
from bpy.types import Operator
def Add_Chain():
# Adds Empty to scene
bpy.ops.object.add(
type='EMPTY',
view_align=False,
enter_editmode=False,
location=(0, 0, 0),
rotation=(0, 0, 0),
)
# Changes name of Empty to rot_link adds variable emp
emp = bpy.context.object
emp.name = "rot_link"
# Rotate emp ~ 90 degrees
emp.rotation_euler = [1.570796, 0, 0]
# Adds Curve Path to scene
bpy.ops.curve.primitive_nurbs_path_add(
view_align=False,
enter_editmode=False,
location=(0, 0, 0),
rotation=(0, 0, 0),
)
# Change Curve name to deform adds variable curv
curv = bpy.context.object
curv.name = "deform"
# Inserts Torus primitive
bpy.ops.mesh.primitive_torus_add(
major_radius=1,
minor_radius=0.25,
major_segments=12,
minor_segments=4,
abso_major_rad=1,
abso_minor_rad=0.5,
)
# Positions Torus primitive to center of scene
bpy.context.active_object.location = 0.0, 0.0, 0.0
# Resetting Torus rotation in case of 'Align to view' option enabled
bpy.context.active_object.rotation_euler = 0.0, 0.0, 0.0
# Changes Torus name to chain adds variable tor
tor = bpy.context.object
tor.name = "chain"
# Adds Array Modifier to tor
bpy.ops.object.modifier_add(type='ARRAY')
# Adds subsurf modifier tor
bpy.ops.object.modifier_add(type='SUBSURF')
# Smooths tor
bpy.ops.object.shade_smooth()
# Select curv
sce = bpy.context.scene
sce.objects.active = curv
# Toggle into editmode
bpy.ops.object.editmode_toggle()
# TODO, may be better to move objects directly
# Translate curve object
bpy.ops.transform.translate(
value=(2, 0, 0),
constraint_axis=(True, False, False),
orient_type='GLOBAL',
mirror=False,
proportional='DISABLED',
proportional_edit_falloff='SMOOTH',
proportional_size=1,
snap=False,
snap_target='CLOSEST',
snap_point=(0, 0, 0),
snap_align=False,
snap_normal=(0, 0, 0),
release_confirm=False,
)
# Toggle into objectmode
bpy.ops.object.editmode_toggle()
# Select tor or chain
sce.objects.active = tor
# Selects Array Modifier for editing
array = tor.modifiers['Array']
# Change Array Modifier Parameters
array.fit_type = 'FIT_CURVE'
array.curve = curv
array.offset_object = emp
array.use_object_offset = True
array.relative_offset_displace = 0.549, 0.0, 0.0
# Add curve modifier
bpy.ops.object.modifier_add(type='CURVE')
# Selects Curve Modifier for editing
cur = tor.modifiers['Curve']
# Change Curve Modifier Parameters
cur.object = curv
class AddChain(Operator):
bl_idname = "mesh.primitive_chain_add"
bl_label = "Add Chain"
bl_description = ("Create a Chain segment with helper objects controlling modifiers:\n"
"1) A Curve Modifier Object (deform) for the length and shape,\n"
"Edit the Path to extend Chain Length\n"
"2) An Empty (rot_link) as an Array Offset for rotation")
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
try:
Add_Chain()
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"mesh.primitive_chain_add\nError: {}".format(e))
return {'CANCELLED'}
return {'FINISHED'}
def register():
bpy.utils.register_class(AddChain)
def unregister():
bpy.utils.unregister_class(AddChain)
if __name__ == "__main__":
register()

View File

@ -1,288 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# TODO: find English versions of created object names
bl_info = {
"name": "Oscurart Chain Maker",
"author": "Oscurart",
"version": (1, 1),
"blender": (2, 56, 0),
"location": "Add > Mesh > Oscurart Chain",
"description": "Create chain links from armatures",
"warning": "",
"wiki_url": "oscurart.blogspot.com",
"category": "Object"}
import bpy
from bpy.props import (
BoolProperty,
FloatProperty,
)
from bpy.types import Operator
def makeChain(self, context, mult, curverig):
if not context.active_object.type == 'ARMATURE':
self.report({'WARNING'}, "Active Object must be an Armature")
return False
bpy.ops.object.mode_set(mode='OBJECT')
VAR_SWITCH = abs(1)
ARMATURE = bpy.context.active_object
def creahuesocero(hueso):
# create data to link
mesh = bpy.data.meshes.new("objectData" + str(hueso.name))
object = bpy.data.objects.new("HardLink" + str(hueso.name), mesh)
mesh.from_pydata(
[(-0.04986128956079483, -0.6918092370033264, -0.17846597731113434),
(-0.04986128956079483, -0.6918091773986816, 0.17846640944480896),
(-0.049861326813697815, -0.154555082321167, 0.17846627533435822),
(-0.049861326813697815, -0.15455523133277893, -0.17846614122390747),
(-0.04986133798956871, -0.03475356101989746, 0.25805795192718506),
(-0.04986133798956871, -0.03475397825241089, -0.25805795192718506),
(-0.049861278384923935, -0.8116106986999512, -0.2580576539039612),
(-0.049861278384923935, -0.8116104602813721, 0.25805822014808655),
(-0.04986128211021423, -0.7692053318023682, 2.6668965347198537e-07),
(-0.04986127093434334, -0.923523485660553, 2.7834033744511544e-07),
(-0.04986133426427841, -0.0771591067314148, 3.5627678585115063e-08),
(-0.04986134544014931, 0.0771591067314148, -3.5627678585115063e-08),
(0.04986133798956871, -0.03475397825241089, -0.25805795192718506),
(0.04986133053898811, 0.0771591067314148, -3.5627678585115063e-08),
(0.04986133798956871, -0.03475356101989746, 0.25805795192718506),
(0.04986134544014931, -0.15455523133277893, -0.17846614122390747),
(0.04986134544014931, -0.0771591067314148, 3.5627678585115063e-08),
(0.04986134544014931, -0.154555082321167, 0.17846627533435822),
(0.049861397594213486, -0.8116106986999512, -0.2580576539039612),
(0.04986140504479408, -0.923523485660553, 2.7834033744511544e-07),
(0.049861397594213486, -0.8116104602813721, 0.25805822014808655),
(0.04986139014363289, -0.6918091773986816, 0.17846640944480896),
(0.04986139014363289, -0.7692053318023682, 2.6668965347198537e-07),
(0.04986139014363289, -0.6918092370033264, -0.17846597731113434)],
[(1, 2), (0, 3), (3, 5), (2, 4), (0, 6), (5, 6), (1, 7), (4, 7), (0, 8), (1, 8),
(7, 9), (6, 9), (8, 9), (2, 10), (3, 10), (4, 11), (5, 11), (10, 11), (5, 12),
(12, 13), (11, 13), (13, 14), (4, 14), (10, 16), (15, 16), (3, 15), (2, 17),
(16, 17), (9, 19), (18, 19), (6, 18), (7, 20), (19, 20), (8, 22), (21, 22),
(1, 21), (0, 23), (22, 23), (14, 20), (12, 18), (15, 23), (17, 21), (12, 15),
(13, 16), (14, 17), (20, 21), (19, 22), (18, 23)],
[(6, 0, 3, 5), (1, 7, 4, 2), (0, 6, 9, 8), (8, 9, 7, 1), (2, 4, 11, 10), (10, 11, 5, 3),
(11, 13, 12, 5), (4, 14, 13, 11), (3, 15, 16, 10), (10, 16, 17, 2), (6, 18, 19, 9),
(9, 19, 20, 7), (1, 21, 22, 8), (23, 0, 8, 22), (7, 20, 14, 4), (5, 12, 18, 6),
(0, 23, 15, 3), (2, 17, 21, 1), (16, 15, 12, 13), (17, 16, 13, 14), (22, 21, 20, 19),
(23, 22, 19, 18), (21, 17, 14, 20), (15, 23, 18, 12)]
)
mesh.validate()
bpy.context.collection.objects.link(object)
# scale to the bone
bpy.data.objects["HardLink" + str(hueso.name)].scale = (hueso.length * mult,
hueso.length * mult,
hueso.length * mult)
# Parent Objects
bpy.data.objects["HardLink" + str(hueso.name)].parent = ARMATURE
bpy.data.objects["HardLink" + str(hueso.name)].parent_type = 'BONE'
bpy.data.objects["HardLink" + str(hueso.name)].parent_bone = hueso.name
def creahuesonoventa(hueso):
# create data to link
mesh = bpy.data.meshes.new("objectData" + str(hueso.name))
object = bpy.data.objects.new("NewLink" + str(hueso.name), mesh)
mesh.from_pydata(
[(0.1784660965204239, -0.6918091773986816, -0.049861203879117966),
(-0.1784662902355194, -0.6918091773986816, -0.04986126348376274),
(-0.17846627533435822, -0.1545550525188446, -0.04986134544014931),
(0.17846617102622986, -0.15455520153045654, -0.04986128583550453),
(-0.25805795192718506, -0.03475359082221985, -0.049861375242471695),
(0.25805795192718506, -0.034753888845443726, -0.04986129328608513),
(0.2580578327178955, -0.8116105794906616, -0.04986117407679558),
(-0.2580580413341522, -0.8116105198860168, -0.049861256033182144),
(-9.672299938756623e-08, -0.7692052721977234, -0.04986122250556946),
(-8.99775329799013e-08, -0.923523485660553, -0.04986120015382767),
(-7.764004550381287e-09, -0.07715904712677002, -0.049861326813697815),
(4.509517737005808e-08, 0.0771591067314148, -0.049861349165439606),
(0.25805795192718506, -0.034753888845443726, 0.049861375242471695),
(-2.2038317837314025e-08, 0.0771591067314148, 0.049861326813697815),
(-0.25805795192718506, -0.03475359082221985, 0.04986129328608513),
(0.17846617102622986, -0.15455520153045654, 0.04986138269305229),
(-1.529285498236277e-08, -0.07715907692909241, 0.049861352890729904),
(-0.17846627533435822, -0.1545550525188446, 0.049861323088407516),
(0.2580578029155731, -0.8116105794906616, 0.049861494451761246),
(-1.5711103173998708e-07, -0.923523485660553, 0.04986147582530975),
(-0.2580580711364746, -0.8116105198860168, 0.04986141249537468),
(-0.1784663051366806, -0.6918091773986816, 0.049861419945955276),
(-1.340541757599567e-07, -0.7692052721977234, 0.049861449748277664),
(0.1784660816192627, -0.6918091773986816, 0.04986146464943886)],
[(1, 2), (0, 3), (3, 5), (2, 4), (0, 6), (5, 6), (1, 7), (4, 7), (0, 8),
(1, 8), (7, 9), (6, 9), (8, 9), (2, 10), (3, 10), (4, 11), (5, 11), (10, 11),
(5, 12), (12, 13), (11, 13), (13, 14), (4, 14), (10, 16), (15, 16), (3, 15),
(2, 17), (16, 17), (9, 19), (18, 19), (6, 18), (7, 20), (19, 20), (8, 22),
(21, 22), (1, 21), (0, 23), (22, 23), (14, 20), (12, 18), (15, 23), (17, 21),
(12, 15), (13, 16), (14, 17), (20, 21), (19, 22), (18, 23)],
[(6, 0, 3, 5), (1, 7, 4, 2), (0, 6, 9, 8), (8, 9, 7, 1), (2, 4, 11, 10),
(10, 11, 5, 3), (11, 13, 12, 5), (4, 14, 13, 11), (3, 15, 16, 10), (10, 16, 17, 2),
(6, 18, 19, 9), (9, 19, 20, 7), (1, 21, 22, 8), (23, 0, 8, 22), (7, 20, 14, 4),
(5, 12, 18, 6), (0, 23, 15, 3), (2, 17, 21, 1), (16, 15, 12, 13), (17, 16, 13, 14),
(22, 21, 20, 19), (23, 22, 19, 18), (21, 17, 14, 20), (15, 23, 18, 12)]
)
mesh.validate()
bpy.context.collection.objects.link(object)
# scale to the bone
bpy.data.objects["NewLink" + str(hueso.name)].scale = (hueso.length * mult,
hueso.length * mult,
hueso.length * mult)
# Parent objects
bpy.data.objects["NewLink" + str(hueso.name)].parent = ARMATURE
bpy.data.objects["NewLink" + str(hueso.name)].parent_type = 'BONE'
bpy.data.objects["NewLink" + str(hueso.name)].parent_bone = hueso.name
for hueso in bpy.context.active_object.pose.bones:
if VAR_SWITCH == 1:
creahuesocero(hueso)
else:
creahuesonoventa(hueso)
if VAR_SWITCH == 1:
VAR_SWITCH = 0
else:
VAR_SWITCH = 1
# if curve rig is activated
if curverig is True:
# variables
LISTA_POINTC = []
ACTARM = bpy.context.active_object
# create data and link the object to the scene
crv = bpy.data.curves.new("CurvaCable", "CURVE")
obCable = bpy.data.objects.new("Cable", crv)
bpy.context.collection.objects.link(obCable)
# set the attributes
crv.dimensions = "3D"
crv.resolution_u = 10
crv.resolution_v = 10
crv.twist_mode = "MINIMUM"
# create the list of tail and head coordinates
LISTA_POINTC.append((
ACTARM.data.bones[0].head_local[0],
ACTARM.data.bones[0].head_local[1],
ACTARM.data.bones[0].head_local[2], 1
))
for hueso in ACTARM.data.bones:
LISTA_POINTC.append((
hueso.tail_local[0],
hueso.tail_local[1],
hueso.tail_local[2], 1
))
# create the Spline
spline = crv.splines.new("NURBS")
lencoord = len(LISTA_POINTC)
rango = range(lencoord)
spline.points.add(lencoord - 1)
for punto in rango:
spline.points[punto].co = LISTA_POINTC[punto]
# set the endpoint
bpy.data.objects['Cable'].data.splines[0].use_endpoint_u = True
# select the curve
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects['Cable'].select = 1
bpy.context.view_layer.objects.active = bpy.data.objects['Cable']
# switch to Edit mode
bpy.ops.object.mode_set(mode='EDIT')
# create hooks
POINTSTEP = 0
for POINT in bpy.data.objects['Cable'].data.splines[0].points:
bpy.ops.curve.select_all(action="DESELECT")
bpy.data.objects['Cable'].data.splines[0].points[POINTSTEP].select = 1
bpy.ops.object.hook_add_newob()
POINTSTEP += 1
# Objects selection step
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
ACTARM.select = 1
bpy.context.view_layer.objects.active = bpy.data.objects['Armature']
bpy.ops.object.mode_set(mode='POSE')
bpy.ops.pose.select_all(action='DESELECT')
ACTARM.data.bones[-1].select = 1
ACTARM.data.bones.active = ACTARM.data.bones[-1]
# set IK Spline
bpy.ops.pose.constraint_add_with_targets(type='SPLINE_IK')
ACTARM.pose.bones[-1].constraints['Spline IK'].target = bpy.data.objects['Cable']
ACTARM.pose.bones[-1].constraints['Spline IK'].chain_count = 100
bpy.context.active_object.pose.bones[-1].constraints['Spline IK'].use_y_stretch = False
# return to Object mode
bpy.ops.object.mode_set(mode='OBJECT')
class MESH_OT_primitive_oscurart_chain_add(Operator):
bl_idname = "mesh.primitive_oscurart_chain_add"
bl_label = "Chain to Bones"
bl_description = ("Add Chain Parented to an Existing Armature\n"
"The Active/Last Selected Object must be an Armature")
bl_options = {'REGISTER', 'UNDO'}
curverig: BoolProperty(
name="Curve Rig",
default=False
)
multiplier: FloatProperty(
name="Scale",
default=1,
min=0.01, max=100.0
)
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == "ARMATURE")
def execute(self, context):
try:
makeChain(self, context, self.multiplier, self.curverig)
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"mesh.primitive_oscurart_chain_add\nError: {}".format(e))
return {'CANCELLED'}
return {'FINISHED'}
def register():
bpy.utils.register_class(MESH_OT_primitive_oscurart_chain_add)
def unregister():
bpy.utils.unregister_class(MESH_OT_primitive_oscurart_chain_add)
if __name__ == "__main__":
register()

View File

@ -1,137 +0,0 @@
# gpl author: liero
# very simple 'pixelization' or 'voxelization' engine #
bl_info = {
"name": "3D Pixelate",
"author": "liero",
"version": (0, 5, 3),
"blender": (2, 74, 0),
"location": "View3D > Tool Shelf",
"description": "Creates a 3d pixelated version of the object",
"category": "Object"}
# Note: winmgr properties are moved to the operator
import bpy
from bpy.types import Operator
from bpy.props import (
FloatProperty,
IntProperty,
)
def pix(self, obj):
sce = bpy.context.scene
obj.hide = obj.hide_render = True
mes = obj.to_mesh(sce, True, 'RENDER')
mes.transform(obj.matrix_world)
dup = bpy.data.objects.new('dup', mes)
sce.objects.link(dup)
dup.instance_type = 'VERTS'
sce.objects.active = dup
bpy.ops.object.mode_set()
ver = mes.vertices
for i in range(250):
fin = True
for i in dup.data.edges:
d = ver[i.vertices[0]].co - ver[i.vertices[1]].co
if d.length > self.size:
ver[i.vertices[0]].select = True
ver[i.vertices[1]].select = True
fin = False
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.subdivide(number_cuts=1, smoothness=self.smooth)
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.editmode_toggle()
if fin:
break
for i in ver:
for n in range(3):
i.co[n] -= (.001 + i.co[n]) % self.size
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.mesh.delete(type='EDGE_FACE')
bpy.ops.object.mode_set()
sca = self.size * (100 - self.gap) * .005
bpy.ops.mesh.primitive_cube_add(layers=[True] + [False] * 19)
bpy.ops.transform.resize(value=[sca] * 3)
bpy.context.view_layer.objects.active = dup
bpy.ops.object.parent_set(type='OBJECT')
class Pixelate(Operator):
bl_idname = "object.pixelate"
bl_label = "Pixelate Object"
bl_description = ("Create a 3d pixelated version of the object\n"
"using a Duplivert Box around each copied vertex\n"
"With high poly objects, it can take some time\n"
"Needs an existing Active Mesh Object")
bl_options = {'REGISTER', 'UNDO'}
size: FloatProperty(
name="Size",
min=.05, max=5,
default=.25,
description="Size of the cube / grid \n"
"Small values (below 0.1) can create a high polygon count"
)
gap: IntProperty(
name="Gap",
min=0, max=90,
default=10,
subtype='PERCENTAGE',
description="Separation - percent of size"
)
smooth: FloatProperty(
name="Smooth",
min=0, max=1,
default=.0,
description="Smooth factor when subdividing mesh"
)
@classmethod
def poll(cls, context):
return (context.active_object and
context.active_object.type == 'MESH' and
context.mode == 'OBJECT')
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.prop(self, "size")
col.prop(self, "gap")
layout.prop(self, "smooth")
def execute(self, context):
objeto = bpy.context.object
try:
pix(self, objeto)
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"object.pixelate\nError: {}".format(e))
return {'CANCELLED'}
return {'FINISHED'}
def register():
bpy.utils.register_class(Pixelate)
def unregister():
bpy.utils.unregister_class(Pixelate)
if __name__ == '__main__':
register()

View File

@ -1,201 +0,0 @@
# gpl: author Dannyboy
bl_info = {
"name": "Add Random Box Structure",
"author": "Dannyboy",
"version": (1, 0, 1),
"location": "View3D > Add > Make Box Structure",
"description": "Fill selected box shaped meshes with randomly sized cubes",
"warning": "",
"wiki_url": "",
"tracker_url": "dannyboypython.blogspot.com",
"category": "Object"}
import bpy
import random
from bpy.types import Operator
from bpy.props import (
BoolProperty,
FloatProperty,
FloatVectorProperty,
IntProperty,
)
class makestructure(Operator):
bl_idname = "object.make_structure"
bl_label = "Add Random Box Structure"
bl_description = ("Create a randomized structure made of boxes\n"
"with various control parameters\n"
"Needs an existing Active Mesh Object")
bl_options = {'REGISTER', 'UNDO'}
dc: BoolProperty(
name="Delete Base Mesh(es)",
default=True
)
wh: BoolProperty(
name="Stay Within Bounds",
description="Keeps cubes from exceeding base mesh bounds",
default=True
)
uf: BoolProperty(
name="Uniform Cube Quantity",
default=False
)
qn: IntProperty(
name="Cube Quantity",
default=10,
min=1, max=1500
)
mn: FloatVectorProperty(
name="Min Scales",
default=(0.1, 0.1, 0.1),
subtype='XYZ'
)
mx: FloatVectorProperty(
name="Max Scales",
default=(2.0, 2.0, 2.0),
subtype='XYZ'
)
lo: FloatVectorProperty(
name="XYZ Offset",
default=(0.0, 0.0, 0.0),
subtype='XYZ'
)
rsd: FloatProperty(
name="Random Seed",
default=1
)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj is not None and obj.type == "MESH" and obj.mode == "OBJECT"
def draw(self, context):
layout = self.layout
box = layout.box()
box.label(text="Options:")
box.prop(self, "dc")
box.prop(self, "wh")
box.prop(self, "uf")
box = layout.box()
box.label(text="Parameters:")
box.prop(self, "qn")
box.prop(self, "mn")
box.prop(self, "mx")
box.prop(self, "lo")
box.prop(self, "rsd")
def execute(self, context):
rsdchange = self.rsd
oblst = []
uvyes = 0
bpy.ops.collection.create(name='Cubagrouper')
bpy.ops.collection.objects_remove()
for ob in bpy.context.selected_objects:
oblst.append(ob)
for obj in oblst:
bpy.ops.object.select_pattern(pattern=obj.name) # Select base mesh
bpy.context.view_layer.objects.active = obj
if obj.data.uv_layers[:] != []:
uvyes = 1
else:
uvyes = 0
bpy.ops.object.collection_link(group='Cubagrouper')
dim = obj.dimensions
rot = obj.rotation_euler
if self.uf is True:
area = dim.x * dim.y * dim.z
else:
area = 75
for cube in range(round((area / 75) * self.qn)):
random.seed(rsdchange)
pmn = self.mn # Proxy values
pmx = self.mx
if self.wh is True:
if dim.x < pmx.x: # Keeping things from exceeding proper size
pmx.x = dim.x
if dim.y < pmx.y:
pmx.y = dim.y
if dim.z < pmx.z:
pmx.z = dim.z
if 0.0 > pmn.x: # Keeping things from going under zero
pmn.x = 0.0
if 0.0 > pmn.y:
pmn.y = 0.0
if 0.0 > pmn.z:
pmn.z = 0.0
sx = (random.random() * (pmx.x - pmn.x)) + pmn.x # Just changed self.mx and .mn to pmx.
sy = (random.random() * (pmx.y - pmn.y)) + pmn.y
sz = (random.random() * (pmx.z - pmn.z)) + pmn.z
if self.wh is True: # This keeps the cubes within the base mesh
ex = (random.random() * (dim.x - sx)) - ((dim.x - sx) / 2) + obj.location.x
wy = (random.random() * (dim.y - sy)) - ((dim.y - sy) / 2) + obj.location.y
ze = (random.random() * (dim.z - sz)) - ((dim.z - sz) / 2) + obj.location.z
elif self.wh is False:
ex = (random.random() * dim.x) - (dim.x / 2) + obj.location.x
wy = (random.random() * dim.y) - (dim.y / 2) + obj.location.y
ze = (random.random() * dim.z) - (dim.z / 2) + obj.location.z
bpy.ops.mesh.primitive_cube_add(
radius=0.5, location=(ex + self.lo.x, wy + self.lo.y, ze + self.lo.z)
)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.transform.resize(
value=(sx, sy, sz), constraint_axis=(True, True, True),
orient_type='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.ops.object.mode_set(mode='OBJECT')
select = bpy.context.object # This is used to keep something selected for poll()
bpy.ops.object.collection_link(group='Cubagrouper')
rsdchange += 3
bpy.ops.object.select_grouped(type='GROUP')
bpy.ops.transform.rotate(
value=rot[0], axis=(1, 0, 0), constraint_axis=(False, False, False),
orient_type='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.ops.transform.rotate(
value=rot[1], axis=(0, 1, 0), constraint_axis=(False, False, False),
orient_type='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.ops.transform.rotate(
value=rot[2], axis=(0, 0, 1), constraint_axis=(False, False, False),
orient_type='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.context.view_layer.objects.active = obj # Again needed to avoid poll() taking me down
bpy.ops.object.make_links_data(type='MODIFIERS')
bpy.ops.object.make_links_data(type='MATERIAL')
if uvyes == 1:
bpy.ops.object.join_uvs()
bpy.ops.collection.objects_remove()
bpy.context.view_layer.objects.active = select
if self.dc is True:
bpy.context.collection.objects.unlink(obj)
return {'FINISHED'}
def register():
bpy.utils.register_class(makestructure)
def unregister():
bpy.utils.unregister_class(makestructure)
if __name__ == "__main__":
register()

View File

@ -1,832 +0,0 @@
# Copyright (c) 2012 Jorge Hernandez - Melendez
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# TODO : prop names into English, add missing tooltips
bl_info = {
"name": "Rope Creator",
"description": "Dynamic rope (with cloth) creator",
"author": "Jorge Hernandez - Melenedez",
"version": (0, 2, 2),
"blender": (2, 73, 0),
"location": "Left Toolbar > ClothRope",
"warning": "",
"wiki_url": "",
"category": "Add Mesh"
}
import bpy
from bpy.types import Operator
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
)
def desocultar(quien):
if quien == "todo":
for ob in bpy.data.objects:
ob.hide = False
else:
bpy.data.objects[quien].hide = False
def deseleccionar_todo():
bpy.ops.object.select_all(action='DESELECT')
def seleccionar_todo():
bpy.ops.object.select_all(action='SELECT')
def salir_de_editmode():
if bpy.context.mode in ["EDIT", "EDIT_MESH", "EDIT_CURVE"]:
bpy.ops.object.mode_set(mode='OBJECT')
# Clear scene:
def reset_scene():
desocultar("todo")
# playback to the start
bpy.ops.screen.frame_jump(end=False)
try:
salir_de_editmode()
except:
pass
try:
area = bpy.context.area
# expand everything in the outliner to be able to select children
old_type = area.type
area.type = 'OUTLINER'
bpy.ops.outliner.expanded_toggle()
# restore the original context
area.type = old_type
seleccionar_todo()
bpy.ops.object.delete(use_global=False)
except Exception as e:
print("\n[rope_alpha]\nfunction: reset_scene\nError: %s" % e)
def entrar_en_editmode():
if bpy.context.mode == "OBJECT":
bpy.ops.object.mode_set(mode='EDIT')
def select_all_in_edit_mode(ob):
if ob.mode != 'EDIT':
entrar_en_editmode()
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True, False, False)
salir_de_editmode()
for v in ob.data.vertices:
if not v.select:
v.select = True
entrar_en_editmode()
def deselect_all_in_edit_mode(ob):
if ob.mode != 'EDIT':
entrar_en_editmode()
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True, False, False)
salir_de_editmode()
for v in ob.data.vertices:
if not v.select:
v.select = False
entrar_en_editmode()
def which_vertex_are_selected(ob):
for v in ob.data.vertices:
if v.select:
print(str(v.index))
print("Vertex " + str(v.index) + " is selected")
def seleccionar_por_nombre(nombre):
scn = bpy.context.scene
bpy.data.objects[nombre].select_set(True)
scn.objects.active = bpy.data.objects[nombre]
def deseleccionar_por_nombre(nombre):
bpy.data.objects[nombre].select_set(False)
def crear_vertices(ob):
ob.data.vertices.add(1)
ob.data.update
def borrar_elementos_seleccionados(tipo):
if tipo == "vertices":
bpy.ops.mesh.delete(type='VERT')
def obtener_coords_vertex_seleccionados():
coordenadas_de_vertices = []
for ob in bpy.context.selected_objects:
if ob.type == 'MESH':
for v in ob.data.vertices:
if v.select:
coordenadas_de_vertices.append([v.co[0], v.co[1], v.co[2]])
return coordenadas_de_vertices[0]
def crear_locator(pos):
bpy.ops.object.empty_add(
type='PLAIN_AXES', radius=1, view_align=False,
location=(pos[0], pos[1], pos[2]),
layers=(True, False, False, False, False, False, False,
False, False, False, False, False, False, False,
False, False, False, False, False, False)
)
def extruir_vertices(longitud, cuantos_segmentos):
bpy.ops.mesh.extrude_region_move(
MESH_OT_extrude_region={"mirror": False},
TRANSFORM_OT_translate={
"value": (longitud / cuantos_segmentos, 0, 0),
"constraint_axis": (True, False, False),
"orient_type": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
def select_all_vertex_in_curve_bezier(bc):
for i in range(len(bc.data.splines[0].points)):
bc.data.splines[0].points[i].select = True
def deselect_all_vertex_in_curve_bezier(bc):
for i in range(len(bc.data.splines[0].points)):
bc.data.splines[0].points[i].select = False
def ocultar_relationships():
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
area.spaces[0].show_relationship_lines = False
class ClothRope(Operator):
bl_idname = "clot.rope"
bl_label = "Rope Cloth"
bl_description = ("Create a new Scene with a Cloth modifier\n"
"Rope Simulation with hooked Helper Objects")
ropelength: IntProperty(
name="Rope Length",
description="Length of the generated Rope",
default=5
)
ropesegments: IntProperty(
name="Rope Segments",
description="Number of the Rope Segments",
default=5
)
qcr: IntProperty(
name="Collision Quality",
description="Rope's Cloth modifier collsion quality",
min=1, max=20,
default=20
)
substeps: IntProperty(
name="Rope Substeps",
description="Rope's Cloth modifier quality",
min=4, max=80,
default=50
)
resrope: IntProperty(
name="Rope Resolution",
description="Rope's Bevel resolution",
default=5
)
radiusrope: FloatProperty(
name="Radius",
description="Rope's Radius",
min=0.04, max=1,
default=0.04
)
hide_emptys: BoolProperty(
name="Hide Empties",
description="Hide Helper Objects",
default=False
)
def execute(self, context):
# add a new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
scene.name = "Test Rope"
seleccionar_todo()
longitud = self.ropelength
# For the middle to have x segments between the first and
# last point, must add 1 to the quantity:
cuantos_segmentos = self.ropesegments + 1
calidad_de_colision = self.qcr
substeps = self.substeps
deseleccionar_todo()
# collect the possible empties that already exist in the data
empties_prev = [obj.name for obj in bpy.data.objects if obj.type == "EMPTY"]
# create an empty that will be the parent of everything
bpy.ops.object.empty_add(
type='SPHERE', radius=1, view_align=False, location=(0, 0, 0),
layers=(True, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False,
False, False, False, False)
)
ob = bpy.context.selected_objects[0]
ob.name = "Rope"
# .001 and friends
rope_name = ob.name
deseleccionar_todo()
# create a plane and delete it
bpy.ops.mesh.primitive_plane_add(
radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0),
layers=(True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False,
False, False, False)
)
ob = bpy.context.selected_objects[0]
# rename:
ob.name = "cuerda"
# .001 and friends
cuerda_1_name = ob.name
entrar_en_editmode() # enter edit mode
select_all_in_edit_mode(ob)
borrar_elementos_seleccionados("vertices")
salir_de_editmode() # leave edit mode
crear_vertices(ob) # create a vertex
# Creating a Group for the PIN
# Group contains the vertices of the pin and the Group.001 contains the single main line
entrar_en_editmode() # enter edit mode
bpy.ops.object.vertex_group_add() # create a group
select_all_in_edit_mode(ob)
bpy.ops.object.vertex_group_assign() # assign it
salir_de_editmode() # leave edit mode
ob.vertex_groups[0].name = "Pin"
deseleccionar_todo()
seleccionar_por_nombre(cuerda_1_name)
# extrude vertices:
for i in range(cuantos_segmentos):
entrar_en_editmode()
extruir_vertices(longitud, cuantos_segmentos)
# delete the PIN group
bpy.ops.object.vertex_group_remove_from()
# get the direction to create the locator on it's position
pos = obtener_coords_vertex_seleccionados()
salir_de_editmode() # leave edit mode
# create locator at position
crear_locator(pos)
deseleccionar_todo()
seleccionar_por_nombre(cuerda_1_name)
deseleccionar_todo()
seleccionar_por_nombre(cuerda_1_name) # select the rope
entrar_en_editmode()
pos = obtener_coords_vertex_seleccionados() # get their positions
salir_de_editmode()
# create the last locator
crear_locator(pos)
deseleccionar_todo()
seleccionar_por_nombre(cuerda_1_name)
entrar_en_editmode() # enter edit mode
bpy.ops.object.vertex_group_add() # Creating Master guide group
select_all_in_edit_mode(ob)
bpy.ops.object.vertex_group_assign() # and assign it
ob.vertex_groups[1].name = "Guide_rope"
# extrude the Curve so it has a minimum thickness for collide
bpy.ops.mesh.extrude_region_move(
MESH_OT_extrude_region={"mirror": False},
TRANSFORM_OT_translate={
"value": (0, 0.005, 0), "constraint_axis": (False, True, False),
"orient_type": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
bpy.ops.object.vertex_group_remove_from()
deselect_all_in_edit_mode(ob)
salir_de_editmode()
bpy.ops.object.modifier_add(type='CLOTH')
bpy.context.object.modifiers["Cloth"].settings.use_pin_cloth = True
bpy.context.object.modifiers["Cloth"].settings.vertex_group_mass = "Pin"
bpy.context.object.modifiers["Cloth"].collision_settings.collision_quality = calidad_de_colision
bpy.context.object.modifiers["Cloth"].settings.quality = substeps
# Duplicate to convert into Curve:
# select the vertices that are the part of the Group.001
seleccionar_por_nombre(cuerda_1_name)
entrar_en_editmode()
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True, False, False)
salir_de_editmode()
gi = ob.vertex_groups["Guide_rope"].index # get group index
for v in ob.data.vertices:
for g in v.groups:
if g.group == gi: # compare with index in VertexGroupElement
v.select = True
# now we have to make a table of names of cuerdas to see which one will be new
cuerda_names = [obj.name for obj in bpy.data.objects if "cuerda" in obj.name]
entrar_en_editmode()
# we already have the selected guide:
# duplicate it:
bpy.ops.mesh.duplicate_move(
MESH_OT_duplicate={"mode": 1},
TRANSFORM_OT_translate={
"value": (0, 0, 0), "constraint_axis": (False, False, False),
"orient_type": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
# separate the selections:
bpy.ops.mesh.separate(type='SELECTED')
salir_de_editmode()
deseleccionar_todo()
cuerda_2_name = "cuerda.001"
test = []
for obj in bpy.data.objects:
if "cuerda" in obj.name and obj.name not in cuerda_names:
cuerda_2_name = obj.name
test.append(obj.name)
seleccionar_por_nombre(cuerda_2_name)
# from the newly created curve remove the Cloth:
bpy.ops.object.modifier_remove(modifier="Cloth")
# convert the Curve:
bpy.ops.object.convert(target='CURVE')
# all Empties that are not previously present
emptys = []
for eo in bpy.data.objects:
if eo.type == 'EMPTY' and eo.name not in empties_prev:
if eo.name != rope_name:
emptys.append(eo)
# select and deselect:
bc = bpy.data.objects[cuerda_2_name]
n = 0
for e in emptys:
deseleccionar_todo()
seleccionar_por_nombre(e.name)
seleccionar_por_nombre(bc.name)
entrar_en_editmode()
deselect_all_vertex_in_curve_bezier(bc)
bc.data.splines[0].points[n].select = True
bpy.ops.object.hook_add_selob(use_bone=False)
salir_de_editmode()
n = n + 1
ob = bpy.data.objects[cuerda_1_name]
n = 0
for e in emptys:
deseleccionar_todo()
seleccionar_por_nombre(e.name)
seleccionar_por_nombre(ob.name)
entrar_en_editmode()
bpy.ops.mesh.select_all(action="DESELECT")
bpy.context.tool_settings.mesh_select_mode = (True, False, False)
salir_de_editmode()
for v in ob.data.vertices:
if v.select:
v.select = False
ob.data.vertices[n].select = True
entrar_en_editmode()
bpy.ops.object.vertex_parent_set()
salir_de_editmode()
n = n + 1
# hide the Empties:
deseleccionar_todo()
# all parented to the spherical empty:
seleccionar_por_nombre(cuerda_2_name)
seleccionar_por_nombre(cuerda_1_name)
seleccionar_por_nombre(rope_name)
bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
deseleccionar_todo()
# do not display the relations
ocultar_relationships()
seleccionar_por_nombre(cuerda_2_name)
# curved rope settings:
bpy.context.object.data.fill_mode = 'FULL'
bpy.context.object.data.bevel_depth = self.radiusrope
bpy.context.object.data.bevel_resolution = self.resrope
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=350)
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column(align=True)
col.label(text="Rope settings:")
rowsub0 = col.row()
rowsub0.prop(self, "ropelength", text="Length")
rowsub0.prop(self, "ropesegments", text="Segments")
rowsub0.prop(self, "radiusrope", text="Radius")
col.label(text="Quality Settings:")
col.prop(self, "resrope", text="Resolution curve")
col.prop(self, "qcr", text="Quality Collision")
col.prop(self, "substeps", text="Substeps")
class BallRope(Operator):
bl_idname = "ball.rope"
bl_label = "Wrecking Ball"
bl_description = ("Create a new Scene with a Rigid Body simulation of\n"
"Wrecking Ball on a rope")
# defaults rope ball
ropelength2: IntProperty(
name="Rope Length",
description="Length of the Wrecking Ball rope",
default=10
)
ropesegments2: IntProperty(
name="Rope Segments",
description="Number of the Wrecking Ball rope segments",
min=0, max=999,
default=6
)
radiuscubes: FloatProperty(
name="Cube Radius",
description="Size of the Linked Cubes helpers",
default=0.5
)
radiusrope: FloatProperty(
name="Rope Radius",
description="Radius of the Rope",
default=0.4
)
worldsteps: IntProperty(
name="World Steps",
description="Rigid Body Solver world steps per second (update)",
min=60, max=1000,
default=250
)
solveriterations: IntProperty(
name="Solver Iterations",
description="How many times the Rigid Body Solver should run",
min=10, max=100,
default=50
)
massball: IntProperty(
name="Ball Mass",
description="Mass of the Wrecking Ball",
default=1
)
resrope: IntProperty(
name="Resolution",
description="Rope resolution",
default=4
)
grados: FloatProperty(
name="Degrees",
description="Angle of the Wrecking Ball compared to the Ground Plane",
default=45
)
separacion: FloatProperty(
name="Link Cubes Gap",
description="Space between the Rope's Linked Cubes",
default=0.1
)
hidecubes: BoolProperty(
name="Hide Link Cubes",
description="Hide helper geometry for the Rope",
default=False
)
def execute(self, context):
world_steps = self.worldsteps
solver_iterations = self.solveriterations
longitud = self.ropelength2
# make a + 2, so the segments will be between the two end points...
segmentos = self.ropesegments2 + 2
offset_del_suelo = 1
offset_del_suelo_real = (longitud / 2) + (segmentos / 2)
radio = self.radiuscubes
radiorope = self.radiusrope
masa = self.massball
resolucion = self.resrope
rotrope = self.grados
separation = self.separacion
hidecubeslinks = self.hidecubes
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
scene.name = "Test Ball"
# collect the possible constraint empties that already exist in the data
constraint_prev = [obj.name for obj in bpy.data.objects if
obj.type == "EMPTY" and "Constraint" in obj.name]
# floor:
bpy.ops.mesh.primitive_cube_add(
radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0),
layers=(True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False,
False, False, False)
)
bpy.context.object.scale.x = 10 + longitud
bpy.context.object.scale.y = 10 + longitud
bpy.context.object.scale.z = 0.05
bpy.context.object.name = "groundplane"
# The secret agents .001, 002 etc.
groundplane_name = bpy.context.object.name
bpy.ops.rigidbody.objects_add(type='PASSIVE')
# create the first cube:
cuboslink = []
n = 0
for i in range(segmentos):
# if 0 start from 1
if i == 0:
i = offset_del_suelo
else: # if it is not 0, add one so it doesn't step on the first one starting from 1
i = i + offset_del_suelo
separacion = longitud * 2 / segmentos # distance between linked cubes
bpy.ops.mesh.primitive_cube_add(
radius=1, view_align=False, enter_editmode=False,
location=(0, 0, i * separacion),
layers=(True, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False,
False, False, False, False)
)
bpy.ops.rigidbody.objects_add(type='ACTIVE')
bpy.context.object.name = "CubeLink"
if n != 0:
bpy.context.object.display_type = 'WIRE'
bpy.context.object.hide_render = True
n += 1
bpy.context.object.scale.z = (longitud * 2) / (segmentos * 2) - separation
bpy.context.object.scale.x = radio
bpy.context.object.scale.y = radio
cuboslink.append(bpy.context.object)
for i in range(len(cuboslink)):
deseleccionar_todo()
if i != len(cuboslink) - 1:
nombre1 = cuboslink[i]
nombre2 = cuboslink[i + 1]
seleccionar_por_nombre(nombre1.name)
seleccionar_por_nombre(nombre2.name)
bpy.ops.rigidbody.connect()
# select by name
constraint_new = [
obj.name for obj in bpy.data.objects if
obj.type == "EMPTY" and "Constraint" in obj.name and
obj.name not in constraint_prev
]
for names in constraint_new:
seleccionar_por_nombre(names)
for c in bpy.context.selected_objects:
c.rigid_body_constraint.type = 'POINT'
deseleccionar_todo()
# create a Bezier curve:
bpy.ops.curve.primitive_bezier_curve_add(
radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0),
layers=(True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False, False)
)
bpy.context.object.name = "Cuerda"
# Blender will automatically append the .001
# if it is already in data
real_name = bpy.context.object.name
for i in range(len(cuboslink)):
cubonombre = cuboslink[i].name
seleccionar_por_nombre(cubonombre)
seleccionar_por_nombre(real_name)
x = cuboslink[i].location[0]
y = cuboslink[i].location[1]
z = cuboslink[i].location[2]
# if it is 0 make it start from 1 as the offset from the ground...
if i == 0:
i = offset_del_suelo
else: # if it is not 0, add one so it doesn't step on the first one starting from 1
i = i + offset_del_suelo
salir_de_editmode()
entrar_en_editmode()
if i == 1:
# select all the vertices and delete them
select_all_vertex_in_curve_bezier(bpy.data.objects[real_name])
bpy.ops.curve.delete(type='VERT')
# create the first vertex:
bpy.ops.curve.vertex_add(location=(x, y, z))
else:
# extrude the rest:
bpy.ops.curve.extrude_move(
CURVE_OT_extrude={"mode": 'TRANSLATION'},
TRANSFORM_OT_translate={
"value": (0, 0, z / i),
"constraint_axis": (False, False, True),
"orient_type": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
bpy.ops.object.hook_add_selob(use_bone=False)
salir_de_editmode()
bpy.context.object.data.bevel_resolution = resolucion
deseleccionar_todo()
# create a sphere ball:
deseleccionar_todo()
seleccionar_por_nombre(cuboslink[0].name)
entrar_en_editmode()
z = cuboslink[0].scale.z + longitud / 2
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.mesh.primitive_uv_sphere_add(
view_align=False, enter_editmode=False,
layers=(True, False, False, False, False, False, False,
False, False, False, False, False, False, False,
False, False, False, False, False, False)
)
bpy.ops.transform.translate(
value=(0, 0, -z + 2), constraint_axis=(False, False, True),
orient_type='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
bpy.ops.transform.resize(
value=(longitud / 2, longitud / 2, longitud / 2),
constraint_axis=(False, False, False),
orient_type='GLOBAL',
mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
deselect_all_in_edit_mode(cuboslink[0])
salir_de_editmode()
bpy.ops.object.shade_smooth()
bpy.context.object.rigid_body.mass = masa
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
# move it all up a bit more:
seleccionar_todo()
deseleccionar_por_nombre(groundplane_name)
bpy.ops.transform.translate(
value=(0, 0, offset_del_suelo_real),
constraint_axis=(False, False, True),
orient_type='GLOBAL', mirror=False,
proportional='DISABLED', proportional_edit_falloff='SMOOTH',
proportional_size=1
)
deseleccionar_todo()
seleccionar_por_nombre(cuboslink[-1].name)
bpy.ops.rigidbody.objects_add(type='PASSIVE')
bpy.context.scene.rigidbody_world.steps_per_second = world_steps
bpy.context.scene.rigidbody_world.solver_iterations = solver_iterations
# move everything from the top one:
seleccionar_por_nombre(cuboslink[-1].name)
bpy.ops.view3d.snap_cursor_to_selected()
seleccionar_todo()
deseleccionar_por_nombre(groundplane_name)
deseleccionar_por_nombre(cuboslink[-1].name)
bpy.context.space_data.pivot_point = 'CURSOR'
bpy.ops.transform.rotate(
value=rotrope, axis=(1, 0, 0),
constraint_axis=(True, False, False),
orient_type='GLOBAL',
mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH',
proportional_size=1
)
bpy.context.space_data.pivot_point = 'MEDIAN_POINT'
deseleccionar_todo()
seleccionar_por_nombre(real_name)
bpy.context.object.data.fill_mode = 'FULL'
bpy.context.object.data.bevel_depth = radiorope
for ob in bpy.data.objects:
if ob.name != cuboslink[0].name:
if ob.name.find("CubeLink") >= 0:
deseleccionar_todo()
seleccionar_por_nombre(ob.name)
if hidecubeslinks:
bpy.context.object.hide = True
ocultar_relationships()
deseleccionar_todo()
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=350)
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column(align=True)
col.label(text="Rope settings:")
rowsub0 = col.row()
rowsub0.prop(self, "hidecubes", text="Hide Link Cubes")
rowsub1 = col.row(align=True)
rowsub1.prop(self, "ropelength2", text="Length")
rowsub1.prop(self, "ropesegments2", text="Segments")
rowsub2 = col.row(align=True)
rowsub2.prop(self, "radiuscubes", text="Radius Link Cubes")
rowsub2.prop(self, "radiusrope", text="Radius Rope")
rowsub3 = col.row(align=True)
rowsub3.prop(self, "grados", text="Degrees")
rowsub3.prop(self, "separacion", text="Separation Link Cubes")
col.label(text="Quality Settings:")
col.prop(self, "resrope", text="Resolution Rope")
col.prop(self, "massball", text="Ball Mass")
col.prop(self, "worldsteps", text="World Steps")
col.prop(self, "solveriterations", text="Solver Iterarions")
# Register
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@ -1,195 +0,0 @@
# gpl: author meta-androcto
import bpy
from bpy.types import Operator
class add_BI_scene(Operator):
bl_idname = "bi.add_scene"
bl_label = "Create test scene"
bl_description = "Blender Internal renderer Scene with Objects"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
try:
blend_data = context.blend_data
# ob = bpy.context.active_object
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
scene.name = "scene_materials"
# render settings
render = scene.render
render.resolution_x = 1920
render.resolution_y = 1080
render.resolution_percentage = 50
# add new world
world = bpy.data.worlds.new("Materials_World")
scene.world = world
world.use_sky_blend = True
world.use_sky_paper = True
world.horizon_color = (0.004393, 0.02121, 0.050)
world.zenith_color = (0.03335, 0.227, 0.359)
world.light_settings.use_ambient_occlusion = True
world.light_settings.ao_factor = 0.25
# add camera
bpy.ops.object.camera_add(
location=(7.48113, -6.50764, 5.34367),
rotation=(1.109319, 0.010817, 0.814928)
)
cam = bpy.context.active_object.data
cam.lens = 35
cam.display_size = 0.1
bpy.ops.view3d.viewnumpad(type='CAMERA')
# add point lamp
bpy.ops.object.light_add(
type="POINT", location=(4.07625, 1.00545, 5.90386),
rotation=(0.650328, 0.055217, 1.866391)
)
lamp1 = bpy.context.active_object.data
lamp1.name = "Point_Right"
lamp1.energy = 1.0
lamp1.distance = 30.0
lamp1.shadow_method = "RAY_SHADOW"
lamp1.use_sphere = True
# add point lamp2
bpy.ops.object.light_add(
type="POINT", location=(-0.57101, -4.24586, 5.53674),
rotation=(1.571, 0, 0.785)
)
lamp2 = bpy.context.active_object.data
lamp2.name = "Point_Left"
lamp2.energy = 1.0
lamp2.distance = 30.0
# Add cube
bpy.ops.mesh.primitive_cube_add()
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.subdivide(number_cuts=2)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
cube = bpy.context.active_object
# add new material
cubeMaterial = blend_data.materials.new("Cube_Material")
bpy.ops.object.material_slot_add()
cube.material_slots[0].material = cubeMaterial
# Diffuse
cubeMaterial.preview_render_type = "CUBE"
cubeMaterial.diffuse_color = (1.000, 0.373, 0.00)
cubeMaterial.diffuse_shader = 'OREN_NAYAR'
cubeMaterial.diffuse_intensity = 1.0
cubeMaterial.roughness = 0.09002
# Specular
cubeMaterial.specular_color = (1.000, 0.800, 0.136)
cubeMaterial.specular_shader = "PHONG"
cubeMaterial.specular_intensity = 1.0
cubeMaterial.specular_hardness = 511.0
# Shading
cubeMaterial.ambient = 1.00
cubeMaterial.use_cubic = False
# Transparency
cubeMaterial.use_transparency = False
cubeMaterial.alpha = 0
# Mirror
cubeMaterial.raytrace_mirror.use = True
cubeMaterial.mirror_color = (1.000, 0.793, 0.0)
cubeMaterial.raytrace_mirror.reflect_factor = 0.394
cubeMaterial.raytrace_mirror.fresnel = 2.0
cubeMaterial.raytrace_mirror.fresnel_factor = 1.641
cubeMaterial.raytrace_mirror.fade_to = "FADE_TO_SKY"
cubeMaterial.raytrace_mirror.gloss_anisotropic = 1.0
# Shadow
cubeMaterial.use_transparent_shadows = True
# Add a texture
cubetex = blend_data.textures.new("CloudTex", type='CLOUDS')
cubetex.noise_type = 'SOFT_NOISE'
cubetex.noise_scale = 0.25
mtex = cubeMaterial.texture_slots.add()
mtex.texture = cubetex
mtex.texture_coords = 'ORCO'
mtex.scale = (0.800, 0.800, 0.800)
mtex.use_map_mirror = True
mtex.mirror_factor = 0.156
mtex.use_map_color_diffuse = True
mtex.diffuse_color_factor = 0.156
mtex.use_map_normal = True
mtex.normal_factor = 0.010
mtex.blend_type = "ADD"
mtex.use_rgb_to_intensity = True
mtex.color = (1.000, 0.207, 0.000)
# Add monkey
bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505))
bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1))
bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0))
bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0))
bpy.ops.object.modifier_add(type='SUBSURF')
bpy.ops.object.shade_smooth()
monkey = bpy.context.active_object
# add new material
monkeyMaterial = blend_data.materials.new("Monkey_Material")
bpy.ops.object.material_slot_add()
monkey.material_slots[0].material = monkeyMaterial
# Material settings
monkeyMaterial.preview_render_type = "MONKEY"
monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288)
monkeyMaterial.specular_color = (0.604, 0.465, 0.136)
monkeyMaterial.diffuse_shader = 'LAMBERT'
monkeyMaterial.diffuse_intensity = 1.0
monkeyMaterial.specular_intensity = 0.3
monkeyMaterial.ambient = 0
monkeyMaterial.type = 'SURFACE'
monkeyMaterial.use_cubic = True
monkeyMaterial.use_transparency = False
monkeyMaterial.alpha = 0
monkeyMaterial.use_transparent_shadows = True
monkeyMaterial.raytrace_mirror.use = True
monkeyMaterial.raytrace_mirror.reflect_factor = 0.65
monkeyMaterial.raytrace_mirror.fade_to = "FADE_TO_MATERIAL"
# Add plane
bpy.ops.mesh.primitive_plane_add(
radius=50, view_align=False, enter_editmode=False, location=(0, 0, -1)
)
bpy.ops.object.editmode_toggle()
bpy.ops.transform.rotate(
value=-0.8, axis=(0, 0, 1), constraint_axis=(False, False, True),
orient_type='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
plane = bpy.context.active_object
# add new material
planeMaterial = blend_data.materials.new("Plane_Material")
bpy.ops.object.material_slot_add()
plane.material_slots[0].material = planeMaterial
# Material settings
planeMaterial.preview_render_type = "CUBE"
planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
planeMaterial.specular_color = (0.604, 0.465, 0.136)
planeMaterial.specular_intensity = 0.3
planeMaterial.ambient = 0
planeMaterial.use_cubic = True
planeMaterial.use_transparency = False
planeMaterial.alpha = 0
planeMaterial.use_transparent_shadows = True
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"bi.add_scene\nError: {}".format(e))
return {'CANCELLED'}
return {"FINISHED"}

View File

@ -1,142 +0,0 @@
# gpl: author meta-androcto
import bpy
from bpy.types import Operator
class add_cycles_scene(Operator):
bl_idname = "objects_cycles.add_scene"
bl_label = "Create test scene"
bl_description = "Cycles renderer Scene with Objects"
bl_options = {'REGISTER'}
def execute(self, context):
try:
blend_data = context.blend_data
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
bpy.context.scene.render.engine = 'CYCLES'
scene.name = "scene_object_cycles"
# render settings
render = scene.render
render.resolution_x = 1920
render.resolution_y = 1080
render.resolution_percentage = 50
# add new world
world = bpy.data.worlds.new("Cycles_Object_World")
scene.world = world
world.use_sky_blend = True
world.use_sky_paper = True
world.horizon_color = (0.004393, 0.02121, 0.050)
world.zenith_color = (0.03335, 0.227, 0.359)
world.light_settings.use_ambient_occlusion = True
world.light_settings.ao_factor = 0.25
# add camera
bpy.ops.object.camera_add(
location=(7.48113, -6.50764, 5.34367),
rotation=(1.109319, 0.010817, 0.814928)
)
cam = bpy.context.active_object.data
cam.lens = 35
cam.display_size = 0.1
bpy.ops.view3d.viewnumpad(type='CAMERA')
# add point lamp
bpy.ops.object.light_add(
type="POINT", location=(4.07625, 1.00545, 5.90386),
rotation=(0.650328, 0.055217, 1.866391)
)
lamp1 = bpy.context.active_object.data
lamp1.name = "Point_Right"
lamp1.energy = 1.0
lamp1.distance = 30.0
lamp1.shadow_method = "RAY_SHADOW"
lamp1.use_sphere = True
# add point lamp2
bpy.ops.object.light_add(
type="POINT", location=(-0.57101, -4.24586, 5.53674),
rotation=(1.571, 0, 0.785)
)
lamp2 = bpy.context.active_object.data
lamp2.name = "Point_Left"
lamp2.energy = 1.0
lamp2.distance = 30.0
# Add cube
bpy.ops.mesh.primitive_cube_add()
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.subdivide(number_cuts=2)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
cube = bpy.context.active_object
# add cube material
cubeMaterial = blend_data.materials.new("Cycles_Cube_Material")
bpy.ops.object.material_slot_add()
cube.material_slots[0].material = cubeMaterial
# Diffuse
cubeMaterial.preview_render_type = "CUBE"
cubeMaterial.diffuse_color = (1.000, 0.373, 0.00)
# Cycles
cubeMaterial.use_nodes = True
# Add monkey
bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505))
bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1))
bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0))
bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0))
bpy.ops.object.modifier_add(type='SUBSURF')
bpy.ops.object.shade_smooth()
monkey = bpy.context.active_object
# add monkey material
monkeyMaterial = blend_data.materials.new("Cycles_Monkey_Material")
bpy.ops.object.material_slot_add()
monkey.material_slots[0].material = monkeyMaterial
# Diffuse
monkeyMaterial.preview_render_type = "MONKEY"
monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288)
# Cycles
monkeyMaterial.use_nodes = True
# Add plane
bpy.ops.mesh.primitive_plane_add(
radius=50, view_align=False,
enter_editmode=False, location=(0, 0, -1)
)
bpy.ops.object.editmode_toggle()
bpy.ops.transform.rotate(
value=-0.8, axis=(0, 0, 1),
constraint_axis=(False, False, True)
)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
plane = bpy.context.active_object
# add plane material
planeMaterial = blend_data.materials.new("Cycles_Plane_Material")
bpy.ops.object.material_slot_add()
plane.material_slots[0].material = planeMaterial
# Diffuse
planeMaterial.preview_render_type = "FLAT"
planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
# Cycles
planeMaterial.use_nodes = True
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"objects_cycles.add_scene\nError: {}".format(e))
return {'CANCELLED'}
return {'FINISHED'}

View File

@ -1,78 +0,0 @@
# gpl: author meta-androcto
import bpy
from bpy.types import Operator
class add_texture_scene(Operator):
bl_idname = "objects_texture.add_scene"
bl_label = "Create test scene"
bl_description = "Cycles renderer Scene: Camera aligned to a plane"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
try:
blend_data = context.blend_data
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
bpy.context.scene.render.engine = 'CYCLES'
scene.name = "scene_texture_cycles"
# render settings
render = scene.render
render.resolution_x = 1080
render.resolution_y = 1080
render.resolution_percentage = 100
# add new world
world = bpy.data.worlds.new("Cycles_Textures_World")
scene.world = world
world.use_sky_blend = True
world.use_sky_paper = True
world.horizon_color = (0.004393, 0.02121, 0.050)
world.zenith_color = (0.03335, 0.227, 0.359)
world.light_settings.use_ambient_occlusion = True
world.light_settings.ao_factor = 0.5
# add camera
bpy.ops.view3d.viewnumpad(type='TOP')
bpy.ops.object.camera_add(
location=(0, 0, 2.1850), rotation=(0, 0, 0), view_align=True
)
cam = bpy.context.active_object.data
cam.lens = 35
cam.display_size = 0.1
# add plane
bpy.ops.mesh.primitive_plane_add(enter_editmode=True, location=(0, 0, 0))
bpy.ops.mesh.subdivide(number_cuts=10, smoothness=0)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
plane = bpy.context.active_object
# add plane material
planeMaterial = blend_data.materials.new("Cycles_Plane_Material")
bpy.ops.object.material_slot_add()
plane.material_slots[0].material = planeMaterial
# Diffuse
planeMaterial.preview_render_type = "FLAT"
planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
# Cycles
planeMaterial.use_nodes = True
# Back to Scene
sc = bpy.context.scene
bpy.ops.view3d.viewnumpad(type='CAMERA')
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"objects_texture.add_scene\nError: {}".format(e))
return {'CANCELLED'}
return {'FINISHED'}

View File

@ -1,239 +0,0 @@
# gpl: author Daniel Schalla
import bpy
from bpy.types import Operator
from bpy.props import (
EnumProperty,
FloatProperty,
IntProperty,
)
from math import (
sin, cos,
radians,
sqrt,
)
class TriLighting(Operator):
bl_idname = "object.trilighting"
bl_label = "Tri-Lighting Creator"
bl_description = ("Add 3 Point Lighting to Selected / Active Object\n"
"Needs an existing Active Object")
bl_options = {'REGISTER', 'UNDO'}
height: FloatProperty(
name="Height",
default=5
)
distance: FloatProperty(
name="Distance",
default=5,
min=0.1,
subtype="DISTANCE"
)
energy: IntProperty(
name="Base Energy",
default=3,
min=1
)
contrast: IntProperty(
name="Contrast",
default=50,
min=-100, max=100,
subtype="PERCENTAGE"
)
leftangle: IntProperty(
name="Left Angle",
default=26,
min=1, max=90,
subtype="ANGLE"
)
rightangle: IntProperty(
name="Right Angle",
default=45,
min=1, max=90,
subtype="ANGLE"
)
backangle: IntProperty(
name="Back Angle",
default=235,
min=90, max=270,
subtype="ANGLE"
)
Light_Type_List = [
('POINT', "Point", "Point Light"),
('SUN', "Sun", "Sun Light"),
('SPOT', "Spot", "Spot Light"),
('HEMI', "Hemi", "Hemi Light"),
('AREA', "Area", "Area Light")
]
primarytype: EnumProperty(
attr='tl_type',
name="Key Type",
description="Choose the types of Key Lights you would like",
items=Light_Type_List,
default='HEMI'
)
secondarytype: EnumProperty(
attr='tl_type',
name="Fill + Back Type",
description="Choose the types of secondary Lights you would like",
items=Light_Type_List,
default="POINT"
)
@classmethod
def poll(cls, context):
return context.active_object is not None
def draw(self, context):
layout = self.layout
layout.label(text="Position:")
col = layout.column(align=True)
col.prop(self, "height")
col.prop(self, "distance")
layout.label(text="Light:")
col = layout.column(align=True)
col.prop(self, "energy")
col.prop(self, "contrast")
layout.label(text="Orientation:")
col = layout.column(align=True)
col.prop(self, "leftangle")
col.prop(self, "rightangle")
col.prop(self, "backangle")
col = layout.column()
col.label(text="Key Light Type:")
col.prop(self, "primarytype", text="")
col.label(text="Fill + Back Type:")
col.prop(self, "secondarytype", text="")
def execute(self, context):
try:
collection = context.collection
scene = context.scene
view = context.space_data
if view.type == 'VIEW_3D' and not view.lock_camera_and_layers:
camera = view.camera
else:
camera = scene.camera
if (camera is None):
cam_data = bpy.data.cameras.new(name='Camera')
cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data)
collection.objects.link(cam_obj)
scene.camera = cam_obj
bpy.ops.view3d.camera_to_view()
camera = cam_obj
bpy.ops.view3d.viewnumpad(type='TOP')
obj = bpy.context.view_layer.objects.active
# Calculate Energy for each Lamp
if(self.contrast > 0):
keyEnergy = self.energy
backEnergy = (self.energy / 100) * abs(self.contrast)
fillEnergy = (self.energy / 100) * abs(self.contrast)
else:
keyEnergy = (self.energy / 100) * abs(self.contrast)
backEnergy = self.energy
fillEnergy = self.energy
# Calculate Direction for each Lamp
# Calculate current Distance and get Delta
obj_position = obj.location
cam_position = camera.location
delta_position = cam_position - obj_position
vector_length = sqrt(
(pow(delta_position.x, 2) +
pow(delta_position.y, 2) +
pow(delta_position.z, 2))
)
if not vector_length:
# division by zero most likely
self.report({'WARNING'},
"Operation Cancelled. No viable object in the scene")
return {'CANCELLED'}
single_vector = (1 / vector_length) * delta_position
# Calc back position
singleback_vector = single_vector.copy()
singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \
(-sin(radians(self.backangle)) * single_vector.y)
singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \
(cos(radians(self.backangle)) * single_vector.y)
backx = obj_position.x + self.distance * singleback_vector.x
backy = obj_position.y + self.distance * singleback_vector.y
backData = bpy.data.lights.new(name="TriLamp-Back", type=self.secondarytype)
backData.energy = backEnergy
backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData)
collection.objects.link(backLamp)
backLamp.location = (backx, backy, self.height)
trackToBack = backLamp.constraints.new(type="TRACK_TO")
trackToBack.target = obj
trackToBack.track_axis = "TRACK_NEGATIVE_Z"
trackToBack.up_axis = "UP_Y"
# Calc right position
singleright_vector = single_vector.copy()
singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \
(-sin(radians(self.rightangle)) * single_vector.y)
singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \
(cos(radians(self.rightangle)) * single_vector.y)
rightx = obj_position.x + self.distance * singleright_vector.x
righty = obj_position.y + self.distance * singleright_vector.y
rightData = bpy.data.lights.new(name="TriLamp-Fill", type=self.secondarytype)
rightData.energy = fillEnergy
rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData)
collection.objects.link(rightLamp)
rightLamp.location = (rightx, righty, self.height)
trackToRight = rightLamp.constraints.new(type="TRACK_TO")
trackToRight.target = obj
trackToRight.track_axis = "TRACK_NEGATIVE_Z"
trackToRight.up_axis = "UP_Y"
# Calc left position
singleleft_vector = single_vector.copy()
singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \
(-sin(radians(-self.leftangle)) * single_vector.y)
singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \
(cos(radians(-self.leftangle)) * single_vector.y)
leftx = obj_position.x + self.distance * singleleft_vector.x
lefty = obj_position.y + self.distance * singleleft_vector.y
leftData = bpy.data.lights.new(name="TriLamp-Key", type=self.primarytype)
leftData.energy = keyEnergy
leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData)
collection.objects.link(leftLamp)
leftLamp.location = (leftx, lefty, self.height)
trackToLeft = leftLamp.constraints.new(type="TRACK_TO")
trackToLeft.target = obj
trackToLeft.track_axis = "TRACK_NEGATIVE_Z"
trackToLeft.up_axis = "UP_Y"
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[Add Advanced Objects]\nOperator: "
"object.trilighting\nError: {}".format(e))
return {'CANCELLED'}
return {'FINISHED'}

View File

@ -1,998 +0,0 @@
# -*- coding: utf-8 -*-
# Voronoi diagram calculator/ Delaunay triangulator
#
# - Voronoi Diagram Sweepline algorithm and C code by Steven Fortune,
# 1987, http://ect.bell-labs.com/who/sjf/
# - Python translation to file voronoi.py by Bill Simons, 2005, http://www.oxfish.com/
# - Additional changes for QGIS by Carson Farmer added November 2010
# - 2012 Ported to Python 3 and additional clip functions by domlysz at gmail.com
#
# Calculate Delaunay triangulation or the Voronoi polygons for a set of
# 2D input points.
#
# Derived from code bearing the following notice:
#
# The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T
# Bell Laboratories.
# Permission to use, copy, modify, and distribute this software for any
# purpose without fee is hereby granted, provided that this entire notice
# is included in all copies of any software which is or includes a copy
# or modification of this software and in all copies of the supporting
# documentation for such software.
# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
# OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
#
# Comments were incorporated from Shane O'Sullivan's translation of the
# original code into C++ (http://mapviewer.skynet.ie/voronoi.html)
#
# Steve Fortune's homepage: http://netlib.bell-labs.com/cm/cs/who/sjf/index.html
#
# For programmatic use, two functions are available:
#
# computeVoronoiDiagram(points, xBuff, yBuff, polygonsOutput=False, formatOutput=False):
# Takes :
# - a list of point objects (which must have x and y fields).
# - x and y buffer values which are the expansion percentages of the
# bounding box rectangle including all input points.
# Returns :
# - With default options :
# A list of 2-tuples, representing the two points of each Voronoi diagram edge.
# Each point contains 2-tuples which are the x,y coordinates of point.
# if formatOutput is True, returns :
# - a list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices.
# - and a list of 2-tuples (v1, v2) representing edges of the Voronoi diagram.
# v1 and v2 are the indices of the vertices at the end of the edge.
# - If polygonsOutput option is True, returns :
# A dictionary of polygons, keys are the indices of the input points,
# values contains n-tuples representing the n points of each Voronoi diagram polygon.
# Each point contains 2-tuples which are the x,y coordinates of point.
# if formatOutput is True, returns :
# - A list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices.
# - and a dictionary of input points indices. Values contains n-tuples representing
# the n points of each Voronoi diagram polygon.
# Each tuple contains the vertex indices of the polygon vertices.
#
# computeDelaunayTriangulation(points):
# Takes a list of point objects (which must have x and y fields).
# Returns a list of 3-tuples: the indices of the points that form a Delaunay triangle.
import bpy
import math
# Globals
TOLERANCE = 1e-9
BIG_FLOAT = 1e38
class Context(object):
def __init__(self):
self.doPrint = 0
self.debug = 0
# tuple (xmin, xmax, ymin, ymax)
self.extent = ()
self.triangulate = False
# list of vertex 2-tuples: (x,y)
self.vertices = []
# equation of line 3-tuple (a b c), for the equation of the line a*x+b*y = c
self.lines = []
# edge 3-tuple: (line index, vertex 1 index, vertex 2 index)
# if either vertex index is -1, the edge extends to infinity
self.edges = []
# 3-tuple of vertex indices
self.triangles = []
# a dict of site:[edges] pairs
self.polygons = {}
# Clip functions #
def getClipEdges(self):
xmin, xmax, ymin, ymax = self.extent
clipEdges = []
for edge in self.edges:
equation = self.lines[edge[0]] # line equation
if edge[1] != -1 and edge[2] != -1: # finite line
x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1]
x2, y2 = self.vertices[edge[2]][0], self.vertices[edge[2]][1]
pt1, pt2 = (x1, y1), (x2, y2)
inExtentP1, inExtentP2 = self.inExtent(x1, y1), self.inExtent(x2, y2)
if inExtentP1 and inExtentP2:
clipEdges.append((pt1, pt2))
elif inExtentP1 and not inExtentP2:
pt2 = self.clipLine(x1, y1, equation, leftDir=False)
clipEdges.append((pt1, pt2))
elif not inExtentP1 and inExtentP2:
pt1 = self.clipLine(x2, y2, equation, leftDir=True)
clipEdges.append((pt1, pt2))
else: # infinite line
if edge[1] != -1:
x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1]
leftDir = False
else:
x1, y1 = self.vertices[edge[2]][0], self.vertices[edge[2]][1]
leftDir = True
if self.inExtent(x1, y1):
pt1 = (x1, y1)
pt2 = self.clipLine(x1, y1, equation, leftDir)
clipEdges.append((pt1, pt2))
return clipEdges
def getClipPolygons(self, closePoly):
xmin, xmax, ymin, ymax = self.extent
poly = {}
for inPtsIdx, edges in self.polygons.items():
clipEdges = []
for edge in edges:
equation = self.lines[edge[0]] # line equation
if edge[1] != -1 and edge[2] != -1: # finite line
x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1]
x2, y2 = self.vertices[edge[2]][0], self.vertices[edge[2]][1]
pt1, pt2 = (x1, y1), (x2, y2)
inExtentP1, inExtentP2 = self.inExtent(x1, y1), self.inExtent(x2, y2)
if inExtentP1 and inExtentP2:
clipEdges.append((pt1, pt2))
elif inExtentP1 and not inExtentP2:
pt2 = self.clipLine(x1, y1, equation, leftDir=False)
clipEdges.append((pt1, pt2))
elif not inExtentP1 and inExtentP2:
pt1 = self.clipLine(x2, y2, equation, leftDir=True)
clipEdges.append((pt1, pt2))
else: # infinite line
if edge[1] != -1:
x1, y1 = self.vertices[edge[1]][0], self.vertices[edge[1]][1]
leftDir = False
else:
x1, y1 = self.vertices[edge[2]][0], self.vertices[edge[2]][1]
leftDir = True
if self.inExtent(x1, y1):
pt1 = (x1, y1)
pt2 = self.clipLine(x1, y1, equation, leftDir)
clipEdges.append((pt1, pt2))
# create polygon definition from edges and check if polygon is completely closed
polyPts, complete = self.orderPts(clipEdges)
if not complete:
startPt = polyPts[0]
endPt = polyPts[-1]
# if start & end points are collinear then they are along an extent border
if startPt[0] == endPt[0] or startPt[1] == endPt[1]:
polyPts.append(polyPts[0]) # simple close
else: # close at extent corner
# upper left
if (startPt[0] == xmin and endPt[1] == ymax) or (endPt[0] == xmin and startPt[1] == ymax):
polyPts.append((xmin, ymax)) # corner point
polyPts.append(polyPts[0]) # close polygon
# upper right
if (startPt[0] == xmax and endPt[1] == ymax) or (endPt[0] == xmax and startPt[1] == ymax):
polyPts.append((xmax, ymax))
polyPts.append(polyPts[0])
# bottom right
if (startPt[0] == xmax and endPt[1] == ymin) or (endPt[0] == xmax and startPt[1] == ymin):
polyPts.append((xmax, ymin))
polyPts.append(polyPts[0])
# bottom left
if (startPt[0] == xmin and endPt[1] == ymin) or (endPt[0] == xmin and startPt[1] == ymin):
polyPts.append((xmin, ymin))
polyPts.append(polyPts[0])
if not closePoly: # unclose polygon
polyPts = polyPts[:-1]
poly[inPtsIdx] = polyPts
return poly
def clipLine(self, x1, y1, equation, leftDir):
xmin, xmax, ymin, ymax = self.extent
a, b, c = equation
if b == 0: # vertical line
if leftDir: # left is bottom of vertical line
return (x1, ymax)
else:
return (x1, ymin)
elif a == 0: # horizontal line
if leftDir:
return (xmin, y1)
else:
return (xmax, y1)
else:
y2_at_xmin = (c - a * xmin) / b
y2_at_xmax = (c - a * xmax) / b
x2_at_ymin = (c - b * ymin) / a
x2_at_ymax = (c - b * ymax) / a
intersectPts = []
if ymin <= y2_at_xmin <= ymax: # valid intersect point
intersectPts.append((xmin, y2_at_xmin))
if ymin <= y2_at_xmax <= ymax:
intersectPts.append((xmax, y2_at_xmax))
if xmin <= x2_at_ymin <= xmax:
intersectPts.append((x2_at_ymin, ymin))
if xmin <= x2_at_ymax <= xmax:
intersectPts.append((x2_at_ymax, ymax))
# delete duplicate (happens if intersect point is at extent corner)
intersectPts = set(intersectPts)
# choose target intersect point
if leftDir:
pt = min(intersectPts) # smaller x value
else:
pt = max(intersectPts)
return pt
def inExtent(self, x, y):
xmin, xmax, ymin, ymax = self.extent
return x >= xmin and x <= xmax and y >= ymin and y <= ymax
def orderPts(self, edges):
poly = [] # returned polygon points list [pt1, pt2, pt3, pt4 ....]
pts = []
# get points list
for edge in edges:
pts.extend([pt for pt in edge])
# try to get start & end point
try:
startPt, endPt = [pt for pt in pts if pts.count(pt) < 2] # start and end point aren't duplicate
except: # all points are duplicate --> polygon is complete --> append some or other edge points
complete = True
firstIdx = 0
poly.append(edges[0][0])
poly.append(edges[0][1])
else: # incomplete --> append the first edge points
complete = False
# search first edge
for i, edge in enumerate(edges):
if startPt in edge: # find
firstIdx = i
break
poly.append(edges[firstIdx][0])
poly.append(edges[firstIdx][1])
if poly[0] != startPt:
poly.reverse()
# append next points in list
del edges[firstIdx]
while edges: # all points will be treated when edges list will be empty
currentPt = poly[-1] # last item
for i, edge in enumerate(edges):
if currentPt == edge[0]:
poly.append(edge[1])
break
elif currentPt == edge[1]:
poly.append(edge[0])
break
del edges[i]
return poly, complete
def setClipBuffer(self, xpourcent, ypourcent):
xmin, xmax, ymin, ymax = self.extent
width = xmax - xmin
height = ymax - ymin
xmin = xmin - width * xpourcent / 100
xmax = xmax + width * xpourcent / 100
ymin = ymin - height * ypourcent / 100
ymax = ymax + height * ypourcent / 100
self.extent = xmin, xmax, ymin, ymax
# End clip functions #
def outSite(self, s):
if(self.debug):
print("site (%d) at %f %f" % (s.sitenum, s.x, s.y))
elif(self.triangulate):
pass
elif(self.doPrint):
print("s %f %f" % (s.x, s.y))
def outVertex(self, s):
self.vertices.append((s.x, s.y))
if(self.debug):
print("vertex(%d) at %f %f" % (s.sitenum, s.x, s.y))
elif(self.triangulate):
pass
elif(self.doPrint):
print("v %f %f" % (s.x, s.y))
def outTriple(self, s1, s2, s3):
self.triangles.append((s1.sitenum, s2.sitenum, s3.sitenum))
if (self.debug):
print("circle through left=%d right=%d bottom=%d" % (s1.sitenum, s2.sitenum, s3.sitenum))
elif (self.triangulate and self.doPrint):
print("%d %d %d" % (s1.sitenum, s2.sitenum, s3.sitenum))
def outBisector(self, edge):
self.lines.append((edge.a, edge.b, edge.c))
if (self.debug):
print("line(%d) %gx+%gy=%g, bisecting %d %d" % (edge.edgenum, edge.a, edge.b,
edge.c, edge.reg[0].sitenum,
edge.reg[1].sitenum)
)
elif(self.doPrint):
print("l %f %f %f" % (edge.a, edge.b, edge.c))
def outEdge(self, edge):
sitenumL = -1
if edge.ep[Edge.LE] is not None:
sitenumL = edge.ep[Edge.LE].sitenum
sitenumR = -1
if edge.ep[Edge.RE] is not None:
sitenumR = edge.ep[Edge.RE].sitenum
# polygons dict add by CF
if edge.reg[0].sitenum not in self.polygons:
self.polygons[edge.reg[0].sitenum] = []
if edge.reg[1].sitenum not in self.polygons:
self.polygons[edge.reg[1].sitenum] = []
self.polygons[edge.reg[0].sitenum].append((edge.edgenum, sitenumL, sitenumR))
self.polygons[edge.reg[1].sitenum].append((edge.edgenum, sitenumL, sitenumR))
self.edges.append((edge.edgenum, sitenumL, sitenumR))
if (not self.triangulate):
if (self.doPrint):
print("e %d" % edge.edgenum)
print(" %d " % sitenumL)
print("%d" % sitenumR)
def voronoi(siteList, context):
context.extent = siteList.extent
edgeList = EdgeList(siteList.xmin, siteList.xmax, len(siteList))
priorityQ = PriorityQueue(siteList.ymin, siteList.ymax, len(siteList))
siteIter = siteList.iterator()
bottomsite = siteIter.next()
context.outSite(bottomsite)
newsite = siteIter.next()
minpt = Site(-BIG_FLOAT, -BIG_FLOAT)
while True:
if not priorityQ.isEmpty():
minpt = priorityQ.getMinPt()
if (newsite and (priorityQ.isEmpty() or newsite < minpt)):
# newsite is smallest - this is a site event
context.outSite(newsite)
# get first Halfedge to the LEFT and RIGHT of the new site
lbnd = edgeList.leftbnd(newsite)
rbnd = lbnd.right
# if this halfedge has no edge, bot = bottom site (whatever that is)
# create a new edge that bisects
bot = lbnd.rightreg(bottomsite)
edge = Edge.bisect(bot, newsite)
context.outBisector(edge)
# create a new Halfedge, setting its pm field to 0 and insert
# this new bisector edge between the left and right vectors in
# a linked list
bisector = Halfedge(edge, Edge.LE)
edgeList.insert(lbnd, bisector)
# if the new bisector intersects with the left edge, remove
# the left edge's vertex, and put in the new one
p = lbnd.intersect(bisector)
if p is not None:
priorityQ.delete(lbnd)
priorityQ.insert(lbnd, p, newsite.distance(p))
# create a new Halfedge, setting its pm field to 1
# insert the new Halfedge to the right of the original bisector
lbnd = bisector
bisector = Halfedge(edge, Edge.RE)
edgeList.insert(lbnd, bisector)
# if this new bisector intersects with the right Halfedge
p = bisector.intersect(rbnd)
if p is not None:
# push the Halfedge into the ordered linked list of vertices
priorityQ.insert(bisector, p, newsite.distance(p))
newsite = siteIter.next()
elif not priorityQ.isEmpty():
# intersection is smallest - this is a vector (circle) event
# pop the Halfedge with the lowest vector off the ordered list of
# vectors. Get the Halfedge to the left and right of the above HE
# and also the Halfedge to the right of the right HE
lbnd = priorityQ.popMinHalfedge()
llbnd = lbnd.left
rbnd = lbnd.right
rrbnd = rbnd.right
# get the Site to the left of the left HE and to the right of
# the right HE which it bisects
bot = lbnd.leftreg(bottomsite)
top = rbnd.rightreg(bottomsite)
# output the triple of sites, stating that a circle goes through them
mid = lbnd.rightreg(bottomsite)
context.outTriple(bot, top, mid)
# get the vertex that caused this event and set the vertex number
# couldn't do this earlier since we didn't know when it would be processed
v = lbnd.vertex
siteList.setSiteNumber(v)
context.outVertex(v)
# set the endpoint of the left and right Halfedge to be this vector
if lbnd.edge.setEndpoint(lbnd.pm, v):
context.outEdge(lbnd.edge)
if rbnd.edge.setEndpoint(rbnd.pm, v):
context.outEdge(rbnd.edge)
# delete the lowest HE, remove all vertex events to do with the
# right HE and delete the right HE
edgeList.delete(lbnd)
priorityQ.delete(rbnd)
edgeList.delete(rbnd)
# if the site to the left of the event is higher than the Site
# to the right of it, then swap them and set 'pm' to RIGHT
pm = Edge.LE
if bot.y > top.y:
bot, top = top, bot
pm = Edge.RE
# Create an Edge (or line) that is between the two Sites. This
# creates the formula of the line, and assigns a line number to it
edge = Edge.bisect(bot, top)
context.outBisector(edge)
# create a HE from the edge
bisector = Halfedge(edge, pm)
# insert the new bisector to the right of the left HE
# set one endpoint to the new edge to be the vector point 'v'
# If the site to the left of this bisector is higher than the right
# Site, then this endpoint is put in position 0; otherwise in pos 1
edgeList.insert(llbnd, bisector)
if edge.setEndpoint(Edge.RE - pm, v):
context.outEdge(edge)
# if left HE and the new bisector don't intersect, then delete
# the left HE, and reinsert it
p = llbnd.intersect(bisector)
if p is not None:
priorityQ.delete(llbnd)
priorityQ.insert(llbnd, p, bot.distance(p))
# if right HE and the new bisector don't intersect, then reinsert it
p = bisector.intersect(rrbnd)
if p is not None:
priorityQ.insert(bisector, p, bot.distance(p))
else:
break
he = edgeList.leftend.right
while he is not edgeList.rightend:
context.outEdge(he.edge)
he = he.right
Edge.EDGE_NUM = 0 # CF
def isEqual(a, b, relativeError=TOLERANCE):
# is nearly equal to within the allowed relative error
norm = max(abs(a), abs(b))
return (norm < relativeError) or (abs(a - b) < (relativeError * norm))
class Site(object):
def __init__(self, x=0.0, y=0.0, sitenum=0):
self.x = x
self.y = y
self.sitenum = sitenum
def dump(self):
print("Site #%d (%g, %g)" % (self.sitenum, self.x, self.y))
def __lt__(self, other):
if self.y < other.y:
return True
elif self.y > other.y:
return False
elif self.x < other.x:
return True
elif self.x > other.x:
return False
else:
return False
def __eq__(self, other):
if self.y == other.y and self.x == other.x:
return True
def distance(self, other):
dx = self.x - other.x
dy = self.y - other.y
return math.sqrt(dx * dx + dy * dy)
class Edge(object):
LE = 0 # left end indice --> edge.ep[Edge.LE]
RE = 1 # right end indice
EDGE_NUM = 0
DELETED = {} # marker value
def __init__(self):
self.a = 0.0 # equation of the line a*x+b*y = c
self.b = 0.0
self.c = 0.0
self.ep = [None, None] # end point (2 tuples of site)
self.reg = [None, None]
self.edgenum = 0
def dump(self):
print("(#%d a=%g, b=%g, c=%g)" % (self.edgenum, self.a, self.b, self.c))
print("ep", self.ep)
print("reg", self.reg)
def setEndpoint(self, lrFlag, site):
self.ep[lrFlag] = site
if self.ep[Edge.RE - lrFlag] is None:
return False
return True
@staticmethod
def bisect(s1, s2):
newedge = Edge()
newedge.reg[0] = s1 # store the sites that this edge is bisecting
newedge.reg[1] = s2
# to begin with, there are no endpoints on the bisector - it goes to infinity
# ep[0] and ep[1] are None
# get the difference in x dist between the sites
dx = float(s2.x - s1.x)
dy = float(s2.y - s1.y)
adx = abs(dx) # make sure that the difference in positive
ady = abs(dy)
# get the slope of the line
newedge.c = float(s1.x * dx + s1.y * dy + (dx * dx + dy * dy) * 0.5)
if adx > ady:
# set formula of line, with x fixed to 1
newedge.a = 1.0
newedge.b = dy / dx
newedge.c /= dx
else:
# set formula of line, with y fixed to 1
newedge.b = 1.0
newedge.a = dx / dy
newedge.c /= dy
newedge.edgenum = Edge.EDGE_NUM
Edge.EDGE_NUM += 1
return newedge
class Halfedge(object):
def __init__(self, edge=None, pm=Edge.LE):
self.left = None # left Halfedge in the edge list
self.right = None # right Halfedge in the edge list
self.qnext = None # priority queue linked list pointer
self.edge = edge # edge list Edge
self.pm = pm
self.vertex = None # Site()
self.ystar = BIG_FLOAT
def dump(self):
print("Halfedge--------------------------")
print("left: ", self.left)
print("right: ", self.right)
print("edge: ", self.edge)
print("pm: ", self.pm)
print("vertex: "),
if self.vertex:
self.vertex.dump()
else:
print("None")
print("ystar: ", self.ystar)
def __lt__(self, other):
if self.ystar < other.ystar:
return True
elif self.ystar > other.ystar:
return False
elif self.vertex.x < other.vertex.x:
return True
elif self.vertex.x > other.vertex.x:
return False
else:
return False
def __eq__(self, other):
if self.ystar == other.ystar and self.vertex.x == other.vertex.x:
return True
def leftreg(self, default):
if not self.edge:
return default
elif self.pm == Edge.LE:
return self.edge.reg[Edge.LE]
else:
return self.edge.reg[Edge.RE]
def rightreg(self, default):
if not self.edge:
return default
elif self.pm == Edge.LE:
return self.edge.reg[Edge.RE]
else:
return self.edge.reg[Edge.LE]
# returns True if p is to right of halfedge self
def isPointRightOf(self, pt):
e = self.edge
topsite = e.reg[1]
right_of_site = pt.x > topsite.x
if(right_of_site and self.pm == Edge.LE):
return True
if(not right_of_site and self.pm == Edge.RE):
return False
if(e.a == 1.0):
dyp = pt.y - topsite.y
dxp = pt.x - topsite.x
fast = 0
if ((not right_of_site and e.b < 0.0) or (right_of_site and e.b >= 0.0)):
above = dyp >= e.b * dxp
fast = above
else:
above = pt.x + pt.y * e.b > e.c
if(e.b < 0.0):
above = not above
if (not above):
fast = 1
if (not fast):
dxs = topsite.x - (e.reg[0]).x
above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1.0 + 2.0 * dxp / dxs + e.b * e.b)
if(e.b < 0.0):
above = not above
else: # e.b == 1.0
yl = e.c - e.a * pt.x
t1 = pt.y - yl
t2 = pt.x - topsite.x
t3 = yl - topsite.y
above = t1 * t1 > t2 * t2 + t3 * t3
if(self.pm == Edge.LE):
return above
else:
return not above
# create a new site where the Halfedges el1 and el2 intersect
def intersect(self, other):
e1 = self.edge
e2 = other.edge
if (e1 is None) or (e2 is None):
return None
# if the two edges bisect the same parent return None
if e1.reg[1] is e2.reg[1]:
return None
d = e1.a * e2.b - e1.b * e2.a
if isEqual(d, 0.0):
return None
xint = (e1.c * e2.b - e2.c * e1.b) / d
yint = (e2.c * e1.a - e1.c * e2.a) / d
if e1.reg[1] < e2.reg[1]:
he = self
e = e1
else:
he = other
e = e2
rightOfSite = xint >= e.reg[1].x
if((rightOfSite and he.pm == Edge.LE) or
(not rightOfSite and he.pm == Edge.RE)):
return None
# create a new site at the point of intersection - this is a new
# vector event waiting to happen
return Site(xint, yint)
class EdgeList(object):
def __init__(self, xmin, xmax, nsites):
if xmin > xmax:
xmin, xmax = xmax, xmin
self.hashsize = int(2 * math.sqrt(nsites + 4))
self.xmin = xmin
self.deltax = float(xmax - xmin)
self.hash = [None] * self.hashsize
self.leftend = Halfedge()
self.rightend = Halfedge()
self.leftend.right = self.rightend
self.rightend.left = self.leftend
self.hash[0] = self.leftend
self.hash[-1] = self.rightend
def insert(self, left, he):
he.left = left
he.right = left.right
left.right.left = he
left.right = he
def delete(self, he):
he.left.right = he.right
he.right.left = he.left
he.edge = Edge.DELETED
# Get entry from hash table, pruning any deleted nodes
def gethash(self, b):
if(b < 0 or b >= self.hashsize):
return None
he = self.hash[b]
if he is None or he.edge is not Edge.DELETED:
return he
# Hash table points to deleted half edge. Patch as necessary.
self.hash[b] = None
return None
def leftbnd(self, pt):
# Use hash table to get close to desired halfedge
bucket = int(((pt.x - self.xmin) / self.deltax * self.hashsize))
if(bucket < 0):
bucket = 0
if(bucket >= self.hashsize):
bucket = self.hashsize - 1
he = self.gethash(bucket)
if(he is None):
i = 1
while True:
he = self.gethash(bucket - i)
if (he is not None):
break
he = self.gethash(bucket + i)
if (he is not None):
break
i += 1
# Now search linear list of halfedges for the correct one
if (he is self.leftend) or (he is not self.rightend and he.isPointRightOf(pt)):
he = he.right
while he is not self.rightend and he.isPointRightOf(pt):
he = he.right
he = he.left
else:
he = he.left
while (he is not self.leftend and not he.isPointRightOf(pt)):
he = he.left
# Update hash table and reference counts
if(bucket > 0 and bucket < self.hashsize - 1):
self.hash[bucket] = he
return he
class PriorityQueue(object):
def __init__(self, ymin, ymax, nsites):
self.ymin = ymin
self.deltay = ymax - ymin
self.hashsize = int(4 * math.sqrt(nsites))
self.count = 0
self.minidx = 0
self.hash = []
for i in range(self.hashsize):
self.hash.append(Halfedge())
def __len__(self):
return self.count
def isEmpty(self):
return self.count == 0
def insert(self, he, site, offset):
he.vertex = site
he.ystar = site.y + offset
last = self.hash[self.getBucket(he)]
next = last.qnext
while((next is not None) and he > next):
last = next
next = last.qnext
he.qnext = last.qnext
last.qnext = he
self.count += 1
def delete(self, he):
if (he.vertex is not None):
last = self.hash[self.getBucket(he)]
while last.qnext is not he:
last = last.qnext
last.qnext = he.qnext
self.count -= 1
he.vertex = None
def getBucket(self, he):
bucket = int(((he.ystar - self.ymin) / self.deltay) * self.hashsize)
if bucket < 0:
bucket = 0
if bucket >= self.hashsize:
bucket = self.hashsize - 1
if bucket < self.minidx:
self.minidx = bucket
return bucket
def getMinPt(self):
while(self.hash[self.minidx].qnext is None):
self.minidx += 1
he = self.hash[self.minidx].qnext
x = he.vertex.x
y = he.ystar
return Site(x, y)
def popMinHalfedge(self):
curr = self.hash[self.minidx].qnext
self.hash[self.minidx].qnext = curr.qnext
self.count -= 1
return curr
class SiteList(object):
def __init__(self, pointList):
self.__sites = []
self.__sitenum = 0
self.__xmin = min([pt.x for pt in pointList])
self.__ymin = min([pt.y for pt in pointList])
self.__xmax = max([pt.x for pt in pointList])
self.__ymax = max([pt.y for pt in pointList])
self.__extent = (self.__xmin, self.__xmax, self.__ymin, self.__ymax)
for i, pt in enumerate(pointList):
self.__sites.append(Site(pt.x, pt.y, i))
self.__sites.sort()
def setSiteNumber(self, site):
site.sitenum = self.__sitenum
self.__sitenum += 1
class Iterator(object):
def __init__(this, lst):
this.generator = (s for s in lst)
def __iter__(this):
return this
def next(this):
try:
# Note: Blender is Python 3.x so no need for 2.x checks
return this.generator.__next__()
except StopIteration:
return None
def iterator(self):
return SiteList.Iterator(self.__sites)
def __iter__(self):
return SiteList.Iterator(self.__sites)
def __len__(self):
return len(self.__sites)
def _getxmin(self):
return self.__xmin
def _getymin(self):
return self.__ymin
def _getxmax(self):
return self.__xmax
def _getymax(self):
return self.__ymax
def _getextent(self):
return self.__extent
xmin = property(_getxmin)
ymin = property(_getymin)
xmax = property(_getxmax)
ymax = property(_getymax)
extent = property(_getextent)
def computeVoronoiDiagram(points, xBuff=0, yBuff=0, polygonsOutput=False,
formatOutput=False, closePoly=True):
"""
Takes :
- a list of point objects (which must have x and y fields).
- x and y buffer values which are the expansion percentages of the bounding box
rectangle including all input points.
Returns :
- With default options :
A list of 2-tuples, representing the two points of each Voronoi diagram edge.
Each point contains 2-tuples which are the x,y coordinates of point.
if formatOutput is True, returns :
- a list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices.
- and a list of 2-tuples (v1, v2) representing edges of the Voronoi diagram.
v1 and v2 are the indices of the vertices at the end of the edge.
- If polygonsOutput option is True, returns :
A dictionary of polygons, keys are the indices of the input points,
values contains n-tuples representing the n points of each Voronoi diagram polygon.
Each point contains 2-tuples which are the x,y coordinates of point.
if formatOutput is True, returns :
- A list of 2-tuples, which are the x,y coordinates of the Voronoi diagram vertices.
- and a dictionary of input points indices. Values contains n-tuples representing
the n points of each Voronoi diagram polygon.
Each tuple contains the vertex indices of the polygon vertices.
- if closePoly is True then, in the list of points of a polygon, last point will be the same of first point
"""
siteList = SiteList(points)
context = Context()
voronoi(siteList, context)
context.setClipBuffer(xBuff, yBuff)
if not polygonsOutput:
clipEdges = context.getClipEdges()
if formatOutput:
vertices, edgesIdx = formatEdgesOutput(clipEdges)
return vertices, edgesIdx
else:
return clipEdges
else:
clipPolygons = context.getClipPolygons(closePoly)
if formatOutput:
vertices, polyIdx = formatPolygonsOutput(clipPolygons)
return vertices, polyIdx
else:
return clipPolygons
def formatEdgesOutput(edges):
# get list of points
pts = []
for edge in edges:
pts.extend(edge)
# get unique values
pts = set(pts) # unique values (tuples are hashable)
# get dict {values:index}
valuesIdxDict = dict(zip(pts, range(len(pts))))
# get edges index reference
edgesIdx = []
for edge in edges:
edgesIdx.append([valuesIdxDict[pt] for pt in edge])
return list(pts), edgesIdx
def formatPolygonsOutput(polygons):
# get list of points
pts = []
for poly in polygons.values():
pts.extend(poly)
# get unique values
pts = set(pts) # unique values (tuples are hashable)
# get dict {values:index}
valuesIdxDict = dict(zip(pts, range(len(pts))))
# get polygons index reference
polygonsIdx = {}
for inPtsIdx, poly in polygons.items():
polygonsIdx[inPtsIdx] = [valuesIdxDict[pt] for pt in poly]
return list(pts), polygonsIdx
def computeDelaunayTriangulation(points):
""" Takes a list of point objects (which must have x and y fields).
Returns a list of 3-tuples: the indices of the points that form a
Delaunay triangle.
"""
siteList = SiteList(points)
context = Context()
context.triangulate = True
voronoi(siteList, context)
return context.triangles

View File

@ -1,515 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Contributed to by:
# meta-androcto, Bill Currie, Jorge Hernandez - Melenedez Jacob Morris, Oscurart #
# Rebellion, Antonis Karvelas, Eleanor Howick, lijenstina, Daniel Schalla, Domlysz #
# Unnikrishnan(kodemax), Florian Meyer, Omar ahmed, Brian Hinton (Nichod), liero #
# Atom, Dannyboy, Mano-Wii, Kursad Karatas, teldredge, Phil Cote #
bl_info = {
"name": "Add Advanced Object Panels",
"author": "meta-androcto",
"version": (1, 1, 5),
"blender": (2, 77, 0),
"description": "Individual Create Panel Activation List",
"location": "Addons Preferences",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
"/Py/Scripts/Object/Add_Advanced",
"category": "Object"
}
import bpy
from bpy.types import (
AddonPreferences,
PropertyGroup,
)
from bpy.props import (
BoolProperty,
BoolVectorProperty,
EnumProperty,
FloatProperty,
FloatVectorProperty,
IntProperty,
StringProperty,
PointerProperty,
)
sub_modules_names = (
"drop_to_ground",
"object_laplace_lightning",
"object_mangle_tools",
"unfold_transition",
"delaunay_voronoi",
"oscurart_constellation",
)
sub_modules = [__import__(__package__ + "." + submod, {}, {}, submod) for submod in sub_modules_names]
sub_modules.sort(key=lambda mod: (mod.bl_info['category'], mod.bl_info['name']))
# Add-ons Preferences
def _get_pref_class(mod):
import inspect
for obj in vars(mod).values():
if inspect.isclass(obj) and issubclass(obj, PropertyGroup):
if hasattr(obj, 'bl_idname') and obj.bl_idname == mod.__name__:
return obj
def get_addon_preferences(name=''):
"""Acquisition and registration"""
addons = bpy.context.preferences.addons
if __name__ not in addons: # wm.read_factory_settings()
return None
addon_prefs = addons[__name__].preferences
if name:
if not hasattr(addon_prefs, name):
for mod in sub_modules:
if mod.__name__.split('.')[-1] == name:
cls = _get_pref_class(mod)
if cls:
prop = PointerProperty(type=cls)
setattr(AdvancedObjPreferences1, name, prop)
bpy.utils.unregister_class(AdvancedObjPreferences1)
bpy.utils.register_class(AdvancedObjPreferences1)
return getattr(addon_prefs, name, None)
else:
return addon_prefs
def register_submodule(mod):
if not hasattr(mod, '__addon_enabled__'):
mod.__addon_enabled__ = False
if not mod.__addon_enabled__:
mod.register()
mod.__addon_enabled__ = True
def unregister_submodule(mod):
if mod.__addon_enabled__:
mod.unregister()
mod.__addon_enabled__ = False
prefs = get_addon_preferences()
name = mod.__name__.split('.')[-1]
if hasattr(AdvancedObjPreferences1, name):
delattr(AdvancedObjPreferences1, name)
if prefs:
bpy.utils.unregister_class(AdvancedObjPreferences1)
bpy.utils.register_class(AdvancedObjPreferences1)
if name in prefs:
del prefs[name]
def enable_all_modules(self, context):
for mod in sub_modules:
mod_name = mod.__name__.split('.')[-1]
setattr(self, 'use_' + mod_name, False)
if not mod.__addon_enabled__:
setattr(self, 'use_' + mod_name, True)
mod.__addon_enabled__ = True
return None
def disable_all_modules(self, context):
for mod in sub_modules:
mod_name = mod.__name__.split('.')[-1]
if mod.__addon_enabled__:
setattr(self, 'use_' + mod_name, False)
mod.__addon_enabled__ = False
return None
class AdvancedObjPreferences1(AddonPreferences):
bl_idname = __name__
enable_all: BoolProperty(
name="Enable all",
description="Enable all Advanced Objects' Panels",
default=False,
update=enable_all_modules
)
disable_all: BoolProperty(
name="Disable all",
description="Disable all Advanced Objects' Panels",
default=False,
update=disable_all_modules
)
def draw(self, context):
layout = self.layout
split = layout.split(percentage=0.5, align=True)
row = split.row()
row.alignment = "LEFT"
sub_box = row.box()
sub_box.prop(self, "enable_all", emboss=False,
icon="VISIBLE_IPO_ON", icon_only=True)
row.label(text="Enable All")
row = split.row()
row.alignment = "RIGHT"
row.label(text="Disable All")
sub_box = row.box()
sub_box.prop(self, "disable_all", emboss=False,
icon="VISIBLE_IPO_OFF", icon_only=True)
for mod in sub_modules:
mod_name = mod.__name__.split('.')[-1]
info = mod.bl_info
column = layout.column()
box = column.box()
# first stage
expand = getattr(self, 'show_expanded_' + mod_name)
icon = 'TRIA_DOWN' if expand else 'TRIA_RIGHT'
col = box.column()
row = col.row()
sub = row.row()
sub.context_pointer_set('addon_prefs', self)
op = sub.operator('wm.context_toggle', text='', icon=icon,
emboss=False)
op.data_path = 'addon_prefs.show_expanded_' + mod_name
sub.label(text='{}: {}'.format(info['category'], info['name']))
sub = row.row()
sub.alignment = 'RIGHT'
if info.get('warning'):
sub.label(text='', icon='ERROR')
sub.prop(self, 'use_' + mod_name, text='')
# The second stage
if expand:
if info.get('description'):
split = col.row().split(percentage=0.15)
split.label(text='Description:')
split.label(text=info['description'])
if info.get('location'):
split = col.row().split(percentage=0.15)
split.label(text='Location:')
split.label(text=info['location'])
if info.get('author'):
split = col.row().split(percentage=0.15)
split.label(text='Author:')
split.label(text=info['author'])
if info.get('version'):
split = col.row().split(percentage=0.15)
split.label(text='Version:')
split.label(text='.'.join(str(x) for x in info['version']),
translate=False)
if info.get('warning'):
split = col.row().split(percentage=0.15)
split.label(text='Warning:')
split.label(text=' ' + info['warning'], icon='ERROR')
tot_row = int(bool(info.get('wiki_url')))
if tot_row:
split = col.row().split(percentage=0.15)
split.label(text='Internet:')
if info.get('wiki_url'):
op = split.operator('wm.url_open',
text='Documentation', icon='HELP')
op.url = info.get('wiki_url')
for i in range(4 - tot_row):
split.separator()
# Details and settings
if getattr(self, 'use_' + mod_name):
prefs = get_addon_preferences(mod_name)
if prefs and hasattr(prefs, 'draw'):
box = box.column()
prefs.layout = box
try:
prefs.draw(context)
except:
import traceback
traceback.print_exc()
box.label(text="Error (see console)", icon="ERROR")
del prefs.layout
row = layout.row()
row.label(text="End of Advanced Object Panels Activations",
icon="FILE_PARENT")
for mod in sub_modules:
info = mod.bl_info
mod_name = mod.__name__.split('.')[-1]
def gen_update(mod):
def update(self, context):
if getattr(self, 'use_' + mod.__name__.split('.')[-1]):
if not mod.__addon_enabled__:
register_submodule(mod)
else:
if mod.__addon_enabled__:
unregister_submodule(mod)
return update
prop = BoolProperty(
name=info['name'],
description=info.get('description', ''),
update=gen_update(mod),
)
setattr(AdvancedObjPreferences1, 'use_' + mod_name, prop)
prop = BoolProperty()
setattr(AdvancedObjPreferences1, 'show_expanded_' + mod_name, prop)
class AdvancedObjProperties1(PropertyGroup):
# object_laplace_lighting props
ORIGIN: FloatVectorProperty(
name="Origin charge"
)
GROUNDZ: IntProperty(
name="Ground Z coordinate"
)
HORDER: IntProperty(
name="Secondary paths orders",
default=1
)
# object_laplace_lighting UI props
TSTEPS: IntProperty(
name="Iterations",
default=350,
description="Number of cells to create\n"
"Will end early if hits ground plane or cloud"
)
GSCALE: FloatProperty(
name="Grid unit size",
default=0.12,
description="scale of cells, .25 = 4 cells per blenderUnit"
)
BIGVAR: FloatProperty(
name="Straightness",
default=6.3,
description="Straightness/branchiness of bolt, \n"
"<2 is mush, >12 is staight line, 6.3 is good"
)
GROUNDBOOL: BoolProperty(
name="Use Ground object",
description="Use ground plane or not",
default=True
)
GROUNDC: IntProperty(
name="Ground charge",
default=-250,
description="Charge of the ground plane"
)
CLOUDBOOL: BoolProperty(
name="Use Cloud object",
default=False,
description="Use cloud object - attracts and terminates like ground but\n"
"any obj instead of z plane\n"
"Can slow down loop if obj is large, overrides ground"
)
CLOUDC: IntProperty(
name="Cloud charge",
default=-1,
description="Charge of a cell in cloud object\n"
"(so total charge also depends on obj size)"
)
VMMESH: BoolProperty(
name="Multi mesh",
default=True,
description="Output to multi-meshes for different materials on main/sec/side branches"
)
VSMESH: BoolProperty(
name="Single mesh",
default=False,
description="Output to single mesh for using build modifier and particles for effects"
)
VCUBE: BoolProperty(
name="Cubes",
default=False,
description="CTRL-J after run to JOIN\n"
"Outputs a bunch of cube objects, mostly for testing"
)
VVOX: BoolProperty(
name="Voxel (experimental)",
default=False,
description="Output to a voxel file to bpy.data.filepath\FSLGvoxels.raw\n"
"(doesn't work well right now)"
)
IBOOL: BoolProperty(
name="Use Insulator object",
default=False,
description="Use insulator mesh object to prevent growth of bolt in areas"
)
OOB: StringProperty(
name="Select",
default="",
description="Origin of bolt, can be an Empty\n"
"if object is a mesh will use all verts as charges")
GOB: StringProperty(
name="Select",
default="",
description="Object to use as ground plane, uses z coord only"
)
COB: StringProperty(
name="Select",
default="",
description="Object to use as cloud, best to use a cube"
)
IOB: StringProperty(
name="Select",
default="",
description="Object to use as insulator, 'voxelized'\n"
"before generating bolt (can be slow)"
)
# object_mangle_tools properties
mangle_constraint_vector = BoolVectorProperty(
name="Mangle Constraint",
default=(True, True, True),
subtype='XYZ',
description="Constrains Mangle Direction"
)
mangle_random_magnitude: IntProperty(
name="Mangle Severity",
default=5,
min=1, max=30,
description="Severity of mangling"
)
mangle_name: StringProperty(
name="Shape Key Name",
default="mangle",
description="Name given for mangled shape keys"
)
# unfold_transition properties
unfold_arm_name: StringProperty(
default=""
)
unfold_modo: EnumProperty(
name="",
items=[("cursor", "3D Cursor", "Use the Distance to 3D Cursor"),
("weight", "Weight Map", "Use a Painted Weight map"),
("index", "Mesh Indices", "Use Faces and Vertices index")],
description="How to Sort Bones for animation", default="cursor"
)
unfold_flip: BoolProperty(
name="Flipping Faces",
default=False,
description="Rotate faces around the Center and skip Scaling - "
"keep checked for both operators"
)
unfold_fold_duration: IntProperty(
name="Total Time",
min=5, soft_min=25,
max=10000, soft_max=2500,
default=200,
description="Total animation length"
)
unfold_sca_time: IntProperty(
name="Scale Time",
min=1,
max=5000, soft_max=500,
default=10,
description="Faces scaling time"
)
unfold_rot_time: IntProperty(
name="Rotation Time",
min=1, soft_min=5,
max=5000, soft_max=500,
default=15,
description="Faces rotation time"
)
unfold_rot_max: IntProperty(
name="Angle",
min=-180,
max=180,
default=135,
description="Faces rotation angle"
)
unfold_fold_noise: IntProperty(
name="Noise",
min=0,
max=500, soft_max=50,
default=0,
description="Offset some faces animation"
)
unfold_bounce: FloatProperty(
name="Bounce",
min=0,
max=10, soft_max=2.5,
default=0,
description="Add some bounce to rotation"
)
unfold_from_point: BoolProperty(
name="Point",
default=False,
description="Scale faces from a Point instead of from an Edge"
)
unfold_wiggle_rot: BoolProperty(
name="Wiggle",
default=False,
description="Use all Axis + Random Rotation instead of X Aligned"
)
# oscurart_constellation
constellation_limit: FloatProperty(
name="Initial Threshold",
description="Edges will be created only if the distance\n"
"between vertices is smaller than this value\n"
"This is a starting value on Operator Invoke",
default=2,
min=0
)
# Class list
classes = (
AdvancedObjPreferences1,
AdvancedObjProperties1,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.advanced_objects1 = PointerProperty(
type=AdvancedObjProperties1
)
prefs = get_addon_preferences()
for mod in sub_modules:
if not hasattr(mod, '__addon_enabled__'):
mod.__addon_enabled__ = False
name = mod.__name__.split('.')[-1]
if getattr(prefs, 'use_' + name):
register_submodule(mod)
def unregister():
for mod in sub_modules:
if mod.__addon_enabled__:
unregister_submodule(mod)
del bpy.types.Scene.advanced_objects1
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
if __name__ == "__main__":
register()

View File

@ -1,344 +0,0 @@
# -*- coding:utf-8 -*-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Delaunay Voronoi",
"description": "Points cloud Delaunay triangulation in 2.5D "
"(suitable for terrain modelling) or Voronoi diagram in 2D",
"author": "Domlysz, Oscurart",
"version": (1, 3),
"blender": (2, 70, 0),
"location": "3D View > Toolshelf > Create > Delaunay Voronoi",
"warning": "",
"wiki_url": "https://github.com/domlysz/BlenderGIS/wiki",
"category": "Add Mesh"
}
import bpy
from .DelaunayVoronoi import (
computeVoronoiDiagram,
computeDelaunayTriangulation,
)
from bpy.types import (
Operator,
Panel,
)
from bpy.props import EnumProperty
try:
from scipy.spatial import Delaunay
import bmesh
import numpy as np
HAS_SCIPY = True
except:
HAS_SCIPY = False
pass
# Globals
# set to True to enable debug_prints
DEBUG = False
def debug_prints(text=""):
global DEBUG
if DEBUG and text:
print(text)
class Point:
def __init__(self, x, y, z):
self.x, self.y, self.z = x, y, z
def unique(L):
"""Return a list of unhashable elements in s, but without duplicates.
[[1, 2], [2, 3], [1, 2]] >>> [[1, 2], [2, 3]]"""
# For unhashable objects, you can sort the sequence and
# then scan from the end of the list, deleting duplicates as you go
nDupli = 0
nZcolinear = 0
# sort() brings the equal elements together; then duplicates
# are easy to weed out in a single pass
L.sort()
last = L[-1]
for i in range(len(L) - 2, -1, -1):
if last[:2] == L[i][:2]: # XY coordinates compararison
if last[2] == L[i][2]: # Z coordinates compararison
nDupli += 1 # duplicates vertices
else: # Z colinear
nZcolinear += 1
del L[i]
else:
last = L[i]
# list data type is mutable, input list will automatically update
# and doesn't need to be returned
return (nDupli, nZcolinear)
def checkEqual(lst):
return lst[1:] == lst[:-1]
class ToolsPanelDelaunay(Panel):
bl_category = "Create"
bl_label = "Delaunay Voronoi"
bl_space_type = "VIEW_3D"
bl_context = "objectmode"
bl_region_type = "TOOLS"
bl_options = {"DEFAULT_CLOSED"}
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column(align=True)
col.label(text="Point Cloud:")
col.operator("delaunay.triangulation")
col.operator("voronoi.tesselation")
class OBJECT_OT_TriangulateButton(Operator):
bl_idname = "delaunay.triangulation"
bl_label = "Triangulation"
bl_description = ("Terrain points cloud Delaunay triangulation in 2.5D\n"
"Needs an existing Active Mesh Object")
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == "MESH")
def execute(self, context):
# move the check into the poll
obj = context.active_object
if HAS_SCIPY:
# Use scipy when present (~18 x faster)
bpy.ops.object.mode_set(mode='EDIT')
bm = bmesh.from_edit_mesh(obj.data)
points_3D = [list(v.co) for v in bm.verts]
points_2D = np.array([[v[0], v[1]] for v in points_3D])
print("Triangulate " + str(len(points_3D)) + " points...")
# Triangulate
tri = Delaunay(points_2D)
faces = tri.simplices.tolist()
# Create new mesh structure
print("Create mesh...")
bpy.ops.object.mode_set(mode='OBJECT')
mesh = bpy.data.meshes.new("TIN")
mesh.from_pydata(points_3D, [], faces)
mesh.update(calc_edges=True)
my = bpy.data.objects.new("TIN", mesh)
context.collection.objects.link(my)
my.matrix_world = obj.matrix_world.copy()
obj.select_set(False)
my.select_set(True)
context.view_layer.objects.active = my
self.report({'INFO'}, "Mesh created (" + str(len(faces)) + " triangles)")
print("Total :%s faces %s verts" % (len(faces), len(points_3D)))
return {'FINISHED'}
# Get points coordinates
r = obj.rotation_euler
s = obj.scale
mesh = obj.data
vertsPts = [vertex.co for vertex in mesh.vertices]
# Remove duplicate
verts = [[vert.x, vert.y, vert.z] for vert in vertsPts]
nDupli, nZcolinear = unique(verts)
nVerts = len(verts)
debug_prints(text=str(nDupli) + " duplicate points ignored")
debug_prints(str(nZcolinear) + " z colinear points excluded")
if nVerts < 3:
self.report({"WARNING"},
"Not enough points to continue. Operation Cancelled")
return {"CANCELLED"}
# Check colinear
xValues = [pt[0] for pt in verts]
yValues = [pt[1] for pt in verts]
if checkEqual(xValues) or checkEqual(yValues):
self.report({'ERROR'}, "Points are colinear")
return {'FINISHED'}
# Triangulate
debug_prints(text="Triangulate " + str(nVerts) + " points...")
vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts]
triangles = computeDelaunayTriangulation(vertsPts)
# reverse point order --> if all triangles are specified anticlockwise then all faces up
triangles = [tuple(reversed(tri)) for tri in triangles]
debug_prints(text=str(len(triangles)) + " triangles")
# Create new mesh structure
debug_prints(text="Create mesh...")
tinMesh = bpy.data.meshes.new("TIN") # create a new mesh
tinMesh.from_pydata(verts, [], triangles) # Fill the mesh with triangles
tinMesh.update(calc_edges=True) # Update mesh with new data
# Create an object with that mesh
tinObj = bpy.data.objects.new("TIN", tinMesh)
# Place object
tinObj.location = obj.location.copy()
tinObj.rotation_euler = r
tinObj.scale = s
# Update scene
bpy.context.collection.objects.link(tinObj) # Link object to collection
bpy.context.view_layer.objects.active = tinObj
tinObj.select_set(True)
obj.select_set(False)
self.report({"INFO"},
"Mesh created (" + str(len(triangles)) + " triangles)")
return {'FINISHED'}
class OBJECT_OT_VoronoiButton(Operator):
bl_idname = "voronoi.tesselation"
bl_label = "Diagram"
bl_description = ("Points cloud Voronoi diagram in 2D\n"
"Needs an existing Active Mesh Object")
bl_options = {"REGISTER", "UNDO"}
meshType: EnumProperty(
items=[('Edges', "Edges", "Edges Only - do not fill Faces"),
('Faces', "Faces", "Fill Faces in the new Object")],
name="Mesh type",
description="Type of geometry to generate"
)
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == "MESH")
def execute(self, context):
# move the check into the poll
obj = context.active_object
# Get points coordinates
r = obj.rotation_euler
s = obj.scale
mesh = obj.data
vertsPts = [vertex.co for vertex in mesh.vertices]
# Remove duplicate
verts = [[vert.x, vert.y, vert.z] for vert in vertsPts]
nDupli, nZcolinear = unique(verts)
nVerts = len(verts)
debug_prints(text=str(nDupli) + " duplicates points ignored")
debug_prints(text=str(nZcolinear) + " z colinear points excluded")
if nVerts < 3:
self.report({"WARNING"},
"Not enough points to continue. Operation Cancelled")
return {"CANCELLED"}
# Check colinear
xValues = [pt[0] for pt in verts]
yValues = [pt[1] for pt in verts]
if checkEqual(xValues) or checkEqual(yValues):
self.report({"WARNING"},
"Points are colinear. Operation Cancelled")
return {"CANCELLED"}
# Create diagram
debug_prints(text="Tesselation... (" + str(nVerts) + " points)")
xbuff, ybuff = 5, 5
zPosition = 0
vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts]
if self.meshType == "Edges":
pts, edgesIdx = computeVoronoiDiagram(
vertsPts, xbuff, ybuff,
polygonsOutput=False, formatOutput=True
)
else:
pts, polyIdx = computeVoronoiDiagram(
vertsPts, xbuff, ybuff, polygonsOutput=True,
formatOutput=True, closePoly=False
)
pts = [[pt[0], pt[1], zPosition] for pt in pts]
# Create new mesh structure
voronoiDiagram = bpy.data.meshes.new("VoronoiDiagram") # create a new mesh
if self.meshType == "Edges":
# Fill the mesh with triangles
voronoiDiagram.from_pydata(pts, edgesIdx, [])
else:
# Fill the mesh with triangles
voronoiDiagram.from_pydata(pts, [], list(polyIdx.values()))
voronoiDiagram.update(calc_edges=True) # Update mesh with new data
# create an object with that mesh
voronoiObj = bpy.data.objects.new("VoronoiDiagram", voronoiDiagram)
# place object
voronoiObj.location = obj.location.copy()
voronoiObj.rotation_euler = r
voronoiObj.scale = s
# update scene
bpy.context.collection.objects.link(voronoiObj) # Link object to collection
bpy.context.view_layer.objects.active = voronoiObj
voronoiObj.select_set(True)
obj.select_set(False)
# Report
if self.meshType == "Edges":
self.report({"INFO"}, "Mesh created (" + str(len(edgesIdx)) + " edges)")
else:
self.report({"INFO"}, "Mesh created (" + str(len(polyIdx)) + " polygons)")
return {'FINISHED'}
# Register
def register():
bpy.utils.register_class(OBJECT_OT_VoronoiButton)
bpy.utils.register_class(OBJECT_OT_TriangulateButton)
bpy.utils.register_class(ToolsPanelDelaunay)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_VoronoiButton)
bpy.utils.unregister_class(OBJECT_OT_TriangulateButton)
bpy.utils.unregister_class(ToolsPanelDelaunay)
if __name__ == "__main__":
register()

View File

@ -1,378 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Drop to Ground1",
"author": "Unnikrishnan(kodemax), Florian Meyer(testscreenings)",
"blender": (2, 71, 0),
"location": "3D View > Toolshelf > Create > Drop To Ground",
"description": "Drop selected objects on active object",
"warning": "",
"category": "Object"}
import bpy
import bmesh
from mathutils import (
Vector,
Matrix,
)
from bpy.types import (
Operator,
Panel,
)
from bpy.props import BoolProperty
def test_ground_object(ground):
if ground.type in {'MESH', 'FONT', 'META', 'CURVE', 'SURFACE'}:
return True
return False
def get_align_matrix(location, normal):
up = Vector((0, 0, 1))
angle = normal.angle(up)
axis = up.cross(normal)
mat_rot = Matrix.Rotation(angle, 4, axis)
mat_loc = Matrix.Translation(location)
mat_align = mat_rot * mat_loc
return mat_align
def transform_ground_to_world(sc, ground):
tmpMesh = ground.to_mesh(sc, True, 'PREVIEW')
tmpMesh.transform(ground.matrix_world)
tmp_ground = bpy.data.objects.new('tmpGround', tmpMesh)
sc.objects.link(tmp_ground)
sc.update()
return tmp_ground
def get_lowest_world_co_from_mesh(ob, mat_parent=None):
bme = bmesh.new()
bme.from_mesh(ob.data)
mat_to_world = ob.matrix_world.copy()
if mat_parent:
mat_to_world = mat_parent * mat_to_world
lowest = None
for v in bme.verts:
if not lowest:
lowest = v
if (mat_to_world * v.co).z < (mat_to_world * lowest.co).z:
lowest = v
lowest_co = mat_to_world * lowest.co
bme.free()
return lowest_co
def get_lowest_world_co(context, ob, mat_parent=None):
if ob.type == 'MESH':
return get_lowest_world_co_from_mesh(ob)
elif ob.type == 'EMPTY' and ob.instance_type == 'COLLECTION':
if not ob.instance_collection:
return None
else:
lowest_co = None
for ob_l in ob.instance_collection.objects:
if ob_l.type == 'MESH':
lowest_ob_l = get_lowest_world_co_from_mesh(ob_l, ob.matrix_world)
if not lowest_co:
lowest_co = lowest_ob_l
if lowest_ob_l.z < lowest_co.z:
lowest_co = lowest_ob_l
return lowest_co
def drop_objectsall(self, context):
ground = context.active_object
name = ground.name
for obs in bpy.context.scene.objects:
obs.select_set(True)
if obs.name == name:
obs.select_set(False)
obs2 = context.selected_objects
tmp_ground = transform_ground_to_world(context.scene, ground)
down = Vector((0, 0, -10000))
for ob in obs2:
if self.use_origin:
lowest_world_co = ob.location
else:
lowest_world_co = get_lowest_world_co(context, ob)
if not lowest_world_co:
message = "Object {} is of type {} works only with Use Center option " \
"checked".format(ob.name, ob.type)
self.reported.append(message)
continue
is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down)
if not is_hit:
message = ob.name + " did not hit the Ground"
self.reported.append(message)
continue
# simple drop down
to_ground_vec = hit_location - lowest_world_co
ob.location += to_ground_vec
# drop with align to hit normal
if self.align:
to_center_vec = ob.location - hit_location # vec: hit_loc to origin
# rotate object to align with face normal
mat_normal = get_align_matrix(hit_location, hit_normal)
rot_euler = mat_normal.to_euler()
mat_ob_tmp = ob.matrix_world.copy().to_3x3()
mat_ob_tmp.rotate(rot_euler)
mat_ob_tmp = mat_ob_tmp.to_4x4()
ob.matrix_world = mat_ob_tmp
# move_object to hit_location
ob.location = hit_location
# move object above surface again
to_center_vec.rotate(rot_euler)
ob.location += to_center_vec
# cleanup
bpy.ops.object.select_all(action='DESELECT')
tmp_ground.select_set(True)
bpy.ops.object.delete('EXEC_DEFAULT')
for ob in obs2:
ob.select_set(True)
ground.select_set(True)
def drop_objects(self, context):
ground = context.active_object
obs = context.selected_objects
if ground in obs:
obs.remove(ground)
tmp_ground = transform_ground_to_world(context.scene, ground)
down = Vector((0, 0, -10000))
for ob in obs:
if self.use_origin:
lowest_world_co = ob.location
else:
lowest_world_co = get_lowest_world_co(context, ob)
if not lowest_world_co:
message = "Object {} is of type {} works only with Use Center option " \
"checked".format(ob.name, ob.type)
self.reported.append(message)
continue
is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down)
if not is_hit:
message = ob.name + " did not hit the Active Object"
self.reported.append(message)
continue
# simple drop down
to_ground_vec = hit_location - lowest_world_co
ob.location += to_ground_vec
# drop with align to hit normal
if self.align:
to_center_vec = ob.location - hit_location # vec: hit_loc to origin
# rotate object to align with face normal
mat_normal = get_align_matrix(hit_location, hit_normal)
rot_euler = mat_normal.to_euler()
mat_ob_tmp = ob.matrix_world.copy().to_3x3()
mat_ob_tmp.rotate(rot_euler)
mat_ob_tmp = mat_ob_tmp.to_4x4()
ob.matrix_world = mat_ob_tmp
# move_object to hit_location
ob.location = hit_location
# move object above surface again
to_center_vec.rotate(rot_euler)
ob.location += to_center_vec
# cleanup
bpy.ops.object.select_all(action='DESELECT')
tmp_ground.select_set(True)
bpy.ops.object.delete('EXEC_DEFAULT')
for ob in obs:
ob.select_set(True)
ground.select_set(True)
# define base dummy class for inheritance
class DropBaseAtributes:
align: BoolProperty(
name="Align to ground",
description="Aligns the objects' rotation to the ground",
default=True)
use_origin: BoolProperty(
name="Use Origins",
description="Drop to objects' origins\n"
"Use this option for dropping all types of Objects",
default=False)
class OBJECT_OT_drop_to_ground(Operator, DropBaseAtributes):
bl_idname = "object.drop_on_active"
bl_label = "Drop to Ground"
bl_description = ("Drop selected objects on the Active object\n"
"Active Object has to be of following the types:\n"
"Mesh, Font, Metaball, Curve, Surface")
bl_options = {'REGISTER', 'UNDO'}
reported = []
@classmethod
def poll(cls, context):
act_obj = context.active_object
return (len(context.selected_objects) >= 2 and
act_obj and test_ground_object(act_obj))
def execute(self, context):
drop_objects(self, context)
if self.reported:
self.report({"INFO"},
"Some objects could not be dropped (See the Console for more Info)")
report_items = " \n".join(self.reported)
print("\n[Drop to Ground Report]\n{}\n".format(report_items))
self.reported[:] = []
return {'FINISHED'}
class OBJECT_OT_drop_all_ground(Operator, DropBaseAtributes):
bl_idname = "object.drop_all_active"
bl_label = "Drop All to Ground (Active Object)"
bl_description = ("Drop all other objects onto Active Object\n"
"Active Object has to be of following the types:\n"
"Mesh, Font, Metaball, Curve, Surface")
bl_options = {'REGISTER', 'UNDO'}
reported = []
@classmethod
def poll(cls, context):
act_obj = context.active_object
return act_obj and test_ground_object(act_obj)
def execute(self, context):
drop_objectsall(self, context)
if self.reported:
self.report({"INFO"},
"Some objects could not be dropped (See the Console for more Info)")
report_items = " \n".join(self.reported)
print("\n[Drop All to Ground Report]\n{}\n".format(report_items))
self.reported[:] = []
return {'FINISHED'}
class Drop_help(Operator):
bl_idname = "help.drop"
bl_label = "Drop to Ground Help"
bl_description = "Clik for some information about Drop to Ground"
bl_options = {"REGISTER", "INTERNAL"}
is_all: BoolProperty(
default=True,
options={"HIDDEN"}
)
def draw(self, context):
layout = self.layout
layout.label(text="General Info:")
layout.label(text="The Active Object has to be of a Mesh, Font,")
layout.label(text="Metaball, Curve or Surface type and")
layout.label(text="be at the lowest Z location")
layout.label(text="The option Use Origins must be enabled to drop")
layout.label(text="objects that are not of a Mesh or DupliGroup type")
layout.label(text="The Active Object has to be big enough to catch them")
layout.label(text="To check that, use the Orthographic Top View")
layout.separator()
layout.label(text="To use:")
if self.is_all is False:
layout.label(text="Select objects to drop")
layout.label(text="Then Shift Select the object to be the ground")
layout.label(text="Drops Selected Object to the Active one")
else:
layout.label(text="Select the ground Mesh and press Drop all")
layout.label(text="The unselected Objects will be moved straight")
layout.label(text="down the Z axis, so they have to be above")
layout.label(text="the Selected / Active one to fall")
def execute(self, context):
return {'FINISHED'}
def invoke(self, context, event):
return context.window_manager.invoke_popup(self, width=300)
class Drop_Operator_Panel(Panel):
bl_label = "Drop To Ground"
bl_region_type = "TOOLS"
bl_space_type = "VIEW_3D"
bl_options = {'DEFAULT_CLOSED'}
bl_context = "objectmode"
bl_category = "Create"
def draw(self, context):
layout = self.layout
row = layout.split(percentage=0.8, align=True)
row.operator(OBJECT_OT_drop_to_ground.bl_idname,
text="Drop Selected")
row.operator("help.drop", text="", icon="LAYER_USED").is_all = False
row = layout.split(percentage=0.8, align=True)
row.operator(OBJECT_OT_drop_all_ground.bl_idname,
text="Drop All")
row.operator("help.drop", text="", icon="LAYER_USED").is_all = True
# Register
def register():
bpy.utils.register_class(OBJECT_OT_drop_all_ground)
bpy.utils.register_class(OBJECT_OT_drop_to_ground)
bpy.utils.register_class(Drop_Operator_Panel)
bpy.utils.register_class(Drop_help)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_drop_all_ground)
bpy.utils.unregister_class(OBJECT_OT_drop_to_ground)
bpy.utils.unregister_class(Drop_Operator_Panel)
bpy.utils.unregister_class(Drop_help)
if __name__ == "__main__":
register()

File diff suppressed because it is too large Load Diff

View File

@ -1,208 +0,0 @@
# mangle_tools.py (c) 2011 Phil Cote (cotejrp1)
# ###### BEGIN GPL LICENSE BLOCK ######
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ###### END GPL LICENCE BLOCK ######
# Note: properties are moved into __init__
bl_info = {
"name": "Mangle Tools",
"author": "Phil Cote",
"blender": (2, 71, 0),
"location": "3D View > Toolshelf > Create > Mangle Tools",
"description": "Set of tools to mangle curves, meshes, and shape keys",
"warning": "",
"wiki_url": "",
"category": "Object"}
import bpy
import random
from bpy.types import (
Operator,
Panel,
)
import time
from math import pi
import bmesh
def move_coordinate(context, co, is_curve=False):
advanced_objects = context.scene.advanced_objects1
xyz_const = advanced_objects.mangle_constraint_vector
random.seed(time.time())
multiplier = 1
# For curves, we base the multiplier on the circumference formula.
# This helps make curve changes more noticeable.
if is_curve:
multiplier = 2 * pi
random_mag = advanced_objects.mangle_random_magnitude
if xyz_const[0]:
co.x += .01 * random.randrange(-random_mag, random_mag) * multiplier
if xyz_const[1]:
co.y += .01 * random.randrange(-random_mag, random_mag) * multiplier
if xyz_const[2]:
co.z += .01 * random.randrange(-random_mag, random_mag) * multiplier
class MeshManglerOperator(Operator):
bl_idname = "ba.mesh_mangler"
bl_label = "Mangle Mesh"
bl_description = ("Push vertices on the selected object around in random\n"
"directions to create a crumpled look")
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
ob = context.active_object
return ob is not None and ob.type == 'MESH'
def execute(self, context):
mesh = context.active_object.data
bm = bmesh.new()
bm.from_mesh(mesh)
verts = bm.verts
advanced_objects = context.scene.advanced_objects1
randomMag = advanced_objects.mangle_random_magnitude
random.seed(time.time())
if mesh.shape_keys is not None:
self.report({'INFO'},
"Cannot mangle mesh: Shape keys present. Operation Cancelled")
return {'CANCELLED'}
for vert in verts:
xVal = .01 * random.randrange(-randomMag, randomMag)
yVal = .01 * random.randrange(-randomMag, randomMag)
zVal = .01 * random.randrange(-randomMag, randomMag)
vert.co.x = vert.co.x + xVal
vert.co.y = vert.co.y + yVal
vert.co.z = vert.co.z + zVal
del verts
bm.to_mesh(mesh)
mesh.update()
return {'FINISHED'}
class AnimanglerOperator(Operator):
bl_idname = "ba.ani_mangler"
bl_label = "Mangle Shape Key"
bl_description = ("Make a shape key and pushes the verts around on it\n"
"to set up for random pulsating animation")
@classmethod
def poll(cls, context):
ob = context.active_object
return ob is not None and ob.type in ['MESH', 'CURVE']
def execute(self, context):
scn = context.scene.advanced_objects1
mangleName = scn.mangle_name
ob = context.object
shapeKey = ob.shape_key_add(name=mangleName)
verts = shapeKey.data
for vert in verts:
move_coordinate(context, vert.co, is_curve=ob.type == 'CURVE')
return {'FINISHED'}
class CurveManglerOp(Operator):
bl_idname = "ba.curve_mangler"
bl_label = "Mangle Curve"
bl_description = "Mangle a curve to the degree the user specifies"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
ob = context.active_object
return ob is not None and ob.type == "CURVE"
def execute(self, context):
ob = context.active_object
if ob.data.shape_keys is not None:
self.report({'INFO'},
"Cannot mangle curve. Shape keys present. Operation Cancelled")
return {'CANCELLED'}
splines = context.object.data.splines
for spline in splines:
if spline.type == 'BEZIER':
points = spline.bezier_points
elif spline.type in {'POLY', 'NURBS'}:
points = spline.points
for point in points:
move_coordinate(context, point.co, is_curve=True)
return {'FINISHED'}
class MangleToolsPanel(Panel):
bl_label = "Mangle Tools"
bl_space_type = "VIEW_3D"
bl_context = "objectmode"
bl_region_type = "TOOLS"
bl_category = "Create"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
scn = context.scene.advanced_objects1
obj = context.object
if obj and obj.type in ['MESH']:
layout = self.layout
row = layout.row(align=True)
row.prop(scn, "mangle_constraint_vector", toggle=True)
col = layout.column()
col.prop(scn, "mangle_random_magnitude")
col.operator("ba.mesh_mangler")
col.separator()
col.prop(scn, "mangle_name")
col.operator("ba.ani_mangler")
else:
layout = self.layout
layout.label(text="Please select a Mesh Object", icon="INFO")
def register():
bpy.utils.register_class(AnimanglerOperator)
bpy.utils.register_class(MeshManglerOperator)
bpy.utils.register_class(CurveManglerOp)
bpy.utils.register_class(MangleToolsPanel)
def unregister():
bpy.utils.unregister_class(AnimanglerOperator)
bpy.utils.unregister_class(MeshManglerOperator)
bpy.utils.unregister_class(MangleToolsPanel)
bpy.utils.unregister_class(CurveManglerOp)
if __name__ == "__main__":
register()

View File

@ -1,145 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Constellation",
"author": "Oscurart",
"blender": (2, 67, 0),
"location": "3D View > Toolshelf > Create > Constellation",
"description": "Create a new Mesh From Selected",
"warning": "",
"wiki_url": "",
"category": "Add Mesh"}
# Note the setting is moved to __init__ search for
# the adv_obj and advanced_objects patterns
import bpy
from bpy.props import FloatProperty
from math import sqrt
from bpy.types import (
Operator,
Panel,
)
def VertDis(a, b):
dst = sqrt(pow(a.co.x - b.co.x, 2) +
pow(a.co.y - b.co.y, 2) +
pow(a.co.z - b.co.z, 2))
return(dst)
def OscConstellation(limit):
actobj = bpy.context.object
vertlist = []
edgelist = []
edgei = 0
for ind, verta in enumerate(actobj.data.vertices[:]):
for vertb in actobj.data.vertices[ind:]:
if VertDis(verta, vertb) <= limit:
vertlist.append(verta.co[:])
vertlist.append(vertb.co[:])
edgelist.append((edgei, edgei + 1))
edgei += 2
mesh = bpy.data.meshes.new("rsdata")
obj = bpy.data.objects.new("rsObject", mesh)
bpy.context.collection.objects.link(obj)
mesh.from_pydata(vertlist, edgelist, [])
class Oscurart_Constellation(Operator):
bl_idname = "mesh.constellation"
bl_label = "Constellation"
bl_description = ("Create a Constellation Mesh - Cloud of Vertices\n"
"Note: can produce a lot of geometry\n"
"Needs an existing Active Mesh Object")
bl_options = {'REGISTER', 'UNDO'}
limit: FloatProperty(
name="Threshold",
description="Edges will be created only if the distance\n"
"between vertices is smaller than this value",
default=2,
min=0
)
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == "MESH")
def invoke(self, context, event):
adv_obj = context.scene.advanced_objects1
self.limit = adv_obj.constellation_limit
return self.execute(context)
def draw(self, context):
layout = self.layout
layout.prop(self, "limit")
def execute(self, context):
try:
OscConstellation(self.limit)
except Exception as e:
print("\n[Add Advanced Objects]\nOperator: mesh.constellation\n{}".format(e))
self.report({"WARNING"},
"Constellation Operation could not be Completed (See Console for more Info)")
return {"CANCELLED"}
return {'FINISHED'}
class Constellation_Operator_Panel(Panel):
bl_label = "Constellation"
bl_region_type = "TOOLS"
bl_space_type = "VIEW_3D"
bl_options = {'DEFAULT_CLOSED'}
bl_context = "objectmode"
bl_category = "Create"
def draw(self, context):
layout = self.layout
adv_obj = context.scene.advanced_objects1
box = layout.box()
col = box.column(align=True)
col.label(text="Constellation:")
col.operator("mesh.constellation", text="Cross Section")
col.prop(adv_obj, "constellation_limit")
# Register
def register():
bpy.utils.register_class(Oscurart_Constellation)
bpy.utils.register_class(Constellation_Operator_Panel)
def unregister():
bpy.utils.unregister_class(Oscurart_Constellation)
bpy.utils.unregister_class(Constellation_Operator_Panel)
if __name__ == "__main__":
register()

View File

@ -1,348 +0,0 @@
# gpl: authors Liero, Atom
bl_info = {
"name": "Unfold transition",
"author": "Liero, Atom",
"location": "3D View > Toolshelf > Create > Unfold Transition",
"description": "Simple unfold transition / animation, will "
"separate faces and set up an armature",
"category": "Animation"}
# Note the properties are moved to __init__
# search for patterns advanced_objects, adv_obj
import bpy
from bpy.types import (
Operator,
Panel,
)
from random import (
randint,
uniform,
)
from mathutils import Vector
from mathutils.geometry import intersect_point_line
class Set_Up_Fold(Operator):
bl_idname = "object.set_up_fold"
bl_label = "Set Up Unfold"
bl_description = ("Set up Faces and Bones for animation\n"
"Needs an existing Active Mesh Object")
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == "MESH")
def execute(self, context):
bpy.ops.object.mode_set()
scn = bpy.context.scene
adv_obj = scn.advanced_objects1
obj = bpy.context.object
dat = obj.data
fac = dat.polygons
ver = dat.vertices
# try to cleanup traces of previous actions
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.remove_doubles(threshold=0.0001, use_unselected=True)
bpy.ops.object.mode_set()
old_vg = [vg for vg in obj.vertex_groups if vg.name.startswith("bone.")]
for vg in old_vg:
obj.vertex_groups.remove(vg)
if "UnFold" in obj.modifiers:
arm = obj.modifiers["UnFold"].object
rig = arm.data
try:
scn.objects.unlink(arm)
bpy.data.objects.remove(arm)
bpy.data.armatures.remove(rig)
except:
pass
obj.modifiers.remove(obj.modifiers["UnFold"])
# try to obtain the face sequence from the vertex weights
if adv_obj.unfold_modo == "weight":
if len(obj.vertex_groups):
i = obj.vertex_groups.active.index
W = []
for f in fac:
v_data = []
for v in f.vertices:
try:
w = ver[v].groups[i].weight
v_data.append((w, v))
except:
v_data.append((0, v))
v_data.sort(reverse=True)
v1 = ver[v_data[0][1]].co
v2 = ver[v_data[1][1]].co
cen = Vector(f.center)
its = intersect_point_line(cen, v2, v1)
head = v2.lerp(v1, its[1])
peso = sum([x[0] for x in v_data])
W.append((peso, f.index, cen, head))
W.sort(reverse=True)
S = [x[1:] for x in W]
else:
self.report({"INFO"}, "First paint a Weight Map for this object")
return {"FINISHED"}
# separate the faces and sort them
bpy.ops.object.mode_set(mode="EDIT")
bpy.ops.mesh.select_all(action="SELECT")
bpy.ops.mesh.edge_split()
bpy.ops.mesh.select_all(action="SELECT")
if adv_obj.unfold_modo == "cursor":
bpy.context.tool_settings.mesh_select_mode = [True, True, True]
bpy.ops.mesh.sort_elements(
type="CURSOR_DISTANCE", elements={"VERT", "EDGE", "FACE"}
)
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
bpy.ops.object.mode_set()
# Get sequence of faces and edges from the face / vertex indices
if adv_obj.unfold_modo != "weight":
S = []
for f in fac:
E = list(f.edge_keys)
E.sort()
v1 = ver[E[0][0]].co
v2 = ver[E[0][1]].co
cen = Vector(f.center)
its = intersect_point_line(cen, v2, v1)
head = v2.lerp(v1, its[1])
S.append((f.index, f.center, head))
# create the armature and the modifier
arm = bpy.data.armatures.new("arm")
rig = bpy.data.objects.new("rig_" + obj.name, arm)
# store the name for checking the right rig
adv_obj.unfold_arm_name = rig.name
rig.matrix_world = obj.matrix_world
scn.objects.link(rig)
scn.objects.active = rig
bpy.ops.object.mode_set(mode="EDIT")
arm.display_type = "WIRE"
rig.show_in_front = True
mod = obj.modifiers.new("UnFold", "ARMATURE")
mod.show_in_editmode = True
mod.object = rig
# create bones and vertex groups
root = arm.edit_bones.new("bone.000")
root.tail = (0, 0, 0)
root.head = (0, 0, 1)
root.select = True
vis = [False, True] + [False] * 30
for fb in S:
f = fac[fb[0]]
b = arm.edit_bones.new("bone.000")
if adv_obj.unfold_flip:
b.tail, b.head = fb[2], fb[1]
else:
b.tail, b.head = fb[1], fb[2]
b.align_roll(f.normal)
b.select_set(False)
b.layers = vis
b.parent = root
vg = obj.vertex_groups.new(name=b.name)
vg.add(f.vertices, 1, "ADD")
bpy.ops.object.mode_set()
if adv_obj.unfold_modo == "weight":
obj.vertex_groups.active_index = 0
scn.objects.active = rig
obj.select_set(False)
return {"FINISHED"}
class Animate_Fold(Operator):
bl_idname = "object.animate_fold"
bl_label = "Animate Unfold"
bl_description = ("Animate bones to simulate unfold. Starts on current frame\n"
"Needs an existing Active Armature Object created in the previous step")
bl_options = {"REGISTER", "UNDO"}
is_not_undo = False
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == "ARMATURE" and obj.is_visible(bpy.context.scene))
def draw(self, context):
layout = self.layout
adv_obj = context.scene.advanced_objects1
if self.is_not_undo is True:
layout.label(text="Warning:", icon="INFO")
layout.label(text="The generated Armature was not selected or it was renamed")
layout.label(text="The animation can fail if it is not generated by the previous step")
layout.separator()
layout.label(text="Expected Armature name:", icon="BONE_DATA")
layout.label(text=str(adv_obj.unfold_arm_name), icon="TRIA_RIGHT")
layout.label(text="To Continue press OK, to Cancel click Outside the Pop-up")
layout.separator()
else:
return
def invoke(self, context, event):
obj = bpy.context.object
scn = bpy.context.scene
adv_obj = scn.advanced_objects1
if obj.name != adv_obj.unfold_arm_name:
self.is_not_undo = True
return context.window_manager.invoke_props_dialog(self, width=400)
else:
return self.execute(context)
def execute(self, context):
obj = bpy.context.object
scn = bpy.context.scene
adv_obj = scn.advanced_objects1
fra = scn.frame_current
if obj.name != adv_obj.unfold_arm_name:
self.report({"INFO"},
"The generated rig was not selected or renamed. The animation can fail")
# clear the animation and get the list of bones
if obj.animation_data:
obj.animation_data_clear()
bpy.ops.object.mode_set(mode="POSE")
bones = obj.pose.bones[0].children_recursive
if adv_obj.unfold_flip:
rot = -3.141592
else:
rot = adv_obj.unfold_rot_max / 57.3
extra = adv_obj.unfold_rot_time * adv_obj.unfold_bounce
ruido = max(adv_obj.unfold_rot_time + extra,
adv_obj.unfold_sca_time) + adv_obj.unfold_fold_noise
len_bones = len(bones) if len(bones) != 0 else 1 # possible division by zero
vel = (adv_obj.unfold_fold_duration - ruido) / len_bones
# introduce scale and rotation keyframes
for a, b in enumerate(bones):
t = fra + a * vel + randint(0, adv_obj.unfold_fold_noise)
if adv_obj.unfold_flip:
b.scale = (1, 1, 1)
elif adv_obj.unfold_from_point:
b.scale = (0, 0, 0)
else:
b.scale = (1, 0, 0)
if not adv_obj.unfold_flip:
b.keyframe_insert("scale", frame=t)
b.scale = (1, 1, 1)
b.keyframe_insert("scale", frame=t + adv_obj.unfold_sca_time)
if adv_obj.unfold_rot_max:
b.rotation_mode = "XYZ"
if adv_obj.unfold_wiggle_rot:
euler = (uniform(-rot, rot), uniform(-rot, rot), uniform(-rot, rot))
else:
euler = (rot, 0, 0)
b.rotation_euler = euler
b.keyframe_insert("rotation_euler", frame=t)
if adv_obj.unfold_bounce:
val = adv_obj.unfold_bounce * -.10
b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2])
b.keyframe_insert(
"rotation_euler", frame=t + adv_obj.unfold_rot_time + .25 * extra
)
val = adv_obj.unfold_bounce * .05
b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2])
b.keyframe_insert(
"rotation_euler", frame=t + adv_obj.unfold_rot_time + .50 * extra
)
val = adv_obj.unfold_bounce * -.025
b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2])
b.keyframe_insert(
"rotation_euler", frame=t + adv_obj.unfold_rot_time + .75 * extra
)
b.rotation_euler = (0, 0, 0)
b.keyframe_insert(
"rotation_euler", frame=t + adv_obj.unfold_rot_time + extra
)
self.is_not_undo = False
return {"FINISHED"}
class PanelFOLD(Panel):
bl_label = "Unfold Transition"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_category = "Create"
bl_context = "objectmode"
bl_options = {"DEFAULT_CLOSED"}
def draw(self, context):
layout = self.layout
adv_obj = context.scene.advanced_objects1
box = layout.box()
col = box.column()
col.operator("object.set_up_fold", text="1. Set Up Unfold")
col.separator()
col.label(text="Unfold Mode:")
col.prop(adv_obj, "unfold_modo")
col.prop(adv_obj, "unfold_flip")
box = layout.box()
col = box.column(align=True)
col.operator("object.animate_fold", text="2. Animate Unfold")
col.separator()
col.prop(adv_obj, "unfold_fold_duration")
col.prop(adv_obj, "unfold_sca_time")
col.prop(adv_obj, "unfold_rot_time")
col.prop(adv_obj, "unfold_rot_max")
row = col.row(align=True)
row.prop(adv_obj, "unfold_fold_noise")
row.prop(adv_obj, "unfold_bounce")
row = col.row(align=True)
row.prop(adv_obj, "unfold_wiggle_rot")
if not adv_obj.unfold_flip:
row.prop(adv_obj, "unfold_from_point")
classes = (
Set_Up_Fold,
Animate_Fold,
PanelFOLD,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
if __name__ == "__main__":
register()