copied across changes which were missed by merging.

This commit is contained in:
Campbell Barton
2011-05-09 02:45:52 +00:00
parent 6ef77cf95a
commit 14a2330d7b
138 changed files with 156 additions and 22164 deletions

View File

@@ -1,1112 +0,0 @@
#!BPY
"""
Name: 'Cal3D (.cfg .xaf .xsf .xmf .xrf)...'
Blender: 243
Group: 'Export'
Tip: 'Export armature/bone/mesh/action data to the Cal3D format.'
"""
# export_cal3d.py
# Copyright (C) 2003-2004 Jean-Baptiste LAMY -- jibalamy@free.fr
# Copyright (C) 2004 Matthias Braun -- matze@braunis.de
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
__version__ = '0.9f'
__author__ = 'Jean-Baptiste, Jiba, Lamy, Campbell Barton (Ideasman42)'
__email__ = ['Authors email, jibalamy:free*fr']
__url__ = ['Soya3ds homepage, http://home.gna.org/oomadness/en/soya/', 'Cal3d, http://cal3d.sourceforge.net']
__bpydoc__ =\
'''This script is a Blender => Cal3D converter.
(See http://blender.org and http://cal3d.sourceforge.net)
USAGE:
To install it, place the script in your $HOME/.blender/scripts directory.
Then open the File->Export->Cal3d v0.9 menu. And select the filename of the .cfg file.
The exporter will create a set of other files with same prefix (ie. bla.cfg, bla.xsf,
bla_Action1.xaf, bla_Action2.xaf, ...).
You should be able to open the .cfg file in cal3d_miniviewer.
NOT (YET) SUPPORTED:
- Rotation, translation, or stretching Blender objects is still quite
buggy, so AVOID MOVING / ROTATING / RESIZE OBJECTS (either mesh or armature) !
Instead, edit the object (with tab), select all points / bones (with "a"),
and move / rotate / resize them.<br>
- no support for exporting springs yet<br>
- no support for exporting material colors (most games should only use images
I think...)
KNOWN ISSUES:
- Cal3D versions <=0.9.1 have a bug where animations aren't played when the root bone
is not animated;<br>
- Cal3D versions <=0.9.1 have a bug where objects that aren't influenced by any bones
are not drawn (fixed in Cal3D CVS).
NOTES:
It requires a very recent version of Blender (>= 2.44).
Build a model following a few rules:<br>
- Use only a single armature;<br>
- Use only a single rootbone (Cal3D doesn't support floating bones);<br>
- Use only locrot keys (Cal3D doesn't support bone's size change);<br>
- Don't try to create child/parent constructs in blender object, that gets exported
incorrectly at the moment;<br>
- Objects or animations whose names start by "_" are not exported (hidden object).
You can pass as many parameters as you want at the end, "EXPORT_FOR_SOYA=1" is just an
example. The parameters are the same as below.
'''
# True (=1) to export for the Soya 3D engine
# (http://oomadness.tuxfamily.org/en/soya).
# (=> rotate meshes and skeletons so as X is right, Y is top and -Z is front)
# EXPORT_FOR_SOYA = 0
# Enables LODs computation. LODs computation is quite slow, and the algo is
# surely not optimal :-(
LODS = 0
# Scale the model (not supported by Soya).
# See also BASE_MATRIX below, if you want to rotate/scale/translate the model at
# the exportation.
#########################################################################################
# Code starts here.
# The script should be quite re-useable for writing another Blender animation exporter.
# Most of the hell of it is to deal with Blender's head-tail-roll bone's definition.
import math
import Blender
import BPyMesh
import BPySys
import BPyArmature
import BPyObject
import bpy
def best_armature_root(armature):
'''
Find the armature root bone with the most children, return that bone
'''
bones = [bone for bone in armature.bones.values() if bone.hasChildren() == True]
if len(bones) == 1:
return bones[0]
# Get the best root since we have more then 1
bones = [(len(bone.getAllChildren()), bone) for bone in bones]
bones.sort()
return bones[-1][1] # bone with most children
Vector = Blender.Mathutils.Vector
Quaternion = Blender.Mathutils.Quaternion
Matrix = Blender.Mathutils.Matrix
# HACK -- it seems that some Blender versions don't define sys.argv,
# which may crash Python if a warning occurs.
# if not hasattr(sys, 'argv'): sys.argv = ['???']
def matrix_multiply(b, a):
return [ [
a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1],
a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2],
0.0,
], [
a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1],
a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2],
0.0,
], [
a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0],
a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1],
a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2],
0.0,
], [
a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + b[3][0],
a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + b[3][1],
a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + b[3][2],
1.0,
] ]
# multiplies 2 quaternions in x,y,z,w notation
def quaternion_multiply(q1, q2):
return Quaternion(\
q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1],
q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2],
q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0],
q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2],\
)
def matrix_translate(m, v):
m[3][0] += v[0]
m[3][1] += v[1]
m[3][2] += v[2]
return m
def matrix2quaternion(m):
s = math.sqrt(abs(m[0][0] + m[1][1] + m[2][2] + m[3][3]))
if s == 0.0:
x = abs(m[2][1] - m[1][2])
y = abs(m[0][2] - m[2][0])
z = abs(m[1][0] - m[0][1])
if (x >= y) and (x >= z): return Quaternion(1.0, 0.0, 0.0, 0.0)
elif (y >= x) and (y >= z): return Quaternion(0.0, 1.0, 0.0, 0.0)
else: return Quaternion(0.0, 0.0, 1.0, 0.0)
q = Quaternion([
-(m[2][1] - m[1][2]) / (2.0 * s),
-(m[0][2] - m[2][0]) / (2.0 * s),
-(m[1][0] - m[0][1]) / (2.0 * s),
0.5 * s,
])
q.normalize()
#print q
return q
def vector_by_matrix_3x3(p, m):
return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0],
p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1],
p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2]]
def vector_add(v1, v2):
return [v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]]
def vector_sub(v1, v2):
return [v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]]
def quaternion2matrix(q):
xx = q[0] * q[0]
yy = q[1] * q[1]
zz = q[2] * q[2]
xy = q[0] * q[1]
xz = q[0] * q[2]
yz = q[1] * q[2]
wx = q[3] * q[0]
wy = q[3] * q[1]
wz = q[3] * q[2]
return Matrix([1.0 - 2.0 * (yy + zz), 2.0 * (xy + wz), 2.0 * (xz - wy), 0.0],
[ 2.0 * (xy - wz), 1.0 - 2.0 * (xx + zz), 2.0 * (yz + wx), 0.0],
[ 2.0 * (xz + wy), 2.0 * (yz - wx), 1.0 - 2.0 * (xx + yy), 0.0],
[0.0 , 0.0 , 0.0 , 1.0])
def matrix_invert(m):
det = (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
+ m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]))
if det == 0.0: return None
det = 1.0 / det
r = [ [
det * (m[1][1] * m[2][2] - m[2][1] * m[1][2]),
- det * (m[0][1] * m[2][2] - m[2][1] * m[0][2]),
det * (m[0][1] * m[1][2] - m[1][1] * m[0][2]),
0.0,
], [
- det * (m[1][0] * m[2][2] - m[2][0] * m[1][2]),
det * (m[0][0] * m[2][2] - m[2][0] * m[0][2]),
- det * (m[0][0] * m[1][2] - m[1][0] * m[0][2]),
0.0
], [
det * (m[1][0] * m[2][1] - m[2][0] * m[1][1]),
- det * (m[0][0] * m[2][1] - m[2][0] * m[0][1]),
det * (m[0][0] * m[1][1] - m[1][0] * m[0][1]),
0.0,
] ]
r.append([
-(m[3][0] * r[0][0] + m[3][1] * r[1][0] + m[3][2] * r[2][0]),
-(m[3][0] * r[0][1] + m[3][1] * r[1][1] + m[3][2] * r[2][1]),
-(m[3][0] * r[0][2] + m[3][1] * r[1][2] + m[3][2] * r[2][2]),
1.0,
])
return r
def point_by_matrix(p, m):
return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0] + m[3][0],
p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1] + m[3][1],
p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2] + m[3][2]]
# Hack for having the model rotated right.
# Put in BASE_MATRIX your own rotation if you need some.
BASE_MATRIX = None
# Cal3D data structures
CAL3D_VERSION = 910
MATERIALS = {} # keys are (mat.name, img.name)
class Cal3DMaterial(object):
__slots__ = 'amb', 'diff', 'spec', 'shininess', 'maps_filenames', 'id'
def __init__(self, blend_world, blend_material, blend_images):
# Material Settings
if blend_world: amb = [ int(c*255) for c in blend_world.amb ]
else: amb = [0,0,0] # Default value
if blend_material:
self.amb = tuple([int(c*blend_material.amb) for c in amb] + [255])
self.diff = tuple([int(c*255) for c in blend_material.rgbCol] + [int(blend_material.alpha*255)])
self.spec = tuple([int(c*255) for c in blend_material.rgbCol] + [int(blend_material.alpha*255)])
self.shininess = (float(blend_material.hard)-1)/5.10
else:
self.amb = tuple(amb + [255])
self.diff = (255,255,255,255)
self.spec = (255,255,255,255)
self.shininess = 1.0
self.maps_filenames = []
for image in blend_images:
if image:
self.maps_filenames.append( image.filename.split('\\')[-1].split('/')[-1] )
self.id = len(MATERIALS)
MATERIALS[blend_material, blend_images] = self
# new xml format
def writeCal3D(self, file):
file.write('<?xml version="1.0"?>\n')
file.write('<HEADER MAGIC="XRF" VERSION="%i"/>\n' % CAL3D_VERSION)
file.write('<MATERIAL NUMMAPS="%s">\n' % len(self.maps_filenames))
file.write('\t<AMBIENT>%i %i %i %i</AMBIENT>\n' % self.amb)
file.write('\t<DIFFUSE>%i %i %i %i</DIFFUSE>\n' % self.diff)
file.write('\t<SPECULAR>%i %i %i %i</SPECULAR>\n' % self.spec)
file.write('\t<SHININESS>%.6f</SHININESS>\n' % self.shininess)
for map_filename in self.maps_filenames:
file.write('\t<MAP>%s</MAP>\n' % map_filename)
file.write('</MATERIAL>\n')
class Cal3DMesh(object):
__slots__ = 'name', 'submeshes', 'matrix', 'matrix_normal'
def __init__(self, ob, blend_mesh, blend_world):
self.name = ob.name
self.submeshes = []
BPyMesh.meshCalcNormals(blend_mesh)
self.matrix = ob.matrixWorld
self.matrix_normal = self.matrix.copy().rotationPart()
#if BASE_MATRIX:
# matrix = matrix_multiply(BASE_MATRIX, matrix)
face_groups = {}
blend_materials = blend_mesh.materials
uvlayers = ()
mat = None # incase we have no materials
if blend_mesh.faceUV:
uvlayers = blend_mesh.getUVLayerNames()
if len(uvlayers) == 1:
for f in blend_mesh.faces:
image = (f.image,) # bit in a tuple so we can match multi UV code
if blend_materials: mat = blend_materials[f.mat] # if no materials, mat will always be None
face_groups.setdefault( (mat,image), (mat,image,[]) )[2].append( f )
else:
# Multi UV's
face_multi_images = [[] for i in xrange(len(blend_mesh.faces))]
face_multi_uvs = [[[] for i in xrange(len(f)) ] for f in blend_mesh.faces]
for uvlayer in uvlayers:
blend_mesh.activeUVLayer = uvlayer
for i, f in enumerate(blend_mesh.faces):
face_multi_images[i].append(f.image)
if f.image:
for j, uv in enumerate(f.uv):
face_multi_uvs[i][j].append( tuple(uv) )
# Convert UV's to tuples so they can be compared with eachother
# when creating new verts
for fuv in face_multi_uvs:
for i, uv in enumerate(fuv):
fuv[i] = tuple(uv)
for i, f in enumerate(blend_mesh.faces):
image = tuple(face_multi_images[i])
if blend_materials: mat = blend_materials[f.mat]
face_groups.setdefault( (mat,image), (mat,image,[]) )[2].append( f )
else:
# No UV's
for f in blend_mesh.faces:
if blend_materials: mat = blend_materials[f.mat]
face_groups.setdefault( (mat,()), (mat,(),[]) )[2].append( f )
for blend_material, blend_images, faces in face_groups.itervalues():
try: material = MATERIALS[blend_material, blend_images]
except: material = MATERIALS[blend_material, blend_images] = Cal3DMaterial(blend_world, blend_material, blend_images)
submesh = Cal3DSubMesh(self, material, len(self.submeshes))
self.submeshes.append(submesh)
# Check weather we need to write UVs, dont do it if theres no image
# Multilayer UV's have alredy checked that they have images when
# building face_multi_uvs
if len(uvlayers) == 1:
if blend_images == (None,):
write_single_layer_uvs = False
else:
write_single_layer_uvs = True
for face in faces:
if not face.smooth:
normal = face.no
face_vertices = []
face_v = face.v
if len(uvlayers)>1:
for i, blend_vert in enumerate(face_v):
if face.smooth: normal = blend_vert.no
vertex = submesh.getVertex(blend_mesh, blend_vert, normal, face_multi_uvs[face.index][i])
face_vertices.append(vertex)
elif len(uvlayers)==1:
if write_single_layer_uvs:
face_uv = face.uv
for i, blend_vert in enumerate(face_v):
if face.smooth: normal = blend_vert.no
if write_single_layer_uvs: uvs = (tuple(face_uv[i]),)
else: uvs = ()
vertex = submesh.getVertex(blend_mesh, blend_vert, normal, uvs )
face_vertices.append(vertex)
else:
# No UVs
for i, blend_vert in enumerate(face_v):
if face.smooth: normal = blend_vert.no
vertex = submesh.getVertex(blend_mesh, blend_vert, normal, () )
face_vertices.append(vertex)
# Split faces with more than 3 vertices
for i in xrange(1, len(face) - 1):
submesh.faces.append(Cal3DFace(face_vertices[0], face_vertices[i], face_vertices[i + 1]))
def writeCal3D(self, file):
file.write('<?xml version="1.0"?>\n')
file.write('<HEADER MAGIC="XMF" VERSION="%i"/>\n' % CAL3D_VERSION)
file.write('<MESH NUMSUBMESH="%i">\n' % len(self.submeshes))
for submesh in self.submeshes:
submesh.writeCal3D(file, self.matrix, self.matrix_normal)
file.write('</MESH>\n')
class Cal3DSubMesh(object):
__slots__ = 'material', 'vertices', 'vert_mapping', 'vert_count', 'faces', 'nb_lodsteps', 'springs', 'id'
def __init__(self, mesh, material, id):
self.material = material
self.vertices = []
self.vert_mapping = {} # map original indicies to local
self.vert_count = 0
self.faces = []
self.nb_lodsteps = 0
self.springs = []
self.id = id
def getVertex(self, blend_mesh, blend_vert, normal, maps):
'''
Request a vertex, and create a new one or return a matching vertex
'''
blend_index = blend_vert.index
index_map = self.vert_mapping.get(blend_index)
if index_map == None:
vertex = Cal3DVertex(blend_vert.co, normal, maps, blend_mesh.getVertexInfluences(blend_index))
self.vertices.append([vertex])
self.vert_mapping[blend_index] = len(self.vert_mapping)
self.vert_count +=1
return vertex
else:
vertex_list = self.vertices[index_map]
for v in vertex_list:
if v.normal == normal and\
v.maps == maps:
return v # reusing
# No match, add a new vert
# Use the first verts influences
vertex = Cal3DVertex(blend_vert.co, normal, maps, vertex_list[0].influences)
vertex_list.append(vertex)
# self.vert_mapping[blend_index] = len(self.vert_mapping)
self.vert_count +=1
return vertex
def compute_lods(self):
'''Computes LODs info for Cal3D (there's no Blender related stuff here).'''
print 'Start LODs computation...'
vertex2faces = {}
for face in self.faces:
for vertex in (face.vertex1, face.vertex2, face.vertex3):
l = vertex2faces.get(vertex)
if not l: vertex2faces[vertex] = [face]
else: l.append(face)
couple_treated = {}
couple_collapse_factor = []
for face in self.faces:
for a, b in ((face.vertex1, face.vertex2), (face.vertex1, face.vertex3), (face.vertex2, face.vertex3)):
a = a.cloned_from or a
b = b.cloned_from or b
if a.id > b.id: a, b = b, a
if not couple_treated.has_key((a, b)):
# The collapse factor is simply the distance between the 2 points :-(
# This should be improved !!
if vector_dotproduct(a.normal, b.normal) < 0.9: continue
couple_collapse_factor.append((point_distance(a.loc, b.loc), a, b))
couple_treated[a, b] = 1
couple_collapse_factor.sort()
collapsed = {}
new_vertices = []
new_faces = []
for factor, v1, v2 in couple_collapse_factor:
# Determines if v1 collapses to v2 or v2 to v1.
# We choose to keep the vertex which is on the smaller number of faces, since
# this one has more chance of being in an extrimity of the body.
# Though heuristic, this rule yields very good results in practice.
if len(vertex2faces[v1]) < len(vertex2faces[v2]): v2, v1 = v1, v2
elif len(vertex2faces[v1]) == len(vertex2faces[v2]):
if collapsed.get(v1, 0): v2, v1 = v1, v2 # v1 already collapsed, try v2
if (not collapsed.get(v1, 0)) and (not collapsed.get(v2, 0)):
collapsed[v1] = 1
collapsed[v2] = 1
# Check if v2 is already colapsed
while v2.collapse_to: v2 = v2.collapse_to
common_faces = filter(vertex2faces[v1].__contains__, vertex2faces[v2])
v1.collapse_to = v2
v1.face_collapse_count = len(common_faces)
for clone in v1.clones:
# Find the clone of v2 that correspond to this clone of v1
possibles = []
for face in vertex2faces[clone]:
possibles.append(face.vertex1)
possibles.append(face.vertex2)
possibles.append(face.vertex3)
clone.collapse_to = v2
for vertex in v2.clones:
if vertex in possibles:
clone.collapse_to = vertex
break
clone.face_collapse_count = 0
new_vertices.append(clone)
# HACK -- all faces get collapsed with v1 (and no faces are collapsed with v1's
# clones). This is why we add v1 in new_vertices after v1's clones.
# This hack has no other incidence that consuming a little few memory for the
# extra faces if some v1's clone are collapsed but v1 is not.
new_vertices.append(v1)
self.nb_lodsteps += 1 + len(v1.clones)
new_faces.extend(common_faces)
for face in common_faces:
face.can_collapse = 1
# Updates vertex2faces
vertex2faces[face.vertex1].remove(face)
vertex2faces[face.vertex2].remove(face)
vertex2faces[face.vertex3].remove(face)
vertex2faces[v2].extend(vertex2faces[v1])
new_vertices.extend(filter(lambda vertex: not vertex.collapse_to, self.vertices))
new_vertices.reverse() # Cal3D want LODed vertices at the end
for i in xrange(len(new_vertices)): new_vertices[i].id = i
self.vertices = new_vertices
new_faces.extend(filter(lambda face: not face.can_collapse, self.faces))
new_faces.reverse() # Cal3D want LODed faces at the end
self.faces = new_faces
print 'LODs computed : %s vertices can be removed (from a total of %s).' % (self.nb_lodsteps, len(self.vertices))
def writeCal3D(self, file, matrix, matrix_normal):
file.write('\t<SUBMESH NUMVERTICES="%i" NUMFACES="%i" MATERIAL="%i" ' % \
(self.vert_count, len(self.faces), self.material.id))
file.write('NUMLODSTEPS="%i" NUMSPRINGS="%i" NUMTEXCOORDS="%i">\n' % \
(self.nb_lodsteps, len(self.springs),
len(self.material.maps_filenames)))
i = 0
for v in self.vertices:
for item in v:
item.id = i
item.writeCal3D(file, matrix, matrix_normal)
i += 1
for item in self.springs:
item.writeCal3D(file)
for item in self.faces:
item.writeCal3D(file)
file.write('\t</SUBMESH>\n')
class Cal3DVertex(object):
__slots__ = 'loc','normal','collapse_to','face_collapse_count','maps','influences','weight','cloned_from','clones','id'
def __init__(self, loc, normal, maps, blend_influences):
self.loc = loc
self.normal = normal
self.collapse_to = None
self.face_collapse_count = 0
self.maps = maps
self.weight = None
self.cloned_from = None
self.clones = []
self.id = -1
if len(blend_influences) == 0 or isinstance(blend_influences[0], Cal3DInfluence):
# This is a copy from another vert
self.influences = blend_influences
else:
# Pass the blender influences
self.influences = []
# should this really be a warning? (well currently enabled,
# because blender has some bugs where it doesn't return
# influences in python api though they are set, and because
# cal3d<=0.9.1 had bugs where objects without influences
# aren't drawn.
#if not blend_influences:
# print 'A vertex of object "%s" has no influences.\n(This occurs on objects placed in an invisible layer, you can fix it by using a single layer)' % ob.name
# sum of influences is not always 1.0 in Blender ?!?!
sum = 0.0
for bone_name, weight in blend_influences:
sum += weight
for bone_name, weight in blend_influences:
bone = BONES.get(bone_name)
if not bone: # keys
# print 'Couldnt find bone "%s" which influences object "%s"' % (bone_name, ob.name)
continue
if weight:
self.influences.append(Cal3DInfluence(bone, weight / sum))
def writeCal3D(self, file, matrix, matrix_normal):
if self.collapse_to:
collapse_id = self.collapse_to.id
else:
collapse_id = -1
file.write('\t\t<VERTEX ID="%i" NUMINFLUENCES="%i">\n' % \
(self.id, len(self.influences)))
file.write('\t\t\t<POS>%.6f %.6f %.6f</POS>\n' % tuple(self.loc*matrix))
file.write('\t\t\t<NORM>%.6f %.6f %.6f</NORM>\n' % tuple( (self.normal*matrix_normal).normalize() ))
if collapse_id != -1:
file.write('\t\t\t<COLLAPSEID>%i</COLLAPSEID>\n' % collapse_id)
file.write('\t\t\t<COLLAPSECOUNT>%i</COLLAPSECOUNT>\n' % \
self.face_collapse_count)
for uv in self.maps:
# we cant have more UV's then our materials image maps
# check for this
file.write('\t\t\t<TEXCOORD>%.6f %.6f</TEXCOORD>\n' % uv)
for item in self.influences:
item.writeCal3D(file)
if self.weight != None:
file.write('\t\t\t<PHYSIQUE>%.6f</PHYSIQUE>\n' % len(self.weight))
file.write('\t\t</VERTEX>\n')
class Cal3DInfluence(object):
__slots__ = 'bone', 'weight'
def __init__(self, bone, weight):
self.bone = bone
self.weight = weight
def writeCal3D(self, file):
file.write('\t\t\t<INFLUENCE ID="%i">%.6f</INFLUENCE>\n' % \
(self.bone.id, self.weight))
class Cal3DSpring(object):
__slots__ = 'vertex1', 'vertex2', 'spring_coefficient', 'idlelength'
def __init__(self, vertex1, vertex2):
self.vertex1 = vertex1
self.vertex2 = vertex2
self.spring_coefficient = 0.0
self.idlelength = 0.0
def writeCal3D(self, file):
file.write('\t\t<SPRING VERTEXID="%i %i" COEF="%.6f" LENGTH="%.6f"/>\n' % \
(self.vertex1.id, self.vertex2.id, self.spring_coefficient, self.idlelength))
class Cal3DFace(object):
__slots__ = 'vertex1', 'vertex2', 'vertex3', 'can_collapse',
def __init__(self, vertex1, vertex2, vertex3):
self.vertex1 = vertex1
self.vertex2 = vertex2
self.vertex3 = vertex3
self.can_collapse = 0
def writeCal3D(self, file):
file.write('\t\t<FACE VERTEXID="%i %i %i"/>\n' % \
(self.vertex1.id, self.vertex2.id, self.vertex3.id))
class Cal3DSkeleton(object):
__slots__ = 'bones'
def __init__(self):
self.bones = []
def writeCal3D(self, file):
file.write('<?xml version="1.0"?>\n')
file.write('<HEADER MAGIC="XSF" VERSION="%i"/>\n' % CAL3D_VERSION)
file.write('<SKELETON NUMBONES="%i">\n' % len(self.bones))
for item in self.bones:
item.writeCal3D(file)
file.write('</SKELETON>\n')
BONES = {}
POSEBONES= {}
class Cal3DBone(object):
__slots__ = 'head', 'tail', 'name', 'cal3d_parent', 'loc', 'quat', 'children', 'matrix', 'lloc', 'lquat', 'id'
def __init__(self, skeleton, blend_bone, arm_matrix, cal3d_parent=None):
# def treat_bone(b, parent = None):
head = blend_bone.head['BONESPACE']
tail = blend_bone.tail['BONESPACE']
#print parent.quat
# Turns the Blender's head-tail-roll notation into a quaternion
#quat = matrix2quaternion(blender_bone2matrix(head, tail, blend_bone.roll['BONESPACE']))
quat = matrix2quaternion(blend_bone.matrix['BONESPACE'].copy().resize4x4())
# Pose location
ploc = POSEBONES[blend_bone.name].loc
if cal3d_parent:
# Compute the translation from the parent bone's head to the child
# bone's head, in the parent bone coordinate system.
# The translation is parent_tail - parent_head + child_head,
# but parent_tail and parent_head must be converted from the parent's parent
# system coordinate into the parent system coordinate.
parent_invert_transform = matrix_invert(quaternion2matrix(cal3d_parent.quat))
parent_head = vector_by_matrix_3x3(cal3d_parent.head, parent_invert_transform)
parent_tail = vector_by_matrix_3x3(cal3d_parent.tail, parent_invert_transform)
ploc = vector_add(ploc, blend_bone.head['BONESPACE'])
# EDIT!!! FIX BONE OFFSET BE CAREFULL OF THIS PART!!! ??
#diff = vector_by_matrix_3x3(head, parent_invert_transform)
parent_tail= vector_add(parent_tail, head)
# DONE!!!
parentheadtotail = vector_sub(parent_tail, parent_head)
# hmm this should be handled by the IPos, but isn't for non-animated
# bones which are transformed in the pose mode...
loc = parentheadtotail
else:
# Apply the armature's matrix to the root bones
head = point_by_matrix(head, arm_matrix)
tail = point_by_matrix(tail, arm_matrix)
loc = head
quat = matrix2quaternion(matrix_multiply(arm_matrix, quaternion2matrix(quat))) # Probably not optimal
self.head = head
self.tail = tail
self.cal3d_parent = cal3d_parent
self.name = blend_bone.name
self.loc = loc
self.quat = quat
self.children = []
self.matrix = matrix_translate(quaternion2matrix(quat), loc)
if cal3d_parent:
self.matrix = matrix_multiply(cal3d_parent.matrix, self.matrix)
# lloc and lquat are the bone => model space transformation (translation and rotation).
# They are probably specific to Cal3D.
m = matrix_invert(self.matrix)
self.lloc = m[3][0], m[3][1], m[3][2]
self.lquat = matrix2quaternion(m)
self.id = len(skeleton.bones)
skeleton.bones.append(self)
BONES[self.name] = self
if not blend_bone.hasChildren(): return
for blend_child in blend_bone.children:
self.children.append(Cal3DBone(skeleton, blend_child, arm_matrix, self))
def writeCal3D(self, file):
file.write('\t<BONE ID="%i" NAME="%s" NUMCHILD="%i">\n' % \
(self.id, self.name, len(self.children)))
# We need to negate quaternion W value, but why ?
file.write('\t\t<TRANSLATION>%.6f %.6f %.6f</TRANSLATION>\n' % \
(self.loc[0], self.loc[1], self.loc[2]))
file.write('\t\t<ROTATION>%.6f %.6f %.6f %.6f</ROTATION>\n' % \
(self.quat[0], self.quat[1], self.quat[2], -self.quat[3]))
file.write('\t\t<LOCALTRANSLATION>%.6f %.6f %.6f</LOCALTRANSLATION>\n' % \
(self.lloc[0], self.lloc[1], self.lloc[2]))
file.write('\t\t<LOCALROTATION>%.6f %.6f %.6f %.6f</LOCALROTATION>\n' % \
(self.lquat[0], self.lquat[1], self.lquat[2], -self.lquat[3]))
if self.cal3d_parent:
file.write('\t\t<PARENTID>%i</PARENTID>\n' % self.cal3d_parent.id)
else:
file.write('\t\t<PARENTID>%i</PARENTID>\n' % -1)
for item in self.children:
file.write('\t\t<CHILDID>%i</CHILDID>\n' % item.id)
file.write('\t</BONE>\n')
class Cal3DAnimation:
def __init__(self, name, duration = 0.0):
self.name = name
self.duration = duration
self.tracks = {} # Map bone names to tracks
def writeCal3D(self, file):
file.write('<?xml version="1.0"?>\n')
file.write('<HEADER MAGIC="XAF" VERSION="%i"/>\n' % CAL3D_VERSION)
file.write('<ANIMATION DURATION="%.6f" NUMTRACKS="%i">\n' % \
(self.duration, len(self.tracks)))
for item in self.tracks.itervalues():
item.writeCal3D(file)
file.write('</ANIMATION>\n')
class Cal3DTrack(object):
__slots__ = 'bone', 'keyframes'
def __init__(self, bone):
self.bone = bone
self.keyframes = []
def writeCal3D(self, file):
file.write('\t<TRACK BONEID="%i" NUMKEYFRAMES="%i">\n' %
(self.bone.id, len(self.keyframes)))
for item in self.keyframes:
item.writeCal3D(file)
file.write('\t</TRACK>\n')
class Cal3DKeyFrame(object):
__slots__ = 'time', 'loc', 'quat'
def __init__(self, time, loc, quat):
self.time = time
self.loc = loc
self.quat = quat
def writeCal3D(self, file):
file.write('\t\t<KEYFRAME TIME="%.6f">\n' % self.time)
file.write('\t\t\t<TRANSLATION>%.6f %.6f %.6f</TRANSLATION>\n' % \
(self.loc[0], self.loc[1], self.loc[2]))
# We need to negate quaternion W value, but why ?
file.write('\t\t\t<ROTATION>%.6f %.6f %.6f %.6f</ROTATION>\n' % \
(self.quat[0], self.quat[1], self.quat[2], -self.quat[3]))
file.write('\t\t</KEYFRAME>\n')
def export_cal3d(filename, PREF_SCALE=0.1, PREF_BAKE_MOTION = True, PREF_ACT_ACTION_ONLY=True, PREF_SCENE_FRAMES=False):
if not filename.endswith('.cfg'):
filename += '.cfg'
file_only = filename.split('/')[-1].split('\\')[-1]
file_only_noext = file_only.split('.')[0]
base_only = filename[:-len(file_only)]
def new_name(dataname, ext):
return file_only_noext + '_' + BPySys.cleanName(dataname) + ext
#if EXPORT_FOR_SOYA:
# global BASE_MATRIX
# BASE_MATRIX = matrix_rotate_x(-math.pi / 2.0)
# Get the sce
sce = bpy.data.scenes.active
blend_world = sce.world
# ---- Export skeleton (armature) ----------------------------------------
skeleton = Cal3DSkeleton()
blender_armature = [ob for ob in sce.objects.context if ob.type == 'Armature']
if len(blender_armature) > 1: print "Found multiple armatures! using ",armatures[0].name
if blender_armature: blender_armature = blender_armature[0]
else:
# Try find a meshes armature
for ob in sce.objects.context:
blender_armature = BPyObject.getObjectArmature(ob)
if blender_armature:
break
if not blender_armature:
Blender.Draw.PupMenu('Aborting%t|No Armature in selection')
return
# we need pose bone locations
for pbone in blender_armature.getPose().bones.values():
POSEBONES[pbone.name] = pbone
Cal3DBone(skeleton, best_armature_root(blender_armature.getData()), blender_armature.matrixWorld)
# ---- Export Mesh data ---------------------------------------------------
meshes = []
for ob in sce.objects.context:
if ob.type != 'Mesh': continue
blend_mesh = ob.getData(mesh=1)
if not blend_mesh.faces: continue
meshes.append( Cal3DMesh(ob, blend_mesh, blend_world) )
# ---- Export animations --------------------------------------------------
backup_action = blender_armature.action
ANIMATIONS = []
SUPPORTED_IPOS = 'QuatW', 'QuatX', 'QuatY', 'QuatZ', 'LocX', 'LocY', 'LocZ'
if PREF_ACT_ACTION_ONLY: action_items = [(blender_armature.action.name, blender_armature.action)]
else: action_items = Blender.Armature.NLA.GetActions().items()
print len(action_items), 'action_items'
for animation_name, blend_action in action_items:
# get frame range
if PREF_SCENE_FRAMES:
action_start= Blender.Get('staframe')
action_end= Blender.Get('endframe')
else:
_frames = blend_action.getFrameNumbers()
action_start= min(_frames);
action_end= max(_frames);
del _frames
blender_armature.action = blend_action
if PREF_BAKE_MOTION:
# We need to set the action active if we are getting baked data
pose_data = BPyArmature.getBakedPoseData(blender_armature, action_start, action_end)
# Fake, all we need is bone names
blend_action_ipos_items = [(pbone, True) for pbone in POSEBONES.iterkeys()]
else:
# real (bone_name, ipo) pairs
blend_action_ipos_items = blend_action.getAllChannelIpos().items()
# Now we mau have some bones with no channels, easiest to add their names and an empty list here
# this way they are exported with dummy keyfraames at teh first used frame
action_bone_names = [name for name, ipo in blend_action_ipos_items]
for bone_name in BONES: # iterkeys
if bone_name not in action_bone_names:
blend_action_ipos_items.append( (bone_name, []) )
animation = Cal3DAnimation(animation_name)
# ----------------------------
ANIMATIONS.append(animation)
animation.duration = 0.0
for bone_name, ipo in blend_action_ipos_items:
# Baked bones may have no IPO's width motion still
if bone_name not in BONES:
print '\tNo Bone "' + bone_name + '" in (from Animation "' + animation_name + '") ?!?'
continue
# So we can loop without errors
if ipo==None: ipo = []
bone = BONES[bone_name]
track = animation.tracks[bone_name] = Cal3DTrack(bone)
if PREF_BAKE_MOTION:
for i in xrange(action_end - action_start):
cal3dtime = i / 25.0 # assume 25FPS by default
if cal3dtime > animation.duration:
animation.duration = cal3dtime
#print pose_data[i][bone_name], i
loc, quat = pose_data[i][bone_name]
loc = vector_by_matrix_3x3(loc, bone.matrix)
loc = vector_add(bone.loc, loc)
quat = quaternion_multiply(quat, bone.quat)
quat = Quaternion(quat)
quat.normalize()
quat = tuple(quat)
track.keyframes.append( Cal3DKeyFrame(cal3dtime, loc, quat) )
else:
#run 1: we need to find all time values where we need to produce keyframes
times = set()
for curve in ipo:
curve_name = curve.name
if curve_name in SUPPORTED_IPOS:
for p in curve.bezierPoints:
times.add( p.pt[0] )
times = list(times)
times.sort()
# Incase we have no keys here or ipo==None
if not times: times.append(action_start)
# run2: now create keyframes
for time in times:
cal3dtime = (time-1) / 25.0 # assume 25FPS by default
if cal3dtime > animation.duration:
animation.duration = cal3dtime
trans = Vector()
quat = Quaternion()
for curve in ipo:
val = curve.evaluate(time)
# val = 0.0
curve_name= curve.name
if curve_name == 'LocX': trans[0] = val
elif curve_name == 'LocY': trans[1] = val
elif curve_name == 'LocZ': trans[2] = val
elif curve_name == 'QuatW': quat[3] = val
elif curve_name == 'QuatX': quat[0] = val
elif curve_name == 'QuatY': quat[1] = val
elif curve_name == 'QuatZ': quat[2] = val
transt = vector_by_matrix_3x3(trans, bone.matrix)
loc = vector_add(bone.loc, transt)
quat = quaternion_multiply(quat, bone.quat)
quat = Quaternion(quat)
quat.normalize()
quat = tuple(quat)
track.keyframes.append( Cal3DKeyFrame(cal3dtime, loc, quat) )
if animation.duration <= 0:
print 'Ignoring Animation "' + animation_name + '": duration is 0.\n'
continue
# Restore the original armature
blender_armature.action = backup_action
# ------------------------------------- End Animation
cfg = open((filename), 'wb')
cfg.write('# Cal3D model exported from Blender with export_cal3d.py\n# from %s\n' % Blender.Get('filename'))
if PREF_SCALE != 1.0: cfg.write('scale=%.6f\n' % PREF_SCALE)
fname = file_only_noext + '.xsf'
file = open( base_only + fname, 'wb')
skeleton.writeCal3D(file)
file.close()
cfg.write('skeleton=%s\n' % fname)
for animation in ANIMATIONS:
if not animation.name.startswith('_'):
if animation.duration > 0.1: # Cal3D does not support animation with only one state
fname = new_name(animation.name, '.xaf')
file = open(base_only + fname, 'wb')
animation.writeCal3D(file)
file.close()
cfg.write('animation=%s\n' % fname)
for mesh in meshes:
if not mesh.name.startswith('_'):
fname = new_name(mesh.name, '.xmf')
file = open(base_only + fname, 'wb')
mesh.writeCal3D(file)
file.close()
cfg.write('mesh=%s\n' % fname)
materials = MATERIALS.values()
materials.sort(key = lambda a: a.id)
for material in materials:
# Just number materials, its less trouble
fname = new_name(str(material.id), '.xrf')
file = open(base_only + fname, 'wb')
material.writeCal3D(file)
file.close()
cfg.write('material=%s\n' % fname)
print 'Cal3D Saved to "%s.cfg"' % file_only_noext
# Warnings
if len(animation.tracks) < 2:
Blender.Draw.PupMenu('Warning, the armature has less then 2 tracks, file may not load in Cal3d')
def export_cal3d_ui(filename):
PREF_SCALE= Blender.Draw.Create(1.0)
PREF_BAKE_MOTION = Blender.Draw.Create(1)
PREF_ACT_ACTION_ONLY= Blender.Draw.Create(1)
PREF_SCENE_FRAMES= Blender.Draw.Create(0)
block = [\
('Scale: ', PREF_SCALE, 0.01, 100, 'The scale to set in the Cal3d .cfg file (unsupported by soya)'),\
('Baked Motion', PREF_BAKE_MOTION, 'use final pose position instead of ipo keyframes (IK and constraint support)'),\
('Active Action', PREF_ACT_ACTION_ONLY, 'Only export action applied to this armature, else export all actions.'),\
('Scene Frames', PREF_SCENE_FRAMES, 'Use scene frame range, else the actions start/end'),\
]
if not Blender.Draw.PupBlock('Cal3D Options', block):
return
Blender.Window.WaitCursor(1)
export_cal3d(filename, 1.0/PREF_SCALE.val, PREF_BAKE_MOTION.val, PREF_ACT_ACTION_ONLY.val, PREF_SCENE_FRAMES.val)
Blender.Window.WaitCursor(0)
#import os
if __name__ == '__main__':
Blender.Window.FileSelector(export_cal3d_ui, 'Cal3D Export', Blender.Get('filename').replace('.blend', '.cfg'))
#export_cal3d('/cally/data/skeleton/skeleton' + '.cfg', 1.0, True, False, False)
#export_cal3d('/test' + '.cfg')
#export_cal3d_ui('/test' + '.cfg')
#os.system('cd /; wine /cal3d_miniviewer.exe /skeleton.cfg')
#os.system('cd /cally/;wine cally')

View File

@@ -1,26 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
# constants
import _bpy
version = _bpy._VERSION
version_string = _bpy._VERSION_STR
home = _bpy._HOME
binary_path = _bpy._BINPATH

View File

@@ -1,200 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8-80 compliant>
# for slightly faster access
from bpy.__ops__ import add as op_add
from bpy.__ops__ import remove as op_remove
from bpy.__ops__ import dir as op_dir
from bpy.__ops__ import call as op_call
from bpy.__ops__ import as_string as op_as_string
from bpy.__ops__ import get_rna as op_get_rna
# Keep in sync with WM_types.h
context_dict = {
'INVOKE_DEFAULT': 0,
'INVOKE_REGION_WIN': 1,
'INVOKE_AREA': 2,
'INVOKE_SCREEN': 3,
'EXEC_DEFAULT': 4,
'EXEC_REGION_WIN': 5,
'EXEC_AREA': 6,
'EXEC_SCREEN': 7,
}
class bpy_ops(object):
'''
Fake module like class.
bpy.ops
'''
def __getattr__(self, module):
'''
gets a bpy.ops submodule
'''
if module.startswith('__'):
raise AttributeError(module)
return bpy_ops_submodule(module)
def add(self, pyop):
op_add(pyop)
def remove(self, pyop):
op_remove(pyop)
def __dir__(self):
submodules = set()
# add this classes functions
for id_name in dir(self.__class__):
if not id_name.startswith('__'):
submodules.add(id_name)
for id_name in op_dir():
id_split = id_name.split('_OT_', 1)
if len(id_split) == 2:
submodules.add(id_split[0].lower())
else:
submodules.add(id_split[0])
return list(submodules)
def __repr__(self):
return "<module like class 'bpy.ops'>"
class bpy_ops_submodule(object):
'''
Utility class to fake submodules.
eg. bpy.ops.object
'''
__keys__ = ('module',)
def __init__(self, module):
self.module = module
def __getattr__(self, func):
'''
gets a bpy.ops.submodule function
'''
if func.startswith('__'):
raise AttributeError(func)
return bpy_ops_submodule_op(self.module, func)
def __dir__(self):
functions = set()
module_upper = self.module.upper()
for id_name in op_dir():
id_split = id_name.split('_OT_', 1)
if len(id_split) == 2 and module_upper == id_split[0]:
functions.add(id_split[1])
return list(functions)
def __repr__(self):
return "<module like class 'bpy.ops.%s'>" % self.module
class bpy_ops_submodule_op(object):
'''
Utility class to fake submodule operators.
eg. bpy.ops.object.somefunc
'''
__keys__ = ('module', 'func')
def _get_doc(self):
return op_as_string(self.idname())
__doc__ = property(_get_doc)
def _get_doc(self):
return op_as_string(self.idname())
__doc__ = property(_get_doc)
def __init__(self, module, func):
self.module = module
self.func = func
def idname(self):
# submod.foo -> SUBMOD_OT_foo
return self.module.upper() + '_OT_' + self.func
def __call__(self, *args, **kw):
# Get the operator from blender
if len(args) > 2:
raise ValueError("1 or 2 args execution context is supported")
C_dict = None
if args:
C_exec = 'EXEC_DEFAULT'
if len(args) == 2:
C_exec = args[0]
C_dict = args[1]
else:
if type(args[0]) != str:
C_dict = args[0]
else:
C_exec = args[0]
try:
context = context_dict[C_exec]
except:
raise ValueError("Expected a single context argument in: " + \
str(list(context_dict.keys())))
if len(args) == 2:
C_dict = args[1]
return op_call(self.idname(), C_dict, kw, context)
else:
return op_call(self.idname(), C_dict, kw)
def get_rna(self):
'''
currently only used for 'bl_rna'
'''
return op_get_rna(self.idname())
def __repr__(self): # useful display, repr(op)
return op_as_string(self.idname())
def __str__(self): # used for print(...)
return "<function bpy.ops.%s.%s at 0x%x'>" % \
(self.module, self.func, id(self))
import bpy
bpy.ops = bpy_ops()

View File

@@ -1,111 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
def collect_baseclasses(_class, bases):
if _class is type or _class is object:
return bases
bases.append(_class)
for _superclass in _class.__bases__:
collect_baseclasses(_superclass, bases)
return bases
def collect_subclasses(_class, subs):
if _class is type or _class is object:
return subs
subs.append(_class)
for _subclass in _class.__subclasses__():
collect_subclasses(_subclass, subs)
return subs
class DynMenu(bpy.types.Menu):
def draw(self, context):
'''
This is a draw function that is used to call all subclasses draw functions
starting from the registered classes draw function and working down.
DynMenu.setup() must be called first.
Sort/group classes could be nice
'''
subclass_ls = []
collect_subclasses(self.__class__, subclass_ls)
# print(subclass_ls)
for subclass in subclass_ls:
# print("drawwing", subclass) # , dir(subclass))
subclass.internal_draw(self, context)
# print("subclass.internal_draw", subclass.internal_draw)
def setup(menu_class):
'''
Setup subclasses (not needed when self.add() is used)
'''
bases = collect_baseclasses(menu_class, [])
# Incase 'DynMenu' isnt last
while bases[-1] is not DynMenu:
bases.pop()
bases.pop() # remove 'DynMenu'
root_class = bases[-1] # this is the registered class
for subclass in collect_subclasses(root_class, []):
#print(subclass)
draw = getattr(subclass, 'draw', None)
if draw and not hasattr(subclass, 'internal_draw'):
# print("replace", subclass, draw)
try:
del subclass.draw
except:
pass
subclass.internal_draw = draw
root_class.draw = DynMenu.draw
def add(menu_class, func):
'''
Add a single function directly without having to make a class
important that the returned value should be stored in the module that called it.
'''
newclass = type('<menuclass>', (menu_class,), {})
newclass.internal_draw = func
setup(menu_class)
return newclass
'''
# so we dont need to import this module
DynMenu.setup = setup
DynMenu.add = add
# Only so we can access as bpy.types.
# dont ever use this directly!
bpy.types.register(DynMenu)
'''

View File

@@ -1,154 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
import mathutils
from math import cos, sin, pi
def add_torus(major_rad, minor_rad, major_seg, minor_seg):
Vector = mathutils.Vector
Quaternion = mathutils.Quaternion
PI_2 = pi * 2.0
z_axis = 0.0, 0.0, 1.0
verts = []
faces = []
i1 = 0
tot_verts = major_seg * minor_seg
for major_index in range(major_seg):
quat = Quaternion(z_axis, (major_index / major_seg) * PI_2)
for minor_index in range(minor_seg):
angle = 2 * pi * minor_index / minor_seg
vec = Vector((major_rad + (cos(angle) * minor_rad), 0.0,
(sin(angle) * minor_rad))) * quat
verts.extend(vec[:])
if minor_index + 1 == minor_seg:
i2 = (major_index) * minor_seg
i3 = i1 + minor_seg
i4 = i2 + minor_seg
else:
i2 = i1 + 1
i3 = i1 + minor_seg
i4 = i3 + 1
if i2 >= tot_verts:
i2 = i2 - tot_verts
if i3 >= tot_verts:
i3 = i3 - tot_verts
if i4 >= tot_verts:
i4 = i4 - tot_verts
# stupid eekadoodle
if i2:
faces.extend([i1, i3, i4, i2])
else:
faces.extend([i2, i1, i3, i4])
i1 += 1
return verts, faces
from bpy.props import *
class AddTorus(bpy.types.Operator):
'''Add a torus mesh'''
bl_idname = "mesh.primitive_torus_add"
bl_label = "Add Torus"
bl_options = {'REGISTER', 'UNDO'}
major_radius = FloatProperty(name="Major Radius",
description="Radius from the origin to the center of the cross sections",
default=1.0, min=0.01, max=100.0)
minor_radius = FloatProperty(name="Minor Radius",
description="Radius of the torus' cross section",
default=0.25, min=0.01, max=100.0)
major_segments = IntProperty(name="Major Segments",
description="Number of segments for the main ring of the torus",
default=48, min=3, max=256)
minor_segments = IntProperty(name="Minor Segments",
description="Number of segments for the minor ring of the torus",
default=12, min=3, max=256)
use_abso = BoolProperty(name="Use Int+Ext Controls",
description="Use the Int / Ext controls for torus dimensions",
default=False)
abso_major_rad = FloatProperty(name="Exterior Radius",
description="Total Exterior Radius of the torus",
default=1.0, min=0.01, max=100.0)
abso_minor_rad = FloatProperty(name="Inside Radius",
description="Total Interior Radius of the torus",
default=0.5, min=0.01, max=100.0)
# generic transform props
view_align = BoolProperty(name="Align to View",
default=False)
location = FloatVectorProperty(name="Location",
subtype='TRANSLATION')
rotation = FloatVectorProperty(name="Rotation",
subtype='EULER')
def execute(self, context):
if self.use_abso == True:
extra_helper = (self.abso_major_rad - self.abso_minor_rad) * 0.5
self.major_radius = self.abso_minor_rad + extra_helper
self.minor_radius = extra_helper
verts_loc, faces = add_torus(self.major_radius,
self.minor_radius,
self.major_segments,
self.minor_segments)
mesh = bpy.data.meshes.new("Torus")
mesh.vertices.add(len(verts_loc) // 3)
mesh.faces.add(len(faces) // 4)
mesh.vertices.foreach_set("co", verts_loc)
mesh.faces.foreach_set("vertices_raw", faces)
mesh.update()
import add_object_utils
add_object_utils.object_data_add(context, mesh, operator=self)
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(AddTorus.bl_idname, text="Torus", icon='MESH_TORUS')
def register():
bpy.utils.register_class(AddTorus)
bpy.types.INFO_MT_mesh_add.append(menu_func)
def unregister():
bpy.utils.unregister_class(AddTorus)
bpy.types.INFO_MT_mesh_add.remove(menu_func)
if __name__ == "__main__":
register()

View File

@@ -1,708 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
data_path_update = [
("ClothCollisionSettings", "min_distance", "distance_min"),
("ClothCollisionSettings", "self_min_distance", "self_distance_min"),
("ClothCollisionSettings", "enable_collision", "use_collision"),
("ClothCollisionSettings", "enable_self_collision", "use_self_collision"),
("ClothSettings", "pin_cloth", "use_pin_cloth"),
("ClothSettings", "stiffness_scaling", "use_stiffness_scale"),
("CollisionSettings", "random_damping", "damping_random"),
("CollisionSettings", "random_friction", "friction_random"),
("CollisionSettings", "inner_thickness", "thickness_inner"),
("CollisionSettings", "outer_thickness", "thickness_outer"),
("CollisionSettings", "kill_particles", "use_particle_kill"),
("Constraint", "proxy_local", "is_proxy_local"),
("ActionConstraint", "maximum", "max"),
("ActionConstraint", "minimum", "min"),
("FollowPathConstraint", "use_fixed_position", "use_fixed_location"),
("KinematicConstraint", "chain_length", "chain_count"),
("KinematicConstraint", "pos_lock_x", "lock_location_x"),
("KinematicConstraint", "pos_lock_y", "lock_location_y"),
("KinematicConstraint", "pos_lock_z", "lock_location_z"),
("KinematicConstraint", "rot_lock_x", "lock_rotation_x"),
("KinematicConstraint", "rot_lock_y", "lock_rotation_y"),
("KinematicConstraint", "rot_lock_z", "lock_rotation_z"),
("KinematicConstraint", "axis_reference", "reference_axis"),
("KinematicConstraint", "use_position", "use_location"),
("LimitLocationConstraint", "maximum_x", "max_x"),
("LimitLocationConstraint", "maximum_y", "max_y"),
("LimitLocationConstraint", "maximum_z", "max_z"),
("LimitLocationConstraint", "minimum_x", "min_x"),
("LimitLocationConstraint", "minimum_y", "min_y"),
("LimitLocationConstraint", "minimum_z", "min_z"),
("LimitLocationConstraint", "use_maximum_x", "use_max_x"),
("LimitLocationConstraint", "use_maximum_y", "use_max_y"),
("LimitLocationConstraint", "use_maximum_z", "use_max_z"),
("LimitLocationConstraint", "use_minimum_x", "use_min_x"),
("LimitLocationConstraint", "use_minimum_y", "use_min_y"),
("LimitLocationConstraint", "use_minimum_z", "use_min_z"),
("LimitLocationConstraint", "limit_transform", "use_transform_limit"),
("LimitRotationConstraint", "maximum_x", "max_x"),
("LimitRotationConstraint", "maximum_y", "max_y"),
("LimitRotationConstraint", "maximum_z", "max_z"),
("LimitRotationConstraint", "minimum_x", "min_x"),
("LimitRotationConstraint", "minimum_y", "min_y"),
("LimitRotationConstraint", "minimum_z", "min_z"),
("LimitRotationConstraint", "limit_transform", "use_transform_limit"),
("LimitScaleConstraint", "maximum_x", "max_x"),
("LimitScaleConstraint", "maximum_y", "max_y"),
("LimitScaleConstraint", "maximum_z", "max_z"),
("LimitScaleConstraint", "minimum_x", "min_x"),
("LimitScaleConstraint", "minimum_y", "min_y"),
("LimitScaleConstraint", "minimum_z", "min_z"),
("LimitScaleConstraint", "use_maximum_x", "use_max_x"),
("LimitScaleConstraint", "use_maximum_y", "use_max_y"),
("LimitScaleConstraint", "use_maximum_z", "use_max_z"),
("LimitScaleConstraint", "use_minimum_x", "use_min_x"),
("LimitScaleConstraint", "use_minimum_y", "use_min_y"),
("LimitScaleConstraint", "use_minimum_z", "use_min_z"),
("LimitScaleConstraint", "limit_transform", "use_transform_limit"),
("PivotConstraint", "enabled_rotation_range", "rotation_range"),
("PivotConstraint", "use_relative_position", "use_relative_location"),
("PythonConstraint", "number_of_targets", "target_count"),
("SplineIKConstraint", "chain_length", "chain_count"),
("SplineIKConstraint", "chain_offset", "use_chain_offset"),
("SplineIKConstraint", "even_divisions", "use_even_divisions"),
("SplineIKConstraint", "y_stretch", "use_y_stretch"),
("SplineIKConstraint", "xz_scaling_mode", "xz_scale_mode"),
("StretchToConstraint", "original_length", "rest_length"),
("TrackToConstraint", "target_z", "use_target_z"),
("TransformConstraint", "extrapolate_motion", "use_motion_extrapolate"),
("FieldSettings", "do_location", "apply_to_location"),
("FieldSettings", "do_rotation", "apply_to_rotation"),
("FieldSettings", "maximum_distance", "distance_max"),
("FieldSettings", "minimum_distance", "distance_min"),
("FieldSettings", "radial_maximum", "radial_max"),
("FieldSettings", "radial_minimum", "radial_min"),
("FieldSettings", "force_2d", "use_2d_force"),
("FieldSettings", "do_absorption", "use_absorption"),
("FieldSettings", "global_coordinates", "use_global_coords"),
("FieldSettings", "guide_path_add", "use_guide_path_add"),
("FieldSettings", "multiple_springs", "use_multiple_springs"),
("FieldSettings", "use_coordinates", "use_object_coords"),
("FieldSettings", "root_coordinates", "use_root_coords"),
("ControlFluidSettings", "reverse_frames", "use_reverse_frames"),
("DomainFluidSettings", "real_world_size", "simulation_scale"),
("DomainFluidSettings", "surface_smoothing", "surface_smooth"),
("DomainFluidSettings", "reverse_frames", "use_reverse_frames"),
("DomainFluidSettings", "generate_speed_vectors", "use_speed_vectors"),
("DomainFluidSettings", "override_time", "use_time_override"),
("FluidFluidSettings", "export_animated_mesh", "use_animated_mesh"),
("InflowFluidSettings", "export_animated_mesh", "use_animated_mesh"),
("InflowFluidSettings", "local_coordinates", "use_local_coords"),
("ObstacleFluidSettings", "export_animated_mesh", "use_animated_mesh"),
("OutflowFluidSettings", "export_animated_mesh", "use_animated_mesh"),
("ParticleFluidSettings", "drops", "use_drops"),
("ParticleFluidSettings", "floats", "use_floats"),
("Armature", "drawtype", "draw_type"),
("Armature", "layer_protection", "layers_protected"),
("Armature", "auto_ik", "use_auto_ik"),
("Armature", "delay_deform", "use_deform_delay"),
("Armature", "deform_envelope", "use_deform_envelopes"),
("Armature", "deform_quaternion", "use_deform_preserve_volume"),
("Armature", "deform_vertexgroups", "use_deform_vertex_groups"),
("Armature", "x_axis_mirror", "use_mirror_x"),
("Curve", "width", "offset"),
("Image", "animation_speed", "fps"),
("Image", "animation_end", "frame_end"),
("Image", "animation_start", "frame_start"),
("Image", "animated", "use_animation"),
("Image", "clamp_x", "use_clamp_x"),
("Image", "clamp_y", "use_clamp_y"),
("Image", "premultiply", "use_premultiply"),
("AreaLamp", "shadow_ray_sampling_method", "shadow_ray_sample_method"),
("AreaLamp", "only_shadow", "use_only_shadow"),
("AreaLamp", "shadow_layer", "use_shadow_layer"),
("AreaLamp", "umbra", "use_umbra"),
("PointLamp", "shadow_ray_sampling_method", "shadow_ray_sample_method"),
("PointLamp", "only_shadow", "use_only_shadow"),
("PointLamp", "shadow_layer", "use_shadow_layer"),
("PointLamp", "sphere", "use_sphere"),
("SpotLamp", "shadow_ray_sampling_method", "shadow_ray_sample_method"),
("SpotLamp", "auto_clip_end", "use_auto_clip_end"),
("SpotLamp", "auto_clip_start", "use_auto_clip_start"),
("SpotLamp", "only_shadow", "use_only_shadow"),
("SpotLamp", "shadow_layer", "use_shadow_layer"),
("SpotLamp", "sphere", "use_sphere"),
("SunLamp", "only_shadow", "use_only_shadow"),
("SunLamp", "shadow_layer", "use_shadow_layer"),
("Material", "z_offset", "offset_z"),
("Material", "shadow_casting_alpha", "shadow_cast_alpha"),
("Material", "cast_approximate", "use_cast_approximate"),
("Material", "cast_buffer_shadows", "use_cast_buffer_shadows"),
("Material", "cast_shadows_only", "use_cast_shadows_only"),
("Material", "face_texture", "use_face_texture"),
("Material", "face_texture_alpha", "use_face_texture_alpha"),
("Material", "full_oversampling", "use_full_oversampling"),
("Material", "light_group_exclusive", "use_light_group_exclusive"),
("Material", "object_color", "use_object_color"),
("Material", "only_shadow", "use_only_shadow"),
("Material", "ray_shadow_bias", "use_ray_shadow_bias"),
("Material", "traceable", "use_raytrace"),
("Material", "shadeless", "use_shadeless"),
("Material", "tangent_shading", "use_tangent_shading"),
("Material", "transparency", "use_transparency"),
("Material", "receive_transparent_shadows", "use_transparent_shadows"),
("Material", "vertex_color_light", "use_vertex_color_light"),
("Material", "vertex_color_paint", "use_vertex_color_paint"),
("Mesh", "autosmooth_angle", "auto_smooth_angle"),
("Mesh", "autosmooth", "use_auto_smooth"),
("Object", "max_draw_type", "draw_type"),
("Object", "use_dupli_verts_rotation", "use_dupli_vertices_rotation"),
("Object", "shape_key_edit_mode", "use_shape_key_edit_mode"),
("Object", "slow_parent", "use_slow_parent"),
("Object", "time_offset_add_parent", "use_time_offset_add_parent"),
("Object", "time_offset_edit", "use_time_offset_edit"),
("Object", "time_offset_parent", "use_time_offset_parent"),
("Object", "time_offset_particle", "use_time_offset_particle"),
("ParticleSettings", "adaptive_pix", "adaptive_pixel"),
("ParticleSettings", "child_effector", "apply_effector_to_children"),
("ParticleSettings", "child_guide", "apply_guide_to_children"),
("ParticleSettings", "billboard_split_offset", "billboard_offset_split"),
("ParticleSettings", "billboard_random_tilt", "billboard_tilt_random"),
("ParticleSettings", "child_length_thres", "child_length_threshold"),
("ParticleSettings", "child_random_size", "child_size_random"),
("ParticleSettings", "clumppow", "clump_shape"),
("ParticleSettings", "damp_factor", "damping"),
("ParticleSettings", "draw_as", "draw_method"),
("ParticleSettings", "random_factor", "factor_random"),
("ParticleSettings", "grid_invert", "invert_grid"),
("ParticleSettings", "random_length", "length_random"),
("ParticleSettings", "random_lifetime", "lifetime_random"),
("ParticleSettings", "billboard_lock", "lock_billboard"),
("ParticleSettings", "boids_2d", "lock_boids_to_surface"),
("ParticleSettings", "object_aligned_factor", "object_align_factor"),
("ParticleSettings", "random_phase_factor", "phase_factor_random"),
("ParticleSettings", "ren_as", "render_type"),
("ParticleSettings", "rendered_child_nbr", "rendered_child_count"),
("ParticleSettings", "random_rotation_factor", "rotation_factor_random"),
("ParticleSettings", "rough1", "roughness_1"),
("ParticleSettings", "rough1_size", "roughness_1_size"),
("ParticleSettings", "rough2", "roughness_2"),
("ParticleSettings", "rough2_size", "roughness_2_size"),
("ParticleSettings", "rough2_thres", "roughness_2_threshold"),
("ParticleSettings", "rough_end_shape", "roughness_end_shape"),
("ParticleSettings", "rough_endpoint", "roughness_endpoint"),
("ParticleSettings", "random_size", "size_random"),
("ParticleSettings", "abs_path_time", "use_absolute_path_time"),
("ParticleSettings", "animate_branching", "use_animate_branching"),
("ParticleSettings", "branching", "use_branching"),
("ParticleSettings", "died", "use_dead"),
("ParticleSettings", "die_on_collision", "use_die_on_collision"),
("ParticleSettings", "rotation_dynamic", "use_dynamic_rotation"),
("ParticleSettings", "even_distribution", "use_even_distribution"),
("ParticleSettings", "rand_group", "use_group_pick_random"),
("ParticleSettings", "hair_bspline", "use_hair_bspline"),
("ParticleSettings", "sizemass", "use_multiply_size_mass"),
("ParticleSettings", "react_multiple", "use_react_multiple"),
("ParticleSettings", "react_start_end", "use_react_start_end"),
("ParticleSettings", "render_adaptive", "use_render_adaptive"),
("ParticleSettings", "self_effect", "use_self_effect"),
("ParticleSettings", "enable_simplify", "use_simplify"),
("ParticleSettings", "size_deflect", "use_size_deflect"),
("ParticleSettings", "render_strand", "use_strand_primitive"),
("ParticleSettings", "symmetric_branching", "use_symmetric_branching"),
("ParticleSettings", "velocity_length", "use_velocity_length"),
("ParticleSettings", "whole_group", "use_whole_group"),
("CloudsTexture", "noise_size", "noise_scale"),
("DistortedNoiseTexture", "noise_size", "noise_scale"),
("EnvironmentMapTexture", "filter_size_minimum", "use_filter_size_min"),
("EnvironmentMapTexture", "mipmap_gauss", "use_mipmap_gauss"),
("ImageTexture", "calculate_alpha", "use_calculate_alpha"),
("ImageTexture", "checker_even", "use_checker_even"),
("ImageTexture", "checker_odd", "use_checker_odd"),
("ImageTexture", "filter_size_minimum", "use_filter_size_min"),
("ImageTexture", "flip_axis", "use_flip_axis"),
("ImageTexture", "mipmap_gauss", "use_mipmap_gauss"),
("ImageTexture", "mirror_x", "use_mirror_x"),
("ImageTexture", "mirror_y", "use_mirror_y"),
("ImageTexture", "normal_map", "use_normal_map"),
("MarbleTexture", "noise_size", "noise_scale"),
("MarbleTexture", "noisebasis2", "noisebasis_2"),
("MusgraveTexture", "highest_dimension", "dimension_max"),
("MusgraveTexture", "noise_size", "noise_scale"),
("StucciTexture", "noise_size", "noise_scale"),
("VoronoiTexture", "coloring", "color_mode"),
("VoronoiTexture", "noise_size", "noise_scale"),
("WoodTexture", "noise_size", "noise_scale"),
("WoodTexture", "noisebasis2", "noisebasis_2"),
("World", "blend_sky", "use_sky_blend"),
("World", "paper_sky", "use_sky_paper"),
("World", "real_sky", "use_sky_real"),
("ImageUser", "auto_refresh", "use_auto_refresh"),
("MaterialHalo", "flares_sub", "flare_subflare_count"),
("MaterialHalo", "flare_subsize", "flare_subflare_size"),
("MaterialHalo", "line_number", "line_count"),
("MaterialHalo", "rings", "ring_count"),
("MaterialHalo", "star_tips", "star_tip_count"),
("MaterialHalo", "xalpha", "use_extreme_alpha"),
("MaterialHalo", "flare_mode", "use_flare_mode"),
("MaterialHalo", "vertex_normal", "use_vertex_normal"),
("MaterialPhysics", "align_to_normal", "use_normal_align"),
("MaterialStrand", "min_size", "size_min"),
("MaterialStrand", "blender_units", "use_blender_units"),
("MaterialStrand", "surface_diffuse", "use_surface_diffuse"),
("MaterialStrand", "tangent_shading", "use_tangent_shading"),
("MaterialSubsurfaceScattering", "error_tolerance", "error_threshold"),
("MaterialVolume", "depth_cutoff", "depth_threshold"),
("MaterialVolume", "lighting_mode", "light_method"),
("MaterialVolume", "step_calculation", "step_method"),
("MaterialVolume", "external_shadows", "use_external_shadows"),
("MaterialVolume", "light_cache", "use_light_cache"),
("ArmatureModifier", "multi_modifier", "use_multi_modifier"),
("ArrayModifier", "constant_offset_displacement", "constant_offset_displace"),
("ArrayModifier", "merge_distance", "merge_threshold"),
("ArrayModifier", "relative_offset_displacement", "relative_offset_displace"),
("ArrayModifier", "constant_offset", "use_constant_offset"),
("ArrayModifier", "merge_adjacent_vertices", "use_merge_vertices"),
("ArrayModifier", "merge_end_vertices", "use_merge_vertices_cap"),
("ArrayModifier", "add_offset_object", "use_object_offset"),
("ArrayModifier", "relative_offset", "use_relative_offset"),
("BevelModifier", "only_vertices", "use_only_vertices"),
("CastModifier", "from_radius", "use_radius_as_size"),
("DisplaceModifier", "midlevel", "mid_level"),
("DisplaceModifier", "texture_coordinates", "texture_coords"),
("EdgeSplitModifier", "use_sharp", "use_edge_sharp"),
("ExplodeModifier", "split_edges", "use_edge_split"),
("MirrorModifier", "merge_limit", "merge_threshold"),
("MirrorModifier", "mirror_u", "use_mirror_u"),
("MirrorModifier", "mirror_v", "use_mirror_v"),
("MirrorModifier", "mirror_vertex_groups", "use_mirror_vertex_groups"),
("ParticleInstanceModifier", "particle_system_number", "particle_system_index"),
("ParticleInstanceModifier", "keep_shape", "use_preserve_shape"),
("ShrinkwrapModifier", "cull_back_faces", "use_cull_back_faces"),
("ShrinkwrapModifier", "cull_front_faces", "use_cull_front_faces"),
("ShrinkwrapModifier", "keep_above_surface", "use_keep_above_surface"),
("SimpleDeformModifier", "lock_x_axis", "lock_x"),
("SimpleDeformModifier", "lock_y_axis", "lock_y"),
("SmokeModifier", "smoke_type", "type"),
("SubsurfModifier", "subsurf_uv", "use_subsurf_uv"),
("UVProjectModifier", "num_projectors", "projector_count"),
("UVProjectModifier", "override_image", "use_image_override"),
("WaveModifier", "texture_coordinates", "texture_coords"),
("WaveModifier", "x_normal", "use_normal_x"),
("WaveModifier", "y_normal", "use_normal_y"),
("WaveModifier", "z_normal", "use_normal_z"),
("NlaStrip", "blending", "blend_type"),
("NlaStrip", "animated_influence", "use_animated_influence"),
("NlaStrip", "animated_time", "use_animated_time"),
("NlaStrip", "animated_time_cyclic", "use_animated_time_cyclic"),
("NlaStrip", "auto_blending", "use_auto_blend"),
("CompositorNodeAlphaOver", "convert_premul", "use_premultiply"),
("CompositorNodeBlur", "sizex", "size_x"),
("CompositorNodeBlur", "sizey", "size_y"),
("CompositorNodeChannelMatte", "algorithm", "limit_method"),
("CompositorNodeChromaMatte", "acceptance", "tolerance"),
("CompositorNodeColorBalance", "correction_formula", "correction_method"),
("CompositorNodeColorSpill", "algorithm", "limit_method"),
("CompositorNodeColorSpill", "unspill", "use_unspill"),
("CompositorNodeCrop", "x2", "max_x"),
("CompositorNodeCrop", "y2", "max_y"),
("CompositorNodeCrop", "x1", "min_x"),
("CompositorNodeCrop", "y1", "min_y"),
("CompositorNodeCrop", "crop_size", "use_crop_size"),
("CompositorNodeDefocus", "max_blur", "blur_max"),
("CompositorNodeDefocus", "gamma_correction", "use_gamma_correction"),
("CompositorNodeGlare", "rotate_45", "use_rotate_45"),
("CompositorNodeImage", "auto_refresh", "use_auto_refresh"),
("CompositorNodeLensdist", "projector", "use_projector"),
("CompositorNodeVecBlur", "max_speed", "speed_max"),
("CompositorNodeVecBlur", "min_speed", "speed_min"),
("ShaderNodeMapping", "maximum", "max"),
("ShaderNodeMapping", "minimum", "min"),
("ShaderNodeMapping", "clamp_maximum", "use_max"),
("ShaderNodeMapping", "clamp_minimum", "use_min"),
("VertexPaint", "all_faces", "use_all_faces"),
("VertexPaint", "spray", "use_spray"),
("ParticleEdit", "add_keys", "default_key_count"),
("ParticleEdit", "selection_mode", "select_mode"),
("ParticleEdit", "auto_velocity", "use_auto_velocity"),
("ParticleEdit", "add_interpolate", "use_default_interpolate"),
("ParticleEdit", "emitter_deflect", "use_emitter_deflect"),
("ParticleEdit", "fade_time", "use_fade_time"),
("ParticleEdit", "keep_lengths", "use_preserve_length"),
("ParticleEdit", "keep_root", "use_preserve_root"),
("ParticleSystem", "vertex_group_clump_negate", "invert_vertex_group_clump"),
("ParticleSystem", "vertex_group_density_negate", "invert_vertex_group_density"),
("ParticleSystem", "vertex_group_field_negate", "invert_vertex_group_field"),
("ParticleSystem", "vertex_group_kink_negate", "invert_vertex_group_kink"),
("ParticleSystem", "vertex_group_length_negate", "invert_vertex_group_length"),
("ParticleSystem", "vertex_group_rotation_negate", "invert_vertex_group_rotation"),
("ParticleSystem", "vertex_group_roughness1_negate", "invert_vertex_group_roughness_1"),
("ParticleSystem", "vertex_group_roughness2_negate", "invert_vertex_group_roughness_2"),
("ParticleSystem", "vertex_group_roughness_end_negate", "invert_vertex_group_roughness_end"),
("ParticleSystem", "vertex_group_size_negate", "invert_vertex_group_size"),
("ParticleSystem", "vertex_group_tangent_negate", "invert_vertex_group_tangent"),
("ParticleSystem", "vertex_group_velocity_negate", "invert_vertex_group_velocity"),
("ParticleSystem", "hair_dynamics", "use_hair_dynamics"),
("ParticleSystem", "keyed_timing", "use_keyed_timing"),
("PointDensity", "falloff_softness", "falloff_soft"),
("PointDensity", "particle_cache", "particle_cache_space"),
("PointDensity", "turbulence_size", "turbulence_scale"),
("PointDensity", "turbulence", "use_turbulence"),
("PointDensity", "vertices_cache", "vertex_cache_space"),
("PoseBone", "ik_lin_weight", "ik_linear_weight"),
("PoseBone", "ik_rot_weight", "ik_rotation_weight"),
("PoseBone", "ik_limit_x", "use_ik_limit_x"),
("PoseBone", "ik_limit_y", "use_ik_limit_y"),
("PoseBone", "ik_limit_z", "use_ik_limit_z"),
("PoseBone", "ik_lin_control", "use_ik_linear_control"),
("PoseBone", "ik_rot_control", "use_ik_rotation_control"),
("Bone", "use_hinge", "use_inherit_rotation"),
("SPHFluidSettings", "spring_k", "spring_force"),
("SPHFluidSettings", "stiffness_k", "stiffness"),
("SPHFluidSettings", "stiffness_knear", "stiffness_near"),
("SceneGameData", "framing_color", "frame_color"),
("SceneGameData", "framing_type", "frame_type"),
("SceneGameData", "eye_separation", "stereo_eye_separation"),
("SceneGameData", "activity_culling", "use_activity_culling"),
("SceneGameData", "auto_start", "use_auto_start"),
("SceneGameData", "glsl_extra_textures", "use_glsl_extra_textures"),
("SceneGameData", "glsl_lights", "use_glsl_lights"),
("SceneGameData", "glsl_nodes", "use_glsl_nodes"),
("SceneGameData", "glsl_ramps", "use_glsl_ramps"),
("SceneGameData", "glsl_shaders", "use_glsl_shaders"),
("SceneGameData", "glsl_shadows", "use_glsl_shadows"),
("Sequence", "blend_opacity", "blend_alpha"),
("Sequence", "blend_mode", "blend_type"),
("Sequence", "frame_final_length", "frame_final_duration"),
("Sequence", "use_effect_default_fade", "use_default_fade"),
("SequenceColorBalance", "inverse_gain", "invert_gain"),
("SequenceColorBalance", "inverse_gamma", "invert_gamma"),
("SequenceColorBalance", "inverse_lift", "invert_lift"),
("EffectSequence", "multiply_colors", "color_multiply"),
("EffectSequence", "de_interlace", "use_deinterlace"),
("EffectSequence", "flip_x", "use_flip_x"),
("EffectSequence", "flip_y", "use_flip_y"),
("EffectSequence", "convert_float", "use_float"),
("EffectSequence", "premultiply", "use_premultiply"),
("EffectSequence", "proxy_custom_directory", "use_proxy_custom_directory"),
("EffectSequence", "proxy_custom_file", "use_proxy_custom_file"),
("EffectSequence", "reverse_frames", "use_reverse_frames"),
("GlowSequence", "blur_distance", "blur_radius"),
("GlowSequence", "only_boost", "use_only_boost"),
("SpeedControlSequence", "curve_compress_y", "use_curve_compress_y"),
("SpeedControlSequence", "curve_velocity", "use_curve_velocity"),
("SpeedControlSequence", "frame_blending", "use_frame_blend"),
("TransformSequence", "uniform_scale", "use_uniform_scale"),
("ImageSequence", "animation_end_offset", "animation_offset_end"),
("ImageSequence", "animation_start_offset", "animation_offset_start"),
("ImageSequence", "multiply_colors", "color_multiply"),
("ImageSequence", "de_interlace", "use_deinterlace"),
("ImageSequence", "flip_x", "use_flip_x"),
("ImageSequence", "flip_y", "use_flip_y"),
("ImageSequence", "convert_float", "use_float"),
("ImageSequence", "premultiply", "use_premultiply"),
("ImageSequence", "proxy_custom_directory", "use_proxy_custom_directory"),
("ImageSequence", "proxy_custom_file", "use_proxy_custom_file"),
("ImageSequence", "reverse_frames", "use_reverse_frames"),
("MetaSequence", "animation_end_offset", "animation_offset_end"),
("MetaSequence", "animation_start_offset", "animation_offset_start"),
("MetaSequence", "multiply_colors", "color_multiply"),
("MetaSequence", "de_interlace", "use_deinterlace"),
("MetaSequence", "flip_x", "use_flip_x"),
("MetaSequence", "flip_y", "use_flip_y"),
("MetaSequence", "convert_float", "use_float"),
("MetaSequence", "premultiply", "use_premultiply"),
("MetaSequence", "proxy_custom_directory", "use_proxy_custom_directory"),
("MetaSequence", "proxy_custom_file", "use_proxy_custom_file"),
("MetaSequence", "reverse_frames", "use_reverse_frames"),
("MovieSequence", "animation_end_offset", "animation_offset_end"),
("MovieSequence", "animation_start_offset", "animation_offset_start"),
("MovieSequence", "multiply_colors", "color_multiply"),
("MovieSequence", "de_interlace", "use_deinterlace"),
("MovieSequence", "flip_x", "use_flip_x"),
("MovieSequence", "flip_y", "use_flip_y"),
("MovieSequence", "convert_float", "use_float"),
("MovieSequence", "premultiply", "use_premultiply"),
("MovieSequence", "proxy_custom_directory", "use_proxy_custom_directory"),
("MovieSequence", "proxy_custom_file", "use_proxy_custom_file"),
("MovieSequence", "reverse_frames", "use_reverse_frames"),
("MulticamSequence", "animation_end_offset", "animation_offset_end"),
("MulticamSequence", "animation_start_offset", "animation_offset_start"),
("MulticamSequence", "multiply_colors", "color_multiply"),
("MulticamSequence", "de_interlace", "use_deinterlace"),
("MulticamSequence", "flip_x", "use_flip_x"),
("MulticamSequence", "flip_y", "use_flip_y"),
("MulticamSequence", "convert_float", "use_float"),
("MulticamSequence", "premultiply", "use_premultiply"),
("MulticamSequence", "proxy_custom_directory", "use_proxy_custom_directory"),
("MulticamSequence", "proxy_custom_file", "use_proxy_custom_file"),
("MulticamSequence", "reverse_frames", "use_reverse_frames"),
("SceneSequence", "animation_end_offset", "animation_offset_end"),
("SceneSequence", "animation_start_offset", "animation_offset_start"),
("SceneSequence", "multiply_colors", "color_multiply"),
("SceneSequence", "de_interlace", "use_deinterlace"),
("SceneSequence", "flip_x", "use_flip_x"),
("SceneSequence", "flip_y", "use_flip_y"),
("SceneSequence", "convert_float", "use_float"),
("SceneSequence", "premultiply", "use_premultiply"),
("SceneSequence", "proxy_custom_directory", "use_proxy_custom_directory"),
("SceneSequence", "proxy_custom_file", "use_proxy_custom_file"),
("SceneSequence", "reverse_frames", "use_reverse_frames"),
("SoundSequence", "animation_end_offset", "animation_offset_end"),
("SoundSequence", "animation_start_offset", "animation_offset_start"),
("SmokeDomainSettings", "smoke_domain_colli", "collision_extents"),
("SmokeDomainSettings", "smoke_cache_high_comp", "point_cache_compress_high_type"),
("SmokeDomainSettings", "smoke_cache_comp", "point_cache_compress_type"),
("SmokeDomainSettings", "maxres", "resolution_max"),
("SmokeDomainSettings", "smoothemitter", "smooth_emitter"),
("SmokeDomainSettings", "dissolve_smoke", "use_dissolve_smoke"),
("SmokeDomainSettings", "dissolve_smoke_log", "use_dissolve_smoke_log"),
("SmokeDomainSettings", "highres", "use_high_resolution"),
("SoftBodySettings", "bending", "bend"),
("SoftBodySettings", "error_limit", "error_threshold"),
("SoftBodySettings", "lcom", "location_mass_center"),
("SoftBodySettings", "lrot", "rotation_estimate"),
("SoftBodySettings", "lscale", "scale_estimate"),
("SoftBodySettings", "maxstep", "step_max"),
("SoftBodySettings", "minstep", "step_min"),
("SoftBodySettings", "diagnose", "use_diagnose"),
("SoftBodySettings", "edge_collision", "use_edge_collision"),
("SoftBodySettings", "estimate_matrix", "use_estimate_matrix"),
("SoftBodySettings", "face_collision", "use_face_collision"),
("SoftBodySettings", "self_collision", "use_self_collision"),
("SoftBodySettings", "stiff_quads", "use_stiff_quads"),
("TexMapping", "maximum", "max"),
("TexMapping", "minimum", "min"),
("TexMapping", "has_maximum", "use_max"),
("TexMapping", "has_minimum", "use_min"),
("TextCharacterFormat", "bold", "use_bold"),
("TextCharacterFormat", "italic", "use_italic"),
("TextCharacterFormat", "underline", "use_underline"),
("TextureSlot", "rgb_to_intensity", "use_rgb_to_intensity"),
("TextureSlot", "stencil", "use_stencil"),
("LampTextureSlot", "texture_coordinates", "texture_coords"),
("LampTextureSlot", "map_color", "use_map_color"),
("LampTextureSlot", "map_shadow", "use_map_shadow"),
("MaterialTextureSlot", "coloremission_factor", "color_emission_factor"),
("MaterialTextureSlot", "colordiff_factor", "diffuse_color_factor"),
("MaterialTextureSlot", "x_mapping", "mapping_x"),
("MaterialTextureSlot", "y_mapping", "mapping_y"),
("MaterialTextureSlot", "z_mapping", "mapping_z"),
("MaterialTextureSlot", "colorreflection_factor", "reflection_color_factor"),
("MaterialTextureSlot", "colorspec_factor", "specular_color_factor"),
("MaterialTextureSlot", "texture_coordinates", "texture_coords"),
("MaterialTextureSlot", "colortransmission_factor", "transmission_color_factor"),
("MaterialTextureSlot", "from_dupli", "use_from_dupli"),
("MaterialTextureSlot", "from_original", "use_from_original"),
("MaterialTextureSlot", "map_alpha", "use_map_alpha"),
("MaterialTextureSlot", "map_ambient", "use_map_ambient"),
("MaterialTextureSlot", "map_colordiff", "use_map_color_diff"),
("MaterialTextureSlot", "map_coloremission", "use_map_color_emission"),
("MaterialTextureSlot", "map_colorreflection", "use_map_color_reflection"),
("MaterialTextureSlot", "map_colorspec", "use_map_color_spec"),
("MaterialTextureSlot", "map_colortransmission", "use_map_color_transmission"),
("MaterialTextureSlot", "map_density", "use_map_density"),
("MaterialTextureSlot", "map_diffuse", "use_map_diffuse"),
("MaterialTextureSlot", "map_displacement", "use_map_displacement"),
("MaterialTextureSlot", "map_emission", "use_map_emission"),
("MaterialTextureSlot", "map_emit", "use_map_emit"),
("MaterialTextureSlot", "map_hardness", "use_map_hardness"),
("MaterialTextureSlot", "map_mirror", "use_map_mirror"),
("MaterialTextureSlot", "map_normal", "use_map_normal"),
("MaterialTextureSlot", "map_raymir", "use_map_raymir"),
("MaterialTextureSlot", "map_reflection", "use_map_reflect"),
("MaterialTextureSlot", "map_scattering", "use_map_scatter"),
("MaterialTextureSlot", "map_specular", "use_map_specular"),
("MaterialTextureSlot", "map_translucency", "use_map_translucency"),
("MaterialTextureSlot", "map_warp", "use_map_warp"),
("WorldTextureSlot", "texture_coordinates", "texture_coords"),
("WorldTextureSlot", "map_blend", "use_map_blend"),
("WorldTextureSlot", "map_horizon", "use_map_horizon"),
("WorldTextureSlot", "map_zenith_down", "use_map_zenith_down"),
("WorldTextureSlot", "map_zenith_up", "use_map_zenith_up"),
("VoxelData", "still_frame_number", "still_frame"),
("WorldLighting", "ao_blend_mode", "ao_blend_type"),
("WorldLighting", "error_tolerance", "error_threshold"),
("WorldLighting", "use_ambient_occlusion", "use_ambient_occlusian"),
("WorldLighting", "pixel_cache", "use_cache"),
("WorldLighting", "use_environment_lighting", "use_environment_light"),
("WorldLighting", "use_indirect_lighting", "use_indirect_light"),
("WorldStarsSettings", "color_randomization", "color_random"),
("WorldStarsSettings", "min_distance", "distance_min"),
("WorldLighting", "falloff", "use_falloff"),
("Constraint", "disabled", "is_valid"),
("ClampToConstraint", "cyclic", "use_cyclic"),
("ImageTexture", "filter", "filter_type"),
("ImageTexture", "interpolation", "use_interpolation"),
("ImageTexture", "mipmap", "use_mipmap"),
("ImageUser", "frames", "frame_duration"),
("ImageUser", "offset", "frame_offset"),
("ImageUser", "cyclic", "use_cyclic"),
("ArmatureModifier", "invert", "invert_vertex_group"),
("ArmatureModifier", "quaternion", "use_deform_preserve_volume"),
("ArrayModifier", "length", "fit_length"),
("BevelModifier", "angle", "angle_limit"),
("BuildModifier", "length", "frame_duration"),
("BuildModifier", "randomize", "use_random_order"),
("CastModifier", "x", "use_x"),
("CastModifier", "y", "use_y"),
("CastModifier", "z", "use_z"),
("ExplodeModifier", "size", "use_size"),
("MaskModifier", "invert", "invert_vertex_group"),
("MeshDeformModifier", "invert", "invert_vertex_group"),
("MeshDeformModifier", "dynamic", "use_dynamic_bind"),
("MirrorModifier", "clip", "use_clip"),
("MirrorModifier", "x", "use_x"),
("MirrorModifier", "y", "use_y"),
("MirrorModifier", "z", "use_z"),
("ParticleInstanceModifier", "children", "use_children"),
("ParticleInstanceModifier", "normal", "use_normal"),
("ParticleInstanceModifier", "size", "use_size"),
("ShrinkwrapModifier", "negative", "use_negative_direction"),
("ShrinkwrapModifier", "positive", "use_positive_direction"),
("ShrinkwrapModifier", "x", "use_project_x"),
("ShrinkwrapModifier", "y", "use_project_y"),
("ShrinkwrapModifier", "z", "use_project_z"),
("ShrinkwrapModifier", "mode", "wrap_method"),
("SimpleDeformModifier", "mode", "deform_method"),
("SimpleDeformModifier", "relative", "use_relative"),
("SmoothModifier", "repeat", "iterations"),
("SmoothModifier", "x", "use_x"),
("SmoothModifier", "y", "use_y"),
("SmoothModifier", "z", "use_z"),
("SolidifyModifier", "invert", "invert_vertex_group"),
("WaveModifier", "cyclic", "use_cyclic"),
("WaveModifier", "normals", "use_normal"),
("WaveModifier", "x", "use_x"),
("WaveModifier", "y", "use_y"),
("DampedTrackConstraint", "track", "track_axis"),
("FloorConstraint", "sticky", "use_sticky"),
("FollowPathConstraint", "forward", "forward_axis"),
("FollowPathConstraint", "up", "up_axis"),
("LockedTrackConstraint", "lock", "lock_axis"),
("LockedTrackConstraint", "track", "track_axis"),
("MaintainVolumeConstraint", "axis", "free_axis"),
("TrackToConstraint", "track", "track_axis"),
("TrackToConstraint", "up", "up_axis"),
("GameProperty", "debug", "show_debug"),
("Image", "tiles", "use_tiles"),
("Lamp", "diffuse", "use_diffuse"),
("Lamp", "negative", "use_negative"),
("Lamp", "layer", "use_own_layer"),
("Lamp", "specular", "use_specular"),
("AreaLamp", "dither", "use_dither"),
("AreaLamp", "jitter", "use_jitter"),
("SpotLamp", "square", "use_square"),
("Material", "cubic", "use_cubic"),
("Material", "shadows", "use_shadows"),
("ParticleSettings", "amount", "count"),
("ParticleSettings", "display", "draw_percentage"),
("ParticleSettings", "velocity", "show_velocity"),
("ParticleSettings", "trand", "use_emit_random"),
("ParticleSettings", "parent", "use_parent_particles"),
("ParticleSettings", "emitter", "use_render_emitter"),
("ParticleSettings", "viewport", "use_simplify_viewport"),
("Texture", "brightness", "intensity"),
("CloudsTexture", "stype", "cloud_type"),
("EnvironmentMapTexture", "filter", "filter_type"),
("EnvironmentMapTexture", "mipmap", "use_mipmap"),
("MarbleTexture", "stype", "marble_type"),
("StucciTexture", "stype", "stucci_type"),
("WoodTexture", "stype", "wood_type"),
("World", "range", "color_range"),
("World", "lighting", "light_settings"),
("World", "mist", "mist_settings"),
("World", "stars", "star_settings"),
("MaterialHalo", "lines", "use_lines"),
("MaterialHalo", "ring", "use_ring"),
("MaterialHalo", "soft", "use_soft"),
("MaterialHalo", "star", "use_star"),
("MaterialHalo", "texture", "use_texture"),
("MaterialPhysics", "damp", "damping"),
("MaterialRaytraceTransparency", "limit", "depth_max"),
("NlaStrip", "reversed", "use_reverse"),
("CompositorNodeBlur", "bokeh", "use_bokeh"),
("CompositorNodeBlur", "gamma", "use_gamma_correction"),
("CompositorNodeBlur", "relative", "use_relative"),
("CompositorNodeChannelMatte", "high", "limit_max"),
("CompositorNodeChannelMatte", "low", "limit_min"),
("CompositorNodeChannelMatte", "channel", "matte_channel"),
("CompositorNodeChromaMatte", "cutoff", "threshold"),
("CompositorNodeColorMatte", "h", "color_hue"),
("CompositorNodeColorMatte", "s", "color_saturation"),
("CompositorNodeColorMatte", "v", "color_value"),
("CompositorNodeDBlur", "wrap", "use_wrap"),
("CompositorNodeDefocus", "preview", "use_preview"),
("CompositorNodeHueSat", "hue", "color_hue"),
("CompositorNodeHueSat", "sat", "color_saturation"),
("CompositorNodeHueSat", "val", "color_value"),
("CompositorNodeImage", "frames", "frame_duration"),
("CompositorNodeImage", "offset", "frame_offset"),
("CompositorNodeImage", "start", "frame_start"),
("CompositorNodeImage", "cyclic", "use_cyclic"),
("CompositorNodeInvert", "alpha", "invert_alpha"),
("CompositorNodeInvert", "rgb", "invert_rgb"),
("CompositorNodeLensdist", "fit", "use_fit"),
("CompositorNodeLensdist", "jitter", "use_jitter"),
("CompositorNodeMixRGB", "alpha", "use_alpha"),
("CompositorNodeRotate", "filter", "filter_type"),
("CompositorNodeTime", "end", "frame_end"),
("CompositorNodeTime", "start", "frame_start"),
("CompositorNodeVecBlur", "curved", "use_curved"),
("ShaderNodeExtendedMaterial", "diffuse", "use_diffuse"),
("ShaderNodeExtendedMaterial", "specular", "use_specular"),
("ShaderNodeMaterial", "diffuse", "use_diffuse"),
("ShaderNodeMaterial", "specular", "use_specular"),
("ShaderNodeMixRGB", "alpha", "use_alpha"),
("TextureNodeCurveTime", "end", "frame_end"),
("TextureNodeCurveTime", "start", "frame_start"),
("TextureNodeMixRGB", "alpha", "use_alpha"),
("TextureSlot", "negate", "invert"),
("TextureSlot", "size", "scale"),
("SoftBodySettings", "damp", "damping"),
("SequenceCrop", "right", "max_x"),
("SequenceCrop", "top", "max_y"),
("SequenceCrop", "bottom", "min_x"),
("SequenceCrop", "left", "min_y"),
("Sequence", "speed_fader", "speed_factor"),
("SpeedControlSequence", "global_speed", "multiply_speed"),
("SpeedControlSequence", "use_curve_velocity", "use_as_speed"),
("SpeedControlSequence", "use_curve_compress_y", "scale_to_length"),
]
import bpy
class UpdateAnimData(bpy.types.Operator):
'''Update data paths from 2.53 to edited data paths of drivers and fcurves'''
bl_idname = "anim.update_data_paths"
bl_label = "Update Animation Data"
def execute(self, context):
import animsys_refactor
animsys_refactor.update_data_paths(data_path_update)
return {'FINISHED'}
if __name__ == "__main__":
bpy.ops.anim.update_data_paths()
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)

View File

@@ -1,294 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import sys
import bpy
language_id = 'python'
# store our own __main__ module, not 100% needed
# but python expects this in some places
_BPY_MAIN_OWN = True
def add_scrollback(text, text_type):
for l in text.split('\n'):
bpy.ops.console.scrollback_append(text=l.replace('\t', ' '),
type=text_type)
def get_console(console_id):
'''
helper function for console operators
currently each text datablock gets its own
console - code.InteractiveConsole()
...which is stored in this function.
console_id can be any hashable type
'''
from code import InteractiveConsole
consoles = getattr(get_console, "consoles", None)
hash_next = hash(bpy.context.window_manager)
if consoles is None:
consoles = get_console.consoles = {}
get_console.consoles_namespace_hash = hash_next
else:
# check if clearning the namespace is needed to avoid a memory leak.
# the window manager is normally loaded with new blend files
# so this is a reasonable way to deal with namespace clearing.
# bpy.data hashing is reset by undo so cant be used.
hash_prev = getattr(get_console, "consoles_namespace_hash", 0)
if hash_prev != hash_next:
get_console.consoles_namespace_hash = hash_next
consoles.clear()
console_data = consoles.get(console_id)
if console_data:
console, stdout, stderr = console_data
# XXX, bug in python 3.1.2 ? (worked in 3.1.1)
# seems there is no way to clear StringIO objects for writing, have to make new ones each time.
import io
stdout = io.StringIO()
stderr = io.StringIO()
else:
if _BPY_MAIN_OWN:
import types
bpy_main_mod = types.ModuleType("__main__")
namespace = bpy_main_mod.__dict__
else:
namespace = {}
namespace["__builtins__"] = sys.modules["builtins"]
namespace["bpy"] = bpy
namespace["C"] = bpy.context
console = InteractiveConsole(locals=namespace, filename="<blender_console>")
console.push("from mathutils import *")
console.push("from math import *")
if _BPY_MAIN_OWN:
console._bpy_main_mod = bpy_main_mod
import io
stdout = io.StringIO()
stderr = io.StringIO()
consoles[console_id] = console, stdout, stderr
return console, stdout, stderr
# Both prompts must be the same length
PROMPT = '>>> '
PROMPT_MULTI = '... '
def execute(context):
sc = context.space_data
try:
line_object = sc.history[-1]
except:
return {'CANCELLED'}
console, stdout, stderr = get_console(hash(context.region))
# redirect output
sys.stdout = stdout
sys.stderr = stderr
# dont allow the stdin to be used, can lock blender.
stdin_backup = sys.stdin
sys.stdin = None
if _BPY_MAIN_OWN:
main_mod_back = sys.modules["__main__"]
sys.modules["__main__"] = console._bpy_main_mod
# incase exception happens
line = "" # incase of encodingf error
is_multiline = False
try:
line = line_object.body
# run the console, "\n" executes a multiline statement
line_exec = line if line.strip() else "\n"
is_multiline = console.push(line_exec)
except:
# unlikely, but this can happen with unicode errors for example.
import traceback
stderr.write(traceback.format_exc())
if _BPY_MAIN_OWN:
sys.modules["__main__"] = main_mod_back
stdout.seek(0)
stderr.seek(0)
output = stdout.read()
output_err = stderr.read()
# cleanup
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
sys.last_traceback = None
# So we can reuse, clear all data
stdout.truncate(0)
stderr.truncate(0)
# special exception. its possible the command loaded a new user interface
if hash(sc) != hash(context.space_data):
return
bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
if is_multiline:
sc.prompt = PROMPT_MULTI
else:
sc.prompt = PROMPT
# insert a new blank line
bpy.ops.console.history_append(text="", current_character=0,
remove_duplicates=True)
# Insert the output into the editor
# not quite correct because the order might have changed,
# but ok 99% of the time.
if output:
add_scrollback(output, 'OUTPUT')
if output_err:
add_scrollback(output_err, 'ERROR')
# restore the stdin
sys.stdin = stdin_backup
# execute any hooks
for func, args in execute.hooks:
func(*args)
return {'FINISHED'}
execute.hooks = []
def autocomplete(context):
from console import intellisense
sc = context.space_data
console = get_console(hash(context.region))[0]
if not console:
return {'CANCELLED'}
# dont allow the stdin to be used, can lock blender.
# note: unlikely stdin would be used for autocomp. but its possible.
stdin_backup = sys.stdin
sys.stdin = None
scrollback = ""
scrollback_error = ""
if _BPY_MAIN_OWN:
main_mod_back = sys.modules["__main__"]
sys.modules["__main__"] = console._bpy_main_mod
try:
current_line = sc.history[-1]
line = current_line.body
# This function isnt aware of the text editor or being an operator
# just does the autocomp then copy its results back
current_line.body, current_line.current_character, scrollback = \
intellisense.expand(
line=current_line.body,
cursor=current_line.current_character,
namespace=console.locals,
private=bpy.app.debug)
except:
# unlikely, but this can happen with unicode errors for example.
# or if the api attribute access its self causes an error.
import traceback
scrollback_error = traceback.format_exc()
if _BPY_MAIN_OWN:
sys.modules["__main__"] = main_mod_back
# Separate automplete output by command prompts
if scrollback != '':
bpy.ops.console.scrollback_append(text=sc.prompt + current_line.body, type='INPUT')
# Now we need to copy back the line from blender back into the
# text editor. This will change when we dont use the text editor
# anymore
if scrollback:
add_scrollback(scrollback, 'INFO')
if scrollback_error:
add_scrollback(scrollback_error, 'ERROR')
# restore the stdin
sys.stdin = stdin_backup
context.area.tag_redraw()
return {'FINISHED'}
def banner(context):
sc = context.space_data
version_string = sys.version.strip().replace('\n', ' ')
add_scrollback("PYTHON INTERACTIVE CONSOLE %s" % version_string, 'OUTPUT')
add_scrollback("", 'OUTPUT')
add_scrollback("Command History: Up/Down Arrow", 'OUTPUT')
add_scrollback("Cursor: Left/Right Home/End", 'OUTPUT')
add_scrollback("Remove: Backspace/Delete", 'OUTPUT')
add_scrollback("Execute: Enter", 'OUTPUT')
add_scrollback("Autocomplete: Ctrl+Space", 'OUTPUT')
add_scrollback("Ctrl +/- Wheel: Zoom", 'OUTPUT')
add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, bgl, blf, mathutils", 'OUTPUT')
add_scrollback("Convenience Imports: from mathutils import *; from math import *", 'OUTPUT')
add_scrollback("", 'OUTPUT')
add_scrollback(" WARNING!!! Blender 2.5 API is subject to change, see API reference for more info.", 'ERROR')
add_scrollback("", 'OUTPUT')
sc.prompt = PROMPT
return {'FINISHED'}
def register():
pass
def unregister():
pass
if __name__ == "__main__":
register()

View File

@@ -1,90 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
from math import *
import bpy
from mathutils import *
def main(context):
def cleanupEulCurve(fcv):
keys = []
for k in fcv.keyframe_points:
keys.append([k.handle_left.copy(), k.co.copy(), k.handle_right.copy()])
print(keys)
for i in range(len(keys)):
cur = keys[i]
prev = keys[i - 1] if i > 0 else None
next = keys[i + 1] if i < len(keys) - 1 else None
if prev is None:
continue
th = pi
if abs(prev[1][1] - cur[1][1]) >= th: # more than 180 degree jump
fac = pi * 2.0
if prev[1][1] > cur[1][1]:
while abs(cur[1][1] - prev[1][1]) >= th: # < prev[1][1]:
cur[0][1] += fac
cur[1][1] += fac
cur[2][1] += fac
elif prev[1][1] < cur[1][1]:
while abs(cur[1][1] - prev[1][1]) >= th:
cur[0][1] -= fac
cur[1][1] -= fac
cur[2][1] -= fac
for i in range(len(keys)):
for x in range(2):
fcv.keyframe_points[i].handle_left[x] = keys[i][0][x]
fcv.keyframe_points[i].co[x] = keys[i][1][x]
fcv.keyframe_points[i].handle_right[x] = keys[i][2][x]
flist = bpy.context.active_object.animation_data.action.fcurves
for f in flist:
if f.select and f.data_path.endswith("rotation_euler"):
cleanupEulCurve(f)
class DiscontFilterOp(bpy.types.Operator):
"""Fixes the most common causes of gimbal lock in the fcurves of the active bone"""
bl_idname = "graph.euler_filter"
bl_label = "Filter out discontinuities in the active fcurves"
@classmethod
def poll(cls, context):
return context.active_object != None
def execute(self, context):
main(context)
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,205 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.props import StringProperty
class EditExternally(bpy.types.Operator):
'''Edit image in an external application'''
bl_idname = "image.external_edit"
bl_label = "Image Edit Externally"
bl_options = {'REGISTER'}
filepath = StringProperty(name="File Path", description="Path to an image file", maxlen=1024, default="")
def _editor_guess(self, context):
import platform
try:
system = platform.system()
except UnicodeDecodeError:
import sys
system = sys.platform
image_editor = context.user_preferences.filepaths.image_editor
# use image editor in the preferences when available.
if not image_editor:
if system in ('Windows', 'win32'):
image_editor = ["start"] # not tested!
elif system == 'Darwin':
image_editor = ["open"]
else:
image_editor = ["gimp"]
else:
if system == 'Darwin':
# blender file selector treats .app as a folder
# and will include a trailing backslash, so we strip it.
image_editor.rstrip('\\')
image_editor = ["open", "-a", image_editor]
else:
image_editor = [image_editor]
return image_editor
def execute(self, context):
import os
import subprocess
filepath = bpy.path.abspath(self.filepath)
if not os.path.exists(filepath):
self.report('ERROR', "Image path '%s' not found." % filepath)
return {'CANCELLED'}
cmd = self._editor_guess(context) + [filepath]
subprocess.Popen(cmd)
return {'FINISHED'}
def invoke(self, context, event):
try:
filepath = context.space_data.image.filepath
except:
self.report({'ERROR'}, "Image not found on disk")
return {'CANCELLED'}
self.filepath = filepath
self.execute(context)
return {'FINISHED'}
class SaveDirty(bpy.types.Operator):
"""Save all modified textures"""
bl_idname = "image.save_dirty"
bl_label = "Save Dirty"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
unique_paths = set()
for image in bpy.data.images:
if image.is_dirty:
filepath = bpy.path.abspath(image.filepath)
if "\\" not in filepath and "/" not in filepath:
self.report({'WARNING'}, "Invalid path: " + filepath)
elif filepath in unique_paths:
self.report({'WARNING'}, "Path used by more then one image: " + filepath)
else:
unique_paths.add(filepath)
image.save()
return {'FINISHED'}
class ProjectEdit(bpy.types.Operator):
"""Edit a snapshot if the viewport in an external image editor"""
bl_idname = "image.project_edit"
bl_label = "Project Edit"
bl_options = {'REGISTER'}
_proj_hack = [""]
def execute(self, context):
import os
import subprocess
EXT = "png" # could be made an option but for now ok
for image in bpy.data.images:
image.tag = True
if 'FINISHED' not in bpy.ops.paint.image_from_view():
return {'CANCELLED'}
image_new = None
for image in bpy.data.images:
if not image.tag:
image_new = image
break
if not image_new:
self.report({'ERROR'}, "Could not make new image")
return {'CANCELLED'}
filepath = os.path.basename(bpy.data.filepath)
filepath = os.path.splitext(filepath)[0]
# filepath = bpy.path.clean_name(filepath) # fixes <memory> rubbish, needs checking
if filepath.startswith(".") or filepath == "":
# TODO, have a way to check if the file is saved, assume startup.blend
tmpdir = context.user_preferences.filepaths.temporary_directory
filepath = os.path.join(tmpdir, "project_edit")
else:
filepath = "//" + filepath
obj = context.object
if obj:
filepath += "_" + bpy.path.clean_name(obj.name)
filepath_final = filepath + "." + EXT
i = 0
while os.path.exists(bpy.path.abspath(filepath_final)):
filepath_final = filepath + ("%.3d.%s" % (i, EXT))
i += 1
image_new.name = os.path.basename(filepath_final)
ProjectEdit._proj_hack[0] = image_new.name
image_new.filepath_raw = filepath_final # TODO, filepath raw is crummy
image_new.file_format = 'PNG'
image_new.save()
bpy.ops.image.external_edit(filepath=filepath_final)
return {'FINISHED'}
class ProjectApply(bpy.types.Operator):
"""Project edited image back onto the object"""
bl_idname = "image.project_apply"
bl_label = "Project Apply"
bl_options = {'REGISTER'}
def execute(self, context):
image_name = ProjectEdit._proj_hack[0] # TODO, deal with this nicer
try:
image = bpy.data.images[image_name]
except KeyError:
self.report({'ERROR'}, "Could not find image '%s'" % image_name)
return {'CANCELLED'}
image.reload()
bpy.ops.paint.project_image(image=image_name)
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,183 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
class MeshSelectInteriorFaces(bpy.types.Operator):
'''Select faces where all edges have more then 2 face users.'''
bl_idname = "mesh.faces_select_interior"
bl_label = "Select Interior Faces"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH')
def execute(self, context):
ob = context.active_object
context.tool_settings.mesh_select_mode = False, False, True
is_editmode = (ob.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
mesh = ob.data
face_list = mesh.faces[:]
face_edge_keys = [face.edge_keys for face in face_list]
edge_face_count = mesh.edge_face_count_dict
def test_interior(index):
for key in face_edge_keys[index]:
if edge_face_count[key] < 3:
return False
return True
for index, face in enumerate(face_list):
if(test_interior(index)):
face.select = True
else:
face.select = False
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
return {'FINISHED'}
class MeshMirrorUV(bpy.types.Operator):
'''Copy mirror UV coordinates on the X axis based on a mirrored mesh'''
bl_idname = "mesh.faces_miror_uv"
bl_label = "Copy Mirrored UV coords"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
ob = context.active_object
return (ob and ob.type == 'MESH')
def execute(self, context):
DIR = 1 # TODO, make an option
from mathutils import Vector
ob = context.active_object
is_editmode = (ob.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
mesh = ob.data
# mirror lookups
mirror_gt = {}
mirror_lt = {}
vcos = [v.co.to_tuple(5) for v in mesh.vertices]
for i, co in enumerate(vcos):
if co[0] > 0.0:
mirror_gt[co] = i
elif co[0] < 0.0:
mirror_lt[co] = i
else:
mirror_gt[co] = i
mirror_lt[co] = i
#for i, v in enumerate(mesh.vertices):
vmap = {}
for mirror_a, mirror_b in (mirror_gt, mirror_lt), (mirror_lt, mirror_gt):
for co, i in mirror_a.items():
nco = (-co[0], co[1], co[2])
j = mirror_b.get(nco)
if j is not None:
vmap[i] = j
active_uv_layer = None
for lay in mesh.uv_textures:
if lay.active:
active_uv_layer = lay.data
break
fuvs = [(uv.uv1, uv.uv2, uv.uv3, uv.uv4) for uv in active_uv_layer]
fuvs_cpy = [(uv[0].copy(), uv[1].copy(), uv[2].copy(), uv[3].copy()) for uv in fuvs]
# as a list
faces = mesh.faces[:]
fuvsel = [(False not in uv.select_uv) for uv in active_uv_layer]
fcents = [f.center for f in faces]
# find mirror faces
mirror_fm = {}
for i, f in enumerate(faces):
verts = f.vertices[:]
verts.sort()
verts = tuple(verts)
mirror_fm[verts] = i
fmap = {}
for i, f in enumerate(faces):
verts = [vmap.get(j) for j in f.vertices]
if None not in verts:
verts.sort()
j = mirror_fm.get(tuple(verts))
if j is not None:
fmap[i] = j
done = [False] * len(faces)
for i, j in fmap.items():
if not fuvsel[i] or not fuvsel[j]:
continue
elif DIR == 0 and fcents[i][0] < 0.0:
continue
elif DIR == 1 and fcents[i][0] > 0.0:
continue
# copy UVs
uv1 = fuvs[i]
uv2 = fuvs_cpy[j]
# get the correct rotation
v1 = faces[j].vertices[:]
v2 = [vmap[k] for k in faces[i].vertices[:]]
for k in range(len(uv1)):
k_map = v1.index(v2[k])
uv1[k].x = - (uv2[k_map].x - 0.5) + 0.5
uv1[k].y = uv2[k_map].y
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,185 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
def pose_info():
from mathutils import Matrix
info = {}
obj = bpy.context.object
pose = obj.pose
pose_items = pose.bones.items()
for name, pbone in pose_items:
binfo = {}
bone = pbone.bone
binfo["parent"] = getattr(bone.parent, "name", None)
binfo["bone"] = bone
binfo["pbone"] = pbone
binfo["matrix_local"] = bone.matrix_local.copy()
try:
binfo["matrix_local_inv"] = binfo["matrix_local"].inverted()
except:
binfo["matrix_local_inv"] = Matrix()
binfo["matrix"] = bone.matrix.copy()
binfo["matrix_pose"] = pbone.matrix.copy()
try:
binfo["matrix_pose_inv"] = binfo["matrix_pose"].inverted()
except:
binfo["matrix_pose_inv"] = Matrix()
print(binfo["matrix_pose"])
info[name] = binfo
for name, pbone in pose_items:
binfo = info[name]
binfo_parent = binfo.get("parent", None)
if binfo_parent:
binfo_parent = info[binfo_parent]
matrix = binfo["matrix_pose"]
rest_matrix = binfo["matrix_local"]
if binfo_parent:
matrix = binfo_parent["matrix_pose_inv"] * matrix
rest_matrix = binfo_parent["matrix_local_inv"] * rest_matrix
matrix = rest_matrix.inverted() * matrix
binfo["matrix_key"] = matrix.copy()
return info
def bake(frame_start, frame_end, step=1, only_selected=False):
scene = bpy.context.scene
obj = bpy.context.object
pose = obj.pose
info_ls = []
frame_range = range(frame_start, frame_end + 1, step)
# could spped this up by applying steps here too...
for f in frame_range:
scene.frame_set(f)
info = pose_info()
info_ls.append(info)
f += 1
action = bpy.data.actions.new("Action")
bpy.context.object.animation_data.action = action
pose_items = pose.bones.items()
for name, pbone in pose_items:
if only_selected and not pbone.select:
continue
for f in frame_range:
matrix = info_ls[int((f - frame_start) / step)][name]["matrix_key"]
#pbone.location = matrix.to_translation()
#pbone.rotation_quaternion = matrix.to_quaternion()
pbone.matrix_basis = matrix
pbone.keyframe_insert("location", -1, f, name)
rotation_mode = pbone.rotation_mode
if rotation_mode == 'QUATERNION':
pbone.keyframe_insert("rotation_quaternion", -1, f, name)
elif rotation_mode == 'AXIS_ANGLE':
pbone.keyframe_insert("rotation_axis_angle", -1, f, name)
else: # euler, XYZ, ZXY etc
pbone.keyframe_insert("rotation_euler", -1, f, name)
pbone.keyframe_insert("scale", -1, f, name)
return action
from bpy.props import *
class BakeAction(bpy.types.Operator):
'''Bake animation to an Action'''
bl_idname = "nla.bake"
bl_label = "Bake Action"
bl_options = {'REGISTER', 'UNDO'}
frame_start = IntProperty(name="Start Frame",
description="Start frame for baking",
default=1, min=1, max=300000)
frame_end = IntProperty(name="End Frame",
description="End frame for baking",
default=250, min=1, max=300000)
step = IntProperty(name="Frame Step",
description="Frame Step",
default=1, min=1, max=120)
only_selected = BoolProperty(name="Only Selected",
default=True)
def execute(self, context):
action = bake(self.frame_start, self.frame_end, self.step, self.only_selected)
# basic cleanup, could move elsewhere
for fcu in action.fcurves:
keyframe_points = fcu.keyframe_points
i = 1
while i < len(fcu.keyframe_points) - 1:
val_prev = keyframe_points[i - 1].co[1]
val_next = keyframe_points[i + 1].co[1]
val = keyframe_points[i].co[1]
if abs(val - val_prev) + abs(val - val_next) < 0.0001:
keyframe_points.remove(keyframe_points[i])
else:
i += 1
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
#def menu_func(self, context):
# self.layout.operator(BakeAction.bl_idname, text="Bake Armature Action")
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,574 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.props import *
class SelectPattern(bpy.types.Operator):
'''Select object matching a naming pattern'''
bl_idname = "object.select_pattern"
bl_label = "Select Pattern"
bl_options = {'REGISTER', 'UNDO'}
pattern = StringProperty(name="Pattern", description="Name filter using '*' and '?' wildcard chars", maxlen=32, default="*")
case_sensitive = BoolProperty(name="Case Sensitive", description="Do a case sensitive compare", default=False)
extend = BoolProperty(name="Extend", description="Extend the existing selection", default=True)
def execute(self, context):
import fnmatch
if self.case_sensitive:
pattern_match = fnmatch.fnmatchcase
else:
pattern_match = lambda a, b: fnmatch.fnmatchcase(a.upper(), b.upper())
obj = context.object
if obj and obj.mode == 'POSE':
items = obj.data.bones
elif obj and obj.type == 'ARMATURE' and obj.mode == 'EDIT':
items = obj.data.edit_bones
else:
items = context.visible_objects
# Can be pose bones or objects
for item in items:
if pattern_match(item.name, self.pattern):
item.select = True
elif not self.extend:
item.select = False
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_popup(self, event)
def draw(self, context):
layout = self.layout
layout.prop(self, "pattern")
row = layout.row()
row.prop(self, "case_sensitive")
row.prop(self, "extend")
class SelectCamera(bpy.types.Operator):
'''Select object matching a naming pattern'''
bl_idname = "object.select_camera"
bl_label = "Select Camera"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.scene.camera is not None
def execute(self, context):
scene = context.scene
camera = scene.camera
if camera.name not in scene.objects:
self.report({'WARNING'}, "Active camera is not in this scene")
context.scene.objects.active = camera
camera.select = True
return {'FINISHED'}
class SelectHierarchy(bpy.types.Operator):
'''Select object relative to the active objects position in the hierarchy'''
bl_idname = "object.select_hierarchy"
bl_label = "Select Hierarchy"
bl_options = {'REGISTER', 'UNDO'}
direction = EnumProperty(items=(
('PARENT', "Parent", ""),
('CHILD', "Child", "")),
name="Direction",
description="Direction to select in the hierarchy",
default='PARENT')
extend = BoolProperty(name="Extend", description="Extend the existing selection", default=False)
@classmethod
def poll(cls, context):
return context.object
def execute(self, context):
select_new = []
act_new = None
selected_objects = context.selected_objects
obj_act = context.object
if context.object not in selected_objects:
selected_objects.append(context.object)
if self.direction == 'PARENT':
for obj in selected_objects:
parent = obj.parent
if parent:
if obj_act == obj:
act_new = parent
select_new.append(parent)
else:
for obj in selected_objects:
select_new.extend(obj.children)
if select_new:
select_new.sort(key=lambda obj_iter: obj_iter.name)
act_new = select_new[0]
# dont edit any object settings above this
if select_new:
if not self.extend:
bpy.ops.object.select_all(action='DESELECT')
for obj in select_new:
obj.select = True
context.scene.objects.active = act_new
return {'FINISHED'}
return {'CANCELLED'}
class SubdivisionSet(bpy.types.Operator):
'''Sets a Subdivision Surface Level (1-5)'''
bl_idname = "object.subdivision_set"
bl_label = "Subdivision Set"
bl_options = {'REGISTER', 'UNDO'}
level = IntProperty(name="Level",
default=1, min=-100, max=100, soft_min=-6, soft_max=6)
relative = BoolProperty(name="Relative", description="Apply the subsurf level as an offset relative to the current level", default=False)
@classmethod
def poll(cls, context):
obs = context.selected_editable_objects
return (obs is not None)
def execute(self, context):
level = self.level
relative = self.relative
if relative and level == 0:
return {'CANCELLED'} # nothing to do
def set_object_subd(obj):
for mod in obj.modifiers:
if mod.type == 'MULTIRES':
if not relative:
if level <= mod.total_levels:
if obj.mode == 'SCULPT':
if mod.sculpt_levels != level:
mod.sculpt_levels = level
elif obj.mode == 'OBJECT':
if mod.levels != level:
mod.levels = level
return
else:
if obj.mode == 'SCULPT':
if mod.sculpt_levels + level <= mod.total_levels:
mod.sculpt_levels += level
elif obj.mode == 'OBJECT':
if mod.levels + level <= mod.total_levels:
mod.levels += level
return
elif mod.type == 'SUBSURF':
if relative:
mod.levels += level
else:
if mod.levels != level:
mod.levels = level
return
# add a new modifier
try:
mod = obj.modifiers.new("Subsurf", 'SUBSURF')
mod.levels = level
except:
self.report({'WARNING'}, "Modifiers cannot be added to object: " + obj.name)
for obj in context.selected_editable_objects:
set_object_subd(obj)
return {'FINISHED'}
class ShapeTransfer(bpy.types.Operator):
'''Copy another selected objects active shape to this one by applying the relative offsets'''
bl_idname = "object.shape_key_transfer"
bl_label = "Transfer Shape Key"
bl_options = {'REGISTER', 'UNDO'}
mode = EnumProperty(items=(
('OFFSET', "Offset", "Apply the relative positional offset"),
('RELATIVE_FACE', "Relative Face", "Calculate the geometricly relative position (using faces)."),
('RELATIVE_EDGE', "Relative Edge", "Calculate the geometricly relative position (using edges).")),
name="Transformation Mode",
description="Method to apply relative shape positions to the new shape",
default='OFFSET')
use_clamp = BoolProperty(name="Clamp Offset",
description="Clamp the transformation to the distance each vertex moves in the original shape.",
default=False)
def _main(self, ob_act, objects, mode='OFFSET', use_clamp=False):
def me_nos(verts):
return [v.normal.copy() for v in verts]
def me_cos(verts):
return [v.co.copy() for v in verts]
def ob_add_shape(ob, name):
me = ob.data
key = ob.shape_key_add(from_mix=False)
if len(me.shape_keys.keys) == 1:
key.name = "Basis"
key = ob.shape_key_add(from_mix=False) # we need a rest
key.name = name
ob.active_shape_key_index = len(me.shape_keys.keys) - 1
ob.show_only_shape_key = True
from mathutils.geometry import barycentric_transform
from mathutils import Vector
if use_clamp and mode == 'OFFSET':
use_clamp = False
me = ob_act.data
orig_key_name = ob_act.active_shape_key.name
orig_shape_coords = me_cos(ob_act.active_shape_key.data)
orig_normals = me_nos(me.vertices)
# orig_coords = me_cos(me.vertices) # the actual mverts location isnt as relyable as the base shape :S
orig_coords = me_cos(me.shape_keys.keys[0].data)
for ob_other in objects:
me_other = ob_other.data
if len(me_other.vertices) != len(me.vertices):
self.report({'WARNING'}, "Skipping '%s', vertex count differs" % ob_other.name)
continue
target_normals = me_nos(me_other.vertices)
if me_other.shape_keys:
target_coords = me_cos(me_other.shape_keys.keys[0].data)
else:
target_coords = me_cos(me_other.vertices)
ob_add_shape(ob_other, orig_key_name)
# editing the final coords, only list that stores wrapped coords
target_shape_coords = [v.co for v in ob_other.active_shape_key.data]
median_coords = [[] for i in range(len(me.vertices))]
# Method 1, edge
if mode == 'OFFSET':
for i, vert_cos in enumerate(median_coords):
vert_cos.append(target_coords[i] + (orig_shape_coords[i] - orig_coords[i]))
elif mode == 'RELATIVE_FACE':
for face in me.faces:
i1, i2, i3, i4 = face.vertices_raw
if i4 != 0:
pt = barycentric_transform(orig_shape_coords[i1],
orig_coords[i4], orig_coords[i1], orig_coords[i2],
target_coords[i4], target_coords[i1], target_coords[i2])
median_coords[i1].append(pt)
pt = barycentric_transform(orig_shape_coords[i2],
orig_coords[i1], orig_coords[i2], orig_coords[i3],
target_coords[i1], target_coords[i2], target_coords[i3])
median_coords[i2].append(pt)
pt = barycentric_transform(orig_shape_coords[i3],
orig_coords[i2], orig_coords[i3], orig_coords[i4],
target_coords[i2], target_coords[i3], target_coords[i4])
median_coords[i3].append(pt)
pt = barycentric_transform(orig_shape_coords[i4],
orig_coords[i3], orig_coords[i4], orig_coords[i1],
target_coords[i3], target_coords[i4], target_coords[i1])
median_coords[i4].append(pt)
else:
pt = barycentric_transform(orig_shape_coords[i1],
orig_coords[i3], orig_coords[i1], orig_coords[i2],
target_coords[i3], target_coords[i1], target_coords[i2])
median_coords[i1].append(pt)
pt = barycentric_transform(orig_shape_coords[i2],
orig_coords[i1], orig_coords[i2], orig_coords[i3],
target_coords[i1], target_coords[i2], target_coords[i3])
median_coords[i2].append(pt)
pt = barycentric_transform(orig_shape_coords[i3],
orig_coords[i2], orig_coords[i3], orig_coords[i1],
target_coords[i2], target_coords[i3], target_coords[i1])
median_coords[i3].append(pt)
elif mode == 'RELATIVE_EDGE':
for ed in me.edges:
i1, i2 = ed.vertices
v1, v2 = orig_coords[i1], orig_coords[i2]
edge_length = (v1 - v2).length
n1loc = v1 + orig_normals[i1] * edge_length
n2loc = v2 + orig_normals[i2] * edge_length
# now get the target nloc's
v1_to, v2_to = target_coords[i1], target_coords[i2]
edlen_to = (v1_to - v2_to).length
n1loc_to = v1_to + target_normals[i1] * edlen_to
n2loc_to = v2_to + target_normals[i2] * edlen_to
pt = barycentric_transform(orig_shape_coords[i1],
v2, v1, n1loc,
v2_to, v1_to, n1loc_to)
median_coords[i1].append(pt)
pt = barycentric_transform(orig_shape_coords[i2],
v1, v2, n2loc,
v1_to, v2_to, n2loc_to)
median_coords[i2].append(pt)
# apply the offsets to the new shape
from functools import reduce
VectorAdd = Vector.__add__
for i, vert_cos in enumerate(median_coords):
if vert_cos:
co = reduce(VectorAdd, vert_cos) / len(vert_cos)
if use_clamp:
# clamp to the same movement as the original
# breaks copy between different scaled meshes.
len_from = (orig_shape_coords[i] - orig_coords[i]).length
ofs = co - target_coords[i]
ofs.length = len_from
co = target_coords[i] + ofs
target_shape_coords[i][:] = co
return {'FINISHED'}
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.mode != 'EDIT')
def execute(self, context):
C = bpy.context
ob_act = C.active_object
objects = [ob for ob in C.selected_editable_objects if ob != ob_act]
if 1: # swap from/to, means we cant copy to many at once.
if len(objects) != 1:
self.report({'ERROR'}, "Expected one other selected mesh object to copy from")
return {'CANCELLED'}
ob_act, objects = objects[0], [ob_act]
if ob_act.type != 'MESH':
self.report({'ERROR'}, "Other object is not a mesh.")
return {'CANCELLED'}
if ob_act.active_shape_key is None:
self.report({'ERROR'}, "Other object has no shape key")
return {'CANCELLED'}
return self._main(ob_act, objects, self.mode, self.use_clamp)
class JoinUVs(bpy.types.Operator):
'''Copy UV Layout to objects with matching geometry'''
bl_idname = "object.join_uvs"
bl_label = "Join as UVs"
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH')
def _main(self, context):
import array
obj = context.active_object
mesh = obj.data
is_editmode = (obj.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
if not mesh.uv_textures:
self.report({'WARNING'}, "Object: %s, Mesh: '%s' has no UVs\n" % (obj.name, mesh.name))
else:
len_faces = len(mesh.faces)
uv_array = array.array('f', [0.0] * 8) * len_faces # seems to be the fastest way to create an array
mesh.uv_textures.active.data.foreach_get("uv_raw", uv_array)
objects = context.selected_editable_objects[:]
for obj_other in objects:
if obj_other.type == 'MESH':
obj_other.data.tag = False
for obj_other in objects:
if obj_other != obj and obj_other.type == 'MESH':
mesh_other = obj_other.data
if mesh_other != mesh:
if mesh_other.tag == False:
mesh_other.tag = True
if len(mesh_other.faces) != len_faces:
self.report({'WARNING'}, "Object: %s, Mesh: '%s' has %d faces, expected %d\n" % (obj_other.name, mesh_other.name, len(mesh_other.faces), len_faces))
else:
uv_other = mesh_other.uv_textures.active
if not uv_other:
uv_other = mesh_other.uv_textures.new() # should return the texture it adds
# finally do the copy
uv_other.data.foreach_set("uv_raw", uv_array)
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
def execute(self, context):
self._main(context)
return {'FINISHED'}
class MakeDupliFace(bpy.types.Operator):
'''Make linked objects into dupli-faces'''
bl_idname = "object.make_dupli_face"
bl_label = "Make Dupli-Face"
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH')
def _main(self, context):
from mathutils import Vector
SCALE_FAC = 0.01
offset = 0.5 * SCALE_FAC
base_tri = Vector((-offset, -offset, 0.0)), Vector((offset, -offset, 0.0)), Vector((offset, offset, 0.0)), Vector((-offset, offset, 0.0))
def matrix_to_quat(matrix):
# scale = matrix.median_scale
trans = matrix.to_translation()
rot = matrix.to_3x3() # also contains scale
return [(b * rot) + trans for b in base_tri]
scene = bpy.context.scene
linked = {}
for obj in bpy.context.selected_objects:
data = obj.data
if data:
linked.setdefault(data, []).append(obj)
for data, objects in linked.items():
face_verts = [axis for obj in objects for v in matrix_to_quat(obj.matrix_world) for axis in v]
faces = list(range(len(face_verts) // 3))
mesh = bpy.data.meshes.new(data.name + "_dupli")
mesh.vertices.add(len(face_verts) // 3)
mesh.faces.add(len(face_verts) // 12)
mesh.vertices.foreach_set("co", face_verts)
mesh.faces.foreach_set("vertices_raw", faces)
mesh.update() # generates edge data
# pick an object to use
obj = objects[0]
ob_new = bpy.data.objects.new(mesh.name, mesh)
base = scene.objects.link(ob_new)
base.layers[:] = obj.layers
ob_inst = bpy.data.objects.new(data.name, data)
base = scene.objects.link(ob_inst)
base.layers[:] = obj.layers
for obj in objects:
scene.objects.unlink(obj)
ob_new.dupli_type = 'FACES'
ob_inst.parent = ob_new
ob_new.use_dupli_faces_scale = True
ob_new.dupli_faces_scale = 1.0 / SCALE_FAC
def execute(self, context):
self._main(context)
return {'FINISHED'}
class IsolateTypeRender(bpy.types.Operator):
'''Hide unselected render objects of same type as active by setting the hide render flag'''
bl_idname = "object.isolate_type_render"
bl_label = "Restrict Render Unselected"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
act_type = context.object.type
for obj in context.visible_objects:
if obj.select:
obj.hide_render = False
else:
if obj.type == act_type:
obj.hide_render = True
return {'FINISHED'}
class ClearAllRestrictRender(bpy.types.Operator):
'''Reveal all render objects by setting the hide render flag'''
bl_idname = "object.hide_render_clear_all"
bl_label = "Clear All Restrict Render"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
for obj in context.scene.objects:
obj.hide_render = False
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,300 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from mathutils import Vector
def align_objects(align_x, align_y, align_z, align_mode, relative_to):
cursor = bpy.context.scene.cursor_location
Left_Up_Front_SEL = [0.0, 0.0, 0.0]
Right_Down_Back_SEL = [0.0, 0.0, 0.0]
flag_first = True
objs = []
for obj in bpy.context.selected_objects:
matrix_world = obj.matrix_world
bb_world = [Vector(v[:]) * matrix_world for v in obj.bound_box]
objs.append((obj, bb_world))
if not objs:
return False
for obj, bb_world in objs:
Left_Up_Front = bb_world[1]
Right_Down_Back = bb_world[7]
# Active Center
if obj == bpy.context.active_object:
center_active_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
center_active_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
center_active_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
size_active_x = (Right_Down_Back[0] - Left_Up_Front[0]) / 2.0
size_active_y = (Right_Down_Back[1] - Left_Up_Front[1]) / 2.0
size_active_z = (Left_Up_Front[2] - Right_Down_Back[2]) / 2.0
# Selection Center
if flag_first:
flag_first = False
Left_Up_Front_SEL[0] = Left_Up_Front[0]
Left_Up_Front_SEL[1] = Left_Up_Front[1]
Left_Up_Front_SEL[2] = Left_Up_Front[2]
Right_Down_Back_SEL[0] = Right_Down_Back[0]
Right_Down_Back_SEL[1] = Right_Down_Back[1]
Right_Down_Back_SEL[2] = Right_Down_Back[2]
else:
# X axis
if Left_Up_Front[0] < Left_Up_Front_SEL[0]:
Left_Up_Front_SEL[0] = Left_Up_Front[0]
# Y axis
if Left_Up_Front[1] < Left_Up_Front_SEL[1]:
Left_Up_Front_SEL[1] = Left_Up_Front[1]
# Z axis
if Left_Up_Front[2] > Left_Up_Front_SEL[2]:
Left_Up_Front_SEL[2] = Left_Up_Front[2]
# X axis
if Right_Down_Back[0] > Right_Down_Back_SEL[0]:
Right_Down_Back_SEL[0] = Right_Down_Back[0]
# Y axis
if Right_Down_Back[1] > Right_Down_Back_SEL[1]:
Right_Down_Back_SEL[1] = Right_Down_Back[1]
# Z axis
if Right_Down_Back[2] < Right_Down_Back_SEL[2]:
Right_Down_Back_SEL[2] = Right_Down_Back[2]
center_sel_x = (Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0]) / 2.0
center_sel_y = (Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1]) / 2.0
center_sel_z = (Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2]) / 2.0
# Main Loop
for obj, bb_world in objs:
loc_world = obj.location
bb_world = [Vector(v[:]) * obj.matrix_world for v in obj.bound_box]
Left_Up_Front = bb_world[1]
Right_Down_Back = bb_world[7]
center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
positive_x = Right_Down_Back[0]
positive_y = Right_Down_Back[1]
positive_z = Left_Up_Front[2]
negative_x = Left_Up_Front[0]
negative_y = Left_Up_Front[1]
negative_z = Right_Down_Back[2]
obj_loc = obj.location
if align_x:
# Align Mode
if relative_to == 'OPT_4': # Active relative
if align_mode == 'OPT_1':
obj_x = obj_loc[0] - negative_x - size_active_x
elif align_mode == 'OPT_3':
obj_x = obj_loc[0] - positive_x + size_active_x
else: # Everything else relative
if align_mode == 'OPT_1':
obj_x = obj_loc[0] - negative_x
elif align_mode == 'OPT_3':
obj_x = obj_loc[0] - positive_x
if align_mode == 'OPT_2': # All relative
obj_x = obj_loc[0] - center_x
# Relative To
if relative_to == 'OPT_1':
loc_x = obj_x
elif relative_to == 'OPT_2':
loc_x = obj_x + cursor[0]
elif relative_to == 'OPT_3':
loc_x = obj_x + center_sel_x
elif relative_to == 'OPT_4':
loc_x = obj_x + center_active_x
obj.location[0] = loc_x
if align_y:
# Align Mode
if relative_to == 'OPT_4': # Active relative
if align_mode == 'OPT_1':
obj_y = obj_loc[1] - negative_y - size_active_y
elif align_mode == 'OPT_3':
obj_y = obj_loc[1] - positive_y + size_active_y
else: # Everything else relative
if align_mode == 'OPT_1':
obj_y = obj_loc[1] - negative_y
elif align_mode == 'OPT_3':
obj_y = obj_loc[1] - positive_y
if align_mode == 'OPT_2': # All relative
obj_y = obj_loc[1] - center_y
# Relative To
if relative_to == 'OPT_1':
loc_y = obj_y
elif relative_to == 'OPT_2':
loc_y = obj_y + cursor[1]
elif relative_to == 'OPT_3':
loc_y = obj_y + center_sel_y
elif relative_to == 'OPT_4':
loc_y = obj_y + center_active_y
obj.location[1] = loc_y
if align_z:
# Align Mode
if relative_to == 'OPT_4': # Active relative
if align_mode == 'OPT_1':
obj_z = obj_loc[2] - negative_z - size_active_z
elif align_mode == 'OPT_3':
obj_z = obj_loc[2] - positive_z + size_active_z
else: # Everything else relative
if align_mode == 'OPT_1':
obj_z = obj_loc[2] - negative_z
elif align_mode == 'OPT_3':
obj_z = obj_loc[2] - positive_z
if align_mode == 'OPT_2': # All relative
obj_z = obj_loc[2] - center_z
# Relative To
if relative_to == 'OPT_1':
loc_z = obj_z
elif relative_to == 'OPT_2':
loc_z = obj_z + cursor[2]
elif relative_to == 'OPT_3':
loc_z = obj_z + center_sel_z
elif relative_to == 'OPT_4':
loc_z = obj_z + center_active_z
obj.location[2] = loc_z
return True
from bpy.props import *
class AlignObjects(bpy.types.Operator):
'''Align Objects'''
bl_idname = "object.align"
bl_label = "Align Objects"
bl_options = {'REGISTER', 'UNDO'}
align_mode = bpy.props.EnumProperty(items=(
('OPT_1', "Negative Sides", ""),
('OPT_2', "Centers", ""),
('OPT_3', "Positive Sides", "")),
name="Align Mode:",
description="",
default='OPT_2')
relative_to = bpy.props.EnumProperty(items=(
('OPT_1', "Scene Origin", ""),
('OPT_2', "3D Cursor", ""),
('OPT_3', "Selection", ""),
('OPT_4', "Active", "")),
name="Relative To:",
description="",
default='OPT_4')
align_axis = EnumProperty(items=(
('X', "X", ""),
('Y', "Y", ""),
('Z', "Z", ""),
),
name="Align",
description="Align to axis",
options={'ENUM_FLAG'})
@classmethod
def poll(cls, context):
return context.mode == 'OBJECT'
def execute(self, context):
align_axis = self.align_axis
ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to)
if not ret:
self.report({'WARNING'}, "No objects with bound-box selected")
return {'CANCELLED'}
else:
return {'FINISHED'}
def menu_func(self, context):
if context.mode == 'OBJECT':
self.layout.operator(AlignObjects.bl_idname,
text="Align Objects")
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_transform.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.VIEW3D_MT_transform.remove(menu_func)
if __name__ == "__main__":
register()

View File

@@ -1,167 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
def randomize_selected(seed, delta, loc, rot, scale, scale_even):
import random
from random import uniform
from mathutils import Vector
random.seed(seed)
def rand_vec(vec_range):
return Vector(uniform(-val, val) for val in vec_range)
for obj in bpy.context.selected_objects:
if loc:
if delta:
obj.delta_location += rand_vec(loc)
else:
obj.location += rand_vec(loc)
else: # otherwise the values change under us
uniform(0.0, 0.0), uniform(0.0, 0.0), uniform(0.0, 0.0)
if rot: # TODO, non euler's
vec = rand_vec(rot)
if delta:
obj.delta_rotation_euler[0] += vec[0]
obj.delta_rotation_euler[1] += vec[1]
obj.delta_rotation_euler[2] += vec[2]
else:
obj.rotation_euler[0] += vec[0]
obj.rotation_euler[1] += vec[1]
obj.rotation_euler[2] += vec[2]
else:
uniform(0.0, 0.0), uniform(0.0, 0.0), uniform(0.0, 0.0)
if scale:
if delta:
org_sca_x, org_sca_y, org_sca_z = obj.delta_scale
else:
org_sca_x, org_sca_y, org_sca_z = obj.scale
if scale_even:
sca_x = sca_y = sca_z = uniform(scale[0], - scale[0])
uniform(0.0, 0.0), uniform(0.0, 0.0)
else:
sca_x, sca_y, sca_z = rand_vec(scale)
if scale_even:
aX = -(sca_x * org_sca_x) + org_sca_x
aY = -(sca_x * org_sca_y) + org_sca_y
aZ = -(sca_x * org_sca_z) + org_sca_z
else:
aX = sca_x + org_sca_x
aY = sca_y + org_sca_y
aZ = sca_z + org_sca_z
if delta:
obj.delta_scale = aX, aY, aZ
else:
obj.scale = aX, aY, aZ
else:
uniform(0.0, 0.0), uniform(0.0, 0.0), uniform(0.0, 0.0)
from bpy.props import *
class RandomizeLocRotSize(bpy.types.Operator):
'''Randomize objects loc/rot/scale'''
bl_idname = "object.randomize_transform"
bl_label = "Randomize Transform"
bl_options = {'REGISTER', 'UNDO'}
random_seed = IntProperty(name="Random Seed",
description="Seed value for the random generator",
default=0, min=0, max=1000)
use_delta = BoolProperty(name="Transform Delta",
description="Randomize delta transform values instead of regular transform", default=False)
use_loc = BoolProperty(name="Randomize Location",
description="Randomize the location values", default=True)
loc = FloatVectorProperty(name="Location",
description="Maximun distance the objects can spread over each axis",
default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='TRANSLATION')
use_rot = BoolProperty(name="Randomize Rotation",
description="Randomize the rotation values", default=True)
rot = FloatVectorProperty(name="Rotation",
description="Maximun rotation over each axis",
default=(0.0, 0.0, 0.0), min=-180.0, max=180.0, subtype='TRANSLATION')
use_scale = BoolProperty(name="Randomize Scale",
description="Randomize the scale values", default=True)
scale_even = BoolProperty(name="Scale Even",
description="Use the same scale value for all axis", default=False)
'''scale_min = FloatProperty(name="Minimun Scale Factor",
description="Lowest scale percentage possible",
default=0.15, min=-1.0, max=1.0, precision=3)'''
scale = FloatVectorProperty(name="Scale",
description="Maximum scale randomization over each axis",
default=(0.0, 0.0, 0.0), min=-100.0, max=100.0, subtype='TRANSLATION')
def execute(self, context):
from math import radians
seed = self.random_seed
delta = self.use_delta
loc = None if not self.use_loc else self.loc
rot = None if not self.use_rot else self.rot * radians(1.0)
scale = None if not self.use_scale else self.scale
scale_even = self.scale_even
#scale_min = self.scale_min
randomize_selected(seed, delta, loc, rot, scale, scale_even)
return {'FINISHED'}
def menu_func(self, context):
if context.mode == 'OBJECT':
self.layout.operator(RandomizeLocRotSize.bl_idname,
text="Randomize Transform")
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_transform.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.VIEW3D_MT_transform.remove(menu_func)
if __name__ == "__main__":
register()

View File

@@ -1,364 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
import os
class AddPresetBase():
'''Base preset class, only for subclassing
subclasses must define
- preset_values
- preset_subdir '''
# bl_idname = "script.preset_base_add"
# bl_label = "Add a Python Preset"
bl_options = {'REGISTER'} # only because invoke_props_popup requires.
name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen=64, default="")
remove_active = bpy.props.BoolProperty(default=False, options={'HIDDEN'})
@staticmethod
def as_filename(name): # could reuse for other presets
for char in " !@#$%^&*(){}:\";'[]<>,.\\/?":
name = name.replace(char, '_')
return name.lower().strip()
def execute(self, context):
import os
if hasattr(self, "pre_cb"):
self.pre_cb(context)
preset_menu_class = getattr(bpy.types, self.preset_menu)
if not self.remove_active:
if not self.name:
return {'FINISHED'}
filename = self.as_filename(self.name)
target_path = bpy.utils.user_resource('SCRIPTS', os.path.join("presets", self.preset_subdir), create=True)
if not target_path:
self.report({'WARNING'}, "Failed to create presets path")
return {'CANCELLED'}
filepath = os.path.join(target_path, filename) + ".py"
if hasattr(self, "add"):
self.add(context, filepath)
else:
file_preset = open(filepath, 'w')
file_preset.write("import bpy\n")
if hasattr(self, "preset_defines"):
for rna_path in self.preset_defines:
exec(rna_path)
file_preset.write("%s\n" % rna_path)
file_preset.write("\n")
for rna_path in self.preset_values:
value = eval(rna_path)
# convert thin wrapped sequences to simple lists to repr()
try:
value = value[:]
except:
pass
file_preset.write("%s = %r\n" % (rna_path, value))
file_preset.close()
preset_menu_class.bl_label = bpy.path.display_name(filename)
else:
preset_active = preset_menu_class.bl_label
# fairly sloppy but convenient.
filepath = bpy.utils.preset_find(preset_active, self.preset_subdir)
if not filepath:
filepath = bpy.utils.preset_find(preset_active, self.preset_subdir, display_name=True)
if not filepath:
return {'CANCELLED'}
if hasattr(self, "remove"):
self.remove(context, filepath)
else:
try:
os.remove(filepath)
except:
import traceback
traceback.print_exc()
# XXX, stupid!
preset_menu_class.bl_label = "Presets"
if hasattr(self, "post_cb"):
self.post_cb(context)
return {'FINISHED'}
def check(self, context):
self.name = self.as_filename(self.name)
def invoke(self, context, event):
if not self.remove_active:
wm = context.window_manager
return wm.invoke_props_dialog(self)
else:
return self.execute(context)
class ExecutePreset(bpy.types.Operator):
''' Executes a preset '''
bl_idname = "script.execute_preset"
bl_label = "Execute a Python Preset"
filepath = bpy.props.StringProperty(name="Path", description="Path of the Python file to execute", maxlen=512, default="")
menu_idname = bpy.props.StringProperty(name="Menu ID Name", description="ID name of the menu this was called from", default="")
def execute(self, context):
from os.path import basename
filepath = self.filepath
# change the menu title to the most recently chosen option
preset_class = getattr(bpy.types, self.menu_idname)
preset_class.bl_label = bpy.path.display_name(basename(filepath))
# execute the preset using script.python_file_run
bpy.ops.script.python_file_run(filepath=filepath)
return {'FINISHED'}
class AddPresetRender(AddPresetBase, bpy.types.Operator):
'''Add a Render Preset'''
bl_idname = "render.preset_add"
bl_label = "Add Render Preset"
preset_menu = "RENDER_MT_presets"
preset_defines = [
"scene = bpy.context.scene"
]
preset_values = [
"scene.render.field_order",
"scene.render.fps",
"scene.render.fps_base",
"scene.render.pixel_aspect_x",
"scene.render.pixel_aspect_y",
"scene.render.resolution_percentage",
"scene.render.resolution_x",
"scene.render.resolution_y",
"scene.render.use_fields",
"scene.render.use_fields_still",
]
preset_subdir = "render"
class AddPresetSSS(AddPresetBase, bpy.types.Operator):
'''Add a Subsurface Scattering Preset'''
bl_idname = "material.sss_preset_add"
bl_label = "Add SSS Preset"
preset_menu = "MATERIAL_MT_sss_presets"
preset_defines = [
"material = (bpy.context.material.active_node_material if bpy.context.material.active_node_material else bpy.context.material)"
]
preset_values = [
"material.subsurface_scattering.back",
"material.subsurface_scattering.color",
"material.subsurface_scattering.color_factor",
"material.subsurface_scattering.error_threshold",
"material.subsurface_scattering.front",
"material.subsurface_scattering.ior",
"material.subsurface_scattering.radius",
"material.subsurface_scattering.scale",
"material.subsurface_scattering.texture_factor",
]
preset_subdir = "sss"
class AddPresetCloth(AddPresetBase, bpy.types.Operator):
'''Add a Cloth Preset'''
bl_idname = "cloth.preset_add"
bl_label = "Add Cloth Preset"
preset_menu = "CLOTH_MT_presets"
preset_defines = [
"cloth = bpy.context.cloth"
]
preset_values = [
"cloth.settings.air_damping",
"cloth.settings.bending_stiffness",
"cloth.settings.mass",
"cloth.settings.quality",
"cloth.settings.spring_damping",
"cloth.settings.structural_stiffness",
]
preset_subdir = "cloth"
class AddPresetSunSky(AddPresetBase, bpy.types.Operator):
'''Add a Sky & Atmosphere Preset'''
bl_idname = "lamp.sunsky_preset_add"
bl_label = "Add Sunsky Preset"
preset_menu = "LAMP_MT_sunsky_presets"
preset_defines = [
"sky = bpy.context.object.data.sky"
]
preset_values = [
"sky.atmosphere_extinction",
"sky.atmosphere_inscattering",
"sky.atmosphere_turbidity",
"sky.backscattered_light",
"sky.horizon_brightness",
"sky.spread",
"sky.sun_brightness",
"sky.sun_intensity",
"sky.sun_size",
"sky.use_sky_blend",
"sky.use_sky_blend_type",
"sky.use_sky_color_space",
"sky.use_sky_exposure",
]
preset_subdir = "sunsky"
class AddPresetInteraction(AddPresetBase, bpy.types.Operator):
'''Add an Application Interaction Preset'''
bl_idname = "wm.interaction_preset_add"
bl_label = "Add Interaction Preset"
preset_menu = "USERPREF_MT_interaction_presets"
preset_defines = [
"user_preferences = bpy.context.user_preferences"
]
preset_values = [
"user_preferences.edit.use_drag_immediately",
"user_preferences.edit.use_insertkey_xyz_to_rgb",
"user_preferences.inputs.invert_mouse_wheel_zoom",
"user_preferences.inputs.select_mouse",
"user_preferences.inputs.use_emulate_numpad",
"user_preferences.inputs.use_mouse_continuous",
"user_preferences.inputs.use_mouse_emulate_3_button",
"user_preferences.inputs.view_rotate_method",
"user_preferences.inputs.view_zoom_axis",
"user_preferences.inputs.view_zoom_method",
]
preset_subdir = "interaction"
class AddPresetKeyconfig(AddPresetBase, bpy.types.Operator):
'''Add a Keyconfig Preset'''
bl_idname = "wm.keyconfig_preset_add"
bl_label = "Add Keyconfig Preset"
preset_menu = "USERPREF_MT_keyconfigs"
preset_subdir = "keyconfig"
def add(self, context, filepath):
bpy.ops.wm.keyconfig_export(filepath=filepath)
bpy.utils.keyconfig_set(filepath)
def pre_cb(self, context):
keyconfigs = bpy.context.window_manager.keyconfigs
if self.remove_active:
preset_menu_class = getattr(bpy.types, self.preset_menu)
preset_menu_class.bl_label = keyconfigs.active.name
def post_cb(self, context):
keyconfigs = bpy.context.window_manager.keyconfigs
if self.remove_active:
keyconfigs.remove(keyconfigs.active)
class AddPresetOperator(AddPresetBase, bpy.types.Operator):
'''Add an Application Interaction Preset'''
bl_idname = "wm.operator_preset_add"
bl_label = "Operator Preset"
preset_menu = "WM_MT_operator_presets"
operator = bpy.props.StringProperty(name="Operator", maxlen=64, options={'HIDDEN'})
# XXX, not ideal
preset_defines = [
"op = bpy.context.space_data.operator",
]
@property
def preset_subdir(self):
return __class__.operator_path(self.operator)
@property
def preset_values(self):
properties_blacklist = bpy.types.Operator.bl_rna.properties.keys()
prefix, suffix = self.operator.split("_OT_", 1)
operator_rna = getattr(getattr(bpy.ops, prefix.lower()), suffix).get_rna().bl_rna
ret = []
for prop_id, prop in operator_rna.properties.items():
if (not prop.is_hidden) and prop_id not in properties_blacklist:
ret.append("op.%s" % prop_id)
return ret
@staticmethod
def operator_path(operator):
import os
prefix, suffix = operator.split("_OT_", 1)
return os.path.join("operator", "%s.%s" % (prefix.lower(), suffix))
class WM_MT_operator_presets(bpy.types.Menu):
bl_label = "Operator Presets"
def draw(self, context):
self.operator = context.space_data.operator.bl_idname
bpy.types.Menu.draw_preset(self, context)
@property
def preset_subdir(self):
return AddPresetOperator.operator_path(self.operator)
preset_operator = "script.execute_preset"
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,157 +0,0 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Campbell J Barton
#
# 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 *****
# <pep8 compliant>
# History
#
# Originally written by Matt Ebb
import bpy
import os
def guess_player_path(preset):
import platform
try:
system = platform.system()
except UnicodeDecodeError:
import sys
system = sys.platform
if preset == 'BLENDER24':
player_path = "blender"
if system == 'Darwin':
test_path = "/Applications/blender 2.49.app/Contents/MacOS/blender"
elif system in ('Windows', 'win32'):
test_path = "/Program Files/Blender Foundation/Blender/blender.exe"
if os.path.exists(test_path):
player_path = test_path
elif preset == 'DJV':
player_path = "djv_view"
if system == 'Darwin':
test_path = '/Applications/djv-0.8.2.app/Contents/Resources/bin/djv_view'
if os.path.exists(test_path):
player_path = test_path
elif preset == 'FRAMECYCLER':
player_path = "framecycler"
elif preset == 'RV':
player_path = "rv"
elif preset == 'MPLAYER':
player_path = "mplayer"
return player_path
class PlayRenderedAnim(bpy.types.Operator):
'''Plays back rendered frames/movies using an external player.'''
bl_idname = "render.play_rendered_anim"
bl_label = "Play Rendered Animation"
bl_options = {'REGISTER'}
def execute(self, context):
import subprocess
scene = context.scene
rd = scene.render
prefs = context.user_preferences
preset = prefs.filepaths.animation_player_preset
player_path = prefs.filepaths.animation_player
file_path = bpy.path.abspath(rd.filepath)
is_movie = rd.is_movie_format
# try and guess a command line if it doesn't exist
if player_path == '':
player_path = guess_player_path(preset)
if is_movie == False and preset in ('FRAMECYCLER', 'RV', 'MPLAYER'):
# replace the number with '#'
file_a = rd.frame_path(frame=0)
# TODO, make an api call for this
frame_tmp = 9
file_b = rd.frame_path(frame=frame_tmp)
while len(file_a) == len(file_b):
frame_tmp = (frame_tmp * 10) + 9
print(frame_tmp)
file_b = rd.frame_path(frame=frame_tmp)
file_b = rd.frame_path(frame=int(frame_tmp / 10))
file = "".join((c if file_b[i] == c else "#") for i, c in enumerate(file_a))
else:
# works for movies and images
file = rd.frame_path(frame=scene.frame_start)
file = bpy.path.abspath(file) # expand '//'
cmd = [player_path]
# extra options, fps controls etc.
if preset == 'BLENDER24':
opts = ["-a", "-f", str(rd.fps), str(rd.fps_base), file]
cmd.extend(opts)
elif preset == 'DJV':
opts = [file, "-playback_speed", str(rd.fps)]
cmd.extend(opts)
elif preset == 'FRAMECYCLER':
opts = [file, "%d-%d" % (scene.frame_start, scene.frame_end)]
cmd.extend(opts)
elif preset == 'RV':
opts = ["-fps", str(rd.fps), "-play", "[ %s ]" % file]
cmd.extend(opts)
elif preset == 'MPLAYER':
opts = []
if is_movie:
opts.append(file)
else:
opts.append("mf://%s" % file.replace("#", "?"))
opts += ["-mf", "fps=%.4f" % (rd.fps / rd.fps_base)]
opts += ["-loop", "0", "-really-quiet", "-fs"]
cmd.extend(opts)
else: # 'CUSTOM'
cmd.append(file)
# launch it
try:
process = subprocess.Popen(cmd)
except:
pass
#raise OSError("Couldn't find an external animation player.")
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,146 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.props import *
class SequencerCrossfadeSounds(bpy.types.Operator):
'''Do crossfading volume animation of two selected sound strips.'''
bl_idname = "sequencer.crossfade_sounds"
bl_label = "Crossfade sounds"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if context.scene and context.scene.sequence_editor and context.scene.sequence_editor.active_strip:
return context.scene.sequence_editor.active_strip.type == 'SOUND'
else:
return False
def execute(self, context):
seq1 = None
seq2 = None
for s in context.scene.sequence_editor.sequences:
if s.select and s.type == 'SOUND':
if seq1 is None:
seq1 = s
elif seq2 is None:
seq2 = s
else:
seq2 = None
break
if seq2 is None:
self.report({'ERROR'}, "Select 2 sound strips.")
return {'CANCELLED'}
if seq1.frame_final_start > seq2.frame_final_start:
s = seq1
seq1 = seq2
seq2 = s
if seq1.frame_final_end > seq2.frame_final_start:
tempcfra = context.scene.frame_current
context.scene.frame_current = seq2.frame_final_start
seq1.keyframe_insert('volume')
context.scene.frame_current = seq1.frame_final_end
seq1.volume = 0
seq1.keyframe_insert('volume')
seq2.keyframe_insert('volume')
context.scene.frame_current = seq2.frame_final_start
seq2.volume = 0
seq2.keyframe_insert('volume')
context.scene.frame_current = tempcfra
return {'FINISHED'}
else:
self.report({'ERROR'}, "The selected strips don't overlap.")
return {'CANCELLED'}
class SequencerCutMulticam(bpy.types.Operator):
'''Cut multicam strip and select camera.'''
bl_idname = "sequencer.cut_multicam"
bl_label = "Cut multicam"
bl_options = {'REGISTER', 'UNDO'}
camera = IntProperty(name="Camera",
default=1, min=1, max=32, soft_min=1, soft_max=32)
@classmethod
def poll(cls, context):
if context.scene and context.scene.sequence_editor and context.scene.sequence_editor.active_strip:
return context.scene.sequence_editor.active_strip.type == 'MULTICAM'
else:
return False
def execute(self, context):
camera = self.camera
s = context.scene.sequence_editor.active_strip
if s.multicam_source == camera or camera >= s.channel:
return {'FINISHED'}
if not s.select:
s.select = True
cfra = context.scene.frame_current
bpy.ops.sequencer.cut(frame=cfra, type='SOFT', side='RIGHT')
for s in context.scene.sequence_editor.sequences_all:
if s.select and s.type == 'MULTICAM' and s.frame_final_start <= cfra and cfra < s.frame_final_end:
context.scene.sequence_editor.active_strip = s
context.scene.sequence_editor.active_strip.multicam_source = camera
return {'FINISHED'}
class SequencerDeinterlaceSelectedMovies(bpy.types.Operator):
'''Deinterlace all selected movie sources.'''
bl_idname = "sequencer.deinterlace_selected_movies"
bl_label = "Deinterlace Movies"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if context.scene and context.scene.sequence_editor:
return True
else:
return False
def execute(self, context):
for s in context.scene.sequence_editor.sequences_all:
if s.select and s.type == 'MOVIE':
s.use_deinterlace = True
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,381 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.props import *
def write_svg(fw, mesh, image_width, image_height, face_iter):
# for making an XML compatible string
from xml.sax.saxutils import escape
from os.path import basename
fw('<?xml version="1.0" standalone="no"?>\n')
fw('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" \n')
fw(' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')
fw('<svg width="%dpx" height="%dpx" viewBox="0px 0px %dpx %dpx"\n' % (image_width, image_height, image_width, image_height))
fw(' xmlns="http://www.w3.org/2000/svg" version="1.1">\n')
desc = "%r, %s, (Blender %s)" % (basename(bpy.data.filepath), mesh.name, bpy.app.version_string)
fw('<desc>%s</desc>\n' % escape(desc))
# svg colors
fill_settings = []
fill_default = 'fill="grey"'
for mat in mesh.materials if mesh.materials else [None]:
if mat:
fill_settings.append('fill="rgb(%d, %d, %d)"' % tuple(int(c * 255) for c in mat.diffuse_color))
else:
fill_settings.append(fill_default)
faces = mesh.faces
for i, uvs in face_iter:
try: # rare cases material index is invalid.
fill = fill_settings[faces[i].material_index]
except IndexError:
fill = fill_default
fw('<polygon %s fill-opacity="0.5" stroke="black" stroke-width="1px" \n' % fill)
fw(' points="')
for j, uv in enumerate(uvs):
x, y = uv[0], 1.0 - uv[1]
fw('%.3f,%.3f ' % (x * image_width, y * image_height))
fw('" />\n')
fw('\n')
fw('</svg>\n')
def write_eps(fw, mesh, image_width, image_height, face_iter):
fw('%!PS-Adobe-3.0 EPSF-3.0\n')
fw("%%%%Creator: Blender %s\n" % bpy.app.version_string)
fw('%%Pages: 1\n')
fw('%%Orientation: Portrait\n')
fw("%%%%BoundingBox: 0 0 %d %d\n" % (image_width, image_height))
fw("%%%%HiResBoundingBox: 0.0 0.0 %.4f %.4f\n" % (image_width, image_height))
fw('%%EndComments\n')
fw('%%Page: 1 1\n')
fw('0 0 translate\n')
fw('1.0 1.0 scale\n')
fw('0 0 0 setrgbcolor\n')
fw('[] 0 setdash\n')
fw('1 setlinewidth\n')
fw('1 setlinejoin\n')
fw('1 setlinecap\n')
fw('/DRAW {')
# can remove from here to next comment to disable filling, aparently alpha is not supported
fw('gsave\n')
fw('0.7 setgray\n')
fw('fill\n')
fw('grestore\n')
fw('0 setgray\n')
# remove to here
fw('stroke\n')
fw('} def\n')
fw('newpath\n')
firstline = True
for i, uvs in face_iter:
for j, uv in enumerate(uvs):
x, y = uv[0], uv[1]
if j == 0:
if not firstline:
fw('closepath\n')
fw('DRAW\n')
fw('newpath\n')
firstline = False
fw('%.5f %.5f moveto\n' % (x * image_width, y * image_height))
else:
fw('%.5f %.5f lineto\n' % (x * image_width, y * image_height))
fw('closepath\n')
fw('DRAW\n')
fw('showpage\n')
fw('%%EOF\n')
def write_png(fw, mesh_source, image_width, image_height, face_iter):
filepath = fw.__self__.name
fw.__self__.close()
material_solids = [bpy.data.materials.new("uv_temp_solid") for i in range(max(1, len(mesh_source.materials)))]
material_wire = bpy.data.materials.new("uv_temp_wire")
scene = bpy.data.scenes.new("uv_temp")
mesh = bpy.data.meshes.new("uv_temp")
for mat_solid in material_solids:
mesh.materials.append(mat_solid)
tot_verts = 0
face_lens = []
for f in mesh_source.faces:
tot_verts += len(f.vertices)
faces_source = mesh_source.faces
# get unique UV's incase there are many overlapping which slow down filling.
face_hash_3 = set()
face_hash_4 = set()
for i, uv in face_iter:
material_index = faces_source[i].material_index
if len(uv) == 3:
face_hash_3.add((uv[0][0], uv[0][1], uv[1][0], uv[1][1], uv[2][0], uv[2][1], material_index))
else:
face_hash_4.add((uv[0][0], uv[0][1], uv[1][0], uv[1][1], uv[2][0], uv[2][1], uv[3][0], uv[3][1], material_index))
# now set the faces coords and locations
# build mesh data
mesh_new_vertices = []
mesh_new_materials = []
mesh_new_face_vertices = []
current_vert = 0
for face_data in face_hash_3:
mesh_new_vertices.extend([face_data[0], face_data[1], 0.0, face_data[2], face_data[3], 0.0, face_data[4], face_data[5], 0.0])
mesh_new_face_vertices.extend([current_vert, current_vert + 1, current_vert + 2, 0])
mesh_new_materials.append(face_data[6])
current_vert += 3
for face_data in face_hash_4:
mesh_new_vertices.extend([face_data[0], face_data[1], 0.0, face_data[2], face_data[3], 0.0, face_data[4], face_data[5], 0.0, face_data[6], face_data[7], 0.0])
mesh_new_face_vertices.extend([current_vert, current_vert + 1, current_vert + 2, current_vert + 3])
mesh_new_materials.append(face_data[8])
current_vert += 4
mesh.vertices.add(len(mesh_new_vertices) // 3)
mesh.faces.add(len(mesh_new_face_vertices) // 4)
mesh.vertices.foreach_set("co", mesh_new_vertices)
mesh.faces.foreach_set("vertices_raw", mesh_new_face_vertices)
mesh.faces.foreach_set("material_index", mesh_new_materials)
mesh.update(calc_edges=True)
obj_solid = bpy.data.objects.new("uv_temp_solid", mesh)
obj_wire = bpy.data.objects.new("uv_temp_wire", mesh)
base_solid = scene.objects.link(obj_solid)
base_wire = scene.objects.link(obj_wire)
base_solid.layers[0] = True
base_wire.layers[0] = True
# place behind the wire
obj_solid.location = 0, 0, -1
obj_wire.material_slots[0].link = 'OBJECT'
obj_wire.material_slots[0].material = material_wire
# setup the camera
cam = bpy.data.cameras.new("uv_temp")
cam.type = 'ORTHO'
cam.ortho_scale = 1.0
obj_cam = bpy.data.objects.new("uv_temp_cam", cam)
obj_cam.location = 0.5, 0.5, 1.0
scene.objects.link(obj_cam)
scene.camera = obj_cam
# setup materials
for i, mat_solid in enumerate(material_solids):
if mesh_source.materials and mesh_source.materials[i]:
mat_solid.diffuse_color = mesh_source.materials[i].diffuse_color
mat_solid.use_shadeless = True
mat_solid.use_transparency = True
mat_solid.alpha = 0.25
material_wire.type = 'WIRE'
material_wire.use_shadeless = True
material_wire.diffuse_color = 0, 0, 0
# scene render settings
scene.render.use_raytrace = False
scene.render.alpha_mode = 'STRAIGHT'
scene.render.color_mode = 'RGBA'
scene.render.resolution_x = image_width
scene.render.resolution_y = image_height
scene.render.resolution_percentage = 100
if image_width > image_height:
scene.render.pixel_aspect_y = image_width / image_height
elif image_width < image_height:
scene.render.pixel_aspect_x = image_height / image_width
scene.frame_start = 1
scene.frame_end = 1
scene.render.file_format = 'PNG'
scene.render.filepath = filepath
data_context = {"blend_data": bpy.context.blend_data, "scene": scene}
bpy.ops.render.render(data_context, write_still=True)
# cleanup
bpy.data.scenes.remove(scene)
bpy.data.objects.remove(obj_cam)
bpy.data.objects.remove(obj_solid)
bpy.data.objects.remove(obj_wire)
bpy.data.cameras.remove(cam)
bpy.data.meshes.remove(mesh)
bpy.data.materials.remove(material_wire)
for mat_solid in material_solids:
bpy.data.materials.remove(mat_solid)
class ExportUVLayout(bpy.types.Operator):
"""Export UV layout to file"""
bl_idname = "uv.export_layout"
bl_label = "Export UV Layout"
bl_options = {'REGISTER', 'UNDO'}
filepath = StringProperty(name="File Path", description="File path used for exporting the SVG file", maxlen=1024, default="", subtype='FILE_PATH')
check_existing = BoolProperty(name="Check Existing", description="Check and warn on overwriting existing files", default=True, options={'HIDDEN'})
export_all = BoolProperty(name="All UV's", description="Export all UVs in this mesh (not just the visible ones)", default=False)
mode = EnumProperty(items=(
('SVG', "Scalable Vector Graphic (.svg)", "Export the UV layout to a vector SVG file"),
('EPS', "Encapsulate PostScript (.eps)", "Export the UV layout to a vector EPS file"),
('PNG', "PNG Image (.png)", "Export the UV layout a bitmap image")),
name="Format",
description="File format to export the UV layout to",
default='PNG')
size = IntVectorProperty(size=2, default=(1024, 1024), min=8, max=32768, description="Dimensions of the exported file")
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj and obj.type == 'MESH' and obj.data.uv_textures)
def _space_image(self, context):
space_data = context.space_data
if isinstance(space_data, bpy.types.SpaceImageEditor):
return space_data
else:
return None
def _image_size(self, context, default_width=1024, default_height=1024):
# fallback if not in image context.
image_width, image_height = default_width, default_height
space_data = self._space_image(context)
if space_data:
image = space_data.image
if image:
width, height = tuple(context.space_data.image.size)
# incase no data is found.
if width and height:
image_width, image_height = width, height
return image_width, image_height
def _face_uv_iter(self, context):
obj = context.active_object
mesh = obj.data
uv_layer = mesh.uv_textures.active.data
uv_layer_len = len(uv_layer)
if not self.export_all:
local_image = Ellipsis
if context.tool_settings.show_uv_local_view:
space_data = self._space_image(context)
if space_data:
local_image = space_data.image
faces = mesh.faces
for i in range(uv_layer_len):
uv_elem = uv_layer[i]
# context checks
if faces[i].select and (local_image is Ellipsis or local_image == uv_elem.image):
#~ uv = uv_elem.uv
#~ if False not in uv_elem.select_uv[:len(uv)]:
#~ yield (i, uv)
# just write what we see.
yield (i, uv_layer[i].uv)
else:
# all, simple
for i in range(uv_layer_len):
yield (i, uv_layer[i].uv)
def execute(self, context):
obj = context.active_object
is_editmode = (obj.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
mesh = obj.data
mode = self.mode
filepath = self.filepath
filepath = bpy.path.ensure_ext(filepath, "." + mode.lower())
file = open(filepath, "w")
fw = file.write
if mode == 'SVG':
func = write_svg
elif mode == 'EPS':
func = write_eps
elif mode == 'PNG':
func = write_png
func(fw, mesh, self.size[0], self.size[1], self._face_uv_iter(context))
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
return {'FINISHED'}
def check(self, context):
filepath = bpy.path.ensure_ext(self.filepath, "." + self.mode.lower())
if filepath != self.filepath:
self.filepath = filepath
return True
else:
return False
def invoke(self, context, event):
import os
self.size = self._image_size(context)
self.filepath = os.path.splitext(bpy.data.filepath)[0]
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
def menu_func(self, context):
self.layout.operator(ExportUVLayout.bl_idname)
def register():
bpy.utils.register_module(__name__)
bpy.types.IMAGE_MT_uvs.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.IMAGE_MT_uvs.remove(menu_func)
if __name__ == "__main__":
register()

View File

@@ -1,264 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
#for full docs see...
# http://mediawiki.blender.org/index.php/Scripts/Manual/UV_Calculate/Follow_active_quads
import bpy
def extend(obj, operator, EXTEND_MODE):
me = obj.data
me_verts = me.vertices
# script will fail without UVs
if not me.uv_textures:
me.uv_textures.new()
# Toggle Edit mode
is_editmode = (obj.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT')
#t = sys.time()
edge_average_lengths = {}
OTHER_INDEX = 2, 3, 0, 1
FAST_INDICIES = 0, 2, 1, 3 # order is faster
def extend_uvs(face_source, face_target, edge_key):
'''
Takes 2 faces,
Projects its extends its UV coords onto the face next to it.
Both faces must share an edge
'''
def face_edge_vs(vi):
# assume a quad
return [(vi[0], vi[1]), (vi[1], vi[2]), (vi[2], vi[3]), (vi[3], vi[0])]
vidx_source = face_source.vertices
vidx_target = face_target.vertices
faceUVsource = me.uv_textures.active.data[face_source.index]
uvs_source = [faceUVsource.uv1, faceUVsource.uv2, faceUVsource.uv3, faceUVsource.uv4]
faceUVtarget = me.uv_textures.active.data[face_target.index]
uvs_target = [faceUVtarget.uv1, faceUVtarget.uv2, faceUVtarget.uv3, faceUVtarget.uv4]
# vertex index is the key, uv is the value
uvs_vhash_source = {vindex: uvs_source[i] for i, vindex in enumerate(vidx_source)}
uvs_vhash_target = {vindex: uvs_target[i] for i, vindex in enumerate(vidx_target)}
edge_idxs_source = face_edge_vs(vidx_source)
edge_idxs_target = face_edge_vs(vidx_target)
source_matching_edge = -1
target_matching_edge = -1
edge_key_swap = edge_key[1], edge_key[0]
try:
source_matching_edge = edge_idxs_source.index(edge_key)
except:
source_matching_edge = edge_idxs_source.index(edge_key_swap)
try:
target_matching_edge = edge_idxs_target.index(edge_key)
except:
target_matching_edge = edge_idxs_target.index(edge_key_swap)
edgepair_inner_source = edge_idxs_source[source_matching_edge]
edgepair_inner_target = edge_idxs_target[target_matching_edge]
edgepair_outer_source = edge_idxs_source[OTHER_INDEX[source_matching_edge]]
edgepair_outer_target = edge_idxs_target[OTHER_INDEX[target_matching_edge]]
if edge_idxs_source[source_matching_edge] == edge_idxs_target[target_matching_edge]:
iA = 0 # Flipped, most common
iB = 1
else: # The normals of these faces must be different
iA = 1
iB = 0
# Set the target UV's touching source face, no tricky calc needed,
uvs_vhash_target[edgepair_inner_target[0]][:] = uvs_vhash_source[edgepair_inner_source[iA]]
uvs_vhash_target[edgepair_inner_target[1]][:] = uvs_vhash_source[edgepair_inner_source[iB]]
# Set the 2 UV's on the target face that are not touching
# for this we need to do basic expaning on the source faces UV's
if EXTEND_MODE == 'LENGTH':
try: # divide by zero is possible
'''
measure the length of each face from the middle of each edge to the opposite
allong the axis we are copying, use this
'''
i1a = edgepair_outer_target[iB]
i2a = edgepair_inner_target[iA]
if i1a > i2a:
i1a, i2a = i2a, i1a
i1b = edgepair_outer_source[iB]
i2b = edgepair_inner_source[iA]
if i1b > i2b:
i1b, i2b = i2b, i1b
# print edge_average_lengths
factor = edge_average_lengths[i1a, i2a][0] / edge_average_lengths[i1b, i2b][0]
except:
# Div By Zero?
factor = 1.0
uvs_vhash_target[edgepair_outer_target[iB]][:] = uvs_vhash_source[edgepair_inner_source[0]] + factor * (uvs_vhash_source[edgepair_inner_source[0]] - uvs_vhash_source[edgepair_outer_source[1]])
uvs_vhash_target[edgepair_outer_target[iA]][:] = uvs_vhash_source[edgepair_inner_source[1]] + factor * (uvs_vhash_source[edgepair_inner_source[1]] - uvs_vhash_source[edgepair_outer_source[0]])
else:
# same as above but with no factors
uvs_vhash_target[edgepair_outer_target[iB]][:] = uvs_vhash_source[edgepair_inner_source[0]] + (uvs_vhash_source[edgepair_inner_source[0]] - uvs_vhash_source[edgepair_outer_source[1]])
uvs_vhash_target[edgepair_outer_target[iA]][:] = uvs_vhash_source[edgepair_inner_source[1]] + (uvs_vhash_source[edgepair_inner_source[1]] - uvs_vhash_source[edgepair_outer_source[0]])
if not me.uv_textures:
me.uv_textures.new()
face_act = me.faces.active
if face_act == -1:
operator.report({'ERROR'}, "No active face.")
return
face_sel = [f for f in me.faces if len(f.vertices) == 4 and f.select]
face_act_local_index = -1
for i, f in enumerate(face_sel):
if f.index == face_act:
face_act_local_index = i
break
if face_act_local_index == -1:
operator.report({'ERROR'}, "Active face not selected.")
return
# Modes
# 0 unsearched
# 1:mapped, use search from this face. - removed!!
# 2:all siblings have been searched. dont search again.
face_modes = [0] * len(face_sel)
face_modes[face_act_local_index] = 1 # extend UV's from this face.
# Edge connectivty
edge_faces = {}
for i, f in enumerate(face_sel):
for edkey in f.edge_keys:
try:
edge_faces[edkey].append(i)
except:
edge_faces[edkey] = [i]
if EXTEND_MODE == 'LENGTH':
edge_loops = me.edge_loops_from_faces(face_sel, [ed.key for ed in me.edges if ed.use_seam])
me_verts = me.vertices
for loop in edge_loops:
looplen = [0.0]
for ed in loop:
edge_average_lengths[ed] = looplen
looplen[0] += (me_verts[ed[0]].co - me_verts[ed[1]].co).length
looplen[0] = looplen[0] / len(loop)
# remove seams, so we dont map accross seams.
for ed in me.edges:
if ed.use_seam:
# remove the edge pair if we can
try:
del edge_faces[ed.key]
except:
pass
# Done finding seams
# face connectivity - faces around each face
# only store a list of indices for each face.
face_faces = [[] for i in range(len(face_sel))]
for edge_key, faces in edge_faces.items():
if len(faces) == 2: # Only do edges with 2 face users for now
face_faces[faces[0]].append((faces[1], edge_key))
face_faces[faces[1]].append((faces[0], edge_key))
# Now we know what face is connected to what other face, map them by connectivity
ok = True
while ok:
ok = False
for i in range(len(face_sel)):
if face_modes[i] == 1: # searchable
for f_sibling, edge_key in face_faces[i]:
if face_modes[f_sibling] == 0:
face_modes[f_sibling] = 1 # mapped and search from.
extend_uvs(face_sel[i], face_sel[f_sibling], edge_key)
face_modes[i] = 1 # we can map from this one now.
ok = True # keep searching
face_modes[i] = 2 # dont search again
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT')
else:
me.update_tag()
def main(context, operator):
obj = context.active_object
extend(obj, operator, operator.properties.mode)
class FollowActiveQuads(bpy.types.Operator):
'''Follow UVs from active quads along continuous face loops'''
bl_idname = "uv.follow_active_quads"
bl_label = "Follow Active Quads"
bl_options = {'REGISTER', 'UNDO'}
mode = bpy.props.EnumProperty(items=(("EVEN", "Even", "Space all UVs evently"), ("LENGTH", "Length", "Average space UVs edge length of each loop")),
name="Edge Length Mode",
description="Method to space UV edge loops",
default="LENGTH")
@classmethod
def poll(cls, context):
obj = context.active_object
return (obj is not None and obj.type == 'MESH')
def execute(self, context):
main(context, self)
return {'FINISHED'}
# Add to a menu
menu_func = (lambda self, context: self.layout.operator(FollowActiveQuads.bl_idname))
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_uv_map.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.VIEW3D_MT_uv_map.remove(menu_func)
if __name__ == "__main__":
register()

View File

@@ -1,1142 +0,0 @@
# --------------------------------------------------------------------------
# Smart Projection UV Projection Unwrapper v1.2 by Campbell Barton (AKA Ideasman)
# --------------------------------------------------------------------------
# ***** 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 *****
# --------------------------------------------------------------------------
# <pep8 compliant>
from mathutils import Matrix, Vector, geometry
import time
import bpy
from math import cos, radians
DEG_TO_RAD = 0.017453292519943295 # pi/180.0
SMALL_NUM = 0.000000001
BIG_NUM = 1e15
global USER_FILL_HOLES
global USER_FILL_HOLES_QUALITY
USER_FILL_HOLES = None
USER_FILL_HOLES_QUALITY = None
dict_matrix = {}
def pointInTri2D(v, v1, v2, v3):
global dict_matrix
key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y
# Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face.
'''
# BOUNDS CHECK
xmin= 1000000
ymin= 1000000
xmax= -1000000
ymax= -1000000
for i in (0,2,4):
x= key[i]
y= key[i+1]
if xmax<x: xmax= x
if ymax<y: ymax= y
if xmin>x: xmin= x
if ymin>y: ymin= y
x= v.x
y= v.y
if x<xmin or x>xmax or y < ymin or y > ymax:
return False
# Done with bounds check
'''
try:
mtx = dict_matrix[key]
if not mtx:
return False
except:
side1 = v2 - v1
side2 = v3 - v1
nor = side1.cross(side2)
mtx = Matrix((side1, side2, nor))
# Zero area 2d tri, even tho we throw away zerop area faces
# the projection UV can result in a zero area UV.
if not mtx.determinant():
dict_matrix[key] = None
return False
mtx.invert()
dict_matrix[key] = mtx
uvw = (v - v1) * mtx
return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
def boundsIsland(faces):
minx = maxx = faces[0].uv[0][0] # Set initial bounds.
miny = maxy = faces[0].uv[0][1]
# print len(faces), minx, maxx, miny , maxy
for f in faces:
for uv in f.uv:
x= uv.x
y= uv.y
if x<minx: minx= x
if y<miny: miny= y
if x>maxx: maxx= x
if y>maxy: maxy= y
return minx, miny, maxx, maxy
"""
def boundsEdgeLoop(edges):
minx = maxx = edges[0][0] # Set initial bounds.
miny = maxy = edges[0][1]
# print len(faces), minx, maxx, miny , maxy
for ed in edges:
for pt in ed:
print 'ass'
x= pt[0]
y= pt[1]
if x<minx: x= minx
if y<miny: y= miny
if x>maxx: x= maxx
if y>maxy: y= maxy
return minx, miny, maxx, maxy
"""
# Turns the islands into a list of unpordered edges (Non internal)
# Onlt for UV's
# only returns outline edges for intersection tests. and unique points.
def island2Edge(island):
# Vert index edges
edges = {}
unique_points= {}
for f in island:
f_uvkey= map(tuple, f.uv)
for vIdx, edkey in enumerate(f.edge_keys):
unique_points[f_uvkey[vIdx]] = f.uv[vIdx]
if f.v[vIdx].index > f.v[vIdx-1].index:
i1= vIdx-1; i2= vIdx
else:
i1= vIdx; i2= vIdx-1
try: edges[ f_uvkey[i1], f_uvkey[i2] ] *= 0 # sets eny edge with more then 1 user to 0 are not returned.
except: edges[ f_uvkey[i1], f_uvkey[i2] ] = (f.uv[i1] - f.uv[i2]).length,
# If 2 are the same then they will be together, but full [a,b] order is not correct.
# Sort by length
length_sorted_edges = [(Vector(key[0]), Vector(key[1]), value) for key, value in edges.items() if value != 0]
try: length_sorted_edges.sort(key = lambda A: -A[2]) # largest first
except: length_sorted_edges.sort(lambda A, B: cmp(B[2], A[2]))
# Its okay to leave the length in there.
#for e in length_sorted_edges:
# e.pop(2)
# return edges and unique points
return length_sorted_edges, [v.to_3d() for v in unique_points.values()]
# ========================= NOT WORKING????
# Find if a points inside an edge loop, un-orderd.
# pt is and x/y
# edges are a non ordered loop of edges.
# #offsets are the edge x and y offset.
"""
def pointInEdges(pt, edges):
#
x1 = pt[0]
y1 = pt[1]
# Point to the left of this line.
x2 = -100000
y2 = -10000
intersectCount = 0
for ed in edges:
xi, yi = lineIntersection2D(x1,y1, x2,y2, ed[0][0], ed[0][1], ed[1][0], ed[1][1])
if xi != None: # Is there an intersection.
intersectCount+=1
return intersectCount % 2
"""
def pointInIsland(pt, island):
vec1, vec2, vec3 = Vector(), Vector(), Vector()
for f in island:
vec1.x, vec1.y = f.uv[0]
vec2.x, vec2.y = f.uv[1]
vec3.x, vec3.y = f.uv[2]
if pointInTri2D(pt, vec1, vec2, vec3):
return True
if len(f.v) == 4:
vec1.x, vec1.y = f.uv[0]
vec2.x, vec2.y = f.uv[2]
vec3.x, vec3.y = f.uv[3]
if pointInTri2D(pt, vec1, vec2, vec3):
return True
return False
# box is (left,bottom, right, top)
def islandIntersectUvIsland(source, target, SourceOffset):
# Is 1 point in the box, inside the vertLoops
edgeLoopsSource = source[6] # Pretend this is offset
edgeLoopsTarget = target[6]
# Edge intersect test
for ed in edgeLoopsSource:
for seg in edgeLoopsTarget:
i = geometry.intersect_line_line_2d(\
seg[0], seg[1], SourceOffset+ed[0], SourceOffset+ed[1])
if i:
return 1 # LINE INTERSECTION
# 1 test for source being totally inside target
SourceOffset.resize_3d()
for pv in source[7]:
if pointInIsland(pv+SourceOffset, target[0]):
return 2 # SOURCE INSIDE TARGET
# 2 test for a part of the target being totaly inside the source.
for pv in target[7]:
if pointInIsland(pv-SourceOffset, source[0]):
return 3 # PART OF TARGET INSIDE SOURCE.
return 0 # NO INTERSECTION
# Returns the X/y Bounds of a list of vectors.
def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1):
# UV's will never extend this far.
minx = miny = BIG_NUM
maxx = maxy = -BIG_NUM
for i, v in enumerate(vecs):
# Do this allong the way
if mat != -1:
v = vecs[i] = v*mat
x= v.x
y= v.y
if x<minx: minx= x
if y<miny: miny= y
if x>maxx: maxx= x
if y>maxy: maxy= y
# Spesific to this algo, bail out if we get bigger then the current area
if bestAreaSoFar != -1 and (maxx-minx) * (maxy-miny) > bestAreaSoFar:
return (BIG_NUM, None), None
w = maxx-minx
h = maxy-miny
return (w*h, w,h), vecs # Area, vecs
# Takes a list of faces that make up a UV island and rotate
# until they optimally fit inside a square.
ROTMAT_2D_POS_90D = Matrix.Rotation( radians(90.0), 2)
ROTMAT_2D_POS_45D = Matrix.Rotation( radians(45.0), 2)
RotMatStepRotation = []
rot_angle = 22.5 #45.0/2
while rot_angle > 0.1:
RotMatStepRotation.append([\
Matrix.Rotation( radians(rot_angle), 2),\
Matrix.Rotation( radians(-rot_angle), 2)])
rot_angle = rot_angle/2.0
def optiRotateUvIsland(faces):
global currentArea
# Bestfit Rotation
def best2dRotation(uvVecs, MAT1, MAT2):
global currentArea
newAreaPos, newfaceProjectionGroupListPos =\
testNewVecLs2DRotIsBetter(uvVecs[:], MAT1, currentArea[0])
# Why do I use newpos here? May as well give the best area to date for an early bailout
# some slight speed increase in this.
# If the new rotation is smaller then the existing, we can
# avoid copying a list and overwrite the old, crappy one.
if newAreaPos[0] < currentArea[0]:
newAreaNeg, newfaceProjectionGroupListNeg =\
testNewVecLs2DRotIsBetter(uvVecs, MAT2, newAreaPos[0]) # Reuse the old bigger list.
else:
newAreaNeg, newfaceProjectionGroupListNeg =\
testNewVecLs2DRotIsBetter(uvVecs[:], MAT2, currentArea[0]) # Cant reuse, make a copy.
# Now from the 3 options we need to discover which to use
# we have cerrentArea/newAreaPos/newAreaNeg
bestArea = min(currentArea[0], newAreaPos[0], newAreaNeg[0])
if currentArea[0] == bestArea:
return uvVecs
elif newAreaPos[0] == bestArea:
uvVecs = newfaceProjectionGroupListPos
currentArea = newAreaPos
elif newAreaNeg[0] == bestArea:
uvVecs = newfaceProjectionGroupListNeg
currentArea = newAreaNeg
return uvVecs
# Serialized UV coords to Vectors
uvVecs = [uv for f in faces for uv in f.uv]
# Theres a small enough number of these to hard code it
# rather then a loop.
# Will not modify anything
currentArea, dummy =\
testNewVecLs2DRotIsBetter(uvVecs)
# Try a 45d rotation
newAreaPos, newfaceProjectionGroupListPos = testNewVecLs2DRotIsBetter(uvVecs[:], ROTMAT_2D_POS_45D, currentArea[0])
if newAreaPos[0] < currentArea[0]:
uvVecs = newfaceProjectionGroupListPos
currentArea = newAreaPos
# 45d done
# Testcase different rotations and find the onfe that best fits in a square
for ROTMAT in RotMatStepRotation:
uvVecs = best2dRotation(uvVecs, ROTMAT[0], ROTMAT[1])
# Only if you want it, make faces verticle!
if currentArea[1] > currentArea[2]:
# Rotate 90d
# Work directly on the list, no need to return a value.
testNewVecLs2DRotIsBetter(uvVecs, ROTMAT_2D_POS_90D)
# Now write the vectors back to the face UV's
i = 0 # count the serialized uv/vectors
for f in faces:
#f.uv = [uv for uv in uvVecs[i:len(f)+i] ]
for j, k in enumerate(range(i, len(f.v)+i)):
f.uv[j][:] = uvVecs[k]
i += len(f.v)
# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
def mergeUvIslands(islandList):
global USER_FILL_HOLES
global USER_FILL_HOLES_QUALITY
# Pack islands to bottom LHS
# Sync with island
#islandTotFaceArea = [] # A list of floats, each island area
#islandArea = [] # a list of tuples ( area, w,h)
decoratedIslandList = []
islandIdx = len(islandList)
while islandIdx:
islandIdx-=1
minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
w, h = maxx-minx, maxy-miny
totFaceArea = 0
offset= Vector((minx, miny))
for f in islandList[islandIdx]:
for uv in f.uv:
uv -= offset
totFaceArea += f.area
islandBoundsArea = w*h
efficiency = abs(islandBoundsArea - totFaceArea)
# UV Edge list used for intersections as well as unique points.
edges, uniqueEdgePoints = island2Edge(islandList[islandIdx])
decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints])
# Sort by island bounding box area, smallest face area first.
# no.. chance that to most simple edge loop first.
decoratedIslandListAreaSort =decoratedIslandList[:]
decoratedIslandListAreaSort.sort(key = lambda A: A[3])
# sort by efficiency, Least Efficient first.
decoratedIslandListEfficSort = decoratedIslandList[:]
# decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
decoratedIslandListEfficSort.sort(key = lambda A: -A[2])
# ================================================== THESE CAN BE TWEAKED.
# This is a quality value for the number of tests.
# from 1 to 4, generic quality value is from 1 to 100
USER_STEP_QUALITY = ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
# If 100 will test as long as there is enough free space.
# this is rarely enough, and testing takes a while, so lower quality speeds this up.
# 1 means they have the same quality
USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY)/100.0) *5)
#print 'USER_STEP_QUALITY', USER_STEP_QUALITY
#print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
removedCount = 0
areaIslandIdx = 0
ctrl = Window.Qual.CTRL
BREAK= False
while areaIslandIdx < len(decoratedIslandListAreaSort) and not BREAK:
sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
# Alredy packed?
if not sourceIsland[0]:
areaIslandIdx+=1
else:
efficIslandIdx = 0
while efficIslandIdx < len(decoratedIslandListEfficSort) and not BREAK:
if Window.GetKeyQualifiers() & ctrl:
BREAK= True
break
# Now we have 2 islands, is the efficience of the islands lowers theres an
# increasing likely hood that we can fit merge into the bigger UV island.
# this ensures a tight fit.
# Just use figures we have about user/unused area to see if they might fit.
targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
if sourceIsland[0] == targetIsland[0] or\
not targetIsland[0] or\
not sourceIsland[0]:
pass
else:
# ([island, totFaceArea, efficiency, islandArea, w,h])
# Waisted space on target is greater then UV bounding island area.
# if targetIsland[3] > (sourceIsland[2]) and\ #
# print USER_FREE_SPACE_TO_TEST_QUALITY, 'ass'
if targetIsland[2] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
targetIsland[4] > sourceIsland[4] and\
targetIsland[5] > sourceIsland[5]:
# DEBUG # print '%.10f %.10f' % (targetIsland[3], sourceIsland[1])
# These enough spare space lets move the box until it fits
# How many times does the source fit into the target x/y
blockTestXUnit = targetIsland[4]/sourceIsland[4]
blockTestYUnit = targetIsland[5]/sourceIsland[5]
boxLeft = 0
# Distllllance we can move between whilst staying inside the targets bounds.
testWidth = targetIsland[4] - sourceIsland[4]
testHeight = targetIsland[5] - sourceIsland[5]
# Increment we move each test. x/y
xIncrement = (testWidth / (blockTestXUnit * ((USER_STEP_QUALITY/50)+0.1)))
yIncrement = (testHeight / (blockTestYUnit * ((USER_STEP_QUALITY/50)+0.1)))
# Make sure were not moving less then a 3rg of our width/height
if xIncrement<sourceIsland[4]/3:
xIncrement= sourceIsland[4]
if yIncrement<sourceIsland[5]/3:
yIncrement= sourceIsland[5]
boxLeft = 0 # Start 1 back so we can jump into the loop.
boxBottom= 0 #-yIncrement
##testcount= 0
while boxBottom <= testHeight:
# Should we use this? - not needed for now.
#if Window.GetKeyQualifiers() & ctrl:
# BREAK= True
# break
##testcount+=1
#print 'Testing intersect'
Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, Vector((boxLeft, boxBottom)))
#print 'Done', Intersect
if Intersect == 1: # Line intersect, dont bother with this any more
pass
if Intersect == 2: # Source inside target
'''
We have an intersection, if we are inside the target
then move us 1 whole width accross,
Its possible this is a bad idea since 2 skinny Angular faces
could join without 1 whole move, but its a lot more optimal to speed this up
since we have already tested for it.
It gives about 10% speedup with minimal errors.
'''
#print 'ass'
# Move the test allong its width + SMALL_NUM
#boxLeft += sourceIsland[4] + SMALL_NUM
boxLeft += sourceIsland[4]
elif Intersect == 0: # No intersection?? Place it.
# Progress
removedCount +=1
#XXX Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
# Move faces into new island and offset
targetIsland[0].extend(sourceIsland[0])
offset= Vector((boxLeft, boxBottom))
for f in sourceIsland[0]:
for uv in f.uv:
uv+= offset
sourceIsland[0][:] = [] # Empty
# Move edge loop into new and offset.
# targetIsland[6].extend(sourceIsland[6])
#while sourceIsland[6]:
targetIsland[6].extend( [ (\
(e[0]+offset, e[1]+offset, e[2])\
) for e in sourceIsland[6] ] )
sourceIsland[6][:] = [] # Empty
# Sort by edge length, reverse so biggest are first.
try: targetIsland[6].sort(key = lambda A: A[2])
except: targetIsland[6].sort(lambda B,A: cmp(A[2], B[2] ))
targetIsland[7].extend(sourceIsland[7])
offset= Vector((boxLeft, boxBottom, 0.0))
for p in sourceIsland[7]:
p+= offset
sourceIsland[7][:] = []
# Decrement the efficiency
targetIsland[1]+=sourceIsland[1] # Increment totFaceArea
targetIsland[2]-=sourceIsland[1] # Decrement efficiency
# IF we ever used these again, should set to 0, eg
sourceIsland[2] = 0 # No area if anyone wants to know
break
# INCREMENR NEXT LOCATION
if boxLeft > testWidth:
boxBottom += yIncrement
boxLeft = 0.0
else:
boxLeft += xIncrement
##print testcount
efficIslandIdx+=1
areaIslandIdx+=1
# Remove empty islands
i = len(islandList)
while i:
i-=1
if not islandList[i]:
del islandList[i] # Can increment islands removed here.
# Takes groups of faces. assumes face groups are UV groups.
def getUvIslands(faceGroups, me):
# Get seams so we dont cross over seams
edge_seams = {} # shoudl be a set
for ed in me.edges:
if ed.use_seam:
edge_seams[ed.key] = None # dummy var- use sets!
# Done finding seams
islandList = []
#XXX Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups))
#print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
# Find grouped faces
faceGroupIdx = len(faceGroups)
while faceGroupIdx:
faceGroupIdx-=1
faces = faceGroups[faceGroupIdx]
if not faces:
continue
# Build edge dict
edge_users = {}
for i, f in enumerate(faces):
for ed_key in f.edge_keys:
if ed_key in edge_seams: # DELIMIT SEAMS! ;)
edge_users[ed_key] = [] # so as not to raise an error
else:
try: edge_users[ed_key].append(i)
except: edge_users[ed_key] = [i]
# Modes
# 0 - face not yet touched.
# 1 - added to island list, and need to search
# 2 - touched and searched - dont touch again.
face_modes = [0] * len(faces) # initialize zero - untested.
face_modes[0] = 1 # start the search with face 1
newIsland = []
newIsland.append(faces[0])
ok = True
while ok:
ok = True
while ok:
ok= False
for i in range(len(faces)):
if face_modes[i] == 1: # search
for ed_key in faces[i].edge_keys:
for ii in edge_users[ed_key]:
if i != ii and face_modes[ii] == 0:
face_modes[ii] = ok = 1 # mark as searched
newIsland.append(faces[ii])
# mark as searched, dont look again.
face_modes[i] = 2
islandList.append(newIsland)
ok = False
for i in range(len(faces)):
if face_modes[i] == 0:
newIsland = []
newIsland.append(faces[i])
face_modes[i] = ok = 1
break
# if not ok will stop looping
#XXX Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
for island in islandList:
optiRotateUvIsland(island)
return islandList
def packIslands(islandList):
if USER_FILL_HOLES:
#XXX Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
mergeUvIslands(islandList) # Modify in place
# Now we have UV islands, we need to pack them.
# Make a synchronised list with the islands
# so we can box pak the islands.
packBoxes = []
# Keep a list of X/Y offset so we can save time by writing the
# uv's and packed data in one pass.
islandOffsetList = []
islandIdx = 0
while islandIdx < len(islandList):
minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
w, h = maxx-minx, maxy-miny
if USER_ISLAND_MARGIN:
minx -= USER_ISLAND_MARGIN# *w
miny -= USER_ISLAND_MARGIN# *h
maxx += USER_ISLAND_MARGIN# *w
maxy += USER_ISLAND_MARGIN# *h
# recalc width and height
w, h = maxx-minx, maxy-miny
if w < 0.00001 or h < 0.00001:
del islandList[islandIdx]
islandIdx -=1
continue
'''Save the offset to be applied later,
we could apply to the UVs now and allign them to the bottom left hand area
of the UV coords like the box packer imagines they are
but, its quicker just to remember their offset and
apply the packing and offset in 1 pass '''
islandOffsetList.append((minx, miny))
# Add to boxList. use the island idx for the BOX id.
packBoxes.append([0, 0, w, h])
islandIdx+=1
# Now we have a list of boxes to pack that syncs
# with the islands.
#print '\tPacking UV Islands...'
#XXX Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(packBoxes) )
time1 = time.time()
packWidth, packHeight = geometry.box_pack_2d(packBoxes)
# print 'Box Packing Time:', time.time() - time1
#if len(pa ckedLs) != len(islandList):
# raise "Error packed boxes differes from original length"
#print '\tWriting Packed Data to faces'
#XXX Window.DrawProgressBar(0.8, 'Writing Packed Data to faces')
# Sort by ID, so there in sync again
islandIdx = len(islandList)
# Having these here avoids devide by 0
if islandIdx:
if USER_STRETCH_ASPECT:
# Maximize to uv area?? Will write a normalize function.
xfactor = 1.0 / packWidth
yfactor = 1.0 / packHeight
else:
# Keep proportions.
xfactor = yfactor = 1.0 / max(packWidth, packHeight)
while islandIdx:
islandIdx -=1
# Write the packed values to the UV's
xoffset = packBoxes[islandIdx][0] - islandOffsetList[islandIdx][0]
yoffset = packBoxes[islandIdx][1] - islandOffsetList[islandIdx][1]
for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
for uv in f.uv:
uv.x= (uv.x+xoffset) * xfactor
uv.y= (uv.y+yoffset) * yfactor
def VectoQuat(vec):
vec = vec.normalized()
if abs(vec.x) > 0.5:
return vec.to_track_quat('Z', 'X')
else:
return vec.to_track_quat('Z', 'Y')
class thickface(object):
__slost__= 'v', 'uv', 'no', 'area', 'edge_keys'
def __init__(self, face, uvface, mesh_verts):
self.v = [mesh_verts[i] for i in face.vertices]
if len(self.v)==4:
self.uv = uvface.uv1, uvface.uv2, uvface.uv3, uvface.uv4
else:
self.uv = uvface.uv1, uvface.uv2, uvface.uv3
self.no = face.normal
self.area = face.area
self.edge_keys = face.edge_keys
global ob
ob = None
def main(context, island_margin, projection_limit):
global USER_FILL_HOLES
global USER_FILL_HOLES_QUALITY
global USER_STRETCH_ASPECT
global USER_ISLAND_MARGIN
#XXX objects= bpy.data.scenes.active.objects
objects = context.selected_editable_objects
# we can will tag them later.
obList = [ob for ob in objects if ob.type == 'MESH']
# Face select object may not be selected.
#XXX ob = objects.active
ob= objects[0]
if ob and (not ob.select) and ob.type == 'MESH':
# Add to the list
obList =[ob]
del objects
if not obList:
raise('error, no selected mesh objects')
# Create the variables.
USER_PROJECTION_LIMIT = projection_limit
USER_ONLY_SELECTED_FACES = (1)
USER_SHARE_SPACE = (1) # Only for hole filling.
USER_STRETCH_ASPECT = (1) # Only for hole filling.
USER_ISLAND_MARGIN = island_margin # Only for hole filling.
USER_FILL_HOLES = (0)
USER_FILL_HOLES_QUALITY = (50) # Only for hole filling.
USER_VIEW_INIT = (0) # Only for hole filling.
USER_AREA_WEIGHT = (1) # Only for hole filling.
# Reuse variable
if len(obList) == 1:
ob = "Unwrap %i Selected Mesh"
else:
ob = "Unwrap %i Selected Meshes"
# HACK, loop until mouse is lifted.
'''
while Window.GetMouseButtons() != 0:
time.sleep(10)
'''
#XXX if not Draw.PupBlock(ob % len(obList), pup_block):
#XXX return
#XXX del ob
# Convert from being button types
USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT/2) * DEG_TO_RAD)
# Toggle Edit mode
is_editmode = (context.active_object.mode == 'EDIT')
if is_editmode:
bpy.ops.object.mode_set(mode='OBJECT')
# Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.
if USER_SHARE_SPACE:
# Sort by data name so we get consistant results
obList.sort(key = lambda ob: ob.data.name)
collected_islandList= []
#XXX Window.WaitCursor(1)
time1 = time.time()
# Tag as False se we dont operate on teh same mesh twice.
#XXX bpy.data.meshes.tag = False
for me in bpy.data.meshes:
me.tag = False
for ob in obList:
me = ob.data
if me.tag or me.library:
continue
# Tag as used
me.tag = True
if not me.uv_textures: # Mesh has no UV Coords, dont bother.
me.uv_textures.new()
uv_layer = me.uv_textures.active.data
me_verts = list(me.vertices)
if USER_ONLY_SELECTED_FACES:
meshFaces = [thickface(f, uv_layer[i], me_verts) for i, f in enumerate(me.faces) if f.select]
#else:
# meshFaces = map(thickface, me.faces)
if not meshFaces:
continue
#XXX Window.DrawProgressBar(0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces)))
# =======
# Generate a projection list from face normals, this is ment to be smart :)
# make a list of face props that are in sync with meshFaces
# Make a Face List that is sorted by area.
# meshFaces = []
# meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first.
meshFaces.sort( key = lambda a: -a.area )
# remove all zero area faces
while meshFaces and meshFaces[-1].area <= SMALL_NUM:
# Set their UV's to 0,0
for uv in meshFaces[-1].uv:
uv.zero()
meshFaces.pop()
# Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.
# Generate Projection Vecs
# 0d is 1.0
# 180 IS -0.59846
# Initialize projectVecs
if USER_VIEW_INIT:
# Generate Projection
projectVecs = [Vector(Window.GetViewVector()) * ob.matrix_world.inverted().to_3x3()] # We add to this allong the way
else:
projectVecs = []
newProjectVec = meshFaces[0].no
newProjectMeshFaces = [] # Popping stuffs it up.
# Predent that the most unique angke is ages away to start the loop off
mostUniqueAngle = -1.0
# This is popped
tempMeshFaces = meshFaces[:]
# This while only gathers projection vecs, faces are assigned later on.
while 1:
# If theres none there then start with the largest face
# add all the faces that are close.
for fIdx in range(len(tempMeshFaces)-1, -1, -1):
# Use half the angle limit so we dont overweight faces towards this
# normal and hog all the faces.
if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
newProjectMeshFaces.append(tempMeshFaces.pop(fIdx))
# Add the average of all these faces normals as a projectionVec
averageVec = Vector((0.0, 0.0, 0.0))
if USER_AREA_WEIGHT:
for fprop in newProjectMeshFaces:
averageVec += (fprop.no * fprop.area)
else:
for fprop in newProjectMeshFaces:
averageVec += fprop.no
if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
projectVecs.append(averageVec.normalized())
# Get the next vec!
# Pick the face thats most different to all existing angles :)
mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
mostUniqueIndex = 0 # dummy
for fIdx in range(len(tempMeshFaces)-1, -1, -1):
angleDifference = -1.0 # 180d difference.
# Get the closest vec angle we are to.
for p in projectVecs:
temp_angle_diff= p.dot(tempMeshFaces[fIdx].no)
if angleDifference < temp_angle_diff:
angleDifference= temp_angle_diff
if angleDifference < mostUniqueAngle:
# We have a new most different angle
mostUniqueIndex = fIdx
mostUniqueAngle = angleDifference
if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
#print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces)
# Now weight the vector to all its faces, will give a more direct projection
# if the face its self was not representive of the normal from surrounding faces.
newProjectVec = tempMeshFaces[mostUniqueIndex].no
newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)]
else:
if len(projectVecs) >= 1: # Must have at least 2 projections
break
# If there are only zero area faces then its possible
# there are no projectionVecs
if not len(projectVecs):
Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
return
faceProjectionGroupList =[[] for i in range(len(projectVecs)) ]
# MAP and Arrange # We know there are 3 or 4 faces here
for fIdx in range(len(meshFaces)-1, -1, -1):
fvec = meshFaces[fIdx].no
i = len(projectVecs)
# Initialize first
bestAng = fvec.dot(projectVecs[0])
bestAngIdx = 0
# Cycle through the remaining, first already done
while i-1:
i-=1
newAng = fvec.dot(projectVecs[i])
if newAng > bestAng: # Reverse logic for dotvecs
bestAng = newAng
bestAngIdx = i
# Store the area for later use.
faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx])
# Cull faceProjectionGroupList,
# Now faceProjectionGroupList is full of faces that face match the project Vecs list
for i in range(len(projectVecs)):
# Account for projectVecs having no faces.
if not faceProjectionGroupList[i]:
continue
# Make a projection matrix from a unit length vector.
MatQuat = VectoQuat(projectVecs[i])
# Get the faces UV's from the projected vertex.
for f in faceProjectionGroupList[i]:
f_uv = f.uv
for j, v in enumerate(f.v):
# XXX - note, between mathutils in 2.4 and 2.5 the order changed.
f_uv[j][:] = (v.co * MatQuat)[:2]
if USER_SHARE_SPACE:
# Should we collect and pack later?
islandList = getUvIslands(faceProjectionGroupList, me)
collected_islandList.extend(islandList)
else:
# Should we pack the islands for this 1 object?
islandList = getUvIslands(faceProjectionGroupList, me)
packIslands(islandList)
# update the mesh here if we need to.
# We want to pack all in 1 go, so pack now
if USER_SHARE_SPACE:
#XXX Window.DrawProgressBar(0.9, "Box Packing for all objects...")
packIslands(collected_islandList)
print("Smart Projection time: %.2f" % (time.time() - time1))
# Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (time.time() - time1))
if is_editmode:
bpy.ops.object.mode_set(mode='EDIT')
#XXX Window.DrawProgressBar(1.0, "")
#XXX Window.WaitCursor(0)
#XXX Window.RedrawAll()
"""
pup_block = [\
'Projection',\
* ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, ''),\
('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
('Init from view', USER_VIEW_INIT, 'The first projection will be from the view vector.'),\
('Area Weight', USER_AREA_WEIGHT, 'Weight projections vector by face area.'),\
'',\
'',\
'',\
'UV Layout',\
('Share Tex Space', USER_SHARE_SPACE, 'Objects Share texture space, map all objects into 1 uvmap.'),\
('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\
* ('Island Margin:', USER_ISLAND_MARGIN, 0.0, 0.5, ''),\
'Fill in empty areas',\
('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\
('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\
]
"""
from bpy.props import *
class SmartProject(bpy.types.Operator):
'''This script projection unwraps the selected faces of a mesh. it operates on all selected mesh objects, and can be used unwrap selected faces, or all faces.'''
bl_idname = "uv.smart_project"
bl_label = "Smart UV Project"
bl_options = {'REGISTER', 'UNDO'}
angle_limit = FloatProperty(name="Angle Limit",
description="lower for more projection groups, higher for less distortion.",
default=66.0, min=1.0, max=89.0)
island_margin = FloatProperty(name="Island Margin",
description="Margin to reduce bleed from adjacent islands.",
default=0.0, min=0.0, max=1.0)
@classmethod
def poll(cls, context):
return context.active_object != None
def execute(self, context):
main(context, self.island_margin, self.angle_limit)
return {'FINISHED'}
# Add to a menu
menu_func = (lambda self, context: self.layout.operator(SmartProject.bl_idname,
text="Smart Project"))
def register():
bpy.utils.register_module(__name__)
bpy.types.VIEW3D_MT_uv_map.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.VIEW3D_MT_uv_map.remove(menu_func)
if __name__ == "__main__":
register()

View File

@@ -1,190 +0,0 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Campbell J Barton
#
# 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 *****
# --------------------------------------------------------------------------
# <pep8 compliant>
# History
#
# Originally written by Campbell Barton aka ideasman42
#
# 2009-11-01: * 2.5 port by Keith "Wahooney" Boshoff
# * Replaced old method with my own, speed is similar (about 0.001 sec on Suzanne)
# but results are far more accurate
#
import bpy
import math
import time
from mathutils import Vector
from bpy.props import *
def applyVertexDirt(me, blur_iterations, blur_strength, clamp_dirt, clamp_clean, dirt_only):
## Window.WaitCursor(1)
#BPyMesh.meshCalcNormals(me)
vert_tone = [0.0] * len(me.vertices)
min_tone = 180.0
max_tone = 0.0
# create lookup table for each vertex's connected vertices (via edges)
con = []
con = [[] for i in range(len(me.vertices))]
# add connected verts
for e in me.edges:
con[e.vertices[0]].append(e.vertices[1])
con[e.vertices[1]].append(e.vertices[0])
for i, v in enumerate(me.vertices):
vec = Vector()
no = v.normal
co = v.co
# get the direction of the vectors between the vertex and it's connected vertices
for c in con[i]:
vec += (me.vertices[c].co - co).normalized()
# normalize the vector by dividing by the number of connected verts
tot_con = len(con[i])
if tot_con == 0:
continue
vec /= tot_con
# angle is the acos of the dot product between vert and connected verts normals
ang = math.acos(no.dot(vec))
# enforce min/max
ang = max(clamp_dirt, ang)
if not dirt_only:
ang = min(clamp_clean, ang)
vert_tone[i] = ang
# blur tones
for i in range(blur_iterations):
# backup the original tones
orig_vert_tone = list(vert_tone)
# use connected verts look up for blurring
for j, c in enumerate(con):
for v in c:
vert_tone[j] += blur_strength * orig_vert_tone[v]
vert_tone[j] /= len(c) * blur_strength + 1
min_tone = min(vert_tone)
max_tone = max(vert_tone)
# debug information
# print(min_tone * 2 * math.pi)
# print(max_tone * 2 * math.pi)
# print(clamp_clean)
# print(clamp_dirt)
tone_range = max_tone - min_tone
if not tone_range:
return
active_col_layer = None
if len(me.vertex_colors):
for lay in me.vertex_colors:
if lay.active:
active_col_layer = lay.data
else:
bpy.ops.mesh.vertex_color_add()
me.vertex_colors[0].active = True
active_col_layer = me.vertex_colors[0].data
if not active_col_layer:
return('CANCELLED', )
for i, f in enumerate(me.faces):
if not me.use_paint_mask or f.select:
f_col = active_col_layer[i]
f_col = [f_col.color1, f_col.color2, f_col.color3, f_col.color4]
for j, v in enumerate(f.vertices):
col = f_col[j]
tone = vert_tone[me.vertices[v].index]
tone = (tone - min_tone) / tone_range
if dirt_only:
tone = min(tone, 0.5)
tone *= 2
col[0] = tone * col[0]
col[1] = tone * col[1]
col[2] = tone * col[2]
## Window.WaitCursor(0)
class VertexPaintDirt(bpy.types.Operator):
bl_idname = "paint.vertex_color_dirt"
bl_label = "Dirty Vertex Colors"
bl_options = {'REGISTER', 'UNDO'}
blur_strength = FloatProperty(name="Blur Strength", description="Blur strength per iteration", default=1.0, min=0.01, max=1.0)
blur_iterations = IntProperty(name="Blur Iterations", description="Number times to blur the colors. (higher blurs more)", default=1, min=0, max=40)
clean_angle = FloatProperty(name="Highlight Angle", description="Less then 90 limits the angle used in the tonal range", default=180.0, min=0.0, max=180.0)
dirt_angle = FloatProperty(name="Dirt Angle", description="Less then 90 limits the angle used in the tonal range", default=0.0, min=0.0, max=180.0)
dirt_only = BoolProperty(name="Dirt Only", description="Dont calculate cleans for convex areas", default=False)
def execute(self, context):
obj = context.object
if not obj or obj.type != 'MESH':
print('Error, no active mesh object, aborting')
return('CANCELLED',)
mesh = obj.data
t = time.time()
applyVertexDirt(mesh, self.blur_iterations, self.blur_strength, math.radians(self.dirt_angle), math.radians(self.clean_angle), self.dirt_only)
print('Dirt calculated in %.6f' % (time.time() - t))
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,859 +0,0 @@
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
from bpy.props import *
from rna_prop_ui import rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear
class MESH_OT_delete_edgeloop(bpy.types.Operator):
'''Delete an edge loop by merging the faces on each side to a single face loop'''
bl_idname = "mesh.delete_edgeloop"
bl_label = "Delete Edge Loop"
def execute(self, context):
if 'FINISHED' in bpy.ops.transform.edge_slide(value=1.0):
bpy.ops.mesh.select_more()
bpy.ops.mesh.remove_doubles()
return {'FINISHED'}
return {'CANCELLED'}
rna_path_prop = StringProperty(name="Context Attributes",
description="rna context string", maxlen=1024, default="")
rna_reverse_prop = BoolProperty(name="Reverse",
description="Cycle backwards", default=False)
rna_relative_prop = BoolProperty(name="Relative",
description="Apply relative to the current value (delta)",
default=False)
def context_path_validate(context, data_path):
import sys
try:
value = eval("context.%s" % data_path) if data_path else Ellipsis
except AttributeError:
if "'NoneType'" in str(sys.exc_info()[1]):
# One of the items in the rna path is None, just ignore this
value = Ellipsis
else:
# We have a real error in the rna path, dont ignore that
raise
return value
def execute_context_assign(self, context):
if context_path_validate(context, self.data_path) is Ellipsis:
return {'PASS_THROUGH'}
if getattr(self, "relative", False):
exec("context.%s+=self.value" % self.data_path)
else:
exec("context.%s=self.value" % self.data_path)
return {'FINISHED'}
class BRUSH_OT_set_active_number(bpy.types.Operator):
'''Set active sculpt/paint brush from it's number'''
bl_idname = "brush.set_active_number"
bl_label = "Set Brush Number"
mode = StringProperty(name="mode",
description="Paint mode to set brush for", maxlen=1024)
number = IntProperty(name="number",
description="Brush number")
_attr_dict = {"sculpt": "use_paint_sculpt",
"vertex_paint": "use_paint_vertex",
"weight_paint": "use_paint_weight",
"image_paint": "use_paint_texture"}
def execute(self, context):
attr = self._attr_dict.get(self.mode)
if attr is None:
return {'CANCELLED'}
for i, brush in enumerate((cur for cur in bpy.data.brushes if getattr(cur, attr))):
if i == self.number:
getattr(context.tool_settings, self.mode).brush = brush
return {'FINISHED'}
return {'CANCELLED'}
class WM_OT_context_set_boolean(bpy.types.Operator):
'''Set a context value.'''
bl_idname = "wm.context_set_boolean"
bl_label = "Context Set Boolean"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = BoolProperty(name="Value",
description="Assignment value", default=True)
execute = execute_context_assign
class WM_OT_context_set_int(bpy.types.Operator): # same as enum
'''Set a context value.'''
bl_idname = "wm.context_set_int"
bl_label = "Context Set"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = IntProperty(name="Value", description="Assign value", default=0)
relative = rna_relative_prop
execute = execute_context_assign
class WM_OT_context_scale_int(bpy.types.Operator):
'''Scale an int context value.'''
bl_idname = "wm.context_scale_int"
bl_label = "Context Set"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = FloatProperty(name="Value", description="Assign value", default=1.0)
always_step = BoolProperty(name="Always Step",
description="Always adjust the value by a minimum of 1 when 'value' is not 1.0.",
default=True)
def execute(self, context):
if context_path_validate(context, self.data_path) is Ellipsis:
return {'PASS_THROUGH'}
value = self.value
data_path = self.data_path
if value == 1.0: # nothing to do
return {'CANCELLED'}
if getattr(self, "always_step", False):
if value > 1.0:
add = "1"
func = "max"
else:
add = "-1"
func = "min"
exec("context.%s = %s(round(context.%s * value), context.%s + %s)" % (data_path, func, data_path, data_path, add))
else:
exec("context.%s *= value" % self.data_path)
return {'FINISHED'}
class WM_OT_context_set_float(bpy.types.Operator): # same as enum
'''Set a context value.'''
bl_idname = "wm.context_set_float"
bl_label = "Context Set Float"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = FloatProperty(name="Value",
description="Assignment value", default=0.0)
relative = rna_relative_prop
execute = execute_context_assign
class WM_OT_context_set_string(bpy.types.Operator): # same as enum
'''Set a context value.'''
bl_idname = "wm.context_set_string"
bl_label = "Context Set String"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = StringProperty(name="Value",
description="Assign value", maxlen=1024, default="")
execute = execute_context_assign
class WM_OT_context_set_enum(bpy.types.Operator):
'''Set a context value.'''
bl_idname = "wm.context_set_enum"
bl_label = "Context Set Enum"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = StringProperty(name="Value",
description="Assignment value (as a string)",
maxlen=1024, default="")
execute = execute_context_assign
class WM_OT_context_set_value(bpy.types.Operator):
'''Set a context value.'''
bl_idname = "wm.context_set_value"
bl_label = "Context Set Value"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = StringProperty(name="Value",
description="Assignment value (as a string)",
maxlen=1024, default="")
def execute(self, context):
if context_path_validate(context, self.data_path) is Ellipsis:
return {'PASS_THROUGH'}
exec("context.%s=%s" % (self.data_path, self.value))
return {'FINISHED'}
class WM_OT_context_toggle(bpy.types.Operator):
'''Toggle a context value.'''
bl_idname = "wm.context_toggle"
bl_label = "Context Toggle"
bl_options = {'UNDO'}
data_path = rna_path_prop
def execute(self, context):
if context_path_validate(context, self.data_path) is Ellipsis:
return {'PASS_THROUGH'}
exec("context.%s=not (context.%s)" %
(self.data_path, self.data_path))
return {'FINISHED'}
class WM_OT_context_toggle_enum(bpy.types.Operator):
'''Toggle a context value.'''
bl_idname = "wm.context_toggle_enum"
bl_label = "Context Toggle Values"
bl_options = {'UNDO'}
data_path = rna_path_prop
value_1 = StringProperty(name="Value", \
description="Toggle enum", maxlen=1024, default="")
value_2 = StringProperty(name="Value", \
description="Toggle enum", maxlen=1024, default="")
def execute(self, context):
if context_path_validate(context, self.data_path) is Ellipsis:
return {'PASS_THROUGH'}
exec("context.%s = ['%s', '%s'][context.%s!='%s']" % \
(self.data_path, self.value_1,\
self.value_2, self.data_path,
self.value_2))
return {'FINISHED'}
class WM_OT_context_cycle_int(bpy.types.Operator):
'''Set a context value. Useful for cycling active material, '''
'''vertex keys, groups' etc.'''
bl_idname = "wm.context_cycle_int"
bl_label = "Context Int Cycle"
bl_options = {'UNDO'}
data_path = rna_path_prop
reverse = rna_reverse_prop
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
if value is Ellipsis:
return {'PASS_THROUGH'}
if self.reverse:
value -= 1
else:
value += 1
exec("context.%s=value" % data_path)
if value != eval("context.%s" % data_path):
# relies on rna clamping int's out of the range
if self.reverse:
value = (1 << 31) - 1
else:
value = -1 << 31
exec("context.%s=value" % data_path)
return {'FINISHED'}
class WM_OT_context_cycle_enum(bpy.types.Operator):
'''Toggle a context value.'''
bl_idname = "wm.context_cycle_enum"
bl_label = "Context Enum Cycle"
bl_options = {'UNDO'}
data_path = rna_path_prop
reverse = rna_reverse_prop
def execute(self, context):
value = context_path_validate(context, self.data_path)
if value is Ellipsis:
return {'PASS_THROUGH'}
orig_value = value
# Have to get rna enum values
rna_struct_str, rna_prop_str = self.data_path.rsplit('.', 1)
i = rna_prop_str.find('[')
# just incse we get "context.foo.bar[0]"
if i != -1:
rna_prop_str = rna_prop_str[0:i]
rna_struct = eval("context.%s.rna_type" % rna_struct_str)
rna_prop = rna_struct.properties[rna_prop_str]
if type(rna_prop) != bpy.types.EnumProperty:
raise Exception("expected an enum property")
enums = rna_struct.properties[rna_prop_str].items.keys()
orig_index = enums.index(orig_value)
# Have the info we need, advance to the next item
if self.reverse:
if orig_index == 0:
advance_enum = enums[-1]
else:
advance_enum = enums[orig_index - 1]
else:
if orig_index == len(enums) - 1:
advance_enum = enums[0]
else:
advance_enum = enums[orig_index + 1]
# set the new value
exec("context.%s=advance_enum" % self.data_path)
return {'FINISHED'}
class WM_OT_context_cycle_array(bpy.types.Operator):
'''Set a context array value.
Useful for cycling the active mesh edit mode.'''
bl_idname = "wm.context_cycle_array"
bl_label = "Context Array Cycle"
bl_options = {'UNDO'}
data_path = rna_path_prop
reverse = rna_reverse_prop
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
if value is Ellipsis:
return {'PASS_THROUGH'}
def cycle(array):
if self.reverse:
array.insert(0, array.pop())
else:
array.append(array.pop(0))
return array
exec("context.%s=cycle(context.%s[:])" % (data_path, data_path))
return {'FINISHED'}
class WM_MT_context_menu_enum(bpy.types.Menu):
bl_label = ""
data_path = "" # BAD DESIGN, set from operator below.
def draw(self, context):
data_path = self.data_path
value = context_path_validate(bpy.context, data_path)
if value is Ellipsis:
return {'PASS_THROUGH'}
base_path, prop_string = data_path.rsplit(".", 1)
value_base = context_path_validate(context, base_path)
values = [(i.name, i.identifier) for i in value_base.bl_rna.properties[prop_string].items]
for name, identifier in values:
prop = self.layout.operator("wm.context_set_enum", text=name)
prop.data_path = data_path
prop.value = identifier
class WM_OT_context_menu_enum(bpy.types.Operator):
bl_idname = "wm.context_menu_enum"
bl_label = "Context Enum Menu"
bl_options = {'UNDO'}
data_path = rna_path_prop
def execute(self, context):
data_path = self.data_path
WM_MT_context_menu_enum.data_path = data_path
bpy.ops.wm.call_menu(name="WM_MT_context_menu_enum")
return {'PASS_THROUGH'}
class WM_OT_context_set_id(bpy.types.Operator):
'''Toggle a context value.'''
bl_idname = "wm.context_set_id"
bl_label = "Set Library ID"
bl_options = {'UNDO'}
data_path = rna_path_prop
value = StringProperty(name="Value",
description="Assign value", maxlen=1024, default="")
def execute(self, context):
value = self.value
data_path = self.data_path
# match the pointer type from the target property to bpy.data.*
# so we lookup the correct list.
data_path_base, data_path_prop = data_path.rsplit(".", 1)
data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop]
data_prop_rna_type = data_prop_rna.fixed_type
id_iter = None
for prop in bpy.data.rna_type.properties:
if prop.rna_type.identifier == "CollectionProperty":
if prop.fixed_type == data_prop_rna_type:
id_iter = prop.identifier
break
if id_iter:
value_id = getattr(bpy.data, id_iter).get(value)
exec("context.%s=value_id" % data_path)
return {'FINISHED'}
doc_id = StringProperty(name="Doc ID",
description="", maxlen=1024, default="", options={'HIDDEN'})
doc_new = StringProperty(name="Edit Description",
description="", maxlen=1024, default="")
class WM_OT_context_modal_mouse(bpy.types.Operator):
'''Adjust arbitrary values with mouse input'''
bl_idname = "wm.context_modal_mouse"
bl_label = "Context Modal Mouse"
data_path_iter = StringProperty(description="The data path relative to the context, must point to an iterable.")
data_path_item = StringProperty(description="The data path from each iterable to the value (int or float)")
input_scale = FloatProperty(default=0.01, description="Scale the mouse movement by this value before applying the delta")
invert = BoolProperty(default=False, description="Invert the mouse input")
initial_x = IntProperty(options={'HIDDEN'})
def _values_store(self, context):
data_path_iter = self.data_path_iter
data_path_item = self.data_path_item
self._values = values = {}
for item in getattr(context, data_path_iter):
try:
value_orig = eval("item." + data_path_item)
except:
continue
# check this can be set, maybe this is library data.
try:
exec("item.%s = %s" % (data_path_item, value_orig))
except:
continue
values[item] = value_orig
def _values_delta(self, delta):
delta *= self.input_scale
if self.invert:
delta = - delta
data_path_item = self.data_path_item
for item, value_orig in self._values.items():
if type(value_orig) == int:
exec("item.%s = int(%d)" % (data_path_item, round(value_orig + delta)))
else:
exec("item.%s = %f" % (data_path_item, value_orig + delta))
def _values_restore(self):
data_path_item = self.data_path_item
for item, value_orig in self._values.items():
exec("item.%s = %s" % (data_path_item, value_orig))
self._values.clear()
def _values_clear(self):
self._values.clear()
def modal(self, context, event):
event_type = event.type
if event_type == 'MOUSEMOVE':
delta = event.mouse_x - self.initial_x
self._values_delta(delta)
elif 'LEFTMOUSE' == event_type:
self._values_clear()
return {'FINISHED'}
elif event_type in ('RIGHTMOUSE', 'ESC'):
self._values_restore()
return {'FINISHED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
self._values_store(context)
if not self._values:
self.report({'WARNING'}, "Nothing to operate on: %s[ ].%s" %
(self.data_path_iter, self.data_path_item))
return {'CANCELLED'}
else:
self.initial_x = event.mouse_x
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
class WM_OT_url_open(bpy.types.Operator):
"Open a website in the Webbrowser"
bl_idname = "wm.url_open"
bl_label = ""
url = StringProperty(name="URL", description="URL to open")
def execute(self, context):
import webbrowser
webbrowser.open(self.url)
return {'FINISHED'}
class WM_OT_path_open(bpy.types.Operator):
"Open a path in a file browser"
bl_idname = "wm.path_open"
bl_label = ""
filepath = StringProperty(name="File Path", maxlen=1024, subtype='FILE_PATH')
def execute(self, context):
import sys
import os
import subprocess
filepath = bpy.path.abspath(self.filepath)
filepath = os.path.normpath(filepath)
if not os.path.exists(filepath):
self.report({'ERROR'}, "File '%s' not found" % filepath)
return {'CANCELLED'}
if sys.platform == 'win32':
subprocess.Popen(['start', filepath], shell=True)
elif sys.platform == 'darwin':
subprocess.Popen(['open', filepath])
else:
try:
subprocess.Popen(['xdg-open', filepath])
except OSError:
# xdg-open *should* be supported by recent Gnome, KDE, Xfce
pass
return {'FINISHED'}
class WM_OT_doc_view(bpy.types.Operator):
'''Load online reference docs'''
bl_idname = "wm.doc_view"
bl_label = "View Documentation"
doc_id = doc_id
_prefix = "http://www.blender.org/documentation/blender_python_api_%s" % "_".join(str(v) for v in bpy.app.version)
def _nested_class_string(self, class_string):
ls = []
class_obj = getattr(bpy.types, class_string, None).bl_rna
while class_obj:
ls.insert(0, class_obj)
class_obj = class_obj.nested
return '.'.join(class_obj.identifier for class_obj in ls)
def execute(self, context):
id_split = self.doc_id.split('.')
if len(id_split) == 1: # rna, class
url = '%s/bpy.types.%s.html' % (self._prefix, id_split[0])
elif len(id_split) == 2: # rna, class.prop
class_name, class_prop = id_split
if hasattr(bpy.types, class_name.upper() + '_OT_' + class_prop):
url = '%s/bpy.ops.%s.html#bpy.ops.%s.%s' % \
(self._prefix, class_name, class_name, class_prop)
else:
# It so happens that epydoc nests these, not sphinx
# class_name_full = self._nested_class_string(class_name)
url = '%s/bpy.types.%s.html#bpy.types.%s.%s' % \
(self._prefix, class_name, class_name, class_prop)
else:
return {'PASS_THROUGH'}
import webbrowser
webbrowser.open(url)
return {'FINISHED'}
class WM_OT_doc_edit(bpy.types.Operator):
'''Load online reference docs'''
bl_idname = "wm.doc_edit"
bl_label = "Edit Documentation"
doc_id = doc_id
doc_new = doc_new
_url = "http://www.mindrones.com/blender/svn/xmlrpc.php"
def _send_xmlrpc(self, data_dict):
print("sending data:", data_dict)
import xmlrpc.client
user = 'blenderuser'
pwd = 'blender>user'
docblog = xmlrpc.client.ServerProxy(self._url)
docblog.metaWeblog.newPost(1, user, pwd, data_dict, 1)
def execute(self, context):
doc_id = self.doc_id
doc_new = self.doc_new
class_name, class_prop = doc_id.split('.')
if not doc_new:
self.report({'ERROR'}, "No input given for '%s'" % doc_id)
return {'CANCELLED'}
# check if this is an operator
op_name = class_name.upper() + '_OT_' + class_prop
op_class = getattr(bpy.types, op_name, None)
# Upload this to the web server
upload = {}
if op_class:
rna = op_class.bl_rna
doc_orig = rna.description
if doc_orig == doc_new:
return {'RUNNING_MODAL'}
print("op - old:'%s' -> new:'%s'" % (doc_orig, doc_new))
upload["title"] = 'OPERATOR %s:%s' % (doc_id, doc_orig)
else:
rna = getattr(bpy.types, class_name).bl_rna
doc_orig = rna.properties[class_prop].description
if doc_orig == doc_new:
return {'RUNNING_MODAL'}
print("rna - old:'%s' -> new:'%s'" % (doc_orig, doc_new))
upload["title"] = 'RNA %s:%s' % (doc_id, doc_orig)
upload["description"] = doc_new
self._send_xmlrpc(upload)
return {'FINISHED'}
def draw(self, context):
layout = self.layout
layout.label(text="Descriptor ID: '%s'" % self.doc_id)
layout.prop(self, "doc_new", text="")
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self, width=600)
from bpy.props import *
rna_path = StringProperty(name="Property Edit",
description="Property data_path edit", maxlen=1024, default="", options={'HIDDEN'})
rna_value = StringProperty(name="Property Value",
description="Property value edit", maxlen=1024, default="")
rna_property = StringProperty(name="Property Name",
description="Property name edit", maxlen=1024, default="")
rna_min = FloatProperty(name="Min", default=0.0, precision=3)
rna_max = FloatProperty(name="Max", default=1.0, precision=3)
class WM_OT_properties_edit(bpy.types.Operator):
'''Internal use (edit a property data_path)'''
bl_idname = "wm.properties_edit"
bl_label = "Edit Property"
bl_options = {'REGISTER'} # only because invoke_props_popup requires.
data_path = rna_path
property = rna_property
value = rna_value
min = rna_min
max = rna_max
description = StringProperty(name="Tip", default="")
def execute(self, context):
data_path = self.data_path
value = self.value
prop = self.property
prop_old = self._last_prop[0]
try:
value_eval = eval(value)
except:
value_eval = value
# First remove
item = eval("context.%s" % data_path)
rna_idprop_ui_prop_clear(item, prop_old)
exec_str = "del item['%s']" % prop_old
# print(exec_str)
exec(exec_str)
# Reassign
exec_str = "item['%s'] = %s" % (prop, repr(value_eval))
# print(exec_str)
exec(exec_str)
self._last_prop[:] = [prop]
prop_type = type(item[prop])
prop_ui = rna_idprop_ui_prop_get(item, prop)
if prop_type in (float, int):
prop_ui['soft_min'] = prop_ui['min'] = prop_type(self.min)
prop_ui['soft_max'] = prop_ui['max'] = prop_type(self.max)
prop_ui['description'] = self.description
return {'FINISHED'}
def invoke(self, context, event):
self._last_prop = [self.property]
item = eval("context.%s" % self.data_path)
# setup defaults
prop_ui = rna_idprop_ui_prop_get(item, self.property, False) # dont create
if prop_ui:
self.min = prop_ui.get("min", -1000000000)
self.max = prop_ui.get("max", 1000000000)
self.description = prop_ui.get("description", "")
wm = context.window_manager
return wm.invoke_props_dialog(self)
class WM_OT_properties_add(bpy.types.Operator):
'''Internal use (edit a property data_path)'''
bl_idname = "wm.properties_add"
bl_label = "Add Property"
data_path = rna_path
def execute(self, context):
item = eval("context.%s" % self.data_path)
def unique_name(names):
prop = 'prop'
prop_new = prop
i = 1
while prop_new in names:
prop_new = prop + str(i)
i += 1
return prop_new
property = unique_name(item.keys())
item[property] = 1.0
return {'FINISHED'}
class WM_OT_properties_remove(bpy.types.Operator):
'''Internal use (edit a property data_path)'''
bl_idname = "wm.properties_remove"
bl_label = "Remove Property"
data_path = rna_path
property = rna_property
def execute(self, context):
item = eval("context.%s" % self.data_path)
del item[self.property]
return {'FINISHED'}
class WM_OT_keyconfig_activate(bpy.types.Operator):
bl_idname = "wm.keyconfig_activate"
bl_label = "Activate Keyconfig"
filepath = StringProperty(name="File Path", maxlen=1024)
def execute(self, context):
bpy.utils.keyconfig_set(self.filepath)
return {'FINISHED'}
class WM_OT_sysinfo(bpy.types.Operator):
'''Generate System Info'''
bl_idname = "wm.sysinfo"
bl_label = "System Info"
def execute(self, context):
import sys_info
sys_info.write_sysinfo(self)
return {'FINISHED'}
def register():
bpy.utils.register_module(__name__)
def unregister():
bpy.utils.unregister_module(__name__)
if __name__ == "__main__":
register()

View File

@@ -1,382 +1,6 @@
# Configuration Maya
# Configuration Blender
import bpy
wm = bpy.context.window_manager
kc = wm.keyconfigs.new('Maya')
# Map 3D View
km = kc.keymaps.new('3D View', space_type='VIEW_3D', region_type='WINDOW', modal=False)
kmi = km.items.new('view3d.manipulator', 'LEFTMOUSE', 'PRESS', any=True)
kmi.properties.release_confirm = True
kmi = km.items.new('view3d.cursor3d', 'ACTIONMOUSE', 'PRESS')
kmi = km.items.new('view3d.rotate', 'LEFTMOUSE', 'PRESS', alt=True)
kmi = km.items.new('view3d.move', 'MIDDLEMOUSE', 'PRESS', alt=True)
kmi = km.items.new('view3d.zoom', 'RIGHTMOUSE', 'PRESS', alt=True)
kmi = km.items.new('view3d.view_selected', 'NUMPAD_PERIOD', 'PRESS')
kmi = km.items.new('view3d.view_center_cursor', 'NUMPAD_PERIOD', 'PRESS', ctrl=True)
kmi = km.items.new('view3d.fly', 'F', 'PRESS', shift=True)
kmi = km.items.new('view3d.smoothview', 'TIMER1', 'ANY', any=True)
kmi = km.items.new('view3d.rotate', 'TRACKPADPAN', 'ANY', alt=True)
kmi = km.items.new('view3d.rotate', 'MOUSEROTATE', 'ANY')
kmi = km.items.new('view3d.move', 'TRACKPADPAN', 'ANY')
kmi = km.items.new('view3d.zoom', 'TRACKPADZOOM', 'ANY')
kmi = km.items.new('view3d.zoom', 'NUMPAD_PLUS', 'PRESS')
kmi.properties.delta = 1
kmi = km.items.new('view3d.zoom', 'NUMPAD_MINUS', 'PRESS')
kmi.properties.delta = -1
kmi = km.items.new('view3d.zoom', 'EQUAL', 'PRESS', ctrl=True)
kmi.properties.delta = 1
kmi = km.items.new('view3d.zoom', 'MINUS', 'PRESS', ctrl=True)
kmi.properties.delta = -1
kmi = km.items.new('view3d.zoom', 'WHEELINMOUSE', 'PRESS')
kmi.properties.delta = 1
kmi = km.items.new('view3d.zoom', 'WHEELOUTMOUSE', 'PRESS')
kmi.properties.delta = -1
kmi = km.items.new('view3d.view_all', 'HOME', 'PRESS')
kmi.properties.center = False
kmi = km.items.new('view3d.view_all', 'C', 'PRESS', shift=True)
kmi.properties.center = True
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_0', 'PRESS')
kmi.properties.type = 'CAMERA'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS')
kmi.properties.type = 'FRONT'
kmi = km.items.new('view3d.view_orbit', 'NUMPAD_2', 'PRESS')
kmi.properties.type = 'ORBITDOWN'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS')
kmi.properties.type = 'RIGHT'
kmi = km.items.new('view3d.view_orbit', 'NUMPAD_4', 'PRESS')
kmi.properties.type = 'ORBITLEFT'
kmi = km.items.new('view3d.view_persportho', 'NUMPAD_5', 'PRESS')
kmi = km.items.new('view3d.view_orbit', 'NUMPAD_6', 'PRESS')
kmi.properties.type = 'ORBITRIGHT'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS')
kmi.properties.type = 'TOP'
kmi = km.items.new('view3d.view_orbit', 'NUMPAD_8', 'PRESS')
kmi.properties.type = 'ORBITUP'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', ctrl=True)
kmi.properties.type = 'BACK'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', ctrl=True)
kmi.properties.type = 'LEFT'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', ctrl=True)
kmi.properties.type = 'BOTTOM'
kmi = km.items.new('view3d.view_pan', 'NUMPAD_2', 'PRESS', ctrl=True)
kmi.properties.type = 'PANDOWN'
kmi = km.items.new('view3d.view_pan', 'NUMPAD_4', 'PRESS', ctrl=True)
kmi.properties.type = 'PANLEFT'
kmi = km.items.new('view3d.view_pan', 'NUMPAD_6', 'PRESS', ctrl=True)
kmi.properties.type = 'PANRIGHT'
kmi = km.items.new('view3d.view_pan', 'NUMPAD_8', 'PRESS', ctrl=True)
kmi.properties.type = 'PANUP'
kmi = km.items.new('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', ctrl=True)
kmi.properties.type = 'PANRIGHT'
kmi = km.items.new('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True)
kmi.properties.type = 'PANLEFT'
kmi = km.items.new('view3d.view_pan', 'WHEELUPMOUSE', 'PRESS', shift=True)
kmi.properties.type = 'PANUP'
kmi = km.items.new('view3d.view_pan', 'WHEELDOWNMOUSE', 'PRESS', shift=True)
kmi.properties.type = 'PANDOWN'
kmi = km.items.new('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', ctrl=True, alt=True)
kmi.properties.type = 'ORBITLEFT'
kmi = km.items.new('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', ctrl=True, alt=True)
kmi.properties.type = 'ORBITRIGHT'
kmi = km.items.new('view3d.view_orbit', 'WHEELUPMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.type = 'ORBITUP'
kmi = km.items.new('view3d.view_orbit', 'WHEELDOWNMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.type = 'ORBITDOWN'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True)
kmi.properties.align_active = True
kmi.properties.type = 'FRONT'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True)
kmi.properties.align_active = True
kmi.properties.type = 'RIGHT'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True)
kmi.properties.align_active = True
kmi.properties.type = 'TOP'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_1', 'PRESS', shift=True, ctrl=True)
kmi.properties.align_active = True
kmi.properties.type = 'BACK'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_3', 'PRESS', shift=True, ctrl=True)
kmi.properties.align_active = True
kmi.properties.type = 'LEFT'
kmi = km.items.new('view3d.viewnumpad', 'NUMPAD_7', 'PRESS', shift=True, ctrl=True)
kmi.properties.align_active = True
kmi.properties.type = 'BOTTOM'
kmi = km.items.new('view3d.localview', 'NUMPAD_SLASH', 'PRESS')
kmi = km.items.new('view3d.layers', 'ACCENT_GRAVE', 'PRESS')
kmi.properties.nr = 0
kmi = km.items.new('view3d.layers', 'ONE', 'PRESS', any=True)
kmi.properties.nr = 1
kmi = km.items.new('view3d.layers', 'TWO', 'PRESS', any=True)
kmi.properties.nr = 2
kmi = km.items.new('view3d.layers', 'THREE', 'PRESS', any=True)
kmi.properties.nr = 3
kmi = km.items.new('view3d.layers', 'FOUR', 'PRESS', any=True)
kmi.properties.nr = 4
kmi = km.items.new('view3d.layers', 'FIVE', 'PRESS', any=True)
kmi.properties.nr = 5
kmi = km.items.new('view3d.layers', 'SIX', 'PRESS', any=True)
kmi.properties.nr = 6
kmi = km.items.new('view3d.layers', 'SEVEN', 'PRESS', any=True)
kmi.properties.nr = 7
kmi = km.items.new('view3d.layers', 'EIGHT', 'PRESS', any=True)
kmi.properties.nr = 8
kmi = km.items.new('view3d.layers', 'NINE', 'PRESS', any=True)
kmi.properties.nr = 9
kmi = km.items.new('view3d.layers', 'ZERO', 'PRESS', any=True)
kmi.properties.nr = 10
kmi = km.items.new('wm.context_toggle_enum', 'Z', 'PRESS')
kmi.properties.data_path = 'space_data.viewport_shade'
kmi.properties.value_1 = 'SOLID'
kmi.properties.value_2 = 'WIREFRAME'
kmi = km.items.new('wm.context_toggle_enum', 'Z', 'PRESS', alt=True)
kmi.properties.data_path = 'space_data.viewport_shade'
kmi.properties.value_1 = 'TEXTURED'
kmi.properties.value_2 = 'SOLID'
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS')
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True)
kmi.properties.extend = True
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True)
kmi.properties.center = True
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', alt=True)
kmi.properties.enumerate = True
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True)
kmi.properties.center = True
kmi.properties.extend = True
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
kmi.properties.center = True
kmi.properties.enumerate = True
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.enumerate = True
kmi.properties.extend = True
kmi = km.items.new('view3d.select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
kmi.properties.center = True
kmi.properties.enumerate = True
kmi.properties.extend = True
kmi = km.items.new('view3d.select_border', 'EVT_TWEAK_S', 'ANY')
kmi.properties.extend = False
kmi = km.items.new('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', ctrl=True)
kmi = km.items.new('view3d.select_lasso', 'EVT_TWEAK_A', 'ANY', shift=True, ctrl=True)
kmi.properties.deselect = True
kmi = km.items.new('view3d.select_circle', 'C', 'PRESS')
kmi = km.items.new('view3d.clip_border', 'B', 'PRESS', alt=True)
kmi = km.items.new('view3d.zoom_border', 'B', 'PRESS', shift=True)
kmi = km.items.new('view3d.render_border', 'B', 'PRESS', shift=True)
kmi = km.items.new('view3d.camera_to_view', 'NUMPAD_0', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('view3d.object_as_camera', 'NUMPAD_0', 'PRESS', ctrl=True)
kmi = km.items.new('wm.call_menu', 'S', 'PRESS', shift=True)
kmi.properties.name = 'VIEW3D_MT_snap'
kmi = km.items.new('wm.context_set_enum', 'COMMA', 'PRESS')
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'BOUNDING_BOX_CENTER'
kmi = km.items.new('wm.context_set_enum', 'COMMA', 'PRESS', ctrl=True)
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'MEDIAN_POINT'
kmi = km.items.new('wm.context_toggle', 'COMMA', 'PRESS', alt=True)
kmi.properties.data_path = 'space_data.use_pivot_point_align'
kmi = km.items.new('wm.context_toggle', 'Q', 'PRESS')
kmi.properties.data_path = 'space_data.show_manipulator'
kmi = km.items.new('wm.context_set_enum', 'PERIOD', 'PRESS')
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'CURSOR'
kmi = km.items.new('wm.context_set_enum', 'PERIOD', 'PRESS', ctrl=True)
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'INDIVIDUAL_ORIGINS'
kmi = km.items.new('wm.context_set_enum', 'PERIOD', 'PRESS', alt=True)
kmi.properties.data_path = 'space_data.pivot_point'
kmi.properties.value = 'ACTIVE_ELEMENT'
kmi = km.items.new('transform.translate', 'G', 'PRESS', shift=True)
kmi = km.items.new('transform.translate', 'EVT_TWEAK_S', 'ANY')
kmi = km.items.new('transform.rotate', 'R', 'PRESS', shift=True)
kmi = km.items.new('transform.resize', 'S', 'PRESS', shift=True)
kmi = km.items.new('transform.warp', 'W', 'PRESS', shift=True)
kmi = km.items.new('transform.tosphere', 'S', 'PRESS', shift=True, alt=True)
kmi = km.items.new('transform.shear', 'S', 'PRESS', shift=True, ctrl=True, alt=True)
kmi = km.items.new('transform.select_orientation', 'SPACE', 'PRESS', alt=True)
kmi = km.items.new('transform.create_orientation', 'SPACE', 'PRESS', ctrl=True, alt=True)
kmi.properties.use = True
kmi = km.items.new('transform.mirror', 'M', 'PRESS', ctrl=True)
kmi = km.items.new('wm.context_toggle', 'TAB', 'PRESS', shift=True)
kmi.properties.data_path = 'tool_settings.use_snap'
kmi = km.items.new('transform.snap_type', 'TAB', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('view3d.enable_manipulator', 'W', 'PRESS')
kmi.properties.translate = True
kmi = km.items.new('view3d.enable_manipulator', 'E', 'PRESS')
kmi.properties.rotate = True
kmi = km.items.new('view3d.enable_manipulator', 'R', 'PRESS')
kmi.properties.scale = True
kmi = km.items.new('view3d.select_border', 'EVT_TWEAK_S', 'ANY', shift=True)
kmi.properties.extend = True
# Map Object Mode
km = kc.keymaps.new('Object Mode', space_type='EMPTY', region_type='WINDOW', modal=False)
kmi = km.items.new('wm.context_cycle_enum', 'O', 'PRESS', shift=True)
kmi.properties.data_path = 'tool_settings.proportional_edit_falloff'
kmi = km.items.new('wm.context_toggle_enum', 'O', 'PRESS')
kmi.properties.data_path = 'tool_settings.proportional_edit'
kmi.properties.value_1 = 'DISABLED'
kmi.properties.value_2 = 'ENABLED'
kmi = km.items.new('view3d.game_start', 'P', 'PRESS')
kmi = km.items.new('object.select_all', 'A', 'PRESS')
kmi = km.items.new('object.select_inverse', 'I', 'PRESS', ctrl=True)
kmi = km.items.new('object.select_linked', 'L', 'PRESS', shift=True)
kmi = km.items.new('object.select_grouped', 'G', 'PRESS', shift=True)
kmi = km.items.new('object.select_mirror', 'M', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS')
kmi.properties.direction = 'PARENT'
kmi = km.items.new('object.select_hierarchy', 'LEFT_BRACKET', 'PRESS', shift=True)
kmi.properties.direction = 'PARENT'
kmi.properties.extend = True
kmi = km.items.new('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS')
kmi.properties.direction = 'CHILD'
kmi = km.items.new('object.select_hierarchy', 'RIGHT_BRACKET', 'PRESS', shift=True)
kmi.properties.direction = 'CHILD'
kmi.properties.extend = True
kmi = km.items.new('object.parent_set', 'P', 'PRESS', ctrl=True)
kmi = km.items.new('object.parent_no_inverse_set', 'P', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('object.parent_clear', 'P', 'PRESS', alt=True)
kmi = km.items.new('object.track_set', 'T', 'PRESS', ctrl=True)
kmi = km.items.new('object.track_clear', 'T', 'PRESS', alt=True)
kmi = km.items.new('object.constraint_add_with_targets', 'C', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('object.constraints_clear', 'C', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('object.location_clear', 'G', 'PRESS', alt=True)
kmi = km.items.new('object.rotation_clear', 'R', 'PRESS', alt=True)
kmi = km.items.new('object.scale_clear', 'S', 'PRESS', alt=True)
kmi = km.items.new('object.origin_clear', 'O', 'PRESS', alt=True)
kmi = km.items.new('object.hide_view_clear', 'H', 'PRESS', alt=True)
kmi = km.items.new('object.hide_view_set', 'H', 'PRESS')
kmi = km.items.new('object.hide_view_set', 'H', 'PRESS', shift=True)
kmi.properties.unselected = True
kmi = km.items.new('object.move_to_layer', 'M', 'PRESS')
kmi = km.items.new('object.delete', 'X', 'PRESS')
kmi = km.items.new('object.delete', 'DEL', 'PRESS')
kmi = km.items.new('wm.call_menu', 'A', 'PRESS', shift=True)
kmi.properties.name = 'INFO_MT_add'
kmi = km.items.new('object.duplicates_make_real', 'A', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('wm.call_menu', 'A', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_object_apply'
kmi = km.items.new('wm.call_menu', 'U', 'PRESS')
kmi.properties.name = 'VIEW3D_MT_make_single_user'
kmi = km.items.new('wm.call_menu', 'L', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_make_links'
kmi = km.items.new('object.duplicate_move', 'D', 'PRESS', shift=True)
kmi = km.items.new('object.duplicate_move_linked', 'D', 'PRESS', alt=True)
kmi = km.items.new('object.join', 'J', 'PRESS', ctrl=True)
kmi = km.items.new('object.convert', 'C', 'PRESS', alt=True)
kmi = km.items.new('object.proxy_make', 'P', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('object.make_local', 'L', 'PRESS')
kmi = km.items.new('anim.keyframe_insert_menu', 'I', 'PRESS')
kmi = km.items.new('anim.keyframe_delete_v3d', 'I', 'PRESS', alt=True)
kmi = km.items.new('anim.keying_set_active_set', 'I', 'PRESS', shift=True, ctrl=True, alt=True)
kmi = km.items.new('group.create', 'G', 'PRESS', ctrl=True)
kmi = km.items.new('group.objects_remove', 'G', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('group.objects_add_active', 'G', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('group.objects_remove_active', 'G', 'PRESS', shift=True, alt=True)
kmi = km.items.new('wm.call_menu', 'W', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_object_specials'
kmi = km.items.new('object.subdivision_set', 'ZERO', 'PRESS', ctrl=True)
kmi.properties.level = 0
kmi = km.items.new('object.subdivision_set', 'ONE', 'PRESS', ctrl=True)
kmi.properties.level = 1
kmi = km.items.new('object.subdivision_set', 'TWO', 'PRESS', ctrl=True)
kmi.properties.level = 2
kmi = km.items.new('object.subdivision_set', 'THREE', 'PRESS', ctrl=True)
kmi.properties.level = 3
kmi = km.items.new('object.subdivision_set', 'FOUR', 'PRESS', ctrl=True)
kmi.properties.level = 4
kmi = km.items.new('object.subdivision_set', 'FIVE', 'PRESS', ctrl=True)
kmi.properties.level = 5
kmi = km.items.new('object.select_all', 'SELECTMOUSE', 'CLICK')
kmi.properties.action = 'DESELECT'
# Map Mesh
km = kc.keymaps.new('Mesh', space_type='EMPTY', region_type='WINDOW', modal=False)
kmi = km.items.new('mesh.loopcut_slide', 'R', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.loop_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('mesh.loop_select', 'SELECTMOUSE', 'PRESS', shift=True, alt=True)
kmi.properties.extend = True
kmi = km.items.new('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('mesh.edgering_select', 'SELECTMOUSE', 'PRESS', shift=True, ctrl=True, alt=True)
kmi.properties.extend = True
kmi = km.items.new('mesh.select_shortest_path', 'SELECTMOUSE', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.select_all', 'A', 'PRESS')
kmi = km.items.new('mesh.select_more', 'NUMPAD_PLUS', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.select_less', 'NUMPAD_MINUS', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.select_inverse', 'I', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.select_non_manifold', 'M', 'PRESS', shift=True, ctrl=True, alt=True)
kmi = km.items.new('mesh.select_linked', 'L', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.select_linked_pick', 'L', 'PRESS')
kmi = km.items.new('mesh.select_linked_pick', 'L', 'PRESS', shift=True)
kmi.properties.deselect = True
kmi = km.items.new('mesh.faces_select_linked_flat', 'F', 'PRESS', shift=True, ctrl=True, alt=True)
kmi.properties.sharpness = 135.0
kmi = km.items.new('mesh.select_similar', 'G', 'PRESS', shift=True)
kmi = km.items.new('wm.call_menu', 'TAB', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_selection_mode'
kmi = km.items.new('mesh.hide', 'H', 'PRESS')
kmi = km.items.new('mesh.hide', 'H', 'PRESS', shift=True)
kmi.properties.unselected = True
kmi = km.items.new('mesh.reveal', 'H', 'PRESS', alt=True)
kmi = km.items.new('mesh.normals_make_consistent', 'N', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.normals_make_consistent', 'N', 'PRESS', shift=True, ctrl=True)
kmi.properties.inside = True
kmi = km.items.new('view3d.edit_mesh_extrude_move_normal', 'E', 'PRESS', ctrl=True)
kmi = km.items.new('view3d.edit_mesh_extrude_individual_move', 'E', 'PRESS', shift=True)
kmi = km.items.new('wm.call_menu', 'E', 'PRESS', alt=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_extrude'
kmi = km.items.new('mesh.spin', 'R', 'PRESS', alt=True)
kmi = km.items.new('mesh.fill', 'F', 'PRESS', alt=True)
kmi = km.items.new('mesh.beautify_fill', 'F', 'PRESS', shift=True, alt=True)
kmi = km.items.new('mesh.quads_convert_to_tris', 'T', 'PRESS', ctrl=True)
kmi = km.items.new('mesh.tris_convert_to_quads', 'J', 'PRESS', alt=True)
kmi = km.items.new('mesh.edge_flip', 'F', 'PRESS', shift=True, ctrl=True)
kmi = km.items.new('mesh.rip_move', 'V', 'PRESS')
kmi = km.items.new('mesh.merge', 'M', 'PRESS', alt=True)
kmi = km.items.new('transform.shrink_fatten', 'S', 'PRESS', ctrl=True, alt=True)
kmi = km.items.new('mesh.edge_face_add', 'F', 'PRESS')
kmi = km.items.new('mesh.duplicate_move', 'D', 'PRESS', shift=True)
kmi = km.items.new('wm.call_menu', 'A', 'PRESS', shift=True)
kmi.properties.name = 'INFO_MT_mesh_add'
kmi = km.items.new('mesh.separate', 'P', 'PRESS')
kmi = km.items.new('mesh.split', 'Y', 'PRESS')
kmi = km.items.new('mesh.dupli_extrude_cursor', 'ACTIONMOUSE', 'CLICK', ctrl=True)
kmi = km.items.new('mesh.delete', 'X', 'PRESS')
kmi = km.items.new('mesh.delete', 'DEL', 'PRESS')
kmi = km.items.new('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', key_modifier='K')
kmi = km.items.new('mesh.knife_cut', 'LEFTMOUSE', 'PRESS', shift=True, key_modifier='K')
kmi.properties.type = 'MIDPOINTS'
kmi = km.items.new('object.vertex_parent_set', 'P', 'PRESS', ctrl=True)
kmi = km.items.new('wm.call_menu', 'W', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_specials'
kmi = km.items.new('wm.call_menu', 'F', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_faces'
kmi = km.items.new('wm.call_menu', 'E', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_edges'
kmi = km.items.new('wm.call_menu', 'V', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_edit_mesh_vertices'
kmi = km.items.new('wm.call_menu', 'H', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_hook'
kmi = km.items.new('wm.call_menu', 'U', 'PRESS')
kmi.properties.name = 'VIEW3D_MT_uv_map'
kmi = km.items.new('wm.call_menu', 'G', 'PRESS', ctrl=True)
kmi.properties.name = 'VIEW3D_MT_vertex_group'
kmi = km.items.new('wm.context_cycle_enum', 'O', 'PRESS', shift=True)
kmi.properties.data_path = 'tool_settings.proportional_edit_falloff'
kmi = km.items.new('wm.context_toggle_enum', 'O', 'PRESS')
kmi.properties.data_path = 'tool_settings.proportional_edit'
kmi.properties.value_1 = 'DISABLED'
kmi.properties.value_2 = 'ENABLED'
kmi = km.items.new('wm.context_toggle_enum', 'O', 'PRESS', alt=True)
kmi.properties.data_path = 'tool_settings.proportional_edit'
kmi.properties.value_1 = 'DISABLED'
kmi.properties.value_2 = 'CONNECTED'
kmi = km.items.new('mesh.select_all', 'SELECTMOUSE', 'CLICK')
kmi.properties.action = 'DESELECT'
wm.keyconfigs.active = kc
bpy.context.user_preferences.edit.use_drag_immediately = True
bpy.context.user_preferences.edit.use_insertkey_xyz_to_rgb = False
bpy.context.user_preferences.inputs.select_mouse = 'LEFT'

View File

@@ -164,8 +164,6 @@ class INFO_MT_file_export(bpy.types.Menu):
bl_label = "Export"
def draw(self, context):
self.layout.operator("export_mesh.wavefront", text="Wavefront (.obj)")
if hasattr(bpy.types, "WM_OT_collada_export"):
self.layout.operator("wm.collada_export", text="COLLADA (.dae)")

View File

@@ -1,63 +0,0 @@
import bpy
def write_some_data(context, filepath, use_some_setting):
print("running write_some_data...")
pass
from bpy.props import *
class ExportSomeData(bpy.types.Operator):
'''This appiers in the tooltip of the operator and in the generated docs.'''
bl_idname = "export.some_data" # this is important since its how bpy.ops.export.some_data is constructed
bl_label = "Export Some Data"
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
# TODO, add better example props
filepath = StringProperty(name="File Path", description="File path used for exporting the PLY file", maxlen= 1024, default= "")
use_setting = BoolProperty(name="Example Boolean", description="Example Tooltip", default= True)
type = bpy.props.EnumProperty(items=(('OPT_A', "First Option", "Description one"), ('OPT_B', "Second Option", "Description two.")),
name="Example Enum",
description="Choose between two items",
default='OPT_A')
@classmethod
def poll(cls, context):
return context.active_object != None
def execute(self, context):
# # Bug, currently isnt working
#if not self.is_property_set("filepath"):
# raise Exception("filename not set")
write_some_data(self.properties.filepath, context, self.properties.use_setting)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
if True:
# File selector
wm.add_fileselect(self) # will run self.execute()
return {'RUNNING_MODAL'}
elif True:
# search the enum
wm.invoke_search_popup(self)
return {'RUNNING_MODAL'}
elif False:
# Redo popup
return wm.invoke_props_popup(self, event) #
elif False:
return self.execute(context)
# Only needed if you want to add into a dynamic menu
menu_func = lambda self, context: self.layout.operator("export.some_data", text="Example Exporter...")
bpy.types.INFO_MT_file_export.append(menu_func)
if __name__ == "__main__":
bpy.ops.export.some_data('INVOKE_DEFAULT', filepath="/tmp/test.ply")

View File

@@ -1,129 +0,0 @@
#!BPY
"""
Name: 'UVs from unselected adjacent'
Blender: 242
Group: 'UVCalculation'
Tooltip: 'Assign UVs to selected faces from surrounding unselected faces.'
"""
__author__ = "Campbell Barton"
__url__ = ("blender", "elysiun")
__version__ = "1.0 2006/02/07"
__bpydoc__ = """\
This script sets the UV mapping and image of selected faces from adjacent unselected faces.
Use this script in face select mode for texturing between textured faces.
"""
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Campbell J Barton
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
from Blender import *
import bpy
def mostUsedImage(imageList): # Returns the image most used in the list.
if not imageList:
return None
elif len(imageList) < 3:
return imageList[0]
# 3+ Images, Get the most used image for surrounding faces.
imageCount = {}
for image in imageList:
if image:
image_key= image.name
else:
image_key = None
try:
imageCount[image_key]['imageCount'] +=1 # an extra user of this image
except:
imageCount[image_key] = {'imageCount':1, 'blenderImage':image} # start with 1 user.
# Now a list of tuples, (imageName, {imageCount, image})
imageCount = imageCount.items()
try: imageCount.sort(key=lambda a: a[1])
except: imageCount.sort(lambda a,b: cmp(a[1], b[1]))
return imageCount[-1][1]['blenderImage']
def main():
sce = bpy.data.scenes.active
ob = sce.objects.active
if ob == None or ob.type != 'Mesh':
Draw.PupMenu('ERROR: No mesh object in face select mode.')
return
me = ob.getData(mesh=1)
if not me.faceUV:
Draw.PupMenu('ERROR: No mesh object in face select mode.')
return
selfaces = [f for f in me.faces if f.sel]
unselfaces = [f for f in me.faces if not f.sel]
# Gather per Vert UV and Image, store in vertUvAverage
vertUvAverage = [[[],[]] for i in xrange(len(me.verts))]
for f in unselfaces: # Unselected faces only.
fuv = f.uv
for i,v in enumerate(f):
vertUvAverage[v.index][0].append(fuv[i])
vertUvAverage[v.index][1].append(f.image)
# Average per vectex UV coords
for vertUvData in vertUvAverage:
uvList = vertUvData[0]
if uvList:
# Convert from a list of vectors into 1 vector.
vertUvData[0] = reduce(lambda a,b: a+b, uvList, Mathutils.Vector(0,0)) * (1.0/len(uvList))
else:
vertUvData[0] = None
# Assign to selected faces
TEX_FLAG = Mesh.FaceModes['TEX']
for f in selfaces:
uvlist = []
imageList = []
for i,v in enumerate(f):
uv, vImages = vertUvAverage[v.index]
uvlist.append( uv )
imageList.extend(vImages)
if None not in uvlist:
# all the faces images used by this faces vert. some faces will be added twice but thats ok.
# Get the most used image and assign to the face.
image = mostUsedImage(imageList)
f.uv = uvlist
if image:
f.image = image
f.mode |= TEX_FLAG
Window.RedrawAll()
if __name__ == '__main__':
main()