Page MenuHome

add_mesh_mixmesh.py

add_mesh_mixmesh.py

# -*- coding: utf-8 -*-
# ##### BEGIN GPL LICENSE BLOCK #####
#
# Blender script - MixMesh - Random Interpolated new mesh data
# 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) Feb 2012 by John Michael Palmer
# All rights reserved.
#
# Contact: john53john@gmail.com
# Information: none yet ###
#
# The Original Code is: all of this file.
#
# Contributor(s): none yet.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "MixMesh",
"author": "John Michael Palmer (jump)",
"version": (0, 0, 2),
"blender": (2, 6, 3),
"location": "",
"description": "Mix meshes to make new objects",
"warning": "",
"wiki_url": "",
"tracker_url": ""\
"",
"category": "Add Mesh"}
import bpy
import mathutils as mut
import bmesh
class MixMeshSettings(bpy.types.PropertyGroup):
nnew = bpy.props.IntProperty(description='number of new objects to create', default=1,min=1,max=200)
rowlength = bpy.props.IntProperty(description='Row Length of new objects group', default=4,min=1,max=200)
meshA = bpy.props.StringProperty(default='???')
meshB = bpy.props.StringProperty(default='???')
shapekeyA = bpy.props.StringProperty(default='???') # choice of shapekey from meshA
shapekeyB = bpy.props.StringProperty(default='???') # choice of shapekey from meshA
#withusers = bpy.props.BoolProperty(description='show meshes with more than 0 users', default=False)
newname = bpy.props.StringProperty(default='Mixed')
absterms = bpy.props.BoolProperty(description='Use Absolute values for terms', default=False)
vrand = bpy.props.FloatProperty(default=0.0,precision=3,description='Random Range for individual vertices', step=10)
rangecC = bpy.props.FloatProperty(description='range center, constant C', default=0.5, step=10)
rangecCrr = bpy.props.FloatProperty(description='constant C, Random Range', default=0.0, step=10)
rangecL = bpy.props.FloatVectorProperty(size = 3, description='Range Center, Linear coefficients for x,y and z', default=(0.0,0.0,0.0), step=10)
rangecLrr = bpy.props.FloatVectorProperty(size = 3, description='Linear coefficients. Random Range', default=(0.0,0.0,0.0), step=10)
rangecQa = bpy.props.FloatVectorProperty(size = 3, description='Range Center. Quadratic coefficients for x^2,y^2 and z^2', default=(0.0,0.0,0.0), step=10)
rangecQarr = bpy.props.FloatVectorProperty(size = 3, description='Quadratic coefficients for x^2,y^2 and z^2. Random Range', default=(0.0,0.0,0.0), step=10)
rangecQb = bpy.props.FloatVectorProperty(size = 3, description='Range Center. Quadratic coefficients for xy,xz and yz', default=(0.0,0.0,0.0), step=10)
rangecQbrr = bpy.props.FloatVectorProperty(size = 3, description='Quadratic coefficients for xy,xz and yz. Random Range', default=(0.0,0.0,0.0), step=10)
termsmeshfrac = bpy.props.FloatProperty(description='Fraction from mesh A to B to use for the x, y, and z terms for Range center calculation', default=0.5, step=10)
termscenter = bpy.props.EnumProperty(items = (('Origins','Mesh Origins','Mesh Center calculated using mesh A, B and Terms mesh fraction'),
('BoxMin','Bounding Boxes Minimum','Uses maximums of bounding boxes of meshes and Terms mesh fraction'),
('BoxMax','Bounding Boxes Maximum','Uses minimums of bounding boxes of meshes and Terms mesh fraction')),
description = 'Choice of origin for the terms', default='Origins')
normalizeterms = bpy.props.EnumProperty(items =(('none','None','Don\'t scale the terms'),
('Box','Bounding Box','Use the dimensions of the bounding box of the terms'),
('BoxMax','Bounding Box Maximum','Use the maximum of the bounding box of the terms')),
description = 'How to rescale the terms to calculate the new mesh', default='none') #('BFirst','Bounding Boxes first','Normalize each mesh first with bounding box dimensions')
verticesfrom = bpy.props.EnumProperty(items =(('objects','Objects','From meshes used by objects'),
('meshes','Meshes','From meshes'),
('shape','Shape Keys','From shape keys used by meshes')),
description = 'Where to take the Vertices data from', default='meshes')
outputmode = bpy.props.EnumProperty(items =(('objects','New Objects','New mesh objects without shape keys'),
('objects +keys','Objects and Shape keys','Makes new objects with new shape keys'),
('objects 2keys','Objects with 2 Shape keys','Makes new objects with only 2 new shape keys'),
('objects b+key','Objects, Basis and Shape key','Makes new objects with new shape keys'),
('one key','One shape key','Adds one new shape key to the mesh with name (will overwrite if name exists)'),
('keys','n Shape keys','Adds n new shape keys to the mesh')),
description = 'How to make output data', default='objects')
panelview = bpy.props.BoolVectorProperty(size=2, default=(False,False))
class MixMeshPanel(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_idname = 'MixMpanel'
bl_label = 'MixMesh'
def draw(self, context):
sm = context.scene.mixmesh
viewicon = ['RESTRICT_VIEW_ON','RESTRICT_VIEW_OFF']
operator_text = 'Make Objects'
lo = self.layout
col = lo.column()
col.prop(sm,'verticesfrom',text='Use')
if(sm.verticesfrom == 'meshes'):
col.prop_search(sm,'meshA',bpy.data,'meshes',icon='MESH_DATA',text='Mesh A')
col.prop_search(sm,'meshB',bpy.data,'meshes',icon='MESH_DATA',text='Mesh B')
elif(sm.verticesfrom == 'objects'):
col.prop_search(sm,'meshA',bpy.data,'objects',icon='OBJECT_DATA',text='Object A')
col.prop_search(sm,'meshB',bpy.data,'objects',icon='OBJECT_DATA',text='Object B')
elif(sm.verticesfrom == 'shape'):
col.prop_search(sm,'meshA',bpy.data,'objects',icon='OBJECT_DATA',text='From Object')
if(sm.meshA in bpy.data.objects.keys()):
meshdata = bpy.data.objects[sm.meshA].data
if(meshdata.shape_keys is not None):
shapekeys = meshdata.shape_keys
col.prop_search(sm,'shapekeyA',shapekeys,'key_blocks',icon='SHAPEKEY_DATA',text='Shape Key A')
col.prop_search(sm,'shapekeyB',shapekeys,'key_blocks',icon='SHAPEKEY_DATA',text='Shape Key B')
col.prop(sm,'newname',text='New Name')
row = col.row(align = True)
row.prop(sm,'nnew',text='New Objects')
row.prop(sm,'rowlength',text='Row Length')
box = col.box()
colb = box.column()
colb.label('Range Center Coefficients')
colb2 = colb.column(align=True)
colb2.prop(sm,'rangecC',text='Constant')
colb2.prop(sm,'rangecCrr',text='Random')
row = colb2.row(align=True)
row.label('Linear Terms')
row.operator('mixmeshops.setpanelview',icon=viewicon[int(sm.panelview[0])],text="",emboss=False).pos=0
if(sm.panelview[0]):
row = colb2.row()
row.label('x')
row.label('y')
row.label('z')
row = colb2.row()
row.prop(sm,'rangecL',text='')
row = colb2.row()
row.prop(sm,'rangecLrr',text='')
row = colb2.row(align=True)
row.label('Quadratic Terms')
row.operator('mixmeshops.setpanelview',icon=viewicon[int(sm.panelview[1])],text="",emboss=False).pos=1
if(sm.panelview[1]):
row = colb2.row()
row.label('x^2')
row.label('y^2')
row.label('z^2')
row = colb2.row()
row.prop(sm,'rangecQa',text='')
row = colb2.row()
row.prop(sm,'rangecQarr',text='')
row = colb2.row()
row.label('xy')
row.label('xz')
row.label('yz')
row = colb2.row()
row.prop(sm,'rangecQb',text='')
row = colb2.row()
row.prop(sm,'rangecQbrr',text='')
colb.prop(sm,'normalizeterms',text='Normalization')
colb.prop(sm,'termscenter',text='Origin')
row = colb.row(align = True)
row.prop(sm,'termsmeshfrac',text='Terms mesh at')
row.prop(sm,'absterms',text='Absolute Values')
row = col.row()
row.prop(sm,'vrand',text='Vertex Random')
col.prop(sm,'outputmode',text='Output Mode')
if(sm.outputmode == 'keys'):
operator_text = 'Make Shape Keys'
elif(sm.outputmode == 'one key'):
operator_text = 'Make Shape Key'
col.operator('mixmeshops.makeobjects', text=operator_text)
@classmethod
def poll(cls, context):
inobjectmode = (bpy.context.mode == 'OBJECT')
#atleastoneobject = bpy.context.selected_objects != []
return (inobjectmode)
class MakeObjects(bpy.types.Operator):
bl_idname = 'mixmeshops.makeobjects'
bl_label = 'Union All Objects'
@classmethod
def poll(cls, context):
sm = context.scene.mixmesh
if(sm.verticesfrom == 'meshes'):
meshAok = sm.meshA in bpy.data.meshes.keys()
meshBok = sm.meshB in bpy.data.meshes.keys()
allok = meshAok and meshBok
elif(sm.verticesfrom == 'objects'):
obAok = sm.meshA in bpy.data.objects.keys()
obBok = sm.meshB in bpy.data.objects.keys()
allok = obAok and obBok
elif(sm.verticesfrom == 'shape'):
obAok = sm.meshA in bpy.data.objects.keys()
if obAok:
key_block_names = bpy.data.objects[sm.meshA].data.shape_keys.key_blocks.keys()
keyAok = sm.shapekeyA in key_block_names
keyBok = sm.shapekeyB in key_block_names
allok = keyAok and keyBok
else:
allok = False
if(sm.normalizeterms == 'BoxMax' and sm.termscenter == 'BoxMax'):
allok = False
return allok
def execute(self, context):
def update_vertices(vertdata):
for vertn in range(numverts):
x,y,z = vector_div(vertturms[vertn],normvec)
if(sm.absterms):
fposL = abs(Lx * x) + abs(Ly * y) + abs(Lz * z)
fposQa = abs(Qxx * x * x) + abs(Qyy * y * y) + abs(Qzz * z * z)
fposQb = abs(Qxy * x * y) + abs(Qxz * x * z) + abs(Qyz * y * z)
else:
fposL = (Lx * x) + (Ly * y) + (Lz * z)
fposQa = (Qxx * x * x) + (Qyy * y * y) + (Qzz * z * z)
fposQb = (Qxy * x * y) + (Qxz * x * z) + (Qyz * y * z)
fpos = fposQa + fposQb + fposL + C
randval = (mut.noise.random() - 0.5) * sm.vrand # random position for each vertex
fpos += randval
verta = vertcalc[vertn][0]
vertrange = vertcalc[vertn][1]
vertdata[vertn].co = verta + (vertrange * fpos)
bd = bpy.data
sc = context.scene
sm = context.scene.mixmesh
cloc = sc.cursor_location
startcloc = mut.Vector(cloc)
startactiveob = sc.objects.active
usinggroup = False
if(sm.outputmode == 'one key' or sm.outputmode == 'keys'):
general_outputmode = 'only keys'
else:
general_outputmode = 'objects'
if (sm.nnew > 1 and general_outputmode == 'objects'):
usinggroup = True
if usinggroup:
mixgroupname = make_new_name("Mix_"+sm.newname,bd.groups.keys())
bpy.data.groups.new(mixgroupname)
if(sm.verticesfrom == 'meshes'):
mesha = bd.meshes[sm.meshA]
vdataa = mesha.vertices
vdatab = bd.meshes[sm.meshB].vertices
if(general_outputmode == 'only keys'):
g = (o for o in bpy.data.objects if o.data.name == sm.meshA)
sc.objects.active = next(g) #when only adding keys, the operator works on the active object!!!
else:
ob = bd.objects[sm.meshA]
sc.objects.active = ob #when only adding keys, the operator works on the active object!!!
mesha = ob.data
if(sm.verticesfrom == 'objects'):
vdataa = mesha.vertices
vdatab = bd.objects[sm.meshB].data.vertices
elif(sm.verticesfrom == 'shape'):
vdataa = mesha.shape_keys.key_blocks[sm.shapekeyA].data
vdatab = mesha.shape_keys.key_blocks[sm.shapekeyB].data
numverts = min(len(vdataa),len(vdatab))
if(general_outputmode == 'objects' and sm.outputmode != 'objects +keys'): #new objects that start with a mesh without keys
frommeshdata = bmesh.new()
if(sm.verticesfrom == 'shape'):
ki = mesha.shape_keys.key_blocks.find(sm.shapekeyA)
frommeshdata.from_mesh(mesha,use_shape_key=True, shape_key_index=ki)
else:
frommeshdata.from_mesh(mesha)
else:
frommeshdata = mesha
# get offset for the data for the new origin
shiftva, shiftvb = dataoffset(vdataa,vdatab,sm.termscenter)
# make a list of vertices for the mesh that will be used for the terms
vertturms, vertcalc = maketerms(numverts,vdataa,vdatab,shiftva,shiftvb,sm.termsmeshfrac)
# Set the normalizing vector
normvec = setnormvec(vertturms, sm.normalizeterms)
# now start making the new objects
colinrow = 0
bhight = 0
shapesperob = 1
if(sm.outputmode == 'objects 2keys'):
shapesperob = 2
for objectn in range(sm.nnew):
colinrow += 1
for shapenum in range(shapesperob):
newvdata = putvdatahere(frommeshdata, sm.newname, sm.outputmode, general_outputmode, shapenum)
if usinggroup and (shapenum == 0):
bpy.ops.object.group_link(group=mixgroupname)
# calculate coefficients using center and random range
C = (mut.noise.random() * sm.rangecCrr) - (sm.rangecCrr / 2.0) + sm.rangecC
Lx,Ly,Lz = vector_times((mut.noise.random_unit_vector() / 2.0), sm.rangecLrr) + mut.Vector(sm.rangecL)
Qxx,Qyy,Qzz = vector_times((mut.noise.random_unit_vector() / 2.0), sm.rangecQarr) + mut.Vector(sm.rangecQa)
Qxy,Qxz,Qyz = vector_times((mut.noise.random_unit_vector() / 2.0), sm.rangecQbrr) + mut.Vector(sm.rangecQb)
update_vertices(newvdata)
# calculate maximum width and height
boboxc = boundingbox(newvdata)
bhight = max(boboxc[1][1] - boboxc[0][1],bhight)
# move the curser position
if colinrow >= sm.rowlength:
cloc[0:3] = startcloc[0], cloc[1] + bhight, cloc[2]
colinrow = 0
bhight = 0
else:
bwidth = boboxc[1][0] - boboxc[0][0]
cloc[0] += bwidth
cloc[0:3] = startcloc
return {'FINISHED'}
class SetPanelView(bpy.types.Operator):
'''Adds a footnode to the active object'''
bl_idname = 'mixmeshops.setpanelview'
bl_label = 'Panel View Toggle'
pos = bpy.props.IntProperty(default=0)
def execute(self, context):
sm = context.scene.mixmesh
sm.panelview[self.pos] = not(sm.panelview[self.pos])
return {'FINISHED'}
def putvdatahere(meshin, newname, omode, gen_omode, shapenum):
bd = bpy.data
sc = bpy.context.scene
if(omode == 'objects'): # here meshin should be a bmesh object
mixname = make_new_name(newname, bd.objects.keys() + bd.meshes.keys())
newmesh = bpy.data.meshes.new(mixname)
meshin.to_mesh(newmesh)
newob = new_meshob(mixname,newmesh)
vdataout = newmesh.vertices
elif(omode == 'objects +keys'):
newmesh = meshin.copy()
mixname = make_new_name(newname, bd.objects.keys() + bd.meshes.keys())
newmesh.name = mixname
newob = new_meshob(mixname,newmesh)
bpy.ops.object.shape_key_add(from_mix=False) #make new shape_key
vdataout = newmesh.shape_keys.key_blocks[-1].data #Take last shape key (the one just made)
elif(omode == 'objects b+key'): # here meshin should be a bmesh object
mixname = make_new_name(newname, bd.objects.keys() + bd.meshes.keys())
newmesh = bpy.data.meshes.new(mixname)
meshin.to_mesh(newmesh)
newob = new_meshob(mixname,newmesh)
bpy.ops.object.shape_key_add(from_mix=False) #make new basis shape_key
bpy.ops.object.shape_key_add(from_mix=False) #make new shape_key
vdataout = newmesh.shape_keys.key_blocks[-1].data #Take last shape key (the one just made)
elif(omode == 'objects 2keys'): # here meshin should be a bmesh object
if(shapenum == 0):
mixname = make_new_name(newname, bd.objects.keys() + bd.meshes.keys())
newmesh = bpy.data.meshes.new(mixname)
meshin.to_mesh(newmesh)
newob = new_meshob(mixname,newmesh)
else:
newmesh = sc.objects.active.data
bpy.ops.object.shape_key_add(from_mix=False) #make new shape_key
vdataout = newmesh.shape_keys.key_blocks[-1].data #Take last shape key (the one just made)
elif(omode == 'keys'):
newmesh = sc.objects.active.data
bpy.ops.object.shape_key_add(from_mix=False) #make new shape_key
vdataout = newmesh.shape_keys.key_blocks[-1].data #Take last shape key (the one just made)
elif(omode == 'one key'):
newmesh = sc.objects.active.data
#look for shapekey with newname
if newname in meshin.shape_keys.key_blocks.keys():
vdataout = newmesh.shape_keys.key_blocks[newname].data
else:
bpy.ops.object.shape_key_add(from_mix=False) #make new shape_key
newmesh.shape_keys.key_blocks[-1].name = newname
vdataout = newmesh.shape_keys.key_blocks[-1].data #Take last shape key (the one just made)
if(shapenum == 0) and (gen_omode == 'objects'):
newob.location = sc.cursor_location
return vdataout
def new_meshob(namein,meshin):
sco = bpy.context.scene.objects
ob = bpy.data.objects.new(namein, meshin)
sco.link(ob)
sco.active = ob
return ob
def setnormvec(terms, normalisewith):
if(normalisewith == 'Box'):
boboxt = boundingbox(terms)
nvec = boboxt[1] - boboxt[0]
elif(normalisewith == 'BoxMax'):
boboxt = boundingbox(terms)
nvec = boboxt[1]
else:
nvec = mut.Vector([1,1,1])
return nvec
def dataoffset(vertdataA,vertdataB,neworigin):
if neworigin == 'Origins':
shiftva = mut.Vector([0,0,0])
shiftvb = mut.Vector([0,0,0])
elif neworigin == 'BoxMin':
boboxa = boundingbox(vertdataA)
boboxb = boundingbox(vertdataB)
shiftva = boboxa[0]
shiftvb = boboxb[0]
elif neworigin == 'BoxMax':
boboxa = boundingbox(vertdataA)
boboxb = boundingbox(vertdataB)
shiftva = boboxa[1]
shiftvb = boboxb[1]
return (shiftva, shiftvb)
def maketerms(numverts,vertdataA,vertdataB,vertshiftA,vertshiftB, tfrac):
terms = []
rangedata = []
for vertn in range(numverts):
verta = vertdataA[vertn].co - vertshiftA
vertb = vertdataB[vertn].co - vertshiftB
vertrange = vertb - verta
vertc = verta + (vertrange * tfrac)
terms.append(vertc)
rangedata.append((verta,vertrange))
return (terms,rangedata)
def make_new_name(prefix, thesenames):
numstart = 0
testname = prefix #+'%03d'%numstart
while testname in thesenames:
numstart += 1
testname = prefix+'%03d'%numstart
return testname
def boundingbox(datain):
if(len(datain) > 0):
if(type(datain) is bpy.types.Mesh):
dtype = 'data'
verticesdata = datain.vertices
elif(type(datain[0]) is bpy.types.ShapeKeyPoint or
type(datain[0]) is bpy.types.MeshVertex):
dtype = 'data'
verticesdata = datain
elif(type(datain) is list):
dtype = 'list'
verticesdata = datain
else:
dtype = 'none'
vx, vy, vz = ([-1], [-1], [-1])
if(dtype == 'data'):
vx = [v.co[0] for v in verticesdata]
vy = [v.co[1] for v in verticesdata]
vz = [v.co[2] for v in verticesdata]
elif(dtype == 'list'):
vx = [v[0] for v in verticesdata]
vy = [v[1] for v in verticesdata]
vz = [v[2] for v in verticesdata]
minv = mut.Vector([min(vx),min(vy),min(vz)])
maxv = mut.Vector([max(vx),max(vy),max(vz)])
else:
minv, maxv = (0, 0)
return [minv,maxv]
def vector_times(v1,v2):
return mut.Vector([v1[0]*v2[0], v1[1]*v2[1],v1[2]*v2[2]])
def vector_div(v1,v2):
return mut.Vector([v1[0]/v2[0], v1[1]/v2[1],v1[2]/v2[2]])
def vector_add(v,n):
return mut.Vector([v[0]+n, v[1]+n, v[2]+n])
def vector_mean(v):
meanval = sum(v)/len(v)
return mut.Vector([meanval,meanval,meanval])
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.mixmesh = bpy.props.PointerProperty(type=MixMeshSettings)
def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.Scene.mixmesh
if __name__ == "__main__":
register()

File Metadata

Mime Type
text/x-python
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
5c/32/c7d4eeea1be8c8dae984dce5d52b

Event Timeline