Initial commit add advanced objects: T51110 T50357

This commit is contained in:
Brendon Murphy 2017-04-15 13:49:21 +10:00
parent 5f883054ce
commit bd42467c77
26 changed files with 9324 additions and 0 deletions

View File

@ -0,0 +1,252 @@
# ##### 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 #
# Dannyboy, Mano-Wii, Kursad Karatas, teldredge
bl_info = {
"name": "Add Advanced Objects",
"author": "Meta Androcto,",
"version": (0, 1, 1),
"blender": (2, 78, 0),
"location": "View3D > Add ",
"description": "Add Object & Camera extras",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
"/Py/Scripts",
"tracker_url": "",
"category": "Object"}
if "bpy" in locals():
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(drop_to_ground)
importlib.reload(circle_array)
importlib.reload(unfold_transition)
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(object_mangle_tools)
importlib.reload(arrange_on_curve)
importlib.reload(object_laplace_lightning)
importlib.reload(mesh_easylattice)
importlib.reload(DelaunayVoronoi)
importlib.reload(delaunayVoronoiBlender)
importlib.reload(oscurart_constellation)
importlib.reload(oscurart_chain_maker)
else:
from . import (
add_light_template,
scene_objects_bi,
scene_objects_cycles,
scene_texture_render,
trilighting,
pixelate_3d,
object_add_chain,
oscurart_chain_maker,
drop_to_ground,
circle_array,
unfold_transition,
copy2,
make_struts,
random_box_structure,
cubester,
rope_alpha,
add_mesh_aggregate,
object_mangle_tools,
arrange_on_curve,
object_laplace_lightning,
mesh_easylattice
)
from .delaunay_voronoi import (
DelaunayVoronoi,
delaunayVoronoiBlender,
oscurart_constellation
)
import bpy
from bpy.types import (
Menu,
AddonPreferences,
)
class INFO_MT_scene_elements_add(Menu):
# Define the "scenes" menu
bl_idname = "INFO_MT_scene_elements"
bl_label = "Test scenes"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("bi.add_scene",
text="Scene_Objects_BI")
layout.operator("objects_cycles.add_scene",
text="Scene_Objects_Cycles")
layout.operator("objects_texture.add_scene",
text="Scene_Textures_Cycles")
class INFO_MT_mesh_lamps_add(Menu):
# Define the "lights" menu
bl_idname = "INFO_MT_scene_lamps"
bl_label = "Lighting Sets"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("object.add_light_template",
text="Add Light Template")
layout.operator("object.trilighting",
text="Add Tri Lighting")
class INFO_MT_mesh_chain_add(Menu):
# Define the "Chains" menu
bl_idname = "INFO_MT_mesh_chain"
bl_label = "Chains"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.primitive_chain_add", icon="LINKED")
layout.operator("mesh.primitive_oscurart_chain_add", icon="LINKED")
class INFO_MT_array_mods_add(Menu):
# Define the "array" menu
bl_idname = "INFO_MT_array_mods"
bl_label = "Array Mods"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
self.layout.menu("INFO_MT_mesh_chain", icon="LINKED")
layout.operator("objects.circle_array_operator",
text="Circle Array", icon='MOD_ARRAY')
layout.operator("object.agregate_mesh",
text="Aggregate Mesh", icon='MOD_ARRAY')
obj = context.object
if obj.type in ['MESH',]:
layout.operator("mesh.copy2",
text="Copy To Vert/Edge", icon='MOD_ARRAY')
class INFO_MT_quick_blocks_add(Menu):
# Define the "Blocks" menu
bl_idname = "INFO_MT_quick_tools"
bl_label = "Block Tools"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator('object.pixelate', icon='MESH_GRID')
obj = context.object
if obj.type in ['MESH',]:
layout.operator("mesh.generate_struts",
text="Struts", icon='GRID')
layout.operator("object.easy_lattice",
text="Easy Lattice", icon='MOD_LATTICE')
layout.operator("object.make_structure",
text="Random Boxes", icon='SEQ_SEQUENCER')
class INFO_MT_Physics_tools_add(Menu):
# Define the "mesh objects" menu
bl_idname = "INFO_MT_physics_tools"
bl_label = "Physics Tools"
def draw(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("object.drop_on_active",
text="Drop To Ground")
layout.operator("ball.rope",
text="Wrecking Ball", icon='PHYSICS')
layout.operator("clot.rope",
text="Cloth Rope", icon='PHYSICS')
# Define "Extras" menu
def menu(self, context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_WIN'
self.layout.separator()
self.layout.menu("INFO_MT_scene_elements", icon="SCENE_DATA")
self.layout.menu("INFO_MT_scene_lamps", icon="LAMP_SPOT")
self.layout.separator()
self.layout.menu("INFO_MT_array_mods", icon="MOD_ARRAY")
self.layout.menu("INFO_MT_quick_tools", icon="MOD_BUILD")
self.layout.menu("INFO_MT_physics_tools", icon="PHYSICS")
# Addons Preferences
class AddonPreferences(AddonPreferences):
bl_idname = __name__
def draw(self, context):
layout = self.layout
layout.label(text="----Add Menu Advanced----")
layout.label(text="Quick Tools:")
layout.label(text="Drop, Pixelate & Wrecking Ball")
layout.label(text="Array Mods:")
layout.label(text="Circle Array, Chains, Vert to Edge, Aggregate")
def register():
object_mangle_tools.register()
arrange_on_curve.register()
bpy.utils.register_module(__name__)
# Add "Extras" menu to the "Add" menu
bpy.types.INFO_MT_add.append(menu)
try:
bpy.types.VIEW3D_MT_AddMenu.append(menu)
except:
pass
def unregister():
object_mangle_tools.unregister()
arrange_on_curve.unregister()
# Remove "Extras" menu from the "Add" menu.
bpy.types.INFO_MT_add.remove(menu)
try:
bpy.types.VIEW3D_MT_AddMenu.remove(menu)
except:
pass
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,136 @@
# gpl: author Rebellion
import bpy
from bpy.types import Operator
from bpy.props import BoolProperty
def add_lamps(self, context):
try:
if self.bKeyLight:
keyLight = bpy.data.lamps.new(name="Key_Light", type="SPOT")
ob = bpy.data.objects.new("Key_Light", keyLight)
constraint = ob.constraints.new(type='COPY_LOCATION')
constraint.use_offset = True
constraint.owner_space = 'LOCAL'
constraint.target = self.camera
constraint = ob.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_X'
constraint.owner_space = 'LOCAL'
bpy.context.scene.objects.link(ob)
ob.rotation_euler[2] = -0.785398
if self.bFillLight:
fillLight = bpy.data.lamps.new(name="Fill_Light", type="SPOT")
ob = bpy.data.objects.new("Fill_Light", fillLight)
constraint = ob.constraints.new(type='COPY_LOCATION')
constraint.use_offset = True
constraint.owner_space = 'LOCAL'
constraint.target = self.camera
constraint = ob.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_X'
constraint.owner_space = 'LOCAL'
bpy.context.scene.objects.link(ob)
ob.rotation_euler[2] = 0.785398
ob.data.energy = 0.3
if self.bBackLight:
backLight = bpy.data.lamps.new(name="Back_Light", type="SPOT")
ob = bpy.data.objects.new("Back_Light", backLight)
constraint = ob.constraints.new(type='COPY_LOCATION')
constraint.use_offset = True
constraint.owner_space = 'LOCAL'
constraint.target = self.camera
constraint = ob.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_X'
constraint.owner_space = 'LOCAL'
bpy.context.scene.objects.link(ob)
ob.rotation_euler[2] = 3.14159
ob.data.energy = 0.2
if self.camera_constraint:
constraint = self.camera.constraints.new(type='TRACK_TO')
constraint.target = self.target
constraint.track_axis = 'TRACK_NEGATIVE_Z'
constraint.up_axis = 'UP_Y'
except Exception as e:
self.report({'WARNING'},
"Some operations could not be performed (See Console for more info)")
print("\n[object.add_light_template]\nError: {}".format(e))
class OBJECT_OT_add_light_template(Operator):
bl_idname = "object.add_light_template"
bl_label = "Add Light Template"
bl_description = "Add Key, Fill & Back Lights"
bl_options = {'REGISTER', 'UNDO'}
camera = None
target = None
bKeyLight = BoolProperty(
name="Key Light",
default=True
)
bFillLight = BoolProperty(
name="Fill Light",
default=True
)
bBackLight = BoolProperty(
name="Back Light",
default=True
)
camera_constraint = BoolProperty(
name="Camera Constraint",
default=False
)
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
objects = context.selected_objects
if len(objects) == 2:
for ob in objects:
if ob.type == 'CAMERA':
self.camera = ob
else:
self.target = ob
elif len(objects) == 1:
if objects[0].type == 'CAMERA':
self.camera = objects[0]
bpy.ops.object.empty_add()
self.target = context.active_object
else:
self.camera = context.scene.camera
self.target = context.active_object
elif len(objects) == 0:
bpy.ops.object.empty_add()
self.target = context.active_object
self.camera = context.scene.camera
add_lamps(self, context)
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

@ -0,0 +1,318 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Simple aggregate of particles / meshes
# Copy the selected objects on the active object
# Based on the position of the cursor and a defined volume
# Allows to control growth by using a Build modifier
bl_info = {
"name": "Aggregate Mesh",
"author": "liero",
"version": (0, 0, 5),
"blender": (2, 7, 0),
"location": "View3D > Tool Shelf",
"description": "Adds geometry to a mesh like in DLA aggregators",
"category": "Object"}
import bpy
import bmesh
from random import (
choice,
gauss,
seed,
)
from mathutils import Matrix
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
)
from bpy.types import Operator
def use_random_seed(self):
seed(self.rSeed)
return
def rg(n):
return (round(gauss(0, n), 2))
def remover(sel=False):
bpy.ops.object.editmode_toggle()
if sel:
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.object.mode_set()
class OBJECT_OT_agregate_mesh(Operator):
bl_idname = "object.agregate_mesh"
bl_label = "Aggregate"
bl_description = ("Adds geometry to a mesh like in DLA aggregators\n"
"Needs at least two selected Mesh objects")
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
updateMeNow = BoolProperty(
name="Update",
description="Update",
default=True
)
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"
)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
col.prop(self, "updateMeNow", toggle=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 = layout.column(align=True)
col.prop(self, "nor")
col.prop(self, "cent")
col.prop(self, "track")
col.prop(self, "anim")
col.prop(self, 'numP')
@classmethod
def poll(cls, context):
return(len(bpy.context.selected_objects) > 1 and bpy.context.object.type == 'MESH')
def invoke(self, context, event):
self.updateMeNow = True
return self.execute(context)
def execute(self, context):
if not self.updateMeNow:
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'}, "Apply transformations to Active Object first!")
return{'FINISHED'}
par = [o for o in bpy.context.selected_objects if o.type == 'MESH' and o != obj]
if not par:
return{'FINISHED'}
bpy.ops.object.mode_set()
bpy.ops.object.select_all(action='DESELECT')
obj.select = True
msv = []
for i in range(len(obj.modifiers)):
msv.append(obj.modifiers[i].show_viewport)
obj.modifiers[i].show_viewport = False
cur = scn.cursor_location
for i in range(self.numP):
mes = choice(par).data
newobj = bpy.data.objects.new('nuevo', mes)
scn.objects.link(newobj)
origen = (rg(self.volX) + cur[0], rg(self.volY) + cur[1], rg(self.volZ) + cur[2])
cpom = obj.closest_point_on_mesh(origen)
if self.cent:
bm = bmesh.new()
bm.from_mesh(obj.data)
if hasattr(bm.verts, "ensure_lookup_table"):
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
newobj.location = bm.faces[cpom[3]].calc_center_median()
bm.free()
else:
newobj.location = cpom[1]
if self.nor:
newobj.rotation_mode = 'QUATERNION'
newobj.rotation_quaternion = cpom[1].to_track_quat('Z', 'Y')
newobj.rotation_mode = 'XYZ'
newobj.rotation_euler[0] += rg(self.rotX)
newobj.rotation_euler[1] += rg(self.rotY)
newobj.rotation_euler[2] += rg(self.rotZ)
else:
newobj.rotation_euler = (rg(self.rotX), rg(self.rotY), rg(self.rotZ))
newobj.scale = [self.baseSca + self.baseSca * rg(self.varSca)] * 3
if self.anim:
newobj.select = True
bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', obdata=True)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bme = bmesh.new()
bme.from_mesh(obj.data)
tmp = bmesh.new()
tmp.from_mesh(newobj.data)
for f in tmp.faces:
# z = len(bme.verts)
for v in f.verts:
bme.verts.new(list(v.co))
bme.faces.new(bme.verts[-len(f.verts):])
bme.to_mesh(obj.data)
remover(True)
newobj.data.user_clear()
bpy.data.meshes.remove(newobj.data)
else:
scn.objects.active = obj
newobj.select = True
bpy.ops.object.join()
if self.track:
cur = scn.cursor_location = cpom[1]
for i in range(len(msv)):
obj.modifiers[i].show_viewport = msv[i]
for o in par:
o.select = True
obj.select = True
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

@ -0,0 +1,362 @@
# gpl author: Mano-Wii
bl_info = {
"name": "Arrange on Curve",
"author": "Mano-Wii",
"version": (6, 3, 0),
"blender": (2, 7, 7),
"location": "View3D > TOOLS",
"description": "Arrange objects along a curve",
"warning": "Select curve",
"wiki_url": "",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
"category": "3D View"
}
import bpy
import mathutils
from bpy.types import (
Operator,
Panel,
)
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
IntProperty,
StringProperty,
)
FLT_MIN = 0.004
class PanelDupliCurve(Panel):
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
bl_context = "objectmode"
bl_category = "Create"
bl_label = "Duplicate on curve"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
return context.object and context.mode == 'OBJECT' and context.object.type == 'CURVE'
def draw(self, context):
layout = self.layout
layout.prop(context.scene, "use_selected")
if not context.scene.use_selected:
layout.prop(context.scene, "select_type", expand=True)
if context.scene.select_type == 'O':
layout.column(align=True).prop_search(
context.scene, "objeto_arranjar",
bpy.data, "objects"
)
elif context.scene.select_type == 'G':
layout.column(align=True).prop_search(
context.scene, "objeto_arranjar",
bpy.data, "groups"
)
if context.object.type == 'CURVE':
layout.operator("object.arranjar_numa_curva", text="Arrange Objects")
class DupliCurve(Operator):
bl_idname = "object.arranjar_numa_curva"
bl_label = "Arrange Objects"
bl_options = {'REGISTER', 'UNDO'}
use_distance = EnumProperty(
items=[
("D", "Distance", "Objects are arranged depending on the distance", 0),
("Q", "Quantity", "Objects are arranged depending on the quantity", 1),
("R", "Range", "Objects are arranged uniformly between the corners", 2)
]
)
distance = FloatProperty(
name="Distance",
description="Distancia entre objetos",
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(
default=0.0,
name="X",
unit='ROTATION'
)
Pitch = FloatProperty(
default=0.0,
name="Y",
unit='ROTATION'
)
Roll = FloatProperty(
default=0.0,
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(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)
if context.scene.use_selected:
G_Objeto = context.selected_objects
G_Objeto.remove(curve)
if not G_Objeto:
return {'CANCELLED'}
elif context.scene.select_type == 'O':
G_Objeto = bpy.data.objects[context.scene.objeto_arranjar],
elif context.scene.select_type == 'G':
G_Objeto = bpy.data.groups[context.scene.objeto_arranjar].objects
yawMatrix = mathutils.Matrix.Rotation(self.Yaw, 4, 'X')
pitchMatrix = mathutils.Matrix.Rotation(self.Pitch, 4, 'Y')
rollMatrix = mathutils.Matrix.Rotation(self.Roll, 4, 'Z')
max_angle = self.max_angle # is this used?
if self.use_distance == "D":
dist = self.distance
for sp_points in Gpoints:
dx = 0.0 # Length of initial calculation of section
last_point = sp_points[0]
j = 0
for point in sp_points[1:]:
vetorx = point - last_point # Vector spline section
quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z') # Tracking the selected objects
quat = quat.to_matrix().to_4x4()
v_len = vetorx.length
if v_len > 0.0:
dx += v_len # Defined length calculation equal total length of the spline section
v_norm = vetorx / v_len
while dx > dist:
object = G_Objeto[j % len(G_Objeto)]
j += 1
dx -= dist # Calculating the remaining length of the section
obj = object.copy()
context.scene.objects.link(obj)
obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
# Placing in the correct position
obj.matrix_world.translation = point - v_norm * dx
obj.scale *= self.scale
last_point = point
elif self.use_distance == "Q":
object_qt = self.object_qt + 1
for i, sp_points in enumerate(Gpoints):
dx = 0.0 # Length of initial calculation of section
dist = lengs[i] / object_qt
last_point = sp_points[0]
j = 0
for point in sp_points[1:]:
vetorx = point - last_point # Vector spline section
# Tracking the selected objects
quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
quat = quat.to_matrix().to_4x4()
v_len = vetorx.length
if v_len > 0.0:
# Defined length calculation equal total length of the spline section
dx += v_len
v_norm = vetorx / v_len
while dx > dist:
object = G_Objeto[j % len(G_Objeto)]
j += 1
dx -= dist # Calculating the remaining length of the section
obj = object.copy()
context.scene.objects.link(obj)
obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
# Placing in the correct position
obj.matrix_world.translation = point - v_norm * dx
obj.scale *= self.scale
last_point = point
else:
dist = self.distance
offset2 = 2 * self.offset
for i, sp_points in enumerate(Gpoints):
leng = lengs[i] - offset2
rest = leng % dist
offset = offset2 + rest
leng -= rest
offset /= 2
last_point = sp_points[0]
dx = dist - offset # Length of initial calculation of section
j = 0
for point in sp_points[1:]:
vetorx = point - last_point # Vector spline section
# Tracking the selected objects
quat = mathutils.Vector.to_track_quat(vetorx, 'X', 'Z')
quat = quat.to_matrix().to_4x4()
v_len = vetorx.length
if v_len > 0.0:
dx += v_len
v_norm = vetorx / v_len
while dx >= dist and leng >= 0.0:
leng -= dist
dx -= dist # Calculating the remaining length of the section
object = G_Objeto[j % len(G_Objeto)]
j += 1
obj = object.copy()
context.scene.objects.link(obj)
obj.matrix_world = quat * yawMatrix * pitchMatrix * rollMatrix
# Placing in the correct position
obj.matrix_world.translation = point - v_norm * dx
obj.scale *= self.scale
last_point = point
return {"FINISHED"}
def register():
bpy.utils.register_class(PanelDupliCurve)
bpy.utils.register_class(DupliCurve)
bpy.types.Scene.use_selected = BoolProperty(
name='Use Selected',
description='Use the selected objects to duplicate',
default=True,
)
bpy.types.Scene.objeto_arranjar = StringProperty(
name=""
)
bpy.types.Scene.select_type = EnumProperty(
name="Type",
description="Select object or group",
items=[
('O', "OBJECT", "make duplicates of a specific object"),
('G', "GROUP", "make duplicates of the objects in a group"),
],
default='O',
)
def unregister():
bpy.utils.unregister_class(PanelDupliCurve)
bpy.utils.unregister_class(DupliCurve)
del bpy.types.Scene.objeto_arranjar
del bpy.types.Scene.use_selected
del bpy.types.Scene.select_type
if __name__ == "__main__":
register()

View File

@ -0,0 +1,143 @@
# gpl author: Antonis Karvelas
# -*- coding: utf-8 -*-
bl_info = {
"name": "Circle Array",
"author": "Antonis Karvelas",
"version": (1, 0),
"blender": (2, 6, 7),
"location": "View3D > Object > Circle_Array",
"description": "Uses an existing array and creates an empty, "
"rotates it properly and makes a Circle Array",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Mesh"
}
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 & Surface")
@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):
try:
allowed_obj = ['MESH', 'CURVE', 'SURFACE', 'FONT']
if context.active_object.type not in allowed_obj:
self.report(
{"WARNING"},
"Operation Cancelled. The active object is not of "
"Mesh, Curve, Surface or Font type"
)
return {'CANCELLED'}
default_name = self.check_empty_name(context) or "EMPTY_C_Array"
bpy.ops.object.modifier_add(type='ARRAY')
if len(bpy.context.selected_objects) == 2:
list = bpy.context.selected_objects
active = list[0]
active.modifiers[0].use_object_offset = True
active.modifiers[0].use_relative_offset = False
active.select = False
bpy.context.scene.objects.active = list[0]
bpy.ops.view3d.snap_cursor_to_selected()
if active.modifiers[0].offset_object is None:
bpy.ops.object.add(type='EMPTY')
empty_name = bpy.context.active_object
empty_name.name = default_name
active.modifiers[0].offset_object = empty_name
else:
empty_name = active.modifiers[0].offset_object
bpy.context.scene.objects.active = active
num = active.modifiers["Array"].count
rotate_num = 360 / num
active.select = True
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
empty_name.rotation_euler = (0, 0, radians(rotate_num))
empty_name.select = False
active.select = True
bpy.ops.object.origin_set(type="ORIGIN_CURSOR")
return {'FINISHED'}
else:
active = context.active_object
active.modifiers[0].use_object_offset = True
active.modifiers[0].use_relative_offset = False
bpy.ops.view3d.snap_cursor_to_selected()
if active.modifiers[0].offset_object is None:
bpy.ops.object.add(type='EMPTY')
empty_name = bpy.context.active_object
empty_name.name = default_name
active.modifiers[0].offset_object = empty_name
else:
empty_name = active.modifiers[0].offset_object
bpy.context.scene.objects.active = active
num = active.modifiers["Array"].count
rotate_num = 360 / num
active.select = True
bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
empty_name.rotation_euler = (0, 0, radians(rotate_num))
empty_name.select = False
active.select = True
return {'FINISHED'}
except Exception as e:
self.report({'WARNING'},
"Circle Array operator could not be executed (See the console for more info)")
print("\n[objects.circle_array_operator]\nError: {}\n".format(e))
return {'CANCELLED'}
# Register
def circle_array_menu(self, context):
self.layout.operator(Circle_Array.bl_idname, text="Circle_Array")
def register():
bpy.utils.register_class(Circle_Array)
bpy.types.VIEW3D_MT_object.append(circle_array_menu)
def unregister():
bpy.utils.unregister_class(Circle_Array)
bpy.types.VIEW3D_MT_object.remove(circle_array_menu)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,288 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 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),
"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 mathutils import Vector, Matrix
class Copy2(bpy.types.Operator):
bl_idname = "mesh.copy2"
bl_label = "Copy 2"
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', 'Y'), ('Z', 'Z', 'Z')]
if self.priaxes == 'Y':
sec_list = [('X', 'X', 'X'), ('Z', 'Z', 'Z')]
if self.priaxes == 'Z':
sec_list = [('X', 'X', 'X'), ('Y', 'Y', 'Y')]
return sec_list
copytype = bpy.props.EnumProperty(items=(('V', '', 'paste to vertices', 'VERTEXSEL', 0),
('E', '', 'paste to edges', 'EDGESEL', 1),
('F', '', 'paste to faces', 'FACESEL', 2)),
description='where to paste to')
copyfromobject = bpy.props.EnumProperty(items=obj_list_cb, name='Copy from:')
priaxes = bpy.props.EnumProperty(items=(('X', 'X', 'along X'),
('Y', 'Y', 'along Y'),
('Z', 'Z', 'along Z')),
)
edgescale = bpy.props.BoolProperty(name='Scale to fill edge?')
secaxes = bpy.props.EnumProperty(items=sec_axes_list_cb, name='Secondary Axis')
scale = bpy.props.FloatProperty(default=1.0, min=0.0, name='Scale')
def draw(self, context):
layout = self.layout
layout.prop(self, 'copyfromobject')
layout.label("to:")
layout.prop(self, 'copytype', expand=True)
layout.label("primary axis:")
layout.prop(self, 'priaxes', expand=True)
layout.label("secondary axis:")
layout.prop(self, 'secaxes', expand=True)
if self.copytype == 'E':
layout.prop(self, 'edgescale')
if self.edgescale:
layout.prop(self, 'scale')
return
def execute(self, context):
copytoobject = context.active_object.name
axes = self.priaxes + self.secaxes
copy_list = copy_to_from(context.scene,
bpy.data.objects[copytoobject],
bpy.data.objects[self.copyfromobject],
self.copytype,
axes,
self.edgescale,
self.scale)
return {"FINISHED"}
def invoke(self, context, event):
Copy2.obj_list = [(obj.name, obj.name, obj.name) for obj in bpy.data.objects]
return {"FINISHED"}
# end Copy2 class
#---------------------------------------------------------------------------------------
def add_to_menu(self, context):
self.layout.operator(Copy2.bl_idname)
return
#-----------------------------------------------------------------
def copy_to_from(scene, to_obj, from_obj, copymode, axes, edgescale, scale):
if copymode == 'V':
copy_list = vertex_copy(scene, to_obj, from_obj, axes)
if copymode == 'E':
copy_list = edge_copy(scene, to_obj, from_obj, axes, edgescale, scale)
if copymode == 'F':
copy_list = face_copy(scene, to_obj, from_obj, axes)
return copy_list
axes_dict = {'XY': (1, 2, 0),
'XZ': (2, 1, 0),
'YX': (0, 2, 1),
'YZ': (2, 0, 1),
'ZX': (0, 1, 2),
'ZY': (1, 0, 2)}
def copyto(scene, source_obj, pos, xdir, zdir, axes, scale=None):
"""
copy the source_obj to pos, so its primary axis points in zdir and its
secondary axis points in xdir
"""
copy_obj = source_obj.copy()
scene.objects.link(copy_obj)
xdir = xdir.normalized()
zdir = zdir.normalized()
# rotation first
z_axis = zdir
x_axis = xdir
y_axis = z_axis.cross(x_axis)
# use axes_dict to assign the axis as chosen in panel
A, B, C = axes_dict[axes]
rot_mat = Matrix()
rot_mat[A].xyz = x_axis
rot_mat[B].xyz = y_axis
rot_mat[C].xyz = z_axis
rot_mat.transpose()
# rotate object
copy_obj.matrix_world = rot_mat
# move object into position
copy_obj.location = pos
# scale object
if scale != None:
copy_obj.scale = scale
return copy_obj
def vertex_copy(scene, obj, source_obj, axes):
# vertex select mode
sel_verts = []
copy_list = []
for v in obj.data.vertices:
if v.select == True:
sel_verts.append(v)
# make a set for each vertex. The set contains all the connected vertices
# use sets so the list is unique
vert_con = [set() for i in range(len(obj.data.vertices))]
for e in obj.data.edges:
vert_con[e.vertices[0]].add(e.vertices[1])
vert_con[e.vertices[1]].add(e.vertices[0])
for v in sel_verts:
pos = v.co * obj.matrix_world.transposed()
xco = obj.data.vertices[list(vert_con[v.index])[0]].co * obj.matrix_world.transposed()
zdir = (v.co + v.normal) * obj.matrix_world.transposed() - pos
zdir = zdir.normalized()
edir = pos - xco
# edir is nor perpendicular to z dir
# want xdir to be projection of edir onto plane through pos with direction zdir
xdir = edir - edir.dot(zdir) * zdir
xdir = -xdir.normalized()
copy = copyto(scene, source_obj, pos, xdir, zdir, axes)
copy_list.append(copy)
# select all copied objects
for copy in copy_list:
copy.select = True
obj.select = False
return copy_list
def edge_copy(scene, obj, source_obj, axes, es, scale):
# edge select mode
sel_edges = []
copy_list = []
for e in obj.data.edges:
if e.select == True:
sel_edges.append(e)
for e in sel_edges:
# pos is average of two edge vertexs
v0 = obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed()
v1 = obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed()
pos = (v0 + v1) / 2
# xdir is along edge
xdir = v0 - v1
xlen = xdir.magnitude
xdir = xdir.normalized()
# project each edge vertex normal onto plane normal to xdir
vn0 = (obj.data.vertices[e.vertices[0]].co * obj.matrix_world.transposed()
+ obj.data.vertices[e.vertices[0]].normal) - v0
vn1 = (obj.data.vertices[e.vertices[1]].co * obj.matrix_world.transposed()
+ obj.data.vertices[e.vertices[1]].normal) - v1
vn0p = vn0 - vn0.dot(xdir) * xdir
vn1p = vn1 - vn1.dot(xdir) * xdir
# the mean of the two projected normals is the zdir
zdir = vn0p + vn1p
zdir = zdir.normalized()
escale = None
if es:
escale = Vector([1.0, 1.0, 1.0])
i = list('XYZ').index(axes[1])
escale[i] = scale * xlen / source_obj.dimensions[i]
copy = copyto(scene, source_obj, pos, xdir, zdir, axes, scale=escale)
copy_list.append(copy)
# select all copied objects
for copy in copy_list:
copy.select = True
obj.select = False
return copy_list
def face_copy(scene, obj, source_obj, axes):
# face select mode
sel_faces = []
copy_list = []
for f in obj.data.polygons:
if f.select == True:
sel_faces.append(f)
for f in sel_faces:
fco = f.center * obj.matrix_world.transposed()
# get first vertex corner of transformed object
vco = obj.data.vertices[f.vertices[0]].co * obj.matrix_world.transposed()
# get face normal of transformed object
fn = (f.center + f.normal) * obj.matrix_world.transposed() - fco
fn = fn.normalized()
copy = copyto(scene, source_obj, fco, vco - fco, fn, axes)
copy_list.append(copy)
# select all copied objects
for copy in copy_list:
copy.select = True
obj.select = False
return copy_list
#-------------------------------------------------------------------
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_object.append(add_to_menu)
def unregister():
bpy.types.VIEW3D_MT_object.remove(add_to_menu)
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
# -*- 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, 7, 0),
"location": "View3D > Tools > GIS",
"warning": "",
"wiki_url": "https://github.com/domlysz/BlenderGIS/wiki",
"tracker_url": "",
"category": ""
}
if "bpy" in locals():
import importlib
importlib.reload(oscurart_constellation)
else:
from . import oscurart_constellation
import bpy
from .delaunayVoronoiBlender import ToolsPanelDelaunay
# Register
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)

View File

@ -0,0 +1,234 @@
# -*- coding:utf-8 -*-
import bpy
from .DelaunayVoronoi import (
computeVoronoiDiagram,
computeDelaunayTriangulation,
)
from bpy.types import (
Operator,
Panel,
)
from bpy.props import EnumProperty
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
layout.label('Constellation')
self.layout.operator("delaunay.triangulation")
self.layout.operator("voronoi.tesselation")
layout.label('Constellation')
layout.operator("mesh.constellation", text="Cross Section")
class OBJECT_OT_TriangulateButton(Operator):
bl_idname = "delaunay.triangulation"
bl_label = "Triangulation"
bl_description = "Terrain points cloud Delaunay triangulation in 2.5D"
bl_options = {"UNDO"}
def execute(self, context):
# Get selected obj
objs = bpy.context.selected_objects
if len(objs) == 0 or len(objs) > 1:
self.report({'INFO'}, "Selection is empty or too much object selected")
return {'FINISHED'}
obj = objs[0]
if obj.type != 'MESH':
self.report({'INFO'}, "Selection isn't a mesh")
return {'FINISHED'}
# Get points coodinates
r = obj.rotation_euler
s = obj.scale
mesh = obj.data
vertsPts = [vertex.co for vertex in mesh.vertices]
# Remove duplicate
verts = [[vert.x, vert.y, vert.z] for vert in vertsPts]
nDupli, nZcolinear = unique(verts)
nVerts = len(verts)
print(str(nDupli) + " duplicates points ignored")
print(str(nZcolinear) + " z colinear points excluded")
if nVerts < 3:
self.report({'ERROR'}, "Not enough points")
return {'FINISHED'}
# 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
print("Triangulate " + str(nVerts) + " points...")
vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts]
triangles = computeDelaunayTriangulation(vertsPts)
# reverse point order --> if all triangles are specified anticlockwise then all faces up
triangles = [tuple(reversed(tri)) for tri in triangles]
print(str(len(triangles)) + " triangles")
# Create new mesh structure
print("Create mesh...")
tinMesh = bpy.data.meshes.new("TIN") # create a new mesh
tinMesh.from_pydata(verts, [], triangles) # Fill the mesh with triangles
tinMesh.update(calc_edges=True) # Update mesh with new data
# Create an object with that mesh
tinObj = bpy.data.objects.new("TIN", tinMesh)
# Place object
tinObj.location = obj.location.copy()
tinObj.rotation_euler = r
tinObj.scale = s
# Update scene
bpy.context.scene.objects.link(tinObj) # Link object to scene
bpy.context.scene.objects.active = tinObj
tinObj.select = True
obj.select = False
# Report
self.report({'INFO'}, "Mesh created (" + str(len(triangles)) + " triangles)")
return {'FINISHED'}
class OBJECT_OT_VoronoiButton(Operator):
bl_idname = "voronoi.tesselation"
bl_label = "Diagram"
bl_description = "Points cloud Voronoi diagram in 2D"
bl_options = {"REGISTER", "UNDO"}
meshType = EnumProperty(
items=[("Edges", "Edges", ""), ("Faces", "Faces", "")],
name="Mesh type",
description=""
)
def execute(self, context):
# Get selected obj
objs = bpy.context.selected_objects
if len(objs) == 0 or len(objs) > 1:
self.report({'INFO'}, "Selection is empty or too much object selected")
return {'FINISHED'}
obj = objs[0]
if obj.type != 'MESH':
self.report({'INFO'}, "Selection isn't a mesh")
return {'FINISHED'}
# Get points coodinates
r = obj.rotation_euler
s = obj.scale
mesh = obj.data
vertsPts = [vertex.co for vertex in mesh.vertices]
# Remove duplicate
verts = [[vert.x, vert.y, vert.z] for vert in vertsPts]
nDupli, nZcolinear = unique(verts)
nVerts = len(verts)
print(str(nDupli) + " duplicates points ignored")
print(str(nZcolinear) + " z colinear points excluded")
if nVerts < 3:
self.report({'ERROR'}, "Not enough points")
return {'FINISHED'}
# 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'}
# Create diagram
print("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.scene.objects.link(voronoiObj) # Link object to scene
bpy.context.scene.objects.active = voronoiObj
voronoiObj.select = True
obj.select = 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'}

View File

@ -0,0 +1,106 @@
# ##### 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": "Mesh: Constellation",
"author": "Oscurart",
"version": (1, 0),
"blender": (2, 67, 0),
"location": "Add > Mesh > Constellation",
"description": "Adds a new Mesh From Selected",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Add Mesh"}
import bpy
from bpy.types import Operator
from bpy.props import FloatProperty
from math import sqrt
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")
object = bpy.data.objects.new("rsObject", mesh)
bpy.context.scene.objects.link(object)
mesh.from_pydata(vertlist, edgelist, [])
class Oscurart_Constellation (Operator):
bl_idname = "mesh.constellation"
bl_label = "Constellation"
bl_description = "Create a Constellation Mesh"
bl_options = {'REGISTER', 'UNDO'}
limit = FloatProperty(
name='Limit',
default=2,
min=0
)
@classmethod
def poll(cls, context):
return(bpy.context.active_object.type == "MESH")
def execute(self, context):
OscConstellation(self.limit)
return {'FINISHED'}
# Register
def add_osc_constellation_button(self, context):
self.layout.operator(
Oscurart_Constellation.bl_idname,
text="Constellation",
icon="PLUGIN")
def register():
bpy.utils.register_class(Oscurart_Constellation)
bpy.types.INFO_MT_mesh_add.append(add_osc_constellation_button)
def unregister():
bpy.utils.unregister_class(Oscurart_Constellation)
bpy.types.INFO_MT_mesh_add.remove(add_osc_constellation_button)
if __name__ == '__main__':
register()

View File

@ -0,0 +1,315 @@
# ##### 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)",
"version": (1, 2),
"blender": (2, 71, 0),
"location": "3D View > Toolshelf > Tools Tab",
"description": "Drop selected objects on active object",
"warning": "",
"wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Object/Drop_to_ground",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
"category": "Object"}
import bpy
import bmesh
from mathutils import (
Vector,
Matrix,
)
from bpy.types import (
Operator,
Panel,
)
from bpy.props import BoolProperty
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.dupli_type == 'GROUP':
if not ob.dupli_group:
return None
else:
lowest_co = None
for ob_l in ob.dupli_group.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 = bpy.context.active_object
name = ground.name
for obs in bpy.context.scene.objects:
obs.select = True
if obs.name == name:
obs.select = 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:
print(ob.type, 'is not supported. Failed to drop', ob.name)
continue
is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down)
if not is_hit:
print(ob.name, 'didn\'t hit the ground')
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 = True
bpy.ops.object.delete('EXEC_DEFAULT')
for ob in obs2:
ob.select = True
ground.select = True
def drop_objects(self, context):
ground = context.object
obs = context.selected_objects
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:
print(ob.type, 'is not supported. Failed to drop', ob.name)
continue
is_hit, hit_location, hit_normal, hit_index = tmp_ground.ray_cast(lowest_world_co, down)
if not is_hit:
print(ob.name, 'didn\'t hit the ground')
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 = True
bpy.ops.object.delete('EXEC_DEFAULT')
for ob in obs:
ob.select = True
ground.select = True
class OBJECT_OT_drop_to_ground(Operator):
bl_idname = "object.drop_on_active"
bl_label = "Drop to Ground"
bl_options = {'REGISTER', 'UNDO'}
bl_description = "Drop selected objects on active object"
align = BoolProperty(
name="Align to ground",
description="Aligns the object to the ground",
default=True)
use_origin = BoolProperty(
name="Use Center",
description="Drop to objects origins",
default=False)
@classmethod
def poll(cls, context):
return len(context.selected_objects) >= 2
def execute(self, context):
print('\nDropping Objects')
drop_objects(self, context)
return {'FINISHED'}
class OBJECT_OT_drop_all_ground(Operator):
bl_idname = "object.drop_all_active"
bl_label = "Drop to Ground"
bl_options = {'REGISTER', 'UNDO'}
bl_description = "Drop selected objects on active object"
align = BoolProperty(
name="Align to ground",
description="Aligns the object to the ground",
default=True)
use_origin = BoolProperty(
name="Use Center",
description="Drop to objects origins",
default=False)
def execute(self, context):
print('\nDropping Objects')
drop_objectsall(self, context)
return {'FINISHED'}
class drop_help(Operator):
bl_idname = "help.drop"
bl_label = ""
def draw(self, context):
layout = self.layout
layout.label("To use:")
layout.label("___________________________")
layout.label("Drop selected :-")
layout.label("Name the base object 'Ground'")
layout.label("Select the object/s to drop")
layout.label("Then Shift Select 'Ground'")
layout.label("___________________________")
layout.label("Drop all :-")
layout.label("select the ground mesh , and press Drop all")
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.row()
row = layout.split(0.80)
row.operator(OBJECT_OT_drop_to_ground.bl_idname,
text="Drop Selected")
row = layout.row()
row.operator(OBJECT_OT_drop_all_ground.bl_idname,
text="Drop All")
row.operator('help.drop', icon='INFO')
# Register
def register():
bpy.utils.register_module(__name__)
pass
def unregister():
bpy.utils.unregister_module(__name__)
pass
if __name__ == "__main__":
register()

View File

@ -0,0 +1,571 @@
# 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>
"""
bl_info = {
"name": "Strut Generator",
"author": "Bill Currie",
"blender": (2, 6, 3),
"api": 35622,
"location": "View3D > Add > Mesh > Struts",
"description": "Add struts meshes based on selected truss meshes",
"warning": "can get very high-poly",
"wiki_url": "",
"tracker_url": "",
"category": "Add Mesh"}
"""
import bpy
import bmesh
from bpy.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 (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, id, od, n, solid, loops):
v1 = Vector(v1)
v2 = Vector(v2)
axis = v2 - v1
pos = [(0, od / 2)]
if loops:
pos += [((od - id) / 2, od / 2),
(axis.length - (od - id) / 2, od / 2)]
pos += [(axis.length, od / 2)]
if solid:
pos += [(axis.length, id / 2)]
if loops:
pos += [(axis.length - (od - id) / 2, id / 2),
((od - id) / 2, id / 2)]
pos += [(0, id / 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]
# print(verts,faces)
return verts, faces
# Project a point along a vector onto a plane.
#
# Really, just find the intersection of the line represented by @a point
# and @a dir with the plane represented by @a norm and @a p. However, if
# the point is on or in front of the plane, or the line is parallel to
# the plane, the original point will be returned.
#
# @param point The point to be projected onto the plane.
# @param dir The vector along which the point will be projected.
# @param norm The normal of the plane onto which the point will be
# projected.
# @param p A point through which the plane passes.
# @return A vector representing the projected point, or the
# original point.
def project_point(point, dir, norm, p):
d = (point - p).dot(norm)
if d >= 0:
# the point is already on or in front of the plane
return point
v = dir.dot(norm)
if v * v < 1e-8:
# the plane is unreachable
return point
return point - dir * d / v
# Make a simple strut for debugging.
#
# The strut is just a single quad representing the Z axis of the edge.
#
# @param mesh The base mesh. Used for finding the edge vertices.
# @param edge_num The number of the current edge. For the face vertex
# indices.
# @param edge The edge for which the strut will be built.
# @param od Twice the width of the strut.
# @return A tuple containing a list of vertices and a list of faces.
# The face vertex indices are pre-adjusted by the edge
# number.
# @fixme The face vertex indices should be accurate for the local
# vertices (consistency)
def make_debug_strut(mesh, edge_num, edge, od):
v = [mesh.verts[edge.verts[0].index].co,
mesh.verts[edge.verts[1].index].co,
None, None]
v[2] = v[1] + edge.z * od / 2
v[3] = v[0] + edge.z * od / 2
f = [[edge_num * 4 + 0, edge_num * 4 + 1,
edge_num * 4 + 2, edge_num * 4 + 3]]
return v, f
# Make a cylinder with ends clipped to the end-planes of the edge.
#
# The strut is just a single quad representing the Z axis of the edge.
#
# @param mesh The base mesh. Used for finding the edge vertices.
# @param edge_num The number of the current edge. For the face vertex
# indices.
# @param edge The edge for which the strut will be built.
# @param od The diameter of the strut.
# @return A tuple containing a list of vertices and a list of faces.
# The face vertex indices are pre-adjusted by the edge
# number.
# @fixme The face vertex indices should be accurate for the local
# vertices (consistency)
def make_clipped_cylinder(mesh, edge_num, edge, od):
n = len(cossin)
cyl = [None] * n
v0 = mesh.verts[edge.verts[0].index].co
c0 = v0 + od * edge.y
v1 = mesh.verts[edge.verts[1].index].co
c1 = v1 - od * edge.y
for i in range(n):
x = cossin[i][0]
y = cossin[i][1]
r = (edge.z * x - edge.x * y) * od / 2
cyl[i] = [c0 + r, c1 + r]
for p in edge.verts[0].planes:
cyl[i][0] = project_point(cyl[i][0], edge.y, p, v0)
for p in edge.verts[1].planes:
cyl[i][1] = project_point(cyl[i][1], -edge.y, p, v1)
v = [None] * n * 2
f = [None] * n
base = edge_num * n * 2
for i in range(n):
v[i * 2 + 0] = cyl[i][1]
v[i * 2 + 1] = cyl[i][0]
f[i] = [None] * 4
f[i][0] = base + i * 2 + 0
f[i][1] = base + i * 2 + 1
f[i][2] = base + (i * 2 + 3) % (n * 2)
f[i][3] = base + (i * 2 + 2) % (n * 2)
return v, f
# Represent a vertex in the base mesh, with additional information.
#
# These vertices are @b not shared between edges.
#
# @var index The index of the vert in the base mesh
# @var edge The edge to which this vertex is attached.
# @var edges A tuple of indicess of edges attached to this vert, not
# including the edge to which this vertex is attached.
# @var planes List of vectors representing the normals of the planes that
# bisect the angle between this vert's edge and each other
# adjacant edge.
class SVert:
# Create a vertex holding additional information about the bmesh vertex.
# @param bmvert The bmesh vertex for which additional information is
# to be stored.
# @param bmedge The edge to which this vertex is attached.
def __init__(self, bmvert, bmedge, edge):
self.index = bmvert.index
self.edge = edge
edges = bmvert.link_edges[:]
edges.remove(bmedge)
self.edges = tuple(map(lambda e: e.index, edges))
self.planes = []
def calc_planes(self, edges):
for ed in self.edges:
self.planes.append(calc_plane_normal(self.edge, edges[ed]))
# Represent an edge in the base mesh, with additional information.
#
# Edges do not share vertices so that the edge is always on the front (back?
# must verify) side of all the planes attached to its vertices. If the
# vertices were shared, the edge could be on either side of the planes, and
# there would be planes attached to the vertex that are irrelevant to the
# edge.
#
# @var index The index of the edge in the base mesh.
# @var bmedge Cached reference to this edge's bmedge
# @var verts A tuple of 2 SVert vertices, one for each end of the
# edge. The vertices are @b not shared between edges.
# However, if two edges are connected via a vertex in the
# bmesh, their corresponding SVert vertices will have the
# the same index value.
# @var x The x axis of the edges local frame of reference.
# Initially invalid.
# @var y The y axis of the edges local frame of reference.
# Initialized such that the edge runs from verts[0] to
# verts[1] along the negative y axis.
# @var z The z axis of the edges local frame of reference.
# Initially invalid.
class SEdge:
def __init__(self, bmesh, bmedge):
self.index = bmedge.index
self.bmedge = bmedge
bmesh.verts.ensure_lookup_table()
self.verts = (SVert(bmedge.verts[0], bmedge, self),
SVert(bmedge.verts[1], bmedge, self))
self.y = (bmesh.verts[self.verts[0].index].co -
bmesh.verts[self.verts[1].index].co)
self.y.normalize()
self.x = self.z = None
def set_frame(self, up):
self.x = self.y.cross(up)
self.x.normalize()
self.z = self.x.cross(self.y)
def calc_frame(self, base_edge):
baxis = base_edge.y
if (self.verts[0].index == base_edge.verts[0].index or
self.verts[1].index == base_edge.verts[1].index):
axis = -self.y
elif (self.verts[0].index == base_edge.verts[1].index or
self.verts[1].index == base_edge.verts[0].index):
axis = self.y
else:
raise ValueError("edges not connected")
if baxis.dot(axis) in (-1, 1):
# aligned axis have their up/z aligned
up = base_edge.z
else:
# Get the unit vector dividing the angle (theta) between baxis and
# axis in two equal parts
h = (baxis + axis)
h.normalize()
# (cos(theta/2), sin(theta/2) * n) where n is the unit vector of the
# axis rotating baxis onto axis
q = Quaternion([baxis.dot(h)] + list(baxis.cross(h)))
# rotate the base edge's up around the rotation axis (blender
# quaternion shortcut:)
up = q * base_edge.z
self.set_frame(up)
def calc_vert_planes(self, edges):
for v in self.verts:
v.calc_planes(edges)
def bisect_faces(self):
n1 = self.bmedge.link_faces[0].normal
if len(self.bmedge.link_faces) > 1:
n2 = self.bmedge.link_faces[1].normal
return (n1 + n2).normalized()
return n1
def calc_simple_frame(self):
return self.y.cross(select_up(self.y)).normalized()
def find_edge_frame(self, sedges):
if self.bmedge.link_faces:
return self.bisect_faces()
if self.verts[0].edges or self.verts[1].edges:
edges = list(self.verts[0].edges + self.verts[1].edges)
for i in range(len(edges)):
edges[i] = sedges[edges[i]]
while edges and edges[-1].y.cross(self.y).length < 1e-3:
edges.pop()
if not edges:
return self.calc_simple_frame()
n1 = edges[-1].y.cross(self.y).normalized()
edges.pop()
while edges and edges[-1].y.cross(self.y).cross(n1).length < 1e-3:
edges.pop()
if not edges:
return n1
n2 = edges[-1].y.cross(self.y).normalized()
return (n1 + n2).normalized()
return self.calc_simple_frame()
def calc_plane_normal(edge1, edge2):
if edge1.verts[0].index == edge2.verts[0].index:
axis1 = -edge1.y
axis2 = edge2.y
elif edge1.verts[1].index == edge2.verts[1].index:
axis1 = edge1.y
axis2 = -edge2.y
elif edge1.verts[0].index == edge2.verts[1].index:
axis1 = -edge1.y
axis2 = -edge2.y
elif edge1.verts[1].index == edge2.verts[0].index:
axis1 = edge1.y
axis2 = edge2.y
else:
raise ValueError("edges not connected")
# Both axis1 and axis2 are unit vectors, so this will produce a vector
# bisects the two, so long as they are not 180 degrees apart (in which
# there are infinite solutions).
return (axis1 + axis2).normalized()
def build_edge_frames(edges):
edge_set = set(edges)
while edge_set:
edge_queue = [edge_set.pop()]
edge_queue[0].set_frame(edge_queue[0].find_edge_frame(edges))
while edge_queue:
current_edge = edge_queue.pop()
for i in (0, 1):
for e in current_edge.verts[i].edges:
edge = edges[e]
if edge.x is not None: # edge already processed
continue
edge_set.remove(edge)
edge_queue.append(edge)
edge.calc_frame(current_edge)
def make_manifold_struts(truss_obj, od, segments):
bpy.context.scene.objects.active = truss_obj
bpy.ops.object.editmode_toggle()
truss_mesh = bmesh.from_edit_mesh(truss_obj.data).copy()
bpy.ops.object.editmode_toggle()
edges = [None] * len(truss_mesh.edges)
for i, e in enumerate(truss_mesh.edges):
edges[i] = SEdge(truss_mesh, e)
build_edge_frames(edges)
verts = []
faces = []
for e, edge in enumerate(edges):
# v, f = make_debug_strut(truss_mesh, e, edge, od)
edge.calc_vert_planes(edges)
v, f = make_clipped_cylinder(truss_mesh, e, edge, od)
verts += v
faces += f
return verts, faces
def make_simple_struts(truss_mesh, id, 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, id, 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
# print(verts,faces)
return verts, faces
def create_struts(self, context, id, od, segments, solid, loops, manifold):
build_cossin(segments)
bpy.context.user_preferences.edit.use_global_undo = False
for truss_obj in bpy.context.scene.objects:
if not truss_obj.select:
continue
truss_obj.select = False
truss_mesh = truss_obj.to_mesh(context.scene, True, 'PREVIEW')
if not truss_mesh.edges:
continue
if manifold:
verts, faces = make_manifold_struts(truss_obj, od, segments)
else:
verts, faces = make_simple_struts(truss_mesh, id, od, segments,
solid, loops)
mesh = bpy.data.meshes.new("Struts")
mesh.from_pydata(verts, [], faces)
obj = bpy.data.objects.new("Struts", mesh)
bpy.context.scene.objects.link(obj)
obj.select = True
obj.location = truss_obj.location
bpy.context.scene.objects.active = obj
mesh.update()
bpy.context.user_preferences.edit.use_global_undo = True
return {'FINISHED'}
class Struts(bpy.types.Operator):
"""Add one or more struts meshes based on selected truss meshes"""
bl_idname = "mesh.generate_struts"
bl_label = "Struts"
bl_description = """Add one or more struts meshes based on selected truss meshes"""
bl_options = {'REGISTER', 'UNDO'}
id = FloatProperty(name="Inside Diameter",
description="diameter of inner surface",
min=0.0,
soft_min=0.0,
max=100,
soft_max=100,
default=0.04)
od = FloatProperty(name="Outside Diameter",
description="diameter of outer surface",
min=0.001,
soft_min=0.001,
max=100,
soft_max=100,
default=0.05)
manifold = BoolProperty(name="Manifold",
description="Connect struts to form a single solid.",
default=False)
solid = BoolProperty(name="Solid",
description="Create inner surface.",
default=False)
loops = BoolProperty(name="Loops",
description="Create sub-surf friendly loops.",
default=False)
segments = IntProperty(name="Segments",
description="Number of segments around strut",
min=3, soft_min=3,
max=64, soft_max=64,
default=12)
def execute(self, context):
keywords = self.as_keywords()
return create_struts(self, context, **keywords)
def menu_func(self, context):
self.layout.operator(Struts.bl_idname, text="Struts", icon='PLUGIN')
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_mesh_add.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,381 @@
# ##### 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, 5),
"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.props import (
EnumProperty,
IntProperty,
)
# Cleanup
def modifiersDelete(obj):
for mod in obj.modifiers:
if mod.name == "latticeeasytemp":
try:
if mod.object == bpy.data.objects['LatticeEasytTemp']:
bpy.ops.object.modifier_apply(apply_as='DATA', modifier=mod.name)
except:
bpy.ops.object.modifier_remove(modifier=mod.name)
def modifiersApplyRemove(obj):
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_pattern(pattern=obj.name, extend=False)
bpy.context.scene.objects.active = obj
for mod in obj.modifiers:
if mod.name == "latticeeasytemp":
if mod.object == bpy.data.objects['LatticeEasytTemp']:
bpy.ops.object.modifier_apply(apply_as='DATA', modifier=mod.name)
def latticeDelete(obj):
bpy.ops.object.select_all(action='DESELECT')
for ob in bpy.context.scene.objects:
if "LatticeEasytTemp" in ob.name:
ob.select = True
bpy.ops.object.delete(use_global=False)
obj.select = True
def createLattice(obj, size, pos, props):
# Create lattice and object
lat = bpy.data.lattices.new('LatticeEasytTemp')
ob = bpy.data.objects.new('LatticeEasytTemp', lat)
loc, rot, scl = getTransformations(obj)
# the position comes from the bbox
ob.location = pos
# the size from bbox
ob.scale = size
# the rotation comes from the combined obj world matrix which was converted to euler pairs
ob.rotation_euler = buildRot_World(obj)
ob.show_x_ray = True
# Link object to scene
scn = bpy.context.scene
scn.objects.link(ob)
scn.objects.active = ob
scn.update()
# Set lattice attributes
lat.interpolation_type_u = props[3]
lat.interpolation_type_v = props[3]
lat.interpolation_type_w = props[3]
lat.use_outside = False
lat.points_u = props[0]
lat.points_v = props[1]
lat.points_w = props[2]
return ob
def selectedVerts_Grp(obj):
vertices = obj.data.vertices
selverts = []
if obj.mode == "EDIT":
bpy.ops.object.editmode_toggle()
for grp in obj.vertex_groups:
if "templatticegrp" in grp.name:
bpy.ops.object.vertex_group_set_active(group=grp.name)
bpy.ops.object.vertex_group_remove()
tempgroup = obj.vertex_groups.new("templatticegrp")
for vert in vertices:
if vert.select is True:
selverts.append(vert)
tempgroup.add([vert.index], 1.0, "REPLACE")
return selverts
def getTransformations(obj):
rot = obj.rotation_euler
loc = obj.location
size = obj.scale
return [loc, rot, size]
def findBBox(obj, selvertsarray):
mat = buildTrnScl_WorldMat(obj)
mat_world = obj.matrix_world
minx, miny, minz = selvertsarray[0].co
maxx, maxy, maxz = selvertsarray[0].co
c = 1
for c in range(len(selvertsarray)):
co = selvertsarray[c].co
if co.x < minx:
minx = co.x
if co.y < miny:
miny = co.y
if co.z < minz:
minz = co.z
if co.x > maxx:
maxx = co.x
if co.y > maxy:
maxy = co.y
if co.z > maxz:
maxz = co.z
c += 1
minpoint = Vector((minx, miny, minz))
maxpoint = Vector((maxx, maxy, maxz))
# middle point has to be calculated based on the real world matrix
middle = ((minpoint + maxpoint) / 2)
minpoint = mat * minpoint # Calculate only based on loc/scale
maxpoint = mat * maxpoint # Calculate only based on loc/scale
middle = mat_world * middle # the middle has to be calculated based on the real world matrix
size = maxpoint - minpoint
size = Vector((abs(size.x), abs(size.y), abs(size.z)))
return [minpoint, maxpoint, size, middle]
def buildTrnSclMat(obj):
# This function builds a local matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at obejct level if there is any
mat_trans = Matrix.Translation(obj.location)
mat_scale = Matrix.Scale(obj.scale[0], 4, (1, 0, 0))
mat_scale *= Matrix.Scale(obj.scale[1], 4, (0, 1, 0))
mat_scale *= Matrix.Scale(obj.scale[2], 4, (0, 0, 1))
mat_final = mat_trans * mat_scale
return mat_final
def buildTrnScl_WorldMat(obj):
# This function builds a real world matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at obejct level if there is any
loc, rot, scl = obj.matrix_world.decompose()
mat_trans = Matrix.Translation(loc)
mat_scale = Matrix.Scale(scl[0], 4, (1, 0, 0))
mat_scale *= Matrix.Scale(scl[1], 4, (0, 1, 0))
mat_scale *= Matrix.Scale(scl[2], 4, (0, 0, 1))
mat_final = mat_trans * mat_scale
return mat_final
# Feature use
def buildRot_WorldMat(obj):
# This function builds a real world matrix that encodes rotation
# and it leaves out translation and scale matrices
loc, rot, scl = obj.matrix_world.decompose()
rot = rot.to_euler()
mat_rot = Matrix.Rotation(rot[0], 4, 'X')
mat_rot *= Matrix.Rotation(rot[1], 4, 'Z')
mat_rot *= Matrix.Rotation(rot[2], 4, 'Y')
return mat_rot
def buildTrn_WorldMat(obj):
# This function builds a real world matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at obejct level if there is any
loc, rot, scl = obj.matrix_world.decompose()
mat_trans = Matrix.Translation(loc)
return mat_trans
def buildScl_WorldMat(obj):
# This function builds a real world matrix that encodes translation
# and scale and it leaves out the rotation matrix
# The rotation is applied at obejct level if there is any
loc, rot, scl = obj.matrix_world.decompose()
mat_scale = Matrix.Scale(scl[0], 4, (1, 0, 0))
mat_scale *= Matrix.Scale(scl[1], 4, (0, 1, 0))
mat_scale *= Matrix.Scale(scl[2], 4, (0, 0, 1))
return mat_scale
def buildRot_World(obj):
# This function builds a real world rotation values
loc, rot, scl = obj.matrix_world.decompose()
rot = rot.to_euler()
return rot
def run(lat_props):
obj = bpy.context.object
if obj.type == "MESH":
# set global property for the currently active latticed object
bpy.types.Scene.activelatticeobject = bpy.props.StringProperty(
name="currentlatticeobject",
default=""
)
bpy.types.Scene.activelatticeobject = obj.name
modifiersDelete(obj)
selvertsarray = selectedVerts_Grp(obj)
bbox = findBBox(obj, selvertsarray)
size = bbox[2]
pos = bbox[3]
latticeDelete(obj)
lat = createLattice(obj, size, pos, lat_props)
modif = obj.modifiers.new("latticeeasytemp", "LATTICE")
modif.object = lat
modif.vertex_group = "templatticegrp"
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_pattern(pattern=lat.name, extend=False)
bpy.context.scene.objects.active = lat
bpy.context.scene.update()
bpy.ops.object.mode_set(mode='EDIT')
if obj.type == "LATTICE":
if bpy.types.Scene.activelatticeobject:
name = bpy.types.Scene.activelatticeobject
# Are we in edit lattice mode? If so move on to object mode
if obj.mode == "EDIT":
bpy.ops.object.editmode_toggle()
for ob in bpy.context.scene.objects:
if ob.name == name: # found the object with the lattice mod
object = ob
modifiersApplyRemove(object)
latticeDelete(obj)
return
def main(context, latticeprops):
run(latticeprops)
class EasyLattice(bpy.types.Operator):
"""Adds a Lattice modifier ready to edit"""
bl_idname = "object.easy_lattice"
bl_label = "Easy Lattice Creator"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
lat_u = IntProperty(
name="Lattice u",
default=3
)
lat_w = IntProperty(
name="Lattice w",
default=3
)
lat_m = IntProperty(
name="Lattice m",
default=3
)
lat_types = (('0', 'KEY_LINEAR', '0'),
('1', 'KEY_CARDINAL', '1'),
('2', 'KEY_BSPLINE', '2'))
lat_type = EnumProperty(
name="Lattice Type",
items=lat_types,
default='0'
)
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
lat_u = self.lat_u
lat_w = self.lat_w
lat_m = self.lat_m
# this is a reference to the "items" used to generate the
# enum property
lat_type = self.lat_types[int(self.lat_type)][1]
lat_props = [lat_u, lat_w, lat_m, lat_type]
main(context, lat_props)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def menu_draw(self, context):
self.layout.operator_context = 'INVOKE_REGION_WIN'
self.layout.operator(EasyLattice.bl_idname, "Easy Lattice")
def register():
bpy.utils.register_class(EasyLattice)
bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_draw)
def unregister():
bpy.utils.unregister_class(EasyLattice)
bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_draw)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,170 @@
# ##### 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,
Panel,
)
def Add_Chain():
# Adds Empty to scene
bpy.ops.object.add(type='EMPTY',
view_align=False,
enter_editmode=False,
location=(0, 0, 0),
rotation=(0, 0, 0),
)
# Changes name of Empty to rot_link adds variable emp
emp = bpy.context.object
emp.name = "rot_link"
# Rotate emp ~ 90 degrees
emp.rotation_euler = [1.570796, 0, 0]
# Adds Curve Path to scene
bpy.ops.curve.primitive_nurbs_path_add(view_align=False,
enter_editmode=False,
location=(0, 0, 0),
rotation=(0, 0, 0),
)
# Change Curve name to deform adds variable curv
curv = bpy.context.object
curv.name = "deform"
# Inserts Torus primitive
bpy.ops.mesh.primitive_torus_add(major_radius=1,
minor_radius=0.25,
major_segments=12,
minor_segments=4,
abso_major_rad=1,
abso_minor_rad=0.5,
)
# Positions Torus primitive to center of scene
bpy.context.active_object.location = 0.0, 0.0, 0.0
# Reseting Torus rotation in case of 'Align to view' option enabled
bpy.context.active_object.rotation_euler = 0.0, 0.0, 0.0
# Changes Torus name to chain adds variable tor
tor = bpy.context.object
tor.name = "chain"
# Adds Array Modifier to tor
bpy.ops.object.modifier_add(type='ARRAY')
# Adds subsurf modifier tor
bpy.ops.object.modifier_add(type='SUBSURF')
# Smooths tor
bpy.ops.object.shade_smooth()
# Select curv
sce = bpy.context.scene
sce.objects.active = curv
# Toggle into editmode
bpy.ops.object.editmode_toggle()
# TODO, may be better to move objects directly.
# Translate curve object
bpy.ops.transform.translate(value=(2, 0, 0),
constraint_axis=(True, False, False),
constraint_orientation='GLOBAL',
mirror=False,
proportional='DISABLED',
proportional_edit_falloff='SMOOTH',
proportional_size=1,
snap=False,
snap_target='CLOSEST',
snap_point=(0, 0, 0),
snap_align=False,
snap_normal=(0, 0, 0),
release_confirm=False,
)
# Toggle into objectmode
bpy.ops.object.editmode_toggle()
# Select tor or chain
sce.objects.active = tor
# Selects Array Modifier for editing
array = tor.modifiers['Array']
# Change Array Modifier Parameters
array.fit_type = 'FIT_CURVE'
array.curve = curv
array.offset_object = emp
array.use_object_offset = True
array.relative_offset_displace = 0.549, 0.0, 0.0
# Add curve modifier
bpy.ops.object.modifier_add(type='CURVE')
# Selects Curve Modifier for editing
cur = tor.modifiers['Curve']
# Change Curve Modifier Parameters
cur.object = curv
class AddChain(Operator):
bl_idname = "mesh.primitive_chain_add"
bl_label = "Add Chain"
bl_description = ("Create a Chain segment with helper objects controlling modifiers:\n"
"1) A Curve Modifier Object (deform) for the length and shape,\n"
"Edit the Path to extend Chain Length\n"
"2) An Empty (rot_link) as an Array Offset for rotation")
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
Add_Chain()
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
pass
def unregister():
bpy.utils.unregister_module(__name__)
pass
if __name__ == "__main__":
register()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,211 @@
# 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 *****
bl_info = {
"name": "Mangle Tools",
"author": "Phil Cote",
"version": (0, 2),
"blender": (2, 71, 0),
"location": "View3D > Toolshelf > Tools Tab",
"description": "Set of tools to mangle curves, meshes, and shape keys",
"warning": "", # used for warning icon and text in addons panel
"wiki_url": "",
"tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
"category": "Object"}
import bpy
import random
import time
from math import pi
import bmesh
def move_coordinate(context, co, is_curve=False):
xyz_const = context.scene.constraint_vector
random.seed(time.time())
multiplier = 1
# For curves, we base the multiplier on the circumference formula.
# This helps make curve changes more noticable.
if is_curve:
multiplier = 2 * pi
random_mag = context.scene.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(bpy.types.Operator):
"""Push vertices on the selected object around in random """ \
"""directions to create a crumpled look"""
bl_idname = "ba.mesh_mangler"
bl_label = "Mangle Mesh"
bl_options = { "REGISTER", "UNDO" }
@classmethod
def poll(cls, context):
ob = context.active_object
return ob != None and ob.type == 'MESH'
def execute(self, context):
mesh = context.active_object.data
bm = bmesh.new()
bm.from_mesh(mesh)
verts, faces = bm.verts, bm.faces
randomMag = context.scene.random_magnitude
random.seed( time.time() )
if mesh.shape_keys != None:
self.report({'INFO'}, "Cannot mangle mesh: Shape keys present")
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
bm.to_mesh(mesh)
mesh.update()
return {'FINISHED'}
class AnimanglerOperator(bpy.types.Operator):
"""Make a shape key and pushes the verts around on it """ \
"""to set up for random pulsating animation"""
bl_idname = "ba.ani_mangler"
bl_label = "Mangle Shape Key"
@classmethod
def poll(cls, context):
ob = context.active_object
return ob != None and ob.type in [ 'MESH', 'CURVE' ]
def execute(self, context):
scn = context.scene
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(bpy.types.Operator):
"""Mangle a curve to the degree the user specifies"""
bl_idname = "ba.curve_mangler"
bl_label = "Mangle Curve"
bl_options = { 'REGISTER', 'UNDO' }
@classmethod
def poll(cls, context):
ob = context.active_object
return ob != None and ob.type == "CURVE"
def execute(self, context):
ob = context.active_object
if ob.data.shape_keys != None:
self.report({'INFO'}, "Cannot mangle curve. Shape keys present")
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(bpy.types.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
obj = context.object
if obj.type in ['MESH',]:
layout = self.layout
col = layout.column()
col.prop(scn, "constraint_vector")
col.prop(scn, "random_magnitude")
col.operator("ba.mesh_mangler")
col.separator()
col.prop(scn, "mangle_name")
col.operator("ba.ani_mangler")
else:
layout = self.layout
col = layout.column()
col.label("Please Select Mesh Object")
IntProperty = bpy.props.IntProperty
StringProperty = bpy.props.StringProperty
BoolVectorProperty = bpy.props.BoolVectorProperty
def register():
bpy.utils.register_class(AnimanglerOperator)
bpy.utils.register_class(MeshManglerOperator)
bpy.utils.register_class(CurveManglerOp)
bpy.utils.register_class(MangleToolsPanel)
scnType = bpy.types.Scene
scnType.constraint_vector = BoolVectorProperty(name="Mangle Constraint",
default=(True,True,True),
subtype='XYZ',
description="Constrains Mangle Direction")
scnType.random_magnitude = IntProperty( name = "Mangle Severity",
default = 5, min = 1, max = 30,
description = "Severity of mangling")
scnType.mangle_name = StringProperty(name="Shape Key Name",
default="mangle",
description="Name given for mangled shape keys")
def unregister():
bpy.utils.unregister_class(AnimanglerOperator)
bpy.utils.unregister_class(MeshManglerOperator)
bpy.utils.unregister_class(MangleToolsPanel)
bpy.utils.unregister_class(CurveManglerOp)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,280 @@
# ##### 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: translate the comments into English
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",
"tracker_url": "",
"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("EslabonCero" + str(hueso.name), mesh)
mesh.from_pydata(
[(-0.04986128956079483, -0.6918092370033264, -0.17846597731113434),
(-0.04986128956079483, -0.6918091773986816, 0.17846640944480896),
(-0.049861326813697815, -0.154555082321167, 0.17846627533435822),
(-0.049861326813697815, -0.15455523133277893, -0.17846614122390747),
(-0.04986133798956871, -0.03475356101989746, 0.25805795192718506),
(-0.04986133798956871, -0.03475397825241089, -0.25805795192718506),
(-0.049861278384923935, -0.8116106986999512, -0.2580576539039612),
(-0.049861278384923935, -0.8116104602813721, 0.25805822014808655),
(-0.04986128211021423, -0.7692053318023682, 2.6668965347198537e-07),
(-0.04986127093434334, -0.923523485660553, 2.7834033744511544e-07),
(-0.04986133426427841, -0.0771591067314148, 3.5627678585115063e-08),
(-0.04986134544014931, 0.0771591067314148, -3.5627678585115063e-08),
(0.04986133798956871, -0.03475397825241089, -0.25805795192718506),
(0.04986133053898811, 0.0771591067314148, -3.5627678585115063e-08),
(0.04986133798956871, -0.03475356101989746, 0.25805795192718506),
(0.04986134544014931, -0.15455523133277893, -0.17846614122390747),
(0.04986134544014931, -0.0771591067314148, 3.5627678585115063e-08),
(0.04986134544014931, -0.154555082321167, 0.17846627533435822),
(0.049861397594213486, -0.8116106986999512, -0.2580576539039612),
(0.04986140504479408, -0.923523485660553, 2.7834033744511544e-07),
(0.049861397594213486, -0.8116104602813721, 0.25805822014808655),
(0.04986139014363289, -0.6918091773986816, 0.17846640944480896),
(0.04986139014363289, -0.7692053318023682, 2.6668965347198537e-07),
(0.04986139014363289, -0.6918092370033264, -0.17846597731113434)],
[(1, 2), (0, 3), (3, 5), (2, 4), (0, 6), (5, 6), (1, 7), (4, 7), (0, 8), (1, 8),
(7, 9), (6, 9), (8, 9), (2, 10), (3, 10), (4, 11), (5, 11), (10, 11), (5, 12),
(12, 13), (11, 13), (13, 14), (4, 14), (10, 16), (15, 16), (3, 15), (2, 17),
(16, 17), (9, 19), (18, 19), (6, 18), (7, 20), (19, 20), (8, 22), (21, 22),
(1, 21), (0, 23), (22, 23), (14, 20), (12, 18), (15, 23), (17, 21), (12, 15),
(13, 16), (14, 17), (20, 21), (19, 22), (18, 23)],
[(6, 0, 3, 5), (1, 7, 4, 2), (0, 6, 9, 8), (8, 9, 7, 1), (2, 4, 11, 10), (10, 11, 5, 3),
(11, 13, 12, 5), (4, 14, 13, 11), (3, 15, 16, 10), (10, 16, 17, 2), (6, 18, 19, 9),
(9, 19, 20, 7), (1, 21, 22, 8), (23, 0, 8, 22), (7, 20, 14, 4), (5, 12, 18, 6),
(0, 23, 15, 3), (2, 17, 21, 1), (16, 15, 12, 13), (17, 16, 13, 14), (22, 21, 20, 19),
(23, 22, 19, 18), (21, 17, 14, 20), (15, 23, 18, 12)]
)
mesh.validate()
bpy.context.scene.objects.link(object)
# scale to the bone
bpy.data.objects['EslabonCero' + str(hueso.name)].scale = (hueso.length * mult,
hueso.length * mult,
hueso.length * mult)
# Parent Objects
bpy.data.objects['EslabonCero' + str(hueso.name)].parent = ARMATURE
bpy.data.objects['EslabonCero' + str(hueso.name)].parent_type = 'BONE'
bpy.data.objects['EslabonCero' + str(hueso.name)].parent_bone = hueso.name
def creahuesonoventa(hueso):
# create data to link
mesh = bpy.data.meshes.new("objectData" + str(hueso.name))
object = bpy.data.objects.new("EslabonNov" + str(hueso.name), mesh)
mesh.from_pydata(
[(0.1784660965204239, -0.6918091773986816, -0.049861203879117966),
(-0.1784662902355194, -0.6918091773986816, -0.04986126348376274),
(-0.17846627533435822, -0.1545550525188446, -0.04986134544014931),
(0.17846617102622986, -0.15455520153045654, -0.04986128583550453),
(-0.25805795192718506, -0.03475359082221985, -0.049861375242471695),
(0.25805795192718506, -0.034753888845443726, -0.04986129328608513),
(0.2580578327178955, -0.8116105794906616, -0.04986117407679558),
(-0.2580580413341522, -0.8116105198860168, -0.049861256033182144),
(-9.672299938756623e-08, -0.7692052721977234, -0.04986122250556946),
(-8.99775329799013e-08, -0.923523485660553, -0.04986120015382767),
(-7.764004550381287e-09, -0.07715904712677002, -0.049861326813697815),
(4.509517737005808e-08, 0.0771591067314148, -0.049861349165439606),
(0.25805795192718506, -0.034753888845443726, 0.049861375242471695),
(-2.2038317837314025e-08, 0.0771591067314148, 0.049861326813697815),
(-0.25805795192718506, -0.03475359082221985, 0.04986129328608513),
(0.17846617102622986, -0.15455520153045654, 0.04986138269305229),
(-1.529285498236277e-08, -0.07715907692909241, 0.049861352890729904),
(-0.17846627533435822, -0.1545550525188446, 0.049861323088407516),
(0.2580578029155731, -0.8116105794906616, 0.049861494451761246),
(-1.5711103173998708e-07, -0.923523485660553, 0.04986147582530975),
(-0.2580580711364746, -0.8116105198860168, 0.04986141249537468),
(-0.1784663051366806, -0.6918091773986816, 0.049861419945955276),
(-1.340541757599567e-07, -0.7692052721977234, 0.049861449748277664),
(0.1784660816192627, -0.6918091773986816, 0.04986146464943886)],
[(1, 2), (0, 3), (3, 5), (2, 4), (0, 6), (5, 6), (1, 7), (4, 7), (0, 8),
(1, 8), (7, 9), (6, 9), (8, 9), (2, 10), (3, 10), (4, 11), (5, 11), (10, 11),
(5, 12), (12, 13), (11, 13), (13, 14), (4, 14), (10, 16), (15, 16), (3, 15),
(2, 17), (16, 17), (9, 19), (18, 19), (6, 18), (7, 20), (19, 20), (8, 22),
(21, 22), (1, 21), (0, 23), (22, 23), (14, 20), (12, 18), (15, 23), (17, 21),
(12, 15), (13, 16), (14, 17), (20, 21), (19, 22), (18, 23)],
[(6, 0, 3, 5), (1, 7, 4, 2), (0, 6, 9, 8), (8, 9, 7, 1), (2, 4, 11, 10),
(10, 11, 5, 3), (11, 13, 12, 5), (4, 14, 13, 11), (3, 15, 16, 10), (10, 16, 17, 2),
(6, 18, 19, 9), (9, 19, 20, 7), (1, 21, 22, 8), (23, 0, 8, 22), (7, 20, 14, 4),
(5, 12, 18, 6), (0, 23, 15, 3), (2, 17, 21, 1), (16, 15, 12, 13), (17, 16, 13, 14),
(22, 21, 20, 19), (23, 22, 19, 18), (21, 17, 14, 20), (15, 23, 18, 12)]
)
mesh.validate()
bpy.context.scene.objects.link(object)
# scale to the bone
bpy.data.objects['EslabonNov' + str(hueso.name)].scale = (hueso.length * mult,
hueso.length * mult,
hueso.length * mult)
# Parent objects
bpy.data.objects['EslabonNov' + str(hueso.name)].parent = ARMATURE
bpy.data.objects['EslabonNov' + str(hueso.name)].parent_type = 'BONE'
bpy.data.objects['EslabonNov' + str(hueso.name)].parent_bone = hueso.name
for hueso in bpy.context.active_object.pose.bones:
if VAR_SWITCH == 1:
creahuesocero(hueso)
else:
creahuesonoventa(hueso)
if VAR_SWITCH == 1:
VAR_SWITCH = 0
else:
VAR_SWITCH = 1
# if curve rig is activated
if curverig is True:
# variables
LISTA_POINTC = []
ACTARM = bpy.context.active_object
# create data and link the object to the scene
crv = bpy.data.curves.new("CurvaCable", "CURVE")
obCable = bpy.data.objects.new("Cable", crv)
bpy.context.scene.objects.link(obCable)
# set the attributes
crv.dimensions = "3D"
crv.resolution_u = 10
crv.resolution_v = 10
crv.twist_mode = "MINIMUM"
# create the list of tail and head coordinates
LISTA_POINTC.append((
ACTARM.data.bones[0].head_local[0],
ACTARM.data.bones[0].head_local[1],
ACTARM.data.bones[0].head_local[2],
1
))
for hueso in ACTARM.data.bones:
LISTA_POINTC.append((
hueso.tail_local[0],
hueso.tail_local[1],
hueso.tail_local[2],
1
))
# create the Spline
spline = crv.splines.new("NURBS")
lencoord = len(LISTA_POINTC)
rango = range(lencoord)
spline.points.add(lencoord - 1)
for punto in rango:
spline.points[punto].co = LISTA_POINTC[punto]
# set the endpoint
bpy.data.objects['Cable'].data.splines[0].use_endpoint_u = True
# select the curve
bpy.ops.object.select_all(action='DESELECT')
bpy.data.objects['Cable'].select = 1
bpy.context.scene.objects.active = bpy.data.objects['Cable']
# switch to Edit mode
bpy.ops.object.mode_set(mode='EDIT')
# create hooks
POINTSTEP = 0
for POINT in bpy.data.objects['Cable'].data.splines[0].points:
bpy.ops.curve.select_all(action="DESELECT")
bpy.data.objects['Cable'].data.splines[0].points[POINTSTEP].select = 1
bpy.ops.object.hook_add_newob()
POINTSTEP += 1
# Objects selection step
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
ACTARM.select = 1
bpy.context.scene.objects.active = bpy.data.objects['Armature']
bpy.ops.object.mode_set(mode='POSE')
bpy.ops.pose.select_all(action='DESELECT')
ACTARM.data.bones[-1].select = 1
ACTARM.data.bones.active = ACTARM.data.bones[-1]
# set IK Spline
bpy.ops.pose.constraint_add_with_targets(type='SPLINE_IK')
ACTARM.pose.bones[-1].constraints['Spline IK'].target = bpy.data.objects['Cable']
ACTARM.pose.bones[-1].constraints['Spline IK'].chain_count = 100
bpy.context.active_object.pose.bones[-1].constraints['Spline IK'].use_y_stretch = False
# return to Object mode
bpy.ops.object.mode_set(mode='OBJECT')
class MESH_OT_primitive_oscurart_chain_add(Operator):
bl_idname = "mesh.primitive_oscurart_chain_add"
bl_label = "Chain to Bones"
bl_description = ("Add Chain Parented to an Existing Armature\n"
"The Active/Last Selected Object must be an Armature")
bl_options = {'REGISTER', 'UNDO'}
curverig = BoolProperty(
name="Curve Rig",
default=False
)
multiplier = FloatProperty(
name="Scale",
default=1,
min=0.01, max=100.0
)
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == "ARMATURE")
def execute(self, context):
makeChain(self, context, self.multiplier, self.curverig)
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

@ -0,0 +1,104 @@
#######################################################
# very simple 'pixelization' or 'voxelization' engine #
#######################################################
bl_info = {
"name": "3D Pix",
"author": "liero",
"version": (0, 5, 1),
"blender": (2, 74, 0),
"location": "View3D > Tool Shelf",
"description": "Creates a 3d pixelated version of the object.",
"category": "Object"}
import bpy
import mathutils
from mathutils import Vector
bpy.types.WindowManager.size = bpy.props.FloatProperty(name='Size', min=.05, max=5, default=.25, description='Size of the cube / grid')
bpy.types.WindowManager.gap = bpy.props.IntProperty(name='Gap', min=0, max=90, default=10, subtype='PERCENTAGE', description='Separation - percent of size')
bpy.types.WindowManager.smooth = bpy.props.FloatProperty(name='Smooth', min=0, max=1, default=.0, description='Smooth factor when subdividing mesh')
def pix(obj):
sce = bpy.context.scene
wm = bpy.context.window_manager
obj.hide = obj.hide_render = True
mes = obj.to_mesh(sce, True, 'RENDER')
mes.transform(obj.matrix_world)
dup = bpy.data.objects.new('dup', mes)
sce.objects.link(dup)
dup.dupli_type = 'VERTS'
sce.objects.active = dup
bpy.ops.object.mode_set()
ver = mes.vertices
for i in range(250):
fin = True
for i in dup.data.edges:
d = ver[i.vertices[0]].co - ver[i.vertices[1]].co
if d.length > wm.size:
ver[i.vertices[0]].select = True
ver[i.vertices[1]].select = True
fin = False
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.subdivide(number_cuts=1, smoothness=wm.smooth)
bpy.ops.mesh.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]) % wm.size
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.mesh.delete(type='EDGE_FACE')
bpy.ops.object.mode_set()
sca = wm.size * (100 - wm.gap) * .005
bpy.ops.mesh.primitive_cube_add(layers=[True] + [False] * 19)
bpy.ops.transform.resize(value=[sca] * 3)
bpy.context.scene.objects.active = dup
bpy.ops.object.parent_set(type='OBJECT')
class Pixelate(bpy.types.Operator):
bl_idname = 'object.pixelate'
bl_label = 'Pixelate Object'
bl_description = 'Create a 3d pixelated version of the object.'
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return (context.active_object and context.active_object.type == 'MESH' and context.mode == 'OBJECT')
def draw(self, context):
layout = self.layout
column = layout.column(align=True)
column.prop(context.window_manager, "size")
column.prop(context.window_manager, "gap")
layout.prop(context.window_manager, "smooth")
def execute(self, context):
objeto = bpy.context.object
pix(objeto)
return {'FINISHED'}
classes = (
Pixelate,
)
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()

View File

@ -0,0 +1,193 @@
bl_info = {
"name": "Add Random Box Structure",
"author": "Dannyboy",
"version": (1, 0),
"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_options = {'REGISTER', 'UNDO'}
dc = BoolProperty(
name="Delete Base Mesh(s)?",
default=True
)
wh = BoolProperty(
name="Stay Within Base Mesh(s)?",
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
)
def execute(self, context):
rsdchange = self.rsd
oblst = []
uvyes = 0
bpy.ops.group.create(name='Cubagrouper')
bpy.ops.group.objects_remove()
for ob in bpy.context.selected_objects:
oblst.append(ob)
for obj in oblst:
bpy.ops.object.select_pattern(pattern=obj.name) # Select base mesh
bpy.context.scene.objects.active = obj
if obj.data.uv_layers[:] != []:
uvyes = 1
else:
uvyes = 0
bpy.ops.object.group_link(group='Cubagrouper')
dim = obj.dimensions
rot = obj.rotation_euler
if self.uf is True:
area = dim.x * dim.y * dim.z
else:
area = 75
for cube in range(round((area / 75) * self.qn)):
random.seed(rsdchange)
pmn = self.mn # Proxy values
pmx = self.mx
if self.wh is True:
if dim.x < pmx.x: # Keeping things from exceeding proper size.
pmx.x = dim.x
if dim.y < pmx.y:
pmx.y = dim.y
if dim.z < pmx.z:
pmx.z = dim.z
if 0.0 > pmn.x: # Keeping things from going under zero.
pmn.x = 0.0
if 0.0 > pmn.y:
pmn.y = 0.0
if 0.0 > pmn.z:
pmn.z = 0.0
sx = (random.random() * (pmx.x - pmn.x)) + pmn.x # Just changed self.mx and .mn to pmx.
sy = (random.random() * (pmx.y - pmn.y)) + pmn.y
sz = (random.random() * (pmx.z - pmn.z)) + pmn.z
if self.wh is True: # This keeps the cubes within the base mesh.
ex = (random.random() * (dim.x - sx)) - ((dim.x - sx) / 2) + obj.location.x
wy = (random.random() * (dim.y - sy)) - ((dim.y - sy) / 2) + obj.location.y
ze = (random.random() * (dim.z - sz)) - ((dim.z - sz) / 2) + obj.location.z
elif self.wh is False:
ex = (random.random() * dim.x) - (dim.x / 2) + obj.location.x
wy = (random.random() * dim.y) - (dim.y / 2) + obj.location.y
ze = (random.random() * dim.z) - (dim.z / 2) + obj.location.z
bpy.ops.mesh.primitive_cube_add(
radius=0.5, location=(ex + self.lo.x, wy + self.lo.y, ze + self.lo.z)
)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.transform.resize(
value=(sx, sy, sz), constraint_axis=(True, True, True),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.ops.object.mode_set(mode='OBJECT')
select = bpy.context.object # This is used to keep something selected for poll().
bpy.ops.object.group_link(group='Cubagrouper')
rsdchange += 3
bpy.ops.object.select_grouped(type='GROUP')
bpy.ops.transform.rotate(
value=rot[0], axis=(1, 0, 0), constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.ops.transform.rotate(
value=rot[1], axis=(0, 1, 0), constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.ops.transform.rotate(
value=rot[2], axis=(0, 0, 1), constraint_axis=(False, False, False),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1, release_confirm=True
)
bpy.context.scene.objects.active = obj # Again needed to avoid poll() taking me down.
bpy.ops.object.make_links_data(type='MODIFIERS')
bpy.ops.object.make_links_data(type='MATERIAL')
if uvyes == 1:
bpy.ops.object.join_uvs()
bpy.ops.group.objects_remove()
bpy.context.scene.objects.active = select
if self.dc is True:
bpy.context.scene.objects.unlink(obj)
return {'FINISHED'}
@classmethod
def poll(cls, context):
ob = context.active_object
return ob is not None and ob.mode == 'OBJECT'
def draw(self, context):
layout = self.layout
box = layout.box()
box.label(text="Options")
box.prop(self, "dc")
box.prop(self, "wh")
box.prop(self, "uf")
box = layout.box()
box.label(text="Parameters")
box.prop(self, "qn")
box.prop(self, "mn")
box.prop(self, "mx")
box.prop(self, "lo")
box.prop(self, "rsd")
def add_object_button(self, context):
self.layout.operator(makestructure.bl_idname, text="Add Random Box structure", icon='PLUGIN')
def register():
bpy.utils.register_class(makestructure)
bpy.types.INFO_MT_add.append(add_object_button)
def unregister():
bpy.utils.unregister_class(makestructure)
bpy.types.INFO_MT_add.remove(add_object_button)
if __name__ == "__main__":
register()

View File

@ -0,0 +1,762 @@
# 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 : translate comments, 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),
"blender": (2, 7, 3),
"location": "Left Toolbar > ClothRope",
"warning": "",
"wiki_url": "",
"tracker_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 == "EDIT" or bpy.context.mode == "EDIT_CURVE" or bpy.context.mode == "EDIT_MESH":
bpy.ops.object.mode_set(mode='OBJECT')
# Clear scene:
def reset_scene():
desocultar("todo")
# el play back al principio
bpy.ops.screen.frame_jump(end=False)
try:
salir_de_editmode()
except:
pass
area = bpy.context.area
# en el outliner expando todo para poder seleccionar los emptys hijos
old_type = area.type
area.type = 'OUTLINER'
bpy.ops.outliner.expanded_toggle()
area.type = old_type
# vuelvo al contexto donde estaba
seleccionar_todo()
bpy.ops.object.delete(use_global=False)
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()
# bpy.ops.mesh.select_all(action="SELECT")
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("el vertice " + str(v.index) + " esta seleccionado")
def seleccionar_por_nombre(nombre):
scn = bpy.context.scene
bpy.data.objects[nombre].select = True
scn.objects.active = bpy.data.objects[nombre]
def deseleccionar_por_nombre(nombre):
# scn = bpy.context.scene
bpy.data.objects[nombre].select = False
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 tab_editmode():
bpy.ops.object.editmode_toggle()
def obtener_coords_vertex_seleccionados():
coordenadas_de_vertices = []
for ob in bpy.context.selected_objects:
print(ob.name)
if ob.type == 'MESH':
for v in ob.data.vertices:
if v.select:
coordenadas_de_vertices.append([v.co[0], v.co[1], v.co[2]])
return coordenadas_de_vertices[0]
def crear_locator(pos):
bpy.ops.object.empty_add(
type='PLAIN_AXES', radius=1, view_align=False,
location=(pos[0], pos[1], pos[2]),
layers=(True, False, False, False, False, False, False,
False, False, False, False, False, False, False,
False, False, False, False, False, False)
)
def extruir_vertices(longitud, cuantos_segmentos):
bpy.ops.mesh.extrude_region_move(
MESH_OT_extrude_region={"mirror": False},
TRANSFORM_OT_translate={
"value": (longitud / cuantos_segmentos, 0, 0),
"constraint_axis": (True, False, False),
"constraint_orientation": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
def select_all_vertex_in_curve_bezier(bc):
for i in range(len(bc.data.splines[0].points)):
bc.data.splines[0].points[i].select = True
def deselect_all_vertex_in_curve_bezier(bc):
for i in range(len(bc.data.splines[0].points)):
bc.data.splines[0].points[i].select = False
def ocultar_relationships():
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
area.spaces[0].show_relationship_lines = False
class ClothRope(Operator):
bl_idname = "clot.rope"
bl_label = "Rope Cloth"
ropelenght = IntProperty(
name="longitud",
default=5
)
ropesegments = IntProperty(
name="rsegments",
default=5
)
qcr = IntProperty(
name="qualcolr",
min=1, max=20,
default=20
)
substeps = IntProperty(
name="rsubsteps",
min=4, max=80,
default=50
)
resrope = IntProperty(
name="resr",
default=5
)
radiusrope = FloatProperty(
name="radius",
min=0.04, max=1,
default=0.04
)
hide_emptys = BoolProperty(
name="hemptys",
default=False
)
def execute(self, context):
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
scene.name = "Test Rope"
seleccionar_todo()
longitud = self.ropelenght
# para que desde el primer punto hasta el ultimo, entre
# medias tenga x segmentos debo sumarle 1 a la cantidad:
cuantos_segmentos = self.ropesegments + 1
calidad_de_colision = self.qcr
substeps = self.substeps
deseleccionar_todo()
# creamos el empty que sera el padre de todo
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"
deseleccionar_todo()
# creamos un plano y lo borramos
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]
# renombrar:
ob.name = "cuerda"
entrar_en_editmode() # entramos en edit mode
select_all_in_edit_mode(ob)
# seleccionar_todo() # ya viene por default seleccionado
borrar_elementos_seleccionados("vertices")
salir_de_editmode() # salimos de edit mode
crear_vertices(ob) # creamos un vertex
# creando el grupo Group para el PIN
# Group contiene los vertices del pin y Group.001 contiene la linea unica principal
entrar_en_editmode() # entramos en edit mode
bpy.ops.object.vertex_group_add() # creamos un grupo
select_all_in_edit_mode(ob)
bpy.ops.object.vertex_group_assign() # y lo asignamos
# los hooks van a la curva no a la guia poligonal...
# creo el primer hook sin necesidad de crear luego el locator a mano:
# bpy.ops.object.hook_add_newob()
salir_de_editmode() # salimos de edit mode
ob.vertex_groups[0].name = "Pin"
deseleccionar_todo()
seleccionar_por_nombre("cuerda")
# hago los extrudes del vertice:
for i in range(cuantos_segmentos):
entrar_en_editmode()
extruir_vertices(longitud, cuantos_segmentos)
# y los ELIMINO del grupo PIN
bpy.ops.object.vertex_group_remove_from()
# obtengo la direccion para lego crear el locator en su posicion
pos = obtener_coords_vertex_seleccionados()
# los hooks van a la curva no a la guia poligonal...
# creo el hook sin necesidad de crear el locator a mano:
# bpy.ops.object.hook_add_newob()
salir_de_editmode() # salimos de edit mode
# creo el locator en su sitio
crear_locator(pos)
deseleccionar_todo()
seleccionar_por_nombre("cuerda")
deseleccionar_todo()
seleccionar_por_nombre("cuerda")
# vuelvo a seleccionar la cuerda
entrar_en_editmode()
pos = obtener_coords_vertex_seleccionados() # y obtenemos su posicion
salir_de_editmode()
# creamos el ultimo locator
crear_locator(pos)
deseleccionar_todo()
seleccionar_por_nombre("cuerda")
entrar_en_editmode() # entramos en edit mode
bpy.ops.object.vertex_group_add() # CREANDO GRUPO GUIA MAESTRA
select_all_in_edit_mode(ob)
bpy.ops.object.vertex_group_assign() # y lo asignamos
ob.vertex_groups[1].name = "Guide_rope"
# extruimos la curva para que tenga un minimo grosor para colisionar
bpy.ops.mesh.extrude_region_move(
MESH_OT_extrude_region={"mirror": False},
TRANSFORM_OT_translate={
"value": (0, 0.005, 0), "constraint_axis": (False, True, False),
"constraint_orientation": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
bpy.ops.object.vertex_group_remove_from()
deselect_all_in_edit_mode(ob)
salir_de_editmode()
bpy.ops.object.modifier_add(type='CLOTH')
bpy.context.object.modifiers["Cloth"].settings.use_pin_cloth = True
bpy.context.object.modifiers["Cloth"].settings.vertex_group_mass = "Pin"
bpy.context.object.modifiers["Cloth"].collision_settings.collision_quality = calidad_de_colision
bpy.context.object.modifiers["Cloth"].settings.quality = substeps
# DUPLICAMOS para convertir a curva:
# selecciono los vertices que forman parte del grupo Group.001
seleccionar_por_nombre("cuerda")
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
entrar_en_editmode()
# ya tenemos la guia seleccionada:
# la duplicamos:
bpy.ops.mesh.duplicate_move(
MESH_OT_duplicate={"mode": 1},
TRANSFORM_OT_translate={
"value": (0, 0, 0), "constraint_axis": (False, False, False),
"constraint_orientation": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
# separamos por seleccion:
bpy.ops.mesh.separate(type='SELECTED')
salir_de_editmode()
deseleccionar_todo()
seleccionar_por_nombre("cuerda.001")
# a la nueva curva copiada le quitamos el cloth:
bpy.ops.object.modifier_remove(modifier="Cloth")
# la convertimos en curva:
bpy.ops.object.convert(target='CURVE')
# todos los emptys:
emptys = []
for eo in bpy.data.objects:
if eo.type == 'EMPTY':
if eo.name != "Rope":
emptys.append(eo)
# print(emptys)
# cuantos puntos tiene la becier:
# len(bpy.data.objects['cuerda.001'].data.splines[0].points)
# seleccionar y deseleccionar:
bc = bpy.data.objects['cuerda.001']
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
# entrar_en_editmode()
ob = bpy.data.objects['cuerda']
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()
# deselect_all_in_edit_mode(ob)
salir_de_editmode()
n = n + 1
# ocultar los emptys:
# for e in emptys:
deseleccionar_todo()
# emparentando todo al empty esferico:
seleccionar_por_nombre("cuerda.001")
seleccionar_por_nombre("cuerda")
seleccionar_por_nombre("Rope")
bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
deseleccionar_todo()
# display que no muestre las relaciones
ocultar_relationships()
seleccionar_por_nombre("cuerda.001")
# cuerda curva 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=310)
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column()
col.label("Rope settings:")
rowsub0 = col.row()
rowsub0.prop(self, "ropelenght", text='Length')
rowsub0.prop(self, "ropesegments", text='Segments')
rowsub0.prop(self, "radiusrope", text='Radius')
col.label("Quality Settings:")
col.prop(self, "resrope", text='Resolution curve')
col.prop(self, "qcr", text='Quality Collision')
col.prop(self, "substeps", text='Substeps')
class BallRope(Operator):
bl_idname = "ball.rope"
bl_label = "Rope Ball"
# defaults rope ball
ropelenght2 = IntProperty(
name="longitud",
default=10
)
ropesegments2 = IntProperty(
name="rsegments",
min=0, max=999,
default=6
)
radiuscubes = FloatProperty(
name="radius",
default=0.5
)
radiusrope = FloatProperty(
name="radius",
default=0.4
)
worldsteps = IntProperty(
name="worldsteps",
min=60, max=1000,
default=250
)
solveriterations = IntProperty(
name="solveriterations",
min=10, max=100,
default=50
)
massball = IntProperty(
name="massball",
default=1
)
resrope = IntProperty(
name="resolucion",
default=4
)
grados = FloatProperty(
name="grados",
default=45
)
separacion = FloatProperty(
name="separacion",
default=0.1
)
hidecubes = BoolProperty(
name="hidecubes",
default=False
)
def execute(self, context):
world_steps = self.worldsteps
solver_iterations = self.solveriterations
longitud = self.ropelenght2
# hago un + 2 para que los segmentos sean los que hay entre los dos extremos...
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"
# suelo:
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"
bpy.ops.rigidbody.objects_add(type='PASSIVE')
# creamos el primer cubo:
cuboslink = []
n = 0
for i in range(segmentos):
# si es 0 le digo que empieza desde 1
if i == 0:
i = offset_del_suelo
else: # si no es 0 les tengo que sumar uno para que no se pisen al empezar el primero desde 1
i = i + offset_del_suelo
separacion = longitud * 2 / segmentos # distancia entre los cubos link
bpy.ops.mesh.primitive_cube_add(
radius=1, view_align=False, enter_editmode=False,
location=(0, 0, i * separacion),
layers=(True, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False,
False, False, False, False)
)
bpy.ops.rigidbody.objects_add(type='ACTIVE')
bpy.context.object.name = "CubeLink"
if n != 0:
bpy.context.object.draw_type = 'WIRE'
bpy.context.object.hide_render = True
n += 1
bpy.context.object.scale.z = (longitud * 2) / (segmentos * 2) - separation
bpy.context.object.scale.x = radio
bpy.context.object.scale.y = radio
cuboslink.append(bpy.context.object)
for i in range(len(cuboslink)):
deseleccionar_todo()
if i != len(cuboslink) - 1:
nombre1 = cuboslink[i]
nombre2 = cuboslink[i + 1]
seleccionar_por_nombre(nombre1.name)
seleccionar_por_nombre(nombre2.name)
bpy.ops.rigidbody.connect()
seleccionar_por_nombre
for i in range(segmentos - 1):
if i == 0:
seleccionar_por_nombre("Constraint")
else:
if i <= 9 and i > 0:
seleccionar_por_nombre("Constraint.00" + str(i))
else:
if i <= 99 and i > 9:
seleccionar_por_nombre("Constraint.0" + str(i))
else:
if i <= 999 and i > 99:
seleccionar_por_nombre("Constraint." + str(i))
for c in bpy.context.selected_objects:
c.rigid_body_constraint.type = 'POINT'
deseleccionar_todo()
# creamos la curva bezier:
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"
for i in range(len(cuboslink)):
cubonombre = cuboslink[i].name
seleccionar_por_nombre(cubonombre)
seleccionar_por_nombre("Cuerda")
x = cuboslink[i].location[0]
y = cuboslink[i].location[1]
z = cuboslink[i].location[2]
# si es 0 le digo que empieza desde 1 es el offset desde el suelo...
if i == 0:
i = offset_del_suelo
else: # si no es 0 les tengo que sumar uno para que no se pisen al empezar el primero desde 1
i = i + offset_del_suelo
salir_de_editmode()
# entrar_en_editmode()
tab_editmode()
if i == 1:
# selecciono todos los vertices y los borro
select_all_vertex_in_curve_bezier(bpy.data.objects["Cuerda"])
bpy.ops.curve.delete(type='VERT')
# creamos el primer vertice:
bpy.ops.curve.vertex_add(location=(x, y, z))
else:
# extruimos el resto:
bpy.ops.curve.extrude_move(
CURVE_OT_extrude={"mode": 'TRANSLATION'},
TRANSFORM_OT_translate={
"value": (0, 0, z / i),
"constraint_axis": (False, False, True),
"constraint_orientation": 'GLOBAL', "mirror": False,
"proportional": 'DISABLED', "proportional_edit_falloff": 'SMOOTH',
"proportional_size": 1, "snap": False, "snap_target": 'CLOSEST',
"snap_point": (0, 0, 0), "snap_align": False, "snap_normal": (0, 0, 0),
"gpencil_strokes": False, "texture_space": False,
"remove_on_cancel": False, "release_confirm": False
}
)
bpy.ops.object.hook_add_selob(use_bone=False)
salir_de_editmode()
bpy.context.object.data.bevel_resolution = resolucion
deseleccionar_todo()
# creando la esfera ball:
deseleccionar_todo()
seleccionar_por_nombre(cuboslink[0].name)
entrar_en_editmode()
z = cuboslink[0].scale.z + longitud / 2
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.mesh.primitive_uv_sphere_add(
view_align=False, enter_editmode=False,
layers=(True, False, False, False, False, False, False,
False, False, False, False, False, False, False,
False, False, False, False, False, False)
)
bpy.ops.transform.translate(
value=(0, 0, -z + 2), constraint_axis=(False, False, True),
constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
bpy.ops.transform.resize(
value=(longitud / 2, longitud / 2, longitud / 2),
constraint_axis=(False, False, False),
constraint_orientation='GLOBAL',
mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH', proportional_size=1
)
deselect_all_in_edit_mode(cuboslink[0])
salir_de_editmode()
bpy.ops.object.shade_smooth()
bpy.context.object.rigid_body.mass = masa
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS')
# lo subo todo para arriba un poco mas:
seleccionar_todo()
deseleccionar_por_nombre("groundplane")
bpy.ops.transform.translate(
value=(0, 0, offset_del_suelo_real),
constraint_axis=(False, False, True),
constraint_orientation='GLOBAL', mirror=False,
proportional='DISABLED', proportional_edit_falloff='SMOOTH',
proportional_size=1
)
deseleccionar_todo()
seleccionar_por_nombre(cuboslink[-1].name)
bpy.ops.rigidbody.objects_add(type='PASSIVE')
bpy.context.scene.rigidbody_world.steps_per_second = world_steps
bpy.context.scene.rigidbody_world.solver_iterations = solver_iterations
# para mover todo desde el primero de arriba:
seleccionar_por_nombre(cuboslink[-1].name)
bpy.ops.view3d.snap_cursor_to_selected()
seleccionar_todo()
deseleccionar_por_nombre("groundplane")
deseleccionar_por_nombre(cuboslink[-1].name)
bpy.context.space_data.pivot_point = 'CURSOR'
bpy.ops.transform.rotate(
value=rotrope, axis=(1, 0, 0),
constraint_axis=(True, False, False),
constraint_orientation='GLOBAL',
mirror=False, proportional='DISABLED',
proportional_edit_falloff='SMOOTH',
proportional_size=1
)
bpy.context.space_data.pivot_point = 'MEDIAN_POINT'
deseleccionar_todo()
seleccionar_por_nombre("Cuerda")
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=310)
def draw(self, context):
layout = self.layout
box = layout.box()
col = box.column()
col.label("Rope settings:")
rowsub0 = col.row()
rowsub0.prop(self, "hidecubes", text='Hide Link Cubes')
rowsub1 = col.row()
rowsub1.prop(self, "ropelenght2", text='Length')
rowsub1.prop(self, "ropesegments2", text='Segments')
rowsub2 = col.row()
rowsub2.prop(self, "radiuscubes", text='Radius Link Cubes')
rowsub2.prop(self, "radiusrope", text='Radius Rope')
rowsub3 = col.row()
rowsub3.prop(self, "grados", text='Degrees')
rowsub3.prop(self, "separacion", text='Separation Link Cubes')
col.label("Quality Settings:")
col.prop(self, "resrope", text='Resolution Rope')
col.prop(self, "massball", text='Ball Mass')
col.prop(self, "worldsteps", text='World Steps')
col.prop(self, "solveriterations", text='Solver Iterarions')
# Register
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

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

View File

@ -0,0 +1,130 @@
# 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 Scene with Objects"
bl_options = {'REGISTER'}
def execute(self, context):
blend_data = context.blend_data
# ob = bpy.context.active_object
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
bpy.context.scene.render.engine = 'CYCLES'
scene.name = "scene_object_cycles"
# render settings
render = scene.render
render.resolution_x = 1920
render.resolution_y = 1080
render.resolution_percentage = 50
# add new world
world = bpy.data.worlds.new("Cycles_Object_World")
scene.world = world
world.use_sky_blend = True
world.use_sky_paper = True
world.horizon_color = (0.004393, 0.02121, 0.050)
world.zenith_color = (0.03335, 0.227, 0.359)
world.light_settings.use_ambient_occlusion = True
world.light_settings.ao_factor = 0.25
# add camera
bpy.ops.object.camera_add(
location=(7.48113, -6.50764, 5.34367),
rotation=(1.109319, 0.010817, 0.814928)
)
cam = bpy.context.active_object.data
cam.lens = 35
cam.draw_size = 0.1
bpy.ops.view3d.viewnumpad(type='CAMERA')
# add point lamp
bpy.ops.object.lamp_add(
type="POINT", location=(4.07625, 1.00545, 5.90386),
rotation=(0.650328, 0.055217, 1.866391)
)
lamp1 = bpy.context.active_object.data
lamp1.name = "Point_Right"
lamp1.energy = 1.0
lamp1.distance = 30.0
lamp1.shadow_method = "RAY_SHADOW"
lamp1.use_sphere = True
# add point lamp2
bpy.ops.object.lamp_add(
type="POINT", location=(-0.57101, -4.24586, 5.53674),
rotation=(1.571, 0, 0.785)
)
lamp2 = bpy.context.active_object.data
lamp2.name = "Point_Left"
lamp2.energy = 1.0
lamp2.distance = 30.0
# Add cube
bpy.ops.mesh.primitive_cube_add()
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.subdivide(number_cuts=2)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
cube = bpy.context.active_object
# add cube material
cubeMaterial = blend_data.materials.new("Cycles_Cube_Material")
bpy.ops.object.material_slot_add()
cube.material_slots[0].material = cubeMaterial
# Diffuse
cubeMaterial.preview_render_type = "CUBE"
cubeMaterial.diffuse_color = (1.000, 0.373, 0.00)
# Cycles
cubeMaterial.use_nodes = True
# Add monkey
bpy.ops.mesh.primitive_monkey_add(location=(-0.1, 0.08901, 1.505))
bpy.ops.transform.rotate(value=(1.15019), axis=(0, 0, 1))
bpy.ops.transform.rotate(value=(-0.673882), axis=(0, 1, 0))
bpy.ops.transform.rotate(value=-0.055, axis=(1, 0, 0))
bpy.ops.object.modifier_add(type='SUBSURF')
bpy.ops.object.shade_smooth()
monkey = bpy.context.active_object
# add monkey material
monkeyMaterial = blend_data.materials.new("Cycles_Monkey_Material")
bpy.ops.object.material_slot_add()
monkey.material_slots[0].material = monkeyMaterial
# Diffuse
monkeyMaterial.preview_render_type = "MONKEY"
monkeyMaterial.diffuse_color = (0.239, 0.288, 0.288)
# Cycles
monkeyMaterial.use_nodes = True
# Add plane
bpy.ops.mesh.primitive_plane_add(
radius=50, view_align=False,
enter_editmode=False, location=(0, 0, -1)
)
bpy.ops.object.editmode_toggle()
bpy.ops.transform.rotate(value=-0.8, axis=(0, 0, 1), constraint_axis=(False, False, True))
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
plane = bpy.context.active_object
# add plane material
planeMaterial = blend_data.materials.new("Cycles_Plane_Material")
bpy.ops.object.material_slot_add()
plane.material_slots[0].material = planeMaterial
# Diffuse
planeMaterial.preview_render_type = "FLAT"
planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
# Cycles
planeMaterial.use_nodes = True
return {'FINISHED'}

View File

@ -0,0 +1,66 @@
# 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 Scene: Camera aligned to plane"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
blend_data = context.blend_data
# ob = bpy.context.active_object
# add new scene
bpy.ops.scene.new(type="NEW")
scene = bpy.context.scene
bpy.context.scene.render.engine = 'CYCLES'
scene.name = "scene_texture_cycles"
# render settings
render = scene.render
render.resolution_x = 1080
render.resolution_y = 1080
render.resolution_percentage = 100
# add new world
world = bpy.data.worlds.new("Cycles_Textures_World")
scene.world = world
world.use_sky_blend = True
world.use_sky_paper = True
world.horizon_color = (0.004393, 0.02121, 0.050)
world.zenith_color = (0.03335, 0.227, 0.359)
world.light_settings.use_ambient_occlusion = True
world.light_settings.ao_factor = 0.5
# add camera
bpy.ops.view3d.viewnumpad(type='TOP')
bpy.ops.object.camera_add(location=(0, 0, 2.1850), rotation=(0, 0, 0), view_align=True)
cam = bpy.context.active_object.data
cam.lens = 35
cam.draw_size = 0.1
# add plane
bpy.ops.mesh.primitive_plane_add(enter_editmode=True, location=(0, 0, 0))
bpy.ops.mesh.subdivide(number_cuts=10, smoothness=0)
bpy.ops.uv.unwrap(method='CONFORMAL', margin=0.001)
bpy.ops.object.editmode_toggle()
plane = bpy.context.active_object
# add plane material
planeMaterial = blend_data.materials.new("Cycles_Plane_Material")
bpy.ops.object.material_slot_add()
plane.material_slots[0].material = planeMaterial
# Diffuse
planeMaterial.preview_render_type = "FLAT"
planeMaterial.diffuse_color = (0.2, 0.2, 0.2)
# Cycles
planeMaterial.use_nodes = True
# Back to Scene
sc = bpy.context.scene
bpy.ops.view3d.viewnumpad(type='CAMERA')
return {'FINISHED'}

View File

@ -0,0 +1,225 @@
# 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 active object in the scene")
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 type off Key Light you would like",
items=Light_Type_List,
default='HEMI'
)
secondarytype = EnumProperty(
attr='tl_type',
name="Fill + Back Type",
description="Choose the type off secondary Light you would like",
items=Light_Type_List,
default="POINT"
)
@classmethod
def poll(cls, context):
return context.active_object is not None
def draw(self, context):
layout = self.layout
layout.label("Position:")
col = layout.column(align=True)
col.prop(self, "height")
col.prop(self, "distance")
layout.label("Light:")
col = layout.column(align=True)
col.prop(self, "energy")
col.prop(self, "contrast")
layout.label("Orientation:")
col = layout.column(align=True)
col.prop(self, "leftangle")
col.prop(self, "rightangle")
col.prop(self, "backangle")
col = layout.column()
col.label("Key Light Type:")
col.prop(self, "primarytype", text="")
col.label("Fill + Back Type:")
col.prop(self, "secondarytype", text="")
def execute(self, context):
scene = context.scene
view = context.space_data
if view.type == 'VIEW_3D' and not view.lock_camera_and_layers:
camera = view.camera
else:
camera = scene.camera
if (camera is None):
cam_data = bpy.data.cameras.new(name='Camera')
cam_obj = bpy.data.objects.new(name='Camera', object_data=cam_data)
scene.objects.link(cam_obj)
scene.camera = cam_obj
bpy.ops.view3d.camera_to_view()
camera = cam_obj
bpy.ops.view3d.viewnumpad(type='TOP')
obj = bpy.context.scene.objects.active
# Calculate Energy for each Lamp
if(self.contrast > 0):
keyEnergy = self.energy
backEnergy = (self.energy / 100) * abs(self.contrast)
fillEnergy = (self.energy / 100) * abs(self.contrast)
else:
keyEnergy = (self.energy / 100) * abs(self.contrast)
backEnergy = self.energy
fillEnergy = self.energy
# Calculate Direction for each Lamp
# Calculate current Distance and get Delta
obj_position = obj.location
cam_position = camera.location
delta_position = cam_position - obj_position
vector_length = sqrt(
(pow(delta_position.x, 2) +
pow(delta_position.y, 2) +
pow(delta_position.z, 2))
)
if not vector_length:
# division by zero most likely
self.report({'WARNING'}, "Operation Cancelled. No viable object in the scene")
return {'CANCELLED'}
single_vector = (1 / vector_length) * delta_position
# Calc back position
singleback_vector = single_vector.copy()
singleback_vector.x = cos(radians(self.backangle)) * single_vector.x + \
(-sin(radians(self.backangle)) * single_vector.y)
singleback_vector.y = sin(radians(self.backangle)) * single_vector.x + \
(cos(radians(self.backangle)) * single_vector.y)
backx = obj_position.x + self.distance * singleback_vector.x
backy = obj_position.y + self.distance * singleback_vector.y
backData = bpy.data.lamps.new(name="TriLamp-Back", type=self.secondarytype)
backData.energy = backEnergy
backLamp = bpy.data.objects.new(name="TriLamp-Back", object_data=backData)
scene.objects.link(backLamp)
backLamp.location = (backx, backy, self.height)
trackToBack = backLamp.constraints.new(type="TRACK_TO")
trackToBack.target = obj
trackToBack.track_axis = "TRACK_NEGATIVE_Z"
trackToBack.up_axis = "UP_Y"
# Calc right position
singleright_vector = single_vector.copy()
singleright_vector.x = cos(radians(self.rightangle)) * single_vector.x + \
(-sin(radians(self.rightangle)) * single_vector.y)
singleright_vector.y = sin(radians(self.rightangle)) * single_vector.x + \
(cos(radians(self.rightangle)) * single_vector.y)
rightx = obj_position.x + self.distance * singleright_vector.x
righty = obj_position.y + self.distance * singleright_vector.y
rightData = bpy.data.lamps.new(name="TriLamp-Fill", type=self.secondarytype)
rightData.energy = fillEnergy
rightLamp = bpy.data.objects.new(name="TriLamp-Fill", object_data=rightData)
scene.objects.link(rightLamp)
rightLamp.location = (rightx, righty, self.height)
trackToRight = rightLamp.constraints.new(type="TRACK_TO")
trackToRight.target = obj
trackToRight.track_axis = "TRACK_NEGATIVE_Z"
trackToRight.up_axis = "UP_Y"
# Calc left position
singleleft_vector = single_vector.copy()
singleleft_vector.x = cos(radians(-self.leftangle)) * single_vector.x + \
(-sin(radians(-self.leftangle)) * single_vector.y)
singleleft_vector.y = sin(radians(-self.leftangle)) * single_vector.x + \
(cos(radians(-self.leftangle)) * single_vector.y)
leftx = obj_position.x + self.distance * singleleft_vector.x
lefty = obj_position.y + self.distance * singleleft_vector.y
leftData = bpy.data.lamps.new(name="TriLamp-Key", type=self.primarytype)
leftData.energy = keyEnergy
leftLamp = bpy.data.objects.new(name="TriLamp-Key", object_data=leftData)
scene.objects.link(leftLamp)
leftLamp.location = (leftx, lefty, self.height)
trackToLeft = leftLamp.constraints.new(type="TRACK_TO")
trackToLeft.target = obj
trackToLeft.track_axis = "TRACK_NEGATIVE_Z"
trackToLeft.up_axis = "UP_Y"
return {'FINISHED'}

View File

@ -0,0 +1,337 @@
bl_info = {
"name": "Unfold transition",
"version": (0, 1, 0),
"location": "Tool bar > Animation tab > UnFold Transition",
"description": "Simple unfold transition / animation, will separate faces and set up an armature",
"category": "Animation"}
import bpy
from bpy.props import (
BoolProperty,
EnumProperty,
FloatProperty,
IntProperty,
# PointerProperty,
)
from bpy.types import (
Operator,
Panel,
)
from random import (
randint,
uniform,
)
from mathutils import Vector
from mathutils.geometry import intersect_point_line
bpy.types.WindowManager.modo = EnumProperty(
name="",
items=[("cursor", "3D Cursor", "Use the Distance to 3D Cursor"),
("weight", "Weight Map", "Use a Painted Weight map"),
("index", "Mesh Indices", "Use Faces and Vertices index")],
description="How to Sort Bones for animation", default="cursor"
)
bpy.types.WindowManager.flip = BoolProperty(
name="Flipping Faces",
default=False,
description="Rotate faces around the Center & skip Scaling - "
"keep checked for both operators"
)
bpy.types.WindowManager.fold_duration = IntProperty(
name="Total Time",
min=5, soft_min=25,
max=10000, soft_max=2500,
default=200,
description="Total animation length"
)
bpy.types.WindowManager.sca_time = IntProperty(
name="Scale Time",
min=1,
max=5000, soft_max=500,
default=10,
description="Faces scaling time"
)
bpy.types.WindowManager.rot_time = IntProperty(
name="Rotation Time",
min=1, soft_min=5,
max=5000, soft_max=500,
default=15,
description="Faces rotation time"
)
bpy.types.WindowManager.rot_max = IntProperty(
name="Angle",
min=-180,
max=180,
default=135,
description="Faces rotation angle"
)
bpy.types.WindowManager.fold_noise = IntProperty(
name="Noise",
min=0,
max=500, soft_max=50,
default=0,
description="Offset some faces animation"
)
bpy.types.WindowManager.bounce = FloatProperty(
name="Bounce",
min=0,
max=10, soft_max=2.5,
default=0,
description="Add some bounce to rotation"
)
bpy.types.WindowManager.from_point = BoolProperty(
name="Point",
default=False,
description="Scale faces from a Point instead of from an Edge"
)
bpy.types.WindowManager.wiggle_rot = BoolProperty(
name="Wiggle",
default=False,
description="Use all Axis + Random Rotation instead of X Aligned"
)
class Set_Up_Fold(Operator):
bl_idname = "object.set_up_fold"
bl_label = "Set Up Unfold"
bl_description = "Set up Faces and Bones for animation"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return (bpy.context.object.type == "MESH")
def execute(self, context):
bpy.ops.object.mode_set()
wm = context.window_manager
scn = bpy.context.scene
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 wm.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 wm.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 wm.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)
rig.matrix_world = obj.matrix_world
scn.objects.link(rig)
scn.objects.active = rig
bpy.ops.object.mode_set(mode="EDIT")
arm.draw_type = "WIRE"
rig.show_x_ray = 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 wm.flip:
b.tail, b.head = fb[2], fb[1]
else:
b.tail, b.head = fb[1], fb[2]
b.align_roll(f.normal)
b.select = False
b.layers = vis
b.parent = root
vg = obj.vertex_groups.new(b.name)
vg.add(f.vertices, 1, "ADD")
bpy.ops.object.mode_set()
if wm.modo == "weight":
obj.vertex_groups.active_index = 0
scn.objects.active = rig
obj.select = 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"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
obj = bpy.context.object
return (obj.type == "ARMATURE" and obj.is_visible(bpy.context.scene))
def execute(self, context):
obj = bpy.context.object
scn = bpy.context.scene
fra = scn.frame_current
wm = context.window_manager
# clear the animation and get the list of bones
if obj.animation_data:
obj.animation_data_clear()
bpy.ops.object.mode_set(mode="POSE")
bones = obj.pose.bones[0].children_recursive
if wm.flip:
rot = -3.141592
else:
rot = wm.rot_max / 57.3
extra = wm.rot_time * wm.bounce
ruido = max(wm.rot_time + extra, wm.sca_time) + wm.fold_noise
vel = (wm.fold_duration - ruido) / len(bones)
# introduce scale and rotation keyframes
for a, b in enumerate(bones):
t = fra + a * vel + randint(0, wm.fold_noise)
if wm.flip:
b.scale = (1, 1, 1)
elif wm.from_point:
b.scale = (0, 0, 0)
else:
b.scale = (1, 0, 0)
if not wm.flip:
b.keyframe_insert("scale", frame=t)
b.scale = (1, 1, 1)
b.keyframe_insert("scale", frame=t + wm.sca_time)
if wm.rot_max:
b.rotation_mode = "XYZ"
if wm.wiggle_rot:
euler = (uniform(-rot, rot), uniform(-rot, rot), uniform(-rot, rot))
else:
euler = (rot, 0, 0)
b.rotation_euler = euler
b.keyframe_insert("rotation_euler", frame=t)
if wm.bounce:
val = wm.bounce * -.10
b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2])
b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + .25 * extra)
val = wm.bounce * .05
b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2])
b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + .50 * extra)
val = wm.bounce * -.025
b.rotation_euler = (val * euler[0], val * euler[1], val * euler[2])
b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + .75 * extra)
b.rotation_euler = (0, 0, 0)
b.keyframe_insert("rotation_euler", frame=t + wm.rot_time + extra)
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):
wm = context.window_manager
layout = self.layout
column = layout.column()
column.operator("object.set_up_fold", text="1. Set Up Unfold")
column.prop(wm, "modo")
column.prop(wm, "flip")
layout.separator()
column = layout.column()
column.operator("object.animate_fold", text="2. Animate Unfold")
column.prop(wm, "fold_duration")
column.prop(wm, "sca_time")
column.prop(wm, "rot_time")
column.prop(wm, "rot_max")
row = column.row(align=True)
row.prop(wm, "fold_noise")
row.prop(wm, "bounce")
row = column.row(align=True)
row.prop(wm, "wiggle_rot")
if not wm.flip:
row.prop(wm, "from_point")
def register():
bpy.utils.register_class(Set_Up_Fold)
bpy.utils.register_class(Animate_Fold)
bpy.utils.register_class(PanelFOLD)
def unregister():
bpy.utils.unregister_class(Set_Up_Fold)
bpy.utils.unregister_class(Animate_Fold)
bpy.utils.unregister_class(PanelFOLD)
if __name__ == "__main__":
register()