Added research data, testing blender mesh generation

This commit is contained in:
2021-05-23 15:34:33 -05:00
parent f2efb7c42d
commit 505df54a1e
205 changed files with 124004 additions and 40 deletions

View File

@@ -1,4 +1,4 @@
# ##### BEGIN GPL LICENSE BLOCK #####
# ##### 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 version 2
@@ -18,8 +18,8 @@
from .msh2 import msh2
msh = msh2.MSH2(None)
bl_info = {
msh_handler = msh2.MSH2(None)
bl_info = {
"name": "Zero Editor MSH2 format",
"author": "Maxim Stewart",
"version": (0, 0, 1),
@@ -100,8 +100,49 @@ class ImportMSH2(bpy.types.Operator, ImportHelper, IOOBJOrientationHelper):
import os
keywords["relpath"] = os.path.dirname(bpy.data.filepath)
data = {**keywords}
msh.import_file(data["filepath"])
data = {**keywords}
msh_handler.import_file(data["filepath"])
msh = msh_handler.get_mesh_obj()
sceen = bpy.context.scene
# Test adding mesh2 to blender
for model in msh.models:
name = model.name.decode("utf-8")
# print(model)
# print(model.name)
# print(model.index)
# print(model.collection)
# print("")
# Create a mesh data block
mesh2 = bpy.data.meshes.new("{}".format(name))
verts = []
edges = []
faces = []
segments = model.segments
for segment in segments:
if segment.classname == "SegmentGeometry":
vertices = segment.vertices
for vert in vertices:
x = vert.x
y = vert.y
z = vert.z
verts.append((x, y, z))
sfaces = segment.faces
for face in sfaces:
'''Using CCW order for importing.'''
faces.append(face.SIindices())
# Add the vertices to the mesh
mesh2.from_pydata(verts, edges, faces)
# Create an object that uses the mesh data
myobj = bpy.data.objects.new("{}_obj".format(name), mesh2)
# Link the object to the scene
sceen.objects.link(myobj)
return {'FINISHED'}

View File

@@ -6,8 +6,12 @@
from msh2 import msh2
msh = msh2.MSH2(None)
msh_wrapper = msh2.MSH2(None)
msh.import_file("../../msh/all_weap_inf_lightsabre.msh")
# /home/abaddon/Downloads/my-py-msh-parser/msh/KAS/msh
msh_wrapper.import_file("../../msh/KAS/msh/kas2_prop_rock_L.msh")
msh_wrapper.export_file("kas2_prop_rock_L.msh")
# msh.export_file("all_weap_inf_lightsabre.msh")
# msh_wrapper.import_file("../../msh/uta1_prop_gunship.msh")
# msh_wrapper.export_file("uta1_prop_gunship.msh")

View File

@@ -1,6 +1,8 @@
# Python imports
import faulthandler
import traceback
import logging
from datetime import datetime
# Gtk imports
@@ -18,15 +20,31 @@ class MSH2():
self.msh = None
def get_mesh_obj(self):
return self.msh
def import_file(self, file_name):
try:
self.msh = msh2_unpack.MSHUnpack(file_name, self.msh_config).unpack()
unpacker = msh2_unpack.MSHUnpack(file_name, self.msh_config)
self.msh = unpacker.unpack()
except Exception as e:
traceback.print_exc()
def export_file(self, file_name):
def export_file(self, file):
logging.info('==========================================')
logging.info('Starting export at {0}.'.format(datetime.now()))
logging.info('.msh file path: {0}'.format(file))
try:
self.msh.save(file_name)
# Convert materials from Blender to msh2.
# self.msh.materials = msh2.MaterialCollection(self.msh)
# self.msh.materials.replace([])
self.msh.models.assign_indices()
self.msh.models.assign_parents()
self.msh.models.remove_multi([])
self.msh.models.assign_cloth_collisions()
self.msh.save(file)
except Exception as e:
traceback.print_exc()

View File

@@ -131,10 +131,17 @@ def return_lowest_bits(n):
return n & 0xFFFFFFFF
def crc(string):
def crc(_string):
'''Calculate the Zero CRC from string and return it as number.'''
crc_ = 0
crc_ = return_lowest_bits(~crc_)
crc_ = 0
crc_ = return_lowest_bits(~crc_)
# string = _string.decode("ascii")
string = _string.decode("utf-8")
print("")
print("")
print(string)
print("")
print("")
if string:
for char in string:
ind = (crc_ >> 24)

View File

@@ -7,7 +7,13 @@
for more information regarding the file format.
'''
import os
import itertools
# import itertools
# iZip is only available in 2.x
try:
from itertools import izip as zip
except ImportError:
pass
import struct
import math
# import logging
@@ -16,9 +22,9 @@ import math
from .Logger import Logger
logging = Logger("Msh2").get_logger()
# import json
try:
import bson
json = bson
import bson as json
except Exception as e:
import json
@@ -120,6 +126,10 @@ class Msh(Packer):
with open(filepath, 'wb') as fh:
fh.write(self.pack())
def save_unchanged(self, filepath):
with open(filepath, 'wb') as fh:
fh.write(self.repack())
def save_json(self, filepath):
'''Saves the .msh in JSON format.'''
data = {
@@ -326,7 +336,7 @@ class SceneInfo(Packer):
def pack(self):
'''Packs the scene information data.'''
data = [b'SINF']
data.append('size_ind')
data.append('size')
data.append(self.pack_NAME())
data.append(self.pack_FRAM())
data.append(self.bbox.pack())
@@ -459,7 +469,7 @@ class Material(Packer):
def pack(self):
'''Packs the material into a MATD chunk.'''
data = [b'MATD']
data.append('size_indicator')
data.append('size')
data.append(self.pack_NAME())
data.append(self.pack_DATA())
data.append(self.pack_ATRB())
@@ -660,7 +670,7 @@ class Model(Packer):
@classmethod
def load_segmented_json(cls, folder, name):
logging.debug('LOADING SEGMENTED MODEL %s %s', folder, name)
# logging.debug('LOADING SEGMENTED MODEL %s %s', folder, name)
with open(os.path.join(folder, '{0}.txt'.format(name)), 'r') as fh:
model = Model.from_json(json.loads(fh.read()))
seg_start = '{0} seg '.format(name)
@@ -734,7 +744,7 @@ class Model(Packer):
def pack(self):
'''Packs the MODL chunk. This should be used to retrieve the model in packed form.'''
data = [b'MODL', 'sizeind']
data = [b'MODL', 'size']
data.append(self.pack_MTYP())
data.append(self.pack_MNDX())
data.append(self.pack_NAME())
@@ -797,7 +807,7 @@ class Model(Packer):
def repack(self):
'''Repacks the MODL chunk. This should be used to retrieve the model in packed form.'''
data = [b'MODL', 'sizeind']
data = [b'MODL', 'size']
data.append(self.pack_MTYP())
data.append(self.pack_MNDX())
data.append(self.pack_NAME())
@@ -915,7 +925,7 @@ class ModelCollection(object):
'''Get model.index for model with name modelname.'''
for model in self.models:
if model.name == modelname:
logging.debug('ModelCollection.get_index: {0} - {1}'.format(model.name, model.index))
# logging.debug('ModelCollection.get_index: {0} - {1}'.format(model.name, model.index))
return model.index
return 0
@@ -1085,14 +1095,14 @@ class SegmentCollection(object):
def pack(self):
data = []
logging.debug(type(self.segments))
logging.debug(self.segments)
# logging.debug(type(self.segments))
# logging.debug(self.segments)
for segment in self.segments:
data.append(segment.pack())
return b''.join(data)
def repack(self):
data = [bsegment.repack() for segment in self.segments]
data = [segment.repack() for segment in self.segments]
return b''.join(data)
@@ -1156,7 +1166,7 @@ class SegmentGeometry(Packer):
len_new_verts += 1
self.vertices.vertices = new_vertices
logging.debug('Cleared %s doubles.', num_cleared_vertices)
# logging.debug('Cleared %s doubles.', num_cleared_vertices)
def dump(self, fh):
'''Dump information to open filehandler fileh.'''
@@ -1190,7 +1200,7 @@ class ShadowGeometry(Packer):
def __init__(self, collection=None):
self.collection = collection
self.classname = 'ShadowGeometry'
self.data = ''
self.data = b''
self.positions = []
self.edges = []
@@ -1218,7 +1228,7 @@ class ShadowGeometry(Packer):
fh.write('\t\t\t\tNo data available.\n')
def repack(self):
data = [b'SHDW', 'size', self.data]
data = [b'SHDW', 'size', self.data.encode("ascii")]
data[1] = struct.pack('<L', len(self.data))
return b''.join(data)
@@ -1612,35 +1622,36 @@ class Face(object):
def pack(self):
'''Packs the vertex indices for the STRP chunk.'''
# index_map = self.collection.segment.index_map
index_map = None
if self.collection:
if self.collection.segment:
index_map = self.collection.segment.index_map
if (self.sides == 4) and (index_map is not None):
logging.debug(f"Vertex indices for the STRP index_map chunk are: {self.sides}...")
# logging.debug(f"Vertex indices for the STRP index_map chunk are: {self.sides}...")
return struct.pack('<HHHH', index_map[self.vertices[0]] + 0x8000,
index_map[self.vertices[1]] + 0x8000,
index_map[self.vertices[2]],
index_map[self.vertices[3]])
elif (self.sides == 3) and (index_map is not None):
logging.debug(f"Vertex indices for the STRP index_map chunk are: {self.sides}...")
# logging.debug(f"Vertex indices for the STRP index_map chunk are: {self.sides}...")
return struct.pack('<HHH', index_map[self.vertices[0]] + 0x8000,
index_map[self.vertices[1]] + 0x8000,
index_map[self.vertices[2]])
elif (self.sides == 4) and (index_map is None):
logging.debug(f"Vertex indices for the STRP chunk are: {self.sides}...")
# logging.debug(f"Vertex indices for the STRP chunk are: {self.sides}...")
return struct.pack('<HHHH', self.vertices[0] + 0x8000,
self.vertices[1] + 0x8000,
self.vertices[2],
self.vertices[3])
elif (self.sides == 3) and (index_map is None):
logging.debug(f"Vertex indices for the STRP chunk are: {self.sides}...")
# logging.debug(f"Vertex indices for the STRP chunk are: {self.sides}...")
return struct.pack('<HHH', self.vertices[0] + 0x8000,
self.vertices[1] + 0x8000,
self.vertices[2])
else:
logging.debug("Vertex indices for the STRP chunk are empty...")
# logging.debug("Vertex indices for the STRP chunk are empty...")
return b''
def pack_tris(self):
@@ -1976,7 +1987,7 @@ class Vertex(object):
def pack_weights(self):
data = []
for index, weight in itertools.izip(self.deformer_indices, self.weights):
for index, weight in zip(self.deformer_indices, self.weights):
data.append(struct.pack('<Lf', index, weight))
return b''.join(data)
@@ -2073,7 +2084,7 @@ class VertexCollection(object):
return b''.join(data)
def set_uvs(self, uv_list):
for uv, vertex in itertools.izip(uv_list, self.vertices):
for uv, vertex in zip(uv_list, self.vertices):
vertex.u = uv[0]
vertex.v = uv[1]
@@ -2083,7 +2094,7 @@ class VertexCollection(object):
yield vertex.color.get()
def set_colors(self, color_list):
for color, vertex in itertools.izip(color_list, self.vertices):
for color, vertex in zip(color_list, self.vertices):
vertex.color = color
def pack_colors(self):
@@ -2108,7 +2119,7 @@ class VertexCollection(object):
return weights
def set_weights(self, weights, deformers, indices):
for weight, deformer_tuple, index_tpl, vertex in itertools.izip(weights, deformers, indices, self.vertices):
for weight, deformer_tuple, index_tpl, vertex in zip(weights, deformers, indices, self.vertices):
vertex.weights = weight
vertex.deformers = deformer_tuple
vertex.deformer_indices = index_tpl

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
#!BPY
""" Registration info for Blender menus: <- these words are ignored
Name: 'Axis Orientation Copy'
Blender: 242
Group: 'Object'
Tip: 'Copy local axis orientation of active object to all selected meshes (changes mesh data)'
"""
__author__ = "A Vanpoucke (xand)"
__url__ = ("blenderartists.org", "www.blender.org",
"French Blender support forum, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
__version__ = "2 17/12/05"
__bpydoc__ = """\
This script copies the axis orientation -- X, Y and Z rotations -- of the
active object to all selected meshes.
It's useful to align the orientations of all meshes of a structure, a human
skeleton, for example.
Usage:
Select all mesh objects that need to have their orientations changed
(reminder: keep SHIFT pressed after the first, to add each new one to the
selection), then select the object whose orientation will be copied from and
finally run this script to update the angles.
Notes:<br>
This script changes mesh data: the vertices are transformed.<br>
Before copying the orientation to each object, the script stores its
transformation matrix. Then the angles are copied and after that the object's
vertices are transformed "back" so that they still have the same positions as
before. In other words, the rotations are updated, but you won't notice that
just from looking at the objects.<br>
Checking their X, Y and Z rotation values with "Transform Properties" in
the 3D View's Object menu shows the angles are now the same of the active
object. Or simply look at the transform manipulator handles in local transform
orientation.
"""
# $Id: Axiscopy.py 9470 2006-12-25 23:14:48Z campbellbarton $
#
#----------------------------------------------
# A Vanpoucke (xand)
#from the previous script realignaxis
#----------------------------------------------
# Communiquer les problemes et erreurs sur:
# http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2003, 2004: A Vanpoucke
#
# 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 *
from Blender import Mathutils
from Blender.Mathutils import *
import BPyMessages
def realusers(data):
users = data.users
if data.fakeUser: users -= 1
return users
def main():
scn_obs= Scene.GetCurrent().objects
ob_act = scn_obs.active
scn_obs = scn_obs.context
if not ob_act:
BPyMessages.Error_NoActive()
obs = [(ob, ob.getData(mesh=1)) for ob in scn_obs if ob != ob_act]
for ob, me in obs:
if ob.type != 'Mesh':
Draw.PupMenu("Error%t|Selection must be made up of mesh objects only")
return
if realusers(me) != 1:
Draw.PupMenu("Error%t|Meshes must be single user")
return
if len(obs) < 1:
Draw.PupMenu("Error: you must select at least 2 objects")
return
result = Draw.PupMenu("Copy axis orientation from: " + ob_act.name + " ?%t|OK")
if result == -1:
return
for ob_target, me_target in obs:
if ob_act.rot != ob_target.rot:
rot_target = ob_target.matrixWorld.rotationPart().toEuler().toMatrix()
rot_source = ob_act.matrixWorld.rotationPart().toEuler().toMatrix()
rot_source_inv = rot_source.copy().invert()
tx_mat = rot_target * rot_source_inv
tx_mat.resize4x4()
me_target.transform(tx_mat)
ob_target.rot=ob_act.rot
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,238 @@
#!BPY
""" Registration info for Blender menus:
Name: 'DirectX(.x)...'
Blender: 244
Group: 'Import'
Tip: 'Import from DirectX text file format format.'
"""
# DirectXImporter.py version 1.2
# Copyright (C) 2005 Arben OMARI -- omariarben@everyday.com
#
# 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.
# This script import meshes from DirectX text file format
# Grab the latest version here :www.omariben.too.it
import bpy
import Blender
from Blender import Mesh,Object,Material,Texture,Image,Draw
class xImport:
def __init__(self, filename):
global my_path
self.file = open(filename, "r")
my_path = Blender.sys.dirname(filename)
#
self.lines = [l_split for l in self.file.readlines() for l_split in (' '.join(l.split()),) if l_split]
def Import(self):
lines = self.lines
print "importing into Blender ..."
scene = bpy.data.scenes.active
mesh_indicies = {} # the index of each 'Mesh' is used as the key for those meshes indicies
context_indicies = None # will raise an error if used!
#Get the line of Texture Coords
nr_uv_ind = 0
#Get Materials
nr_fac_mat = 0
i = -1
mat_list = []
tex_list = []
mesh_line_indicies = []
for j, line in enumerate(lines):
l = line.strip()
words = line.split()
if words[0] == "Material" :
#context_indicies["Material"] = j
self.loadMaterials(j, mat_list, tex_list)
elif words[0] == "MeshTextureCoords" :
context_indicies["MeshTextureCoords"] = j
#nr_uv_ind = j
elif words[0] == "MeshMaterialList" :
context_indicies["MeshMaterialList"] = j+2
#nr_fac_mat = j + 2
elif words[0] == "Mesh": # Avoid a second loop
context_indicies = mesh_indicies[j] = {'MeshTextureCoords':0, 'MeshMaterialList':0}
for mesh_index, value in mesh_indicies.iteritems():
mesh = Mesh.New()
self.loadVertices(mesh_index, mesh, value['MeshTextureCoords'], value['MeshMaterialList'], tex_list)
mesh.materials = mat_list[:16]
if value['MeshMaterialList']:
self.loadMeshMaterials(value['MeshMaterialList'], mesh)
scene.objects.new(mesh)
self.file.close()
print "... finished"
#------------------------------------------------------------------------------
# CREATE THE MESH
#------------------------------------------------------------------------------
def loadVertices(self, nr_vr_ind, mesh, nr_uv, nr_fac_mat, tex_list):
v_ind = nr_vr_ind + 1
lin = self.lines[v_ind]
if lin :
lin_c = self.CleanLine(lin)
nr_vert = int((lin_c.split()[0]))
else :
v_ind = nr_vr_ind + 2
lin = self.lines[v_ind]
lin_c = self.CleanLine(lin)
nr_vert = int((lin_c.split()[0]))
#--------------------------------------------------
nr_fac_li = v_ind + nr_vert +1
lin_f = self.lines[nr_fac_li]
if lin_f :
lin_fc = self.CleanLine(lin_f)
nr_face = int((lin_fc.split()[0]))
else :
nr_fac_li = v_ind + nr_vert +1
lin_f = self.lines[nr_fac_li]
lin_fc = self.CleanLine(lin_f)
nr_face = int((lin_fc.split()[0]))
#Get Coordinates
verts_list = [(0,0,0)] # WARNING - DUMMY VERT - solves EEKADOODLE ERROR
for l in xrange(v_ind + 1, (v_ind + nr_vert +1)):
line_v = self.lines[l]
lin_v = self.CleanLine(line_v)
words = lin_v.split()
if len(words)==3:
verts_list.append((float(words[0]),float(words[1]),float(words[2])))
mesh.verts.extend(verts_list)
del verts_list
face_list = []
#Make Faces
i = 0
mesh_verts = mesh.verts
for f in xrange(nr_fac_li + 1, (nr_fac_li + nr_face + 1)):
i += 1
line_f = self.lines[f]
lin_f = self.CleanLine(line_f)
# +1 for dummy vert only!
words = lin_f.split()
if len(words) == 5:
face_list.append((1+int(words[1]), 1+int(words[2]), 1+int(words[3]), 1+int(words[4])))
elif len(words) == 4:
face_list.append((1+int(words[1]), 1+int(words[2]), 1+int(words[3])))
mesh.faces.extend(face_list)
del face_list
if nr_uv :
mesh.faceUV = True
for f in mesh.faces:
fuv = f.uv
for ii, v in enumerate(f):
# _u, _v = self.CleanLine(self.lines[nr_uv + 2 + v.index]).split()
# Use a dummy vert
_u, _v = self.CleanLine(self.lines[nr_uv + 1 + v.index]).split()
fuv[ii].x = float(_u)
fuv[ii].y = float(_v)
if nr_fac_mat :
fac_line = self.lines[nr_fac_mat + i]
fixed_fac = self.CleanLine(fac_line)
w_tex = int(fixed_fac.split()[0])
f.image = tex_list[w_tex]
# remove dummy vert
mesh.verts.delete([0,])
def CleanLine(self,line):
return line.replace(\
";", " ").replace(\
'"', ' ').replace(\
"{", " ").replace(\
"}", " ").replace(\
",", " ").replace(\
"'", " ")
#------------------------------------------------------------------
# CREATE MATERIALS
#------------------------------------------------------------------
def loadMaterials(self, nr_mat, mat_list, tex_list):
def load_image(name):
try:
return Image.Load(Blender.sys.join(my_path,name))
except:
return None
mat = bpy.data.materials.new()
line = self.lines[nr_mat + 1]
fixed_line = self.CleanLine(line)
words = fixed_line.split()
mat.rgbCol = [float(words[0]),float(words[1]),float(words[2])]
mat.setAlpha(float(words[3]))
mat_list.append(mat)
l = self.lines[nr_mat + 5]
fix_3_line = self.CleanLine(l)
tex_n = fix_3_line.split()
if tex_n and tex_n[0] == "TextureFilename" :
if len(tex_n) > 1:
tex_list.append(load_image(tex_n[1]))
if len(tex_n) <= 1 :
l_succ = self.lines[nr_mat + 6]
fix_3_succ = self.CleanLine(l_succ)
tex_n_succ = fix_3_succ.split()
tex_list.append(load_image(tex_n_succ[0]))
else :
tex_list.append(None) # no texture for this index
return mat_list, tex_list
#------------------------------------------------------------------
# SET MATERIALS
#------------------------------------------------------------------
def loadMeshMaterials(self, nr_fc_mat, mesh):
for face in mesh.faces:
nr_fc_mat += 1
line = self.lines[nr_fc_mat]
fixed_line = self.CleanLine(line)
wrd = fixed_line.split()
mat_idx = int(wrd[0])
face.mat = mat_idx
#------------------------------------------------------------------
# MAIN
#------------------------------------------------------------------
def my_callback(filename):
if not filename.lower().endswith('.x'): print "Not an .x file"
ximport = xImport(filename)
ximport.Import()
arg = __script__['arg']
if __name__ == '__main__':
Blender.Window.FileSelector(my_callback, "Import DirectX", "*.x")
#my_callback('/fe/x/directxterrain.x')
#my_callback('/fe/x/Male_Normal_MAX.X')
#my_callback('/fe/x/male_ms3d.x')

View File

@@ -0,0 +1,523 @@
#!BPY
"""
Name: 'ID Property Browser'
Blender: 242
Group: 'Help'
Tooltip: 'Browse ID properties'
"""
__author__ = "Joe Eagar"
__version__ = "0.3.108"
__email__ = "joeedh@gmail.com"
__bpydoc__ = """\
Allows browsing, creating and editing of ID Properties
for various ID block types such as mesh, scene, object,
etc.
"""
# --------------------------------------------------------------------------
# ID Property Browser.
# --------------------------------------------------------------------------
# ***** 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 LICENCE BLOCK *****
# --------------------------------------------------------------------------
from Blender import *
from Blender.BGL import *
from Blender.Types import IDGroupType, IDArrayType
import Blender
def IsInRectWH(mx, my, x, y, wid, hgt):
if mx >= x and mx <= x + wid:
if my >= y and my <= y + hgt:
return 1
return 0
Button_Back = 1
Button_New = 2
Button_MatMenu = 3
Button_TypeMenu = 4
ButStart = 55
IDP_String = 0
IDP_Int = 1
IDP_Float = 2
IDP_Array = 5
IDP_Group = 6
ButDelStart = 255
#max limit for string input button
strmax = 100
State_Normal = 0
State_InArray = 1
#IDTypeModules entries are of form [module, active_object_index, module_name]
IDTypeModules = [[Scene, 0, "Scenes"], [Object, 0, "Objects"], [Mesh, 0, "Meshes"]]
IDTypeModules += [[Material, 0, "Materials"], [Texture, 0, "Textures"]]
IDTypeModules += [[Image, 0, "Images"]]
class IDArrayBrowser:
array = 0
parentbrowser = 0
buts = 0
def __init__(self):
self.buts = []
def Draw(self):
pb = self.parentbrowser
x = pb.x
y = pb.y
width = pb.width
height = pb.height
pad = pb.pad
itemhgt = pb.itemhgt
cellwid = 65
y = y + height - itemhgt - pad
Draw.PushButton("Back", Button_Back, x, y, 40, 20)
y -= itemhgt + pad
self.buts = []
Draw.BeginAlign()
for i in xrange(len(self.array)):
st = ""
if type(self.array[0]) == float:
st = "%.5f" % self.array[i]
else: st = str(self.array[i])
b = Draw.String("", ButStart+i, x, y, cellwid, itemhgt, st, 30)
self.buts.append(b)
x += cellwid + pad
if x + cellwid + pad > width:
x = 0
y -= itemhgt + pad
Draw.EndAlign()
def Button(self, bval):
if bval == Button_Back:
self.parentbrowser.state = State_Normal
self.parentbrowser.array = 0
self.buts = []
Draw.Draw()
self.array = 0
elif bval >= ButStart:
i = bval - ButStart
st = self.buts[i].val
n = 0
if type(self.array[0]) == float:
try:
n = int(st)
except:
return
elif type(self.array[0]) == int:
try:
n = float(st)
except:
return
self.array[i] = n
Draw.Draw()
def Evt(self, evt, val):
if evt == Draw.ESCKEY:
Draw.Exit()
class IDPropertyBrowser:
width = 0
height = 0
x = 0
y = 0
scrollx = 0
scrolly = 0
itemhgt = 22
pad = 2
group = 0
parents = 0 #list stack of parent groups
active_item = -1
mousecursor = 0
_i = 0
buts = []
state = 0
array = 0
prop = 0
IDList = 0
idindex = 0
idblock = 0
type = 0 # attach buildin type() method to class
# since oddly it's not available to button
# callbacks! EEK! :(
def __init__(self, idgroup, mat, x, y, wid, hgt):
self.group = idgroup
self.prop = idgroup
self.x = x
self.y = y
self.width = wid
self.height = hgt
self.mousecursor = [0, 0]
self.parents = []
self.idblock = mat
self.type = type
def DrawBox(self, glmode, x, y, width, height):
glBegin(glmode)
glVertex2f(x, y)
glVertex2f(x+width, y)
glVertex2f(x+width, y+height)
glVertex2f(x, y+height)
glEnd()
def Draw(self):
global IDTypeModules
#first draw outlining box :)
glColor3f(0, 0, 0)
self.DrawBox(GL_LINE_LOOP, self.x, self.y, self.width, self.height)
itemhgt = self.itemhgt
pad = self.pad
x = self.x
y = self.y + self.height - itemhgt - pad
if self.state == State_InArray:
self.array.Draw()
return
plist = []
self.buts = []
for p in self.group.iteritems():
plist.append(p)
#-------do top buttons----------#
Draw.BeginAlign()
Draw.PushButton("New", Button_New, x, y, 40, 20)
x += 40 + pad
#do the menu button for all materials
st = ""
blocks = IDTypeModules[self.IDList][0].Get()
i = 1
mi = 0
for m in blocks:
if m.name == self.idblock.name:
mi = i
st += m.name + " %x" + str(i) + "|"
i += 1
self.menubut = Draw.Menu(st, Button_MatMenu, x, y, 100, 20, mi)
x += 100 + pad
st = ""
i = 0
for e in IDTypeModules:
st += e[2] + " %x" + str(i+1) + "|"
i += 1
cur = self.IDList + 1
self.idmenu = Draw.Menu(st, Button_TypeMenu, x, y, 100, 20, cur)
x = self.x
y -= self.itemhgt + self.pad
Draw.EndAlign()
#-----------do property items---------#
i = 0
while y > self.y - 20 - pad and i < len(plist):
k = plist[i][0]
p = plist[i][1]
if i == self.active_item:
glColor3f(0.5, 0.4, 0.3)
self.DrawBox(GL_POLYGON, x+pad, y, self.width-pad*2, itemhgt)
glColor3f(0, 0, 0)
self.DrawBox(GL_LINE_LOOP, x+pad, y, self.width-pad*2, itemhgt)
glRasterPos2f(x+pad*2, y+5)
Draw.Text(str(k)) #str(self.mousecursor) + " " + str(self.active_item)) #p.name)
tlen = Draw.GetStringWidth(str(k))
type_p = type(p)
if type_p == str:
b = Draw.String("", ButStart+i, x+pad*5+tlen, y, 200, itemhgt, p, strmax)
self.buts.append(b)
elif type_p in [int, float]:
#only do precision to 5 points on floats
st = ""
if type_p == float:
st = "%.5f" % p
else: st = str(p)
b = Draw.String("", ButStart+i, x+pad*5+tlen, y, 75, itemhgt, st, strmax)
self.buts.append(b)
else:
glRasterPos2f(x+pad*2 +tlen+10, y+5)
if type_p == Types.IDArrayType:
Draw.Text('(array, click to edit)')
elif type_p == Types.IDGroupType:
Draw.Text('(group, click to edit)')
self.buts.append(None)
Draw.PushButton("Del", ButDelStart+i, x+self.width-35, y, 30, 20)
i += 1
y -= self.itemhgt + self.pad
if len(self.parents) != 0:
Draw.PushButton("Back", Button_Back, x, y, 40, 20)
x = x + 40 + pad
def SetActive(self):
m = self.mousecursor
itemhgt = self.itemhgt
pad = self.pad
x = self.x + pad
y = self.y + self.height - itemhgt - pad - itemhgt
plist = []
for p in self.group.iteritems():
plist.append(p)
self.active_item = -1
i = 0
while y > self.y and i < len(plist):
p = plist[i]
if IsInRectWH(m[0], m[1], x, y, self.width-pad, itemhgt):
self.active_item = i
i += 1
y -= self.itemhgt + self.pad
def EventIn(self, evt, val):
if self.state == State_InArray:
self.array.Evt(evt, val)
if evt == Draw.ESCKEY:
Draw.Exit()
if evt == Draw.MOUSEX or evt == Draw.MOUSEY:
size = Buffer(GL_FLOAT, 4)
glGetFloatv(GL_SCISSOR_BOX, size)
if evt == Draw.MOUSEX:
self.mousecursor[0] = val - size[0]
else:
self.mousecursor[1] = val - size[1]
del size
self.SetActive()
self._i += 1
if self._i == 5:
Draw.Draw()
self._i = 0
if evt == Draw.LEFTMOUSE and val == 1:
plist = list(self.group.iteritems())
a = self.active_item
if a >= 0 and a < len(plist):
p = plist[a]
basictypes = [IDGroupType, float, str, int]
if type(p[1]) == IDGroupType:
self.parents.append(self.group)
self.group = p[1]
self.active_item = -1
Draw.Draw()
elif type(p[1]) == IDArrayType:
self.array = IDArrayBrowser()
self.array.array = p[1]
self.array.parentbrowser = self
self.state = State_InArray
Draw.Draw()
if evt == Draw.TKEY and val == 1:
try:
self.prop['float'] = 0.0
self.prop['int'] = 1
self.prop['string'] = "hi!"
self.prop['float array'] = [0, 0, 1.0, 0]
self.prop['int array'] = [0, 0, 0, 0]
self.prop.data['a subgroup'] = {"int": 0, "float": 0.0, "anothergroup": {"a": 0.0, "intarr": [0, 0, 0, 0]}}
Draw.Draw()
except:
Draw.PupMenu("Can only do T once per block, the test names are already taken!")
def Button(self, bval):
global IDTypeModules
if self.state == State_InArray:
self.array.Button(bval)
return
if bval == Button_MatMenu:
global IDTypeModules
val = self.idindex = self.menubut.val - 1
i = self.IDList
block = IDTypeModules[i][0].Get()[val]
self.idblock = block
self.prop = block.properties
self.group = self.prop
self.active_item = -1
self.parents = []
Draw.Draw()
if bval == Button_TypeMenu:
i = IDTypeModules[self.idmenu.val-1]
if len(i[0].Get()) == 0:
Draw.PupMenu("Error%t|There are no " + i[2] + "!")
return
IDTypeModules[self.IDList][1] = self.idindex
self.IDList = self.idmenu.val-1
val = self.idindex = IDTypeModules[self.IDList][1]
i = self.IDList
block = IDTypeModules[i][0].Get()[val]
self.idblock = block
self.prop = block.properties
self.group = self.prop
self.active_item = -1
self.parents = []
Draw.Draw()
if bval >= ButDelStart:
plist = [p for p in self.group]
prop = plist[bval - ButDelStart]
del self.group[prop]
Draw.Draw()
elif bval >= ButStart:
plist = list(self.group.iteritems())
prop = plist[bval - ButStart]
print prop
if self.type(prop[1]) == str:
self.group[prop[0]] = self.buts[bval - ButStart].val
elif self.type(prop[1]) == int:
i = self.buts[bval - ButStart].val
try:
i = int(i)
self.group[prop[0]] = i
except:
Draw.Draw()
return
Draw.Draw()
elif self.type(prop[1]) == float:
f = self.buts[bval - ButStart].val
try:
f = float(f)
self.group[prop[0]] = f
except:
Draw.Draw()
return
Draw.Draw()
elif bval == Button_Back:
self.group = self.parents[len(self.parents)-1]
self.parents.pop(len(self.parents)-1)
Draw.Draw()
elif bval == Button_New:
name = Draw.Create("untitled")
stype = Draw.Create(0)
gtype = Draw.Create(0)
ftype = Draw.Create(0)
itype = Draw.Create(0)
atype = Draw.Create(0)
block = []
block.append(("Name: ", name, 0, 30, "Click to type in the name of the new ID property"))
block.append("Type")
block.append(("String", stype))
block.append(("Subgroup", gtype))
block.append(("Float", ftype))
block.append(("Int", itype))
block.append(("Array", atype))
retval = Blender.Draw.PupBlock("New IDProperty", block)
if retval == 0: return
name = name.val
i = 1
stop = 0
while stop == 0:
stop = 1
for p in self.group:
if p == name:
d = name.rfind(".")
if d != -1:
name = name[:d]
name = name + "." + str(i).zfill(3)
i += 1
stop = 0
type = "String"
if stype.val:
self.group[name] = ""
elif gtype.val:
self.group[name] = {}
elif ftype.val:
self.group[name] = 0.0
elif itype.val:
self.group[name] = 0 #newProperty("Int", name, 0)
elif atype.val:
arrfloat = Draw.Create(1)
arrint = Draw.Create(0)
arrlen = Draw.Create(3)
block = []
block.append("Type")
block.append(("Float", arrfloat, "Make a float array"))
block.append(("Int", arrint, "Make an integer array"))
block.append(("Len", arrlen, 2, 200))
if Blender.Draw.PupBlock("Array Properties", block):
if arrfloat.val:
tmpl = 0.0
elif arrint.val:
tmpl = 0
else:
return
self.group[name] = [tmpl] * arrlen.val
def Go(self):
Draw.Register(self.Draw, self.EventIn, self.Button)
scenes = Scene.Get()
size = Window.GetAreaSize()
browser = IDPropertyBrowser(scenes[0].properties, scenes[0], 2, 2, size[0], size[1])
browser.Go()
#a = prop.newProperty("String", "hwello!", "bleh")
#b = prop.newProperty("Group", "subgroup")
#for p in prop:
#print p.name

View File

@@ -0,0 +1,828 @@
#!BPY
""" Registration info for Blender menus:
Name: 'AC3D (.ac)...'
Blender: 243
Group: 'Export'
Tip: 'Export selected meshes to AC3D (.ac) format'
"""
__author__ = "Willian P. Germano"
__url__ = ("blender", "blenderartists.org", "AC3D's homepage, http://www.ac3d.org",
"PLib 3d gaming lib, http://plib.sf.net")
__version__ = "2.44 2007-05-05"
__bpydoc__ = """\
This script exports selected Blender meshes to AC3D's .ac file format.
AC3D is a simple commercial 3d modeller also built with OpenGL.
The .ac file format is an easy to parse text format well supported,
for example, by the PLib 3d gaming library (AC3D 3.x).
Supported:<br>
UV-textured meshes with hierarchy (grouping) information.
Missing:<br>
The 'url' tag, specific to AC3D. It is easy to add by hand to the exported
file, if needed.
Known issues:<br>
The ambient and emit data we can retrieve from Blender are single values,
that this script copies to R, G, B, giving shades of gray.<br>
Loose edges (lines) receive the first material found in the mesh, if any, or a default white material.<br>
In AC3D 4 "compatibility mode":<br>
- shininess of materials is taken from the shader specularity value in Blender, mapped from [0.0, 2.0] to [0, 128];<br>
- crease angle is exported, but in Blender it is limited to [1, 80], since there are other more powerful ways to control surface smoothing. In AC3D 4.0 crease's range is [0.0, 180.0];
Config Options:<br>
toggle:<br>
- AC3D 4 mode: unset it to export without the 'crease' tag that was
introduced with AC3D 4.0 and with the old material handling;<br>
- global coords: transform all vertices of all meshes to global coordinates;<br>
- skip data: set it if you don't want mesh names (ME:, not OB: field)
to be exported as strings for AC's "data" tags (19 chars max);<br>
- rgb mirror color can be exported as ambient and/or emissive if needed,
since Blender handles these differently;<br>
- default mat: a default (white) material is added if some mesh was
left without mats -- it's better to always add your own materials;<br>
- no split: don't split meshes (see above);<br>
- set texture dir: override the actual textures path with a given default
path (or simply export the texture names, without dir info, if the path is
empty);<br>
- per face 1 or 2 sided: override the "Double Sided" button that defines this behavior per whole mesh in favor of the UV Face Select mode "twosided" per face atribute;<br>
- only selected: only consider selected objects when looking for meshes
to export (read notes below about tokens, too);<br>
strings:<br>
- export dir: default dir to export to;<br>
- texture dir: override textures path with this path if 'set texture dir'
toggle is "on".
Notes:<br>
This version updates:<br>
- modified meshes are correctly exported, no need to apply the modifiers in Blender;<br>
- correctly export each used material, be it assigned to the object or to its mesh data;<br>
- exporting lines (edges) is again supported; color comes from first material found in the mesh, if any, or a default white one.<br>
- there's a new option to choose between exporting meshes with transformed (global) coordinates or local ones;<br>
Multiple textures per mesh are supported (mesh gets split);<br>
Parents are exported as a group containing both the parent and its children;<br>
Start mesh object names (OB: field) with "!" or "#" if you don't want them to be exported;<br>
Start mesh object names (OB: field) with "=" or "$" to prevent them from being split (meshes with multiple textures or both textured and non textured faces are split unless this trick is used or the "no split" option is set.
"""
# $Id: ac3d_export.py 14530 2008-04-23 14:04:05Z campbellbarton $
#
# --------------------------------------------------------------------------
# AC3DExport version 2.44
# Program versions: Blender 2.42+ and AC3Db files (means version 0xb)
# new: updated for new Blender version and Mesh module; supports lines (edges) again;
# option to export vertices transformed to global coordinates or not; now the modified
# (by existing mesh modifiers) mesh is exported; materials are properly exported, no
# matter if each of them is linked to the mesh or to the object. New (2.43.1): loose
# edges use color of first material found in the mesh, if any.
# --------------------------------------------------------------------------
# Thanks: Steve Baker for discussions and inspiration; for testing, bug
# reports, suggestions, patches: David Megginson, Filippo di Natale,
# Franz Melchior, Campbell Barton, Josh Babcock, Ralf Gerlich, Stewart Andreason.
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2004-2007: Willian P. Germano, wgermano _at_ ig.com.br
#
# 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,
# --------------------------------------------------------------------------
import Blender
from Blender import Object, Mesh, Material, Image, Mathutils, Registry
from Blender import sys as bsys
# Globals
REPORT_DATA = {
'main': [],
'errors': [],
'warns': [],
'nosplit': [],
'noexport': []
}
TOKENS_DONT_EXPORT = ['!', '#']
TOKENS_DONT_SPLIT = ['=', '$']
MATIDX_ERROR = 0
# flags:
LOOSE = Mesh.EdgeFlags['LOOSE']
FACE_TWOSIDED = Mesh.FaceModes['TWOSIDE']
MESH_TWOSIDED = Mesh.Modes['TWOSIDED']
REG_KEY = 'ac3d_export'
# config options:
GLOBAL_COORDS = True
SKIP_DATA = False
MIRCOL_AS_AMB = False
MIRCOL_AS_EMIS = False
ADD_DEFAULT_MAT = True
SET_TEX_DIR = True
TEX_DIR = ''
AC3D_4 = True # export crease value, compatible with AC3D 4 loaders
NO_SPLIT = False
ONLY_SELECTED = True
EXPORT_DIR = ''
PER_FACE_1_OR_2_SIDED = True
tooltips = {
'GLOBAL_COORDS': "transform all vertices of all meshes to global coordinates",
'SKIP_DATA': "don't export mesh names as data fields",
'MIRCOL_AS_AMB': "export mirror color as ambient color",
'MIRCOL_AS_EMIS': "export mirror color as emissive color",
'ADD_DEFAULT_MAT': "always add a default white material",
'SET_TEX_DIR': "don't export default texture paths (edit also \"tex dir\")",
'EXPORT_DIR': "default / last folder used to export .ac files to",
'TEX_DIR': "(see \"set tex dir\") dir to prepend to all exported texture names (leave empty for no dir)",
'AC3D_4': "compatibility mode, adds 'crease' tag and slightly better material support",
'NO_SPLIT': "don't split meshes with multiple textures (or both textured and non textured polygons)",
'ONLY_SELECTED': "export only selected objects",
'PER_FACE_1_OR_2_SIDED': "override \"Double Sided\" button in favor of per face \"twosided\" attribute (UV Face Select mode)"
}
def update_RegistryInfo():
d = {}
d['SKIP_DATA'] = SKIP_DATA
d['MIRCOL_AS_AMB'] = MIRCOL_AS_AMB
d['MIRCOL_AS_EMIS'] = MIRCOL_AS_EMIS
d['ADD_DEFAULT_MAT'] = ADD_DEFAULT_MAT
d['SET_TEX_DIR'] = SET_TEX_DIR
d['TEX_DIR'] = TEX_DIR
d['AC3D_4'] = AC3D_4
d['NO_SPLIT'] = NO_SPLIT
d['EXPORT_DIR'] = EXPORT_DIR
d['ONLY_SELECTED'] = ONLY_SELECTED
d['PER_FACE_1_OR_2_SIDED'] = PER_FACE_1_OR_2_SIDED
d['tooltips'] = tooltips
d['GLOBAL_COORDS'] = GLOBAL_COORDS
Registry.SetKey(REG_KEY, d, True)
# Looking for a saved key in Blender.Registry dict:
rd = Registry.GetKey(REG_KEY, True)
if rd:
try:
AC3D_4 = rd['AC3D_4']
SKIP_DATA = rd['SKIP_DATA']
MIRCOL_AS_AMB = rd['MIRCOL_AS_AMB']
MIRCOL_AS_EMIS = rd['MIRCOL_AS_EMIS']
ADD_DEFAULT_MAT = rd['ADD_DEFAULT_MAT']
SET_TEX_DIR = rd['SET_TEX_DIR']
TEX_DIR = rd['TEX_DIR']
EXPORT_DIR = rd['EXPORT_DIR']
ONLY_SELECTED = rd['ONLY_SELECTED']
NO_SPLIT = rd['NO_SPLIT']
PER_FACE_1_OR_2_SIDED = rd['PER_FACE_1_OR_2_SIDED']
GLOBAL_COORDS = rd['GLOBAL_COORDS']
except KeyError: update_RegistryInfo()
else:
update_RegistryInfo()
VERBOSE = True
CONFIRM_OVERWRITE = True
# check General scripts config key for default behaviors
rd = Registry.GetKey('General', True)
if rd:
try:
VERBOSE = rd['verbose']
CONFIRM_OVERWRITE = rd['confirm_overwrite']
except: pass
# The default material to be used when necessary (see ADD_DEFAULT_MAT)
DEFAULT_MAT = \
'MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 \
spec 0.5 0.5 0.5 shi 64 trans 0'
# This transformation aligns Blender and AC3D coordinate systems:
BLEND_TO_AC3D_MATRIX = Mathutils.Matrix([1,0,0,0], [0,0,-1,0], [0,1,0,0], [0,0,0,1])
def Round_s(f):
"Round to default precision and turn value to a string"
r = round(f,6) # precision set to 10e-06
if r == int(r):
return str(int(r))
else:
return str(r)
def transform_verts(verts, m):
vecs = []
for v in verts:
x, y, z = v.co
vec = Mathutils.Vector([x, y, z, 1])
vecs.append(vec*m)
return vecs
def get_loose_edges(mesh):
loose = LOOSE
return [e for e in mesh.edges if e.flag & loose]
# ---
# meshes with more than one texture assigned
# are split and saved as these foomeshes
class FooMesh:
class FooVert:
def __init__(self, v):
self.v = v
self.index = 0
class FooFace:
def __init__(self, foomesh, f):
self.f = f
foov = foomesh.FooVert
self.v = [foov(f.v[0]), foov(f.v[1])]
len_fv = len(f.v)
if len_fv > 2 and f.v[2]:
self.v.append(foov(f.v[2]))
if len_fv > 3 and f.v[3]: self.v.append(foov(f.v[3]))
def __getattr__(self, attr):
if attr == 'v': return self.v
return getattr(self.f, attr)
def __len__(self):
return len(self.f)
def __init__(self, tex, faces, mesh):
self.name = mesh.name
self.mesh = mesh
self.looseEdges = []
self.faceUV = mesh.faceUV
self.degr = mesh.degr
vidxs = [0]*len(mesh.verts)
foofaces = []
for f in faces:
foofaces.append(self.FooFace(self, f))
for v in f.v:
if v: vidxs[v.index] = 1
i = 0
fooverts = []
for v in mesh.verts:
if vidxs[v.index]:
fooverts.append(v)
vidxs[v.index] = i
i += 1
for f in foofaces:
for v in f.v:
if v: v.index = vidxs[v.v.index]
self.faces = foofaces
self.verts = fooverts
class AC3DExport: # the ac3d exporter part
def __init__(self, scene_objects, file):
global ARG, SKIP_DATA, ADD_DEFAULT_MAT, DEFAULT_MAT
header = 'AC3Db'
self.file = file
self.buf = ''
self.mbuf = []
self.mlist = []
world_kids = 0
parents_list = self.parents_list = []
kids_dict = self.kids_dict = {}
objs = []
exp_objs = self.exp_objs = []
tree = {}
file.write(header+'\n')
objs = \
[o for o in scene_objects if o.type in ['Mesh', 'Empty']]
# create a tree from parents to children objects
for obj in objs[:]:
parent = obj.parent
lineage = [obj]
while parent:
parents_list.append(parent.name)
obj = parent
parent = parent.getParent()
lineage.insert(0, obj)
d = tree
for i in xrange(len(lineage)):
lname = lineage[i].getType()[:2] + lineage[i].name
if lname not in d.keys():
d[lname] = {}
d = d[lname]
# traverse the tree to get an ordered list of names of objects to export
self.traverse_dict(tree)
world_kids = len(tree.keys())
# get list of objects to export, start writing the .ac file
objlist = [Object.Get(name) for name in exp_objs]
meshlist = [o for o in objlist if o.type == 'Mesh']
# create a temporary mesh to hold actual (modified) mesh data
TMP_mesh = Mesh.New('tmp_for_ac_export')
# write materials
self.MATERIALS(meshlist, TMP_mesh)
mbuf = self.mbuf
if not mbuf or ADD_DEFAULT_MAT:
mbuf.insert(0, "%s\n" % DEFAULT_MAT)
mbuf = "".join(mbuf)
file.write(mbuf)
file.write('OBJECT world\nkids %s\n' % world_kids)
# write the objects
for obj in objlist:
self.obj = obj
objtype = obj.type
objname = obj.name
kidsnum = kids_dict[objname]
# A parent plus its children are exported as a group.
# If the parent is a mesh, its rot and loc are exported as the
# group rot and loc and the mesh (w/o rot and loc) is added to the group.
if kidsnum:
self.OBJECT('group')
self.name(objname)
if objtype == 'Mesh':
kidsnum += 1
if not GLOBAL_COORDS:
localmatrix = obj.getMatrix('localspace')
if not obj.getParent():
localmatrix *= BLEND_TO_AC3D_MATRIX
self.rot(localmatrix.rotationPart())
self.loc(localmatrix.translationPart())
self.kids(kidsnum)
if objtype == 'Mesh':
mesh = TMP_mesh # temporary mesh to hold actual (modified) mesh data
mesh.getFromObject(objname)
self.mesh = mesh
if mesh.faceUV:
meshes = self.split_mesh(mesh)
else:
meshes = [mesh]
if len(meshes) > 1:
if NO_SPLIT or self.dont_split(objname):
self.export_mesh(mesh, ob)
REPORT_DATA['nosplit'].append(objname)
else:
self.OBJECT('group')
self.name(objname)
self.kids(len(meshes))
counter = 0
for me in meshes:
self.export_mesh(me, obj,
name = '%s_%s' % (obj.name, counter), foomesh = True)
self.kids()
counter += 1
else:
self.export_mesh(mesh, obj)
self.kids()
def traverse_dict(self, d):
kids_dict = self.kids_dict
exp_objs = self.exp_objs
keys = d.keys()
keys.sort() # sort for predictable output
keys.reverse()
for k in keys:
objname = k[2:]
klen = len(d[k])
kids_dict[objname] = klen
if self.dont_export(objname):
d.pop(k)
parent = Object.Get(objname).getParent()
if parent: kids_dict[parent.name] -= 1
REPORT_DATA['noexport'].append(objname)
continue
if klen:
self.traverse_dict(d[k])
exp_objs.insert(0, objname)
else:
if k.find('Em', 0) == 0: # Empty w/o children
d.pop(k)
parent = Object.Get(objname).getParent()
if parent: kids_dict[parent.name] -= 1
else:
exp_objs.insert(0, objname)
def dont_export(self, name): # if name starts with '!' or '#'
length = len(name)
if length >= 1:
if name[0] in TOKENS_DONT_EXPORT: # '!' or '#' doubled (escaped): export
if length > 1 and name[1] == name[0]:
return 0
return 1
def dont_split(self, name): # if name starts with '=' or '$'
length = len(name)
if length >= 1:
if name[0] in TOKENS_DONT_SPLIT: # '=' or '$' doubled (escaped): split
if length > 1 and name[1] == name[0]:
return 0
return 1
def split_mesh(self, mesh):
tex_dict = {0:[]}
for f in mesh.faces:
if f.image:
if not f.image.name in tex_dict: tex_dict[f.image.name] = []
tex_dict[f.image.name].append(f)
else: tex_dict[0].append(f)
keys = tex_dict.keys()
len_keys = len(keys)
if not tex_dict[0]:
len_keys -= 1
tex_dict.pop(0)
keys.remove(0)
elif len_keys > 1:
lines = []
anyimgkey = [k for k in keys if k != 0][0]
for f in tex_dict[0]:
if len(f.v) < 3:
lines.append(f)
if len(tex_dict[0]) == len(lines):
for l in lines:
tex_dict[anyimgkey].append(l)
len_keys -= 1
tex_dict.pop(0)
if len_keys > 1:
foo_meshes = []
for k in keys:
faces = tex_dict[k]
foo_meshes.append(FooMesh(k, faces, mesh))
foo_meshes[0].edges = get_loose_edges(mesh)
return foo_meshes
return [mesh]
def export_mesh(self, mesh, obj, name = None, foomesh = False):
file = self.file
self.OBJECT('poly')
if not name: name = obj.name
self.name(name)
if not SKIP_DATA:
meshname = obj.getData(name_only = True)
self.data(len(meshname), meshname)
if mesh.faceUV:
texline = self.texture(mesh.faces)
if texline: file.write(texline)
if AC3D_4:
self.crease(mesh.degr)
# If exporting using local coordinates, children object coordinates should not be
# transformed to ac3d's coordinate system, since that will be accounted for in
# their topmost parents (the parents w/o parents) transformations.
if not GLOBAL_COORDS:
# We hold parents in a list, so they also don't get transformed,
# because for each parent we create an ac3d group to hold both the
# parent and its children.
if obj.name not in self.parents_list:
localmatrix = obj.getMatrix('localspace')
if not obj.getParent():
localmatrix *= BLEND_TO_AC3D_MATRIX
self.rot(localmatrix.rotationPart())
self.loc(localmatrix.translationPart())
matrix = None
else:
matrix = obj.getMatrix() * BLEND_TO_AC3D_MATRIX
self.numvert(mesh.verts, matrix)
self.numsurf(mesh, foomesh)
def MATERIALS(self, meshlist, me):
for meobj in meshlist:
me.getFromObject(meobj)
mats = me.materials
mbuf = []
mlist = self.mlist
for m in mats:
if not m: continue
name = m.name
if name not in mlist:
mlist.append(name)
M = Material.Get(name)
material = 'MATERIAL "%s"' % name
mirCol = "%s %s %s" % (Round_s(M.mirCol[0]), Round_s(M.mirCol[1]),
Round_s(M.mirCol[2]))
rgb = "rgb %s %s %s" % (Round_s(M.R), Round_s(M.G), Round_s(M.B))
ambval = Round_s(M.amb)
amb = "amb %s %s %s" % (ambval, ambval, ambval)
spec = "spec %s %s %s" % (Round_s(M.specCol[0]),
Round_s(M.specCol[1]), Round_s(M.specCol[2]))
if AC3D_4:
emit = Round_s(M.emit)
emis = "emis %s %s %s" % (emit, emit, emit)
shival = int(M.spec * 64)
else:
emis = "emis 0 0 0"
shival = 72
shi = "shi %s" % shival
trans = "trans %s" % (Round_s(1 - M.alpha))
if MIRCOL_AS_AMB:
amb = "amb %s" % mirCol
if MIRCOL_AS_EMIS:
emis = "emis %s" % mirCol
mbuf.append("%s %s %s %s %s %s %s\n" \
% (material, rgb, amb, emis, spec, shi, trans))
self.mlist = mlist
self.mbuf.append("".join(mbuf))
def OBJECT(self, type):
self.file.write('OBJECT %s\n' % type)
def name(self, name):
if name[0] in TOKENS_DONT_EXPORT or name[0] in TOKENS_DONT_SPLIT:
if len(name) > 1: name = name[1:]
self.file.write('name "%s"\n' % name)
def kids(self, num = 0):
self.file.write('kids %s\n' % num)
def data(self, num, str):
self.file.write('data %s\n%s\n' % (num, str))
def texture(self, faces):
tex = ""
for f in faces:
if f.image:
tex = f.image.name
break
if tex:
image = Image.Get(tex)
texfname = image.filename
if SET_TEX_DIR:
texfname = bsys.basename(texfname)
if TEX_DIR:
texfname = bsys.join(TEX_DIR, texfname)
buf = 'texture "%s"\n' % texfname
xrep = image.xrep
yrep = image.yrep
buf += 'texrep %s %s\n' % (xrep, yrep)
self.file.write(buf)
def rot(self, matrix):
rot = ''
not_I = 0 # not identity
matstr = []
for i in [0, 1, 2]:
r = map(Round_s, matrix[i])
not_I += (r[0] != '0')+(r[1] != '0')+(r[2] != '0')
not_I -= (r[i] == '1')
for j in [0, 1, 2]:
matstr.append(' %s' % r[j])
if not_I: # no need to write identity
self.file.write('rot%s\n' % "".join(matstr))
def loc(self, loc):
loc = map(Round_s, loc)
if loc != ['0', '0', '0']: # no need to write default
self.file.write('loc %s %s %s\n' % (loc[0], loc[1], loc[2]))
def crease(self, crease):
self.file.write('crease %f\n' % crease)
def numvert(self, verts, matrix):
file = self.file
nvstr = []
nvstr.append("numvert %s\n" % len(verts))
if matrix:
verts = transform_verts(verts, matrix)
for v in verts:
v = map (Round_s, v)
nvstr.append("%s %s %s\n" % (v[0], v[1], v[2]))
else:
for v in verts:
v = map(Round_s, v.co)
nvstr.append("%s %s %s\n" % (v[0], v[1], v[2]))
file.write("".join(nvstr))
def numsurf(self, mesh, foomesh = False):
global MATIDX_ERROR
# local vars are faster and so better in tight loops
lc_ADD_DEFAULT_MAT = ADD_DEFAULT_MAT
lc_MATIDX_ERROR = MATIDX_ERROR
lc_PER_FACE_1_OR_2_SIDED = PER_FACE_1_OR_2_SIDED
lc_FACE_TWOSIDED = FACE_TWOSIDED
lc_MESH_TWOSIDED = MESH_TWOSIDED
faces = mesh.faces
hasFaceUV = mesh.faceUV
if foomesh:
looseEdges = mesh.looseEdges
else:
looseEdges = get_loose_edges(mesh)
file = self.file
file.write("numsurf %s\n" % (len(faces) + len(looseEdges)))
if not foomesh: verts = list(self.mesh.verts)
materials = self.mesh.materials
mlist = self.mlist
matidx_error_reported = False
objmats = []
for omat in materials:
if omat: objmats.append(omat.name)
else: objmats.append(None)
for f in faces:
if not objmats:
m_idx = 0
elif objmats[f.mat] in mlist:
m_idx = mlist.index(objmats[f.mat])
else:
if not lc_MATIDX_ERROR:
rdat = REPORT_DATA['warns']
rdat.append("Object %s" % self.obj.name)
rdat.append("has at least one material *index* assigned but not")
rdat.append("defined (not linked to an existing material).")
rdat.append("Result: some faces may be exported with a wrong color.")
rdat.append("You can assign materials in the Edit Buttons window (F9).")
elif not matidx_error_reported:
midxmsg = "- Same for object %s." % self.obj.name
REPORT_DATA['warns'].append(midxmsg)
lc_MATIDX_ERROR += 1
matidx_error_reported = True
m_idx = 0
if lc_ADD_DEFAULT_MAT: m_idx -= 1
refs = len(f)
flaglow = 0 # polygon
if lc_PER_FACE_1_OR_2_SIDED and hasFaceUV: # per face attribute
two_side = f.mode & lc_FACE_TWOSIDED
else: # global, for the whole mesh
two_side = self.mesh.mode & lc_MESH_TWOSIDED
two_side = (two_side > 0) << 1
flaghigh = f.smooth | two_side
surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow)
if lc_ADD_DEFAULT_MAT and objmats: m_idx += 1
matstr = "mat %s\n" % m_idx
refstr = "refs %s\n" % refs
u, v, vi = 0, 0, 0
fvstr = []
if foomesh:
for vert in f.v:
fvstr.append(str(vert.index))
if hasFaceUV:
u = f.uv[vi][0]
v = f.uv[vi][1]
vi += 1
fvstr.append(" %s %s\n" % (u, v))
else:
for vert in f.v:
fvstr.append(str(verts.index(vert)))
if hasFaceUV:
u = f.uv[vi][0]
v = f.uv[vi][1]
vi += 1
fvstr.append(" %s %s\n" % (u, v))
fvstr = "".join(fvstr)
file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr))
# material for loose edges
edges_mat = 0 # default to first material
for omat in objmats: # but look for a material from this mesh
if omat in mlist:
edges_mat = mlist.index(omat)
if lc_ADD_DEFAULT_MAT: edges_mat += 1
break
for e in looseEdges:
fvstr = []
#flaglow = 2 # 1 = closed line, 2 = line
#flaghigh = 0
#surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow)
surfstr = "SURF 0x02\n"
fvstr.append("%d 0 0\n" % verts.index(e.v1))
fvstr.append("%d 0 0\n" % verts.index(e.v2))
fvstr = "".join(fvstr)
matstr = "mat %d\n" % edges_mat # for now, use first material
refstr = "refs 2\n" # 2 verts
file.write("%s%s%s%s" % (surfstr, matstr, refstr, fvstr))
MATIDX_ERROR = lc_MATIDX_ERROR
# End of Class AC3DExport
from Blender.Window import FileSelector
def report_data():
global VERBOSE
if not VERBOSE: return
d = REPORT_DATA
msgs = {
'0main': '%s\nExporting meshes to AC3D format' % str(19*'-'),
'1warns': 'Warnings',
'2errors': 'Errors',
'3nosplit': 'Not split (because name starts with "=" or "$")',
'4noexport': 'Not exported (because name starts with "!" or "#")'
}
if NO_SPLIT:
l = msgs['3nosplit']
l = "%s (because OPTION NO_SPLIT is set)" % l.split('(')[0]
msgs['3nosplit'] = l
keys = msgs.keys()
keys.sort()
for k in keys:
msgk = msgs[k]
msg = '\n'.join(d[k[1:]])
if msg:
print '\n-%s:' % msgk
print msg
# File Selector callback:
def fs_callback(filename):
global EXPORT_DIR, OBJS, CONFIRM_OVERWRITE, VERBOSE
if not filename.endswith('.ac'): filename = '%s.ac' % filename
if bsys.exists(filename) and CONFIRM_OVERWRITE:
if Blender.Draw.PupMenu('OVERWRITE?%t|File exists') != 1:
return
Blender.Window.WaitCursor(1)
starttime = bsys.time()
export_dir = bsys.dirname(filename)
if export_dir != EXPORT_DIR:
EXPORT_DIR = export_dir
update_RegistryInfo()
try:
file = open(filename, 'w')
except IOError, (errno, strerror):
error = "IOError #%s: %s" % (errno, strerror)
REPORT_DATA['errors'].append("Saving failed - %s." % error)
error_msg = "Couldn't save file!%%t|%s" % error
Blender.Draw.PupMenu(error_msg)
return
try:
test = AC3DExport(OBJS, file)
except:
file.close()
raise
else:
file.close()
endtime = bsys.time() - starttime
REPORT_DATA['main'].append("Done. Saved to: %s" % filename)
REPORT_DATA['main'].append("Data exported in %.3f seconds." % endtime)
if VERBOSE: report_data()
Blender.Window.WaitCursor(0)
# -- End of definitions
scn = Blender.Scene.GetCurrent()
if ONLY_SELECTED:
OBJS = list(scn.objects.context)
else:
OBJS = list(scn.objects)
if not OBJS:
Blender.Draw.PupMenu('ERROR: no objects selected')
else:
fname = bsys.makename(ext=".ac")
if EXPORT_DIR:
fname = bsys.join(EXPORT_DIR, bsys.basename(fname))
FileSelector(fs_callback, "Export AC3D", fname)

View File

@@ -0,0 +1,783 @@
#!BPY
""" Registration info for Blender menus:
Name: 'AC3D (.ac)...'
Blender: 243
Group: 'Import'
Tip: 'Import an AC3D (.ac) file.'
"""
__author__ = "Willian P. Germano"
__url__ = ("blender", "blenderartists.org", "AC3D's homepage, http://www.ac3d.org",
"PLib 3d gaming lib, http://plib.sf.net")
__version__ = "2.48.1 2009-01-11"
__bpydoc__ = """\
This script imports AC3D models into Blender.
AC3D is a simple and affordable commercial 3d modeller also built with OpenGL.
The .ac file format is an easy to parse text format well supported,
for example, by the PLib 3d gaming library.
Supported:<br>
UV-textured meshes with hierarchy (grouping) information.
Missing:<br>
The url tag is irrelevant for Blender.
Known issues:<br>
- Some objects may be imported with wrong normals due to wrong information in the model itself. This can be noticed by strange shading, like darker than expected parts in the model. To fix this, select the mesh with wrong normals, enter edit mode and tell Blender to recalculate the normals, either to make them point outside (the usual case) or inside.<br>
Config Options:<br>
- display transp (toggle): if "on", objects that have materials with alpha < 1.0 are shown with translucency (transparency) in the 3D View.<br>
- subdiv (toggle): if "on", ac3d objects meant to be subdivided receive a SUBSURF modifier in Blender.<br>
- emis as mircol: store the emissive rgb color from AC3D as mirror color in Blender -- this is a hack to preserve the values and be able to export them using the equivalent option in the exporter.<br>
- textures dir (string): if non blank, when imported texture paths are
wrong in the .ac file, Blender will also look for them at this dir.
Notes:<br>
- When looking for assigned textures, Blender tries in order: the actual
paths from the .ac file, the .ac file's dir and the default textures dir path
users can configure (see config options above).
"""
# $Id: ac3d_import.py 18451 2009-01-11 16:17:41Z ianwill $
#
# --------------------------------------------------------------------------
# AC3DImport version 2.43.1 Feb 21, 2007
# Program versions: Blender 2.43 and AC3Db files (means version 0xb)
# changed: better triangulation of ngons, more fixes to support bad .ac files,
# option to display transp mats in 3d view, support "subdiv" tag (via SUBSURF modifier)
# --------------------------------------------------------------------------
# Thanks: Melchior Franz for extensive bug testing and reporting, making this
# version cope much better with old or bad .ac files, among other improvements;
# Stewart Andreason for reporting a serious crash; Francesco Brisa for the
# emis as mircol functionality (w/ patch).
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2004-2009: Willian P. Germano, wgermano _at_ ig.com.br
#
# 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 math import radians
import Blender
from Blender import Scene, Object, Mesh, Lamp, Registry, sys as bsys, Window, Image, Material, Modifier
from Blender.sys import dirsep
from Blender.Mathutils import Vector, Matrix, Euler
from Blender.Geometry import PolyFill
# Default folder for AC3D textures, to override wrong paths, change to your
# liking or leave as "":
TEXTURES_DIR = ""
DISPLAY_TRANSP = True
SUBDIV = True
EMIS_AS_MIRCOL = False
tooltips = {
'DISPLAY_TRANSP': 'Turn transparency on in the 3d View for objects using materials with alpha < 1.0.',
'SUBDIV': 'Apply a SUBSURF modifier to objects meant to appear subdivided.',
'TEXTURES_DIR': 'Additional folder to look for missing textures.',
'EMIS_AS_MIRCOL': 'Store emis color as mirror color in Blender.'
}
def update_registry():
global TEXTURES_DIR, DISPLAY_TRANSP, EMIS_AS_MIRCOL
rd = dict([('tooltips', tooltips), ('TEXTURES_DIR', TEXTURES_DIR), ('DISPLAY_TRANSP', DISPLAY_TRANSP), ('SUBDIV', SUBDIV), ('EMIS_AS_MIRCOL', EMIS_AS_MIRCOL)])
Registry.SetKey('ac3d_import', rd, True)
rd = Registry.GetKey('ac3d_import', True)
if rd:
if 'GROUP' in rd:
update_registry()
try:
TEXTURES_DIR = rd['TEXTURES_DIR']
DISPLAY_TRANSP = rd['DISPLAY_TRANSP']
SUBDIV = rd['SUBDIV']
EMIS_AS_MIRCOL = rd['EMIS_AS_MIRCOL']
except:
update_registry()
else: update_registry()
if TEXTURES_DIR:
oldtexdir = TEXTURES_DIR
if dirsep == '/': TEXTURES_DIR = TEXTURES_DIR.replace('\\', '/')
if TEXTURES_DIR[-1] != dirsep: TEXTURES_DIR = "%s%s" % (TEXTURES_DIR, dirsep)
if oldtexdir != TEXTURES_DIR: update_registry()
VERBOSE = True
rd = Registry.GetKey('General', True)
if rd:
if rd.has_key('verbose'):
VERBOSE = rd['verbose']
errmsg = ""
# Matrix to align ac3d's coordinate system with Blender's one,
# it's a -90 degrees rotation around the x axis:
AC_TO_BLEND_MATRIX = Matrix([1, 0, 0], [0, 0, 1], [0, -1, 0])
AC_WORLD = 0
AC_GROUP = 1
AC_POLY = 2
AC_LIGHT = 3
AC_OB_TYPES = {
'world': AC_WORLD,
'group': AC_GROUP,
'poly': AC_POLY,
'light': AC_LIGHT
}
AC_OB_BAD_TYPES_LIST = [] # to hold references to unknown (wrong) ob types
def inform(msg):
global VERBOSE
if VERBOSE: print msg
def euler_in_radians(eul):
"Used while there's a bug in the BPY API"
eul.x = radians(eul.x)
eul.y = radians(eul.y)
eul.z = radians(eul.z)
return eul
class Obj:
def __init__(self, type):
self.type = type
self.dad = None
self.name = ''
self.data = ''
self.tex = ''
self.texrep = [1,1]
self.texoff = None
self.loc = []
self.rot = []
self.size = []
self.crease = 30
self.subdiv = 0
self.vlist = []
self.flist_cfg = []
self.flist_v = []
self.flist_uv = []
self.elist = []
self.matlist = []
self.kids = 0
self.bl_obj = None # the actual Blender object created from this data
class AC3DImport:
def __init__(self, filename):
global errmsg
self.scene = Scene.GetCurrent()
self.i = 0
errmsg = ''
self.importdir = bsys.dirname(filename)
try:
file = open(filename, 'r')
except IOError, (errno, strerror):
errmsg = "IOError #%s: %s" % (errno, strerror)
Blender.Draw.PupMenu('ERROR: %s' % errmsg)
inform(errmsg)
return None
header = file.read(5)
header, version = header[:4], header[-1]
if header != 'AC3D':
file.close()
errmsg = 'AC3D header not found (invalid file)'
Blender.Draw.PupMenu('ERROR: %s' % errmsg)
inform(errmsg)
return None
elif version != 'b':
inform('AC3D file version 0x%s.' % version)
inform('This importer is for version 0xb, so it may fail.')
self.token = {'OBJECT': self.parse_obj,
'numvert': self.parse_vert,
'numsurf': self.parse_surf,
'name': self.parse_name,
'data': self.parse_data,
'kids': self.parse_kids,
'loc': self.parse_loc,
'rot': self.parse_rot,
'MATERIAL': self.parse_mat,
'texture': self.parse_tex,
'texrep': self.parse_texrep,
'texoff': self.parse_texoff,
'subdiv': self.parse_subdiv,
'crease': self.parse_crease}
self.objlist = []
self.mlist = []
self.kidsnumlist = []
self.dad = None
self.lines = file.readlines()
self.lines.append('')
self.parse_file()
file.close()
self.testAC3DImport()
def parse_obj(self, value):
kidsnumlist = self.kidsnumlist
if kidsnumlist:
while not kidsnumlist[-1]:
kidsnumlist.pop()
if kidsnumlist:
self.dad = self.dad.dad
else:
inform('Ignoring unexpected data at end of file.')
return -1 # bad file with more objects than reported
kidsnumlist[-1] -= 1
if value in AC_OB_TYPES:
new = Obj(AC_OB_TYPES[value])
else:
if value not in AC_OB_BAD_TYPES_LIST:
AC_OB_BAD_TYPES_LIST.append(value)
inform('Unexpected object type keyword: "%s". Assuming it is of type: "poly".' % value)
new = Obj(AC_OB_TYPES['poly'])
new.dad = self.dad
new.name = value
self.objlist.append(new)
def parse_kids(self, value):
kids = int(value)
if kids:
self.kidsnumlist.append(kids)
self.dad = self.objlist[-1]
self.objlist[-1].kids = kids
def parse_name(self, value):
name = value.split('"')[1]
self.objlist[-1].name = name
def parse_data(self, value):
data = self.lines[self.i].strip()
self.objlist[-1].data = data
def parse_tex(self, value):
line = self.lines[self.i - 1] # parse again to properly get paths with spaces
texture = line.split('"')[1]
self.objlist[-1].tex = texture
def parse_texrep(self, trash):
trep = self.lines[self.i - 1]
trep = trep.split()
trep = [float(trep[1]), float(trep[2])]
self.objlist[-1].texrep = trep
self.objlist[-1].texoff = [0, 0]
def parse_texoff(self, trash):
toff = self.lines[self.i - 1]
toff = toff.split()
toff = [float(toff[1]), float(toff[2])]
self.objlist[-1].texoff = toff
def parse_mat(self, value):
i = self.i - 1
lines = self.lines
line = lines[i].split()
mat_name = ''
mat_col = mat_amb = mat_emit = mat_spec_col = mat_mir_col = [0,0,0]
mat_alpha = 1
mat_spec = 1.0
while line[0] == 'MATERIAL':
mat_name = line[1].split('"')[1]
mat_col = map(float,[line[3],line[4],line[5]])
v = map(float,[line[7],line[8],line[9]])
mat_amb = (v[0]+v[1]+v[2]) / 3.0
v = map(float,[line[11],line[12],line[13]])
mat_emit = (v[0]+v[1]+v[2]) / 3.0
if EMIS_AS_MIRCOL:
mat_emit = 0
mat_mir_col = map(float,[line[11],line[12],line[13]])
mat_spec_col = map(float,[line[15],line[16],line[17]])
mat_spec = float(line[19]) / 64.0
mat_alpha = float(line[-1])
mat_alpha = 1 - mat_alpha
self.mlist.append([mat_name, mat_col, mat_amb, mat_emit, mat_spec_col, mat_spec, mat_mir_col, mat_alpha])
i += 1
line = lines[i].split()
self.i = i
def parse_rot(self, trash):
i = self.i - 1
ob = self.objlist[-1]
rot = self.lines[i].split(' ', 1)[1]
rot = map(float, rot.split())
matrix = Matrix(rot[:3], rot[3:6], rot[6:])
ob.rot = matrix
size = matrix.scalePart() # vector
ob.size = size
def parse_loc(self, trash):
i = self.i - 1
loc = self.lines[i].split(' ', 1)[1]
loc = map(float, loc.split())
self.objlist[-1].loc = Vector(loc)
def parse_crease(self, value):
# AC3D: range is [0.0, 180.0]; Blender: [1, 80]
value = float(value)
self.objlist[-1].crease = int(value)
def parse_subdiv(self, value):
self.objlist[-1].subdiv = int(value)
def parse_vert(self, value):
i = self.i
lines = self.lines
obj = self.objlist[-1]
vlist = obj.vlist
n = int(value)
while n:
line = lines[i].split()
line = map(float, line)
vlist.append(line)
n -= 1
i += 1
if vlist: # prepend a vertex at 1st position to deal with vindex 0 issues
vlist.insert(0, line)
self.i = i
def parse_surf(self, value):
i = self.i
is_smooth = 0
double_sided = 0
lines = self.lines
obj = self.objlist[-1]
vlist = obj.vlist
matlist = obj.matlist
numsurf = int(value)
NUMSURF = numsurf
badface_notpoly = badface_multirefs = 0
while numsurf:
flags = lines[i].split()[1][2:]
if len(flags) > 1:
flaghigh = int(flags[0])
flaglow = int(flags[1])
else:
flaghigh = 0
flaglow = int(flags[0])
is_smooth = flaghigh & 1
twoside = flaghigh & 2
nextline = lines[i+1].split()
if nextline[0] != 'mat': # the "mat" line may be missing (found in one buggy .ac file)
matid = 0
if not matid in matlist: matlist.append(matid)
i += 2
else:
matid = int(nextline[1])
if not matid in matlist: matlist.append(matid)
nextline = lines[i+2].split()
i += 3
refs = int(nextline[1])
face = []
faces = []
edges = []
fuv = []
fuvs = []
rfs = refs
while rfs:
line = lines[i].split()
v = int(line[0]) + 1 # + 1 to avoid vindex == 0
uv = [float(line[1]), float(line[2])]
face.append(v)
fuv.append(Vector(uv))
rfs -= 1
i += 1
if flaglow: # it's a line or closed line, not a polygon
while len(face) >= 2:
cut = face[:2]
edges.append(cut)
face = face[1:]
if flaglow == 1 and edges: # closed line
face = [edges[-1][-1], edges[0][0]]
edges.append(face)
else: # polygon
# check for bad face, that references same vertex more than once
lenface = len(face)
if lenface < 3:
# less than 3 vertices, not a face
badface_notpoly += 1
elif sum(map(face.count, face)) != lenface:
# multiple references to the same vertex
badface_multirefs += 1
else: # ok, seems fine
if len(face) > 4: # ngon, triangulate it
polyline = []
for vi in face:
polyline.append(Vector(vlist[vi]))
tris = PolyFill([polyline])
for t in tris:
tri = [face[t[0]], face[t[1]], face[t[2]]]
triuvs = [fuv[t[0]], fuv[t[1]], fuv[t[2]]]
faces.append(tri)
fuvs.append(triuvs)
else: # tri or quad
faces.append(face)
fuvs.append(fuv)
obj.flist_cfg.extend([[matid, is_smooth, twoside]] * len(faces))
obj.flist_v.extend(faces)
obj.flist_uv.extend(fuvs)
obj.elist.extend(edges) # loose edges
numsurf -= 1
if badface_notpoly or badface_multirefs:
inform('Object "%s" - ignoring bad faces:' % obj.name)
if badface_notpoly:
inform('\t%d face(s) with less than 3 vertices.' % badface_notpoly)
if badface_multirefs:
inform('\t%d face(s) with multiple references to a same vertex.' % badface_multirefs)
self.i = i
def parse_file(self):
i = 1
lines = self.lines
line = lines[i].split()
while line:
kw = ''
for k in self.token.keys():
if line[0] == k:
kw = k
break
i += 1
if kw:
self.i = i
result = self.token[kw](line[1])
if result:
break # bad .ac file, stop parsing
i = self.i
line = lines[i].split()
# for each group of meshes we try to find one that can be used as
# parent of the group in Blender.
# If not found, we can use an Empty as parent.
def found_parent(self, groupname, olist):
l = [o for o in olist if o.type == AC_POLY \
and not o.kids and not o.rot and not o.loc]
if l:
for o in l:
if o.name == groupname:
return o
#return l[0]
return None
def build_hierarchy(self):
blmatrix = AC_TO_BLEND_MATRIX
olist = self.objlist[1:]
olist.reverse()
scene = self.scene
newlist = []
for o in olist:
kids = o.kids
if kids:
children = newlist[-kids:]
newlist = newlist[:-kids]
if o.type == AC_GROUP:
parent = self.found_parent(o.name, children)
if parent:
children.remove(parent)
o.bl_obj = parent.bl_obj
else: # not found, use an empty
empty = scene.objects.new('Empty', o.name)
o.bl_obj = empty
bl_children = [c.bl_obj for c in children if c.bl_obj != None]
o.bl_obj.makeParent(bl_children, 0, 1)
for child in children:
blob = child.bl_obj
if not blob: continue
if child.rot:
eul = euler_in_radians(child.rot.toEuler())
blob.setEuler(eul)
if child.size:
blob.size = child.size
if not child.loc:
child.loc = Vector(0.0, 0.0, 0.0)
blob.setLocation(child.loc)
newlist.append(o)
for o in newlist: # newlist now only has objs w/o parents
blob = o.bl_obj
if not blob:
continue
if o.size:
o.bl_obj.size = o.size
if not o.rot:
blob.setEuler([1.5707963267948966, 0, 0])
else:
matrix = o.rot * blmatrix
eul = euler_in_radians(matrix.toEuler())
blob.setEuler(eul)
if o.loc:
o.loc *= blmatrix
else:
o.loc = Vector(0.0, 0.0, 0.0)
blob.setLocation(o.loc) # forces DAG update, so we do it even for 0, 0, 0
# XXX important: until we fix the BPy API so it doesn't increase user count
# when wrapping a Blender object, this piece of code is needed for proper
# object (+ obdata) deletion in Blender:
for o in self.objlist:
if o.bl_obj:
o.bl_obj = None
def testAC3DImport(self):
FACE_TWOSIDE = Mesh.FaceModes['TWOSIDE']
FACE_TEX = Mesh.FaceModes['TEX']
MESH_AUTOSMOOTH = Mesh.Modes['AUTOSMOOTH']
MAT_MODE_ZTRANSP = Material.Modes['ZTRANSP']
MAT_MODE_TRANSPSHADOW = Material.Modes['TRANSPSHADOW']
scene = self.scene
bl_images = {} # loaded texture images
missing_textures = [] # textures we couldn't find
objlist = self.objlist[1:] # skip 'world'
bmat = []
has_transp_mats = False
for mat in self.mlist:
name = mat[0]
m = Material.New(name)
m.rgbCol = (mat[1][0], mat[1][1], mat[1][2])
m.amb = mat[2]
m.emit = mat[3]
m.specCol = (mat[4][0], mat[4][1], mat[4][2])
m.spec = mat[5]
m.mirCol = (mat[6][0], mat[6][1], mat[6][2])
m.alpha = mat[7]
if m.alpha < 1.0:
m.mode |= MAT_MODE_ZTRANSP
has_transp_mats = True
bmat.append(m)
if has_transp_mats:
for mat in bmat:
mat.mode |= MAT_MODE_TRANSPSHADOW
obj_idx = 0 # index of current obj in loop
for obj in objlist:
if obj.type == AC_GROUP:
continue
elif obj.type == AC_LIGHT:
light = Lamp.New('Lamp')
object = scene.objects.new(light, obj.name)
#object.select(True)
obj.bl_obj = object
if obj.data:
light.name = obj.data
continue
# type AC_POLY:
# old .ac files used empty meshes as groups, convert to a real ac group
if not obj.vlist and obj.kids:
obj.type = AC_GROUP
continue
mesh = Mesh.New()
object = scene.objects.new(mesh, obj.name)
#object.select(True)
obj.bl_obj = object
if obj.data: mesh.name = obj.data
mesh.degr = obj.crease # will auto clamp to [1, 80]
if not obj.vlist: # no vertices? nothing more to do
continue
mesh.verts.extend(obj.vlist)
objmat_indices = []
for mat in bmat:
if bmat.index(mat) in obj.matlist:
objmat_indices.append(bmat.index(mat))
mesh.materials += [mat]
if DISPLAY_TRANSP and mat.alpha < 1.0:
object.transp = True
for e in obj.elist:
mesh.edges.extend(e)
if obj.flist_v:
mesh.faces.extend(obj.flist_v)
facesnum = len(mesh.faces)
if facesnum == 0: # shouldn't happen, of course
continue
mesh.faceUV = True
# checking if the .ac file had duplicate faces (Blender ignores them)
if facesnum != len(obj.flist_v):
# it has, ugh. Let's clean the uv list:
lenfl = len(obj.flist_v)
flist = obj.flist_v
uvlist = obj.flist_uv
cfglist = obj.flist_cfg
for f in flist:
f.sort()
fi = lenfl
while fi > 0: # remove data related to duplicates
fi -= 1
if flist[fi] in flist[:fi]:
uvlist.pop(fi)
cfglist.pop(fi)
img = None
if obj.tex != '':
if obj.tex in bl_images.keys():
img = bl_images[obj.tex]
elif obj.tex not in missing_textures:
texfname = None
objtex = obj.tex
baseimgname = bsys.basename(objtex)
if bsys.exists(objtex) == 1:
texfname = objtex
elif bsys.exists(bsys.join(self.importdir, objtex)):
texfname = bsys.join(self.importdir, objtex)
else:
if baseimgname.find('\\') > 0:
baseimgname = bsys.basename(objtex.replace('\\','/'))
objtex = bsys.join(self.importdir, baseimgname)
if bsys.exists(objtex) == 1:
texfname = objtex
else:
objtex = bsys.join(TEXTURES_DIR, baseimgname)
if bsys.exists(objtex):
texfname = objtex
if texfname:
try:
img = Image.Load(texfname)
# Commented because it's unnecessary:
#img.xrep = int(obj.texrep[0])
#img.yrep = int(obj.texrep[1])
if img:
bl_images[obj.tex] = img
except:
inform("Couldn't load texture: %s" % baseimgname)
else:
missing_textures.append(obj.tex)
inform("Couldn't find texture: %s" % baseimgname)
for i in range(facesnum):
f = obj.flist_cfg[i]
fmat = f[0]
is_smooth = f[1]
twoside = f[2]
bface = mesh.faces[i]
bface.smooth = is_smooth
if twoside: bface.mode |= FACE_TWOSIDE
if img:
bface.mode |= FACE_TEX
bface.image = img
bface.mat = objmat_indices.index(fmat)
fuv = obj.flist_uv[i]
if obj.texoff:
uoff = obj.texoff[0]
voff = obj.texoff[1]
urep = obj.texrep[0]
vrep = obj.texrep[1]
for uv in fuv:
uv[0] *= urep
uv[1] *= vrep
uv[0] += uoff
uv[1] += voff
mesh.faces[i].uv = fuv
# finally, delete the 1st vertex we added to prevent vindices == 0
mesh.verts.delete(0)
mesh.calcNormals()
mesh.mode = MESH_AUTOSMOOTH
# subdiv: create SUBSURF modifier in Blender
if SUBDIV and obj.subdiv > 0:
subdiv = obj.subdiv
subdiv_render = subdiv
# just to be safe:
if subdiv_render > 6: subdiv_render = 6
if subdiv > 3: subdiv = 3
modif = object.modifiers.append(Modifier.Types.SUBSURF)
modif[Modifier.Settings.LEVELS] = subdiv
modif[Modifier.Settings.RENDLEVELS] = subdiv_render
obj_idx += 1
self.build_hierarchy()
scene.update()
# End of class AC3DImport
def filesel_callback(filename):
inform("\nTrying to import AC3D model(s) from:\n%s ..." % filename)
Window.WaitCursor(1)
starttime = bsys.time()
test = AC3DImport(filename)
Window.WaitCursor(0)
endtime = bsys.time() - starttime
inform('Done! Data imported in %.3f seconds.\n' % endtime)
Window.EditMode(0)
Window.FileSelector(filesel_callback, "Import AC3D", "*.ac")

View File

@@ -0,0 +1,13 @@
#!BPY
"""
Name: 'Empty mesh'
Blender: 243
Group: 'AddMesh'
"""
import BPyAddMesh
import Blender
def main():
BPyAddMesh.add_mesh_simple('EmptyMesh', [], [], [])
main()

View File

@@ -0,0 +1,69 @@
#!BPY
"""
Name: 'Torus'
Blender: 243
Group: 'AddMesh'
"""
import BPyAddMesh
import Blender
try: from math import cos, sin, pi
except: math = None
def add_torus(PREF_MAJOR_RAD, PREF_MINOR_RAD, PREF_MAJOR_SEG, PREF_MINOR_SEG):
Vector = Blender.Mathutils.Vector
RotationMatrix = Blender.Mathutils.RotationMatrix
verts = []
faces = []
i1 = 0
tot_verts = PREF_MAJOR_SEG * PREF_MINOR_SEG
for major_index in xrange(PREF_MAJOR_SEG):
verts_tmp = []
mtx = RotationMatrix( 360 * float(major_index)/PREF_MAJOR_SEG, 3, 'z' )
for minor_index in xrange(PREF_MINOR_SEG):
angle = 2*pi*minor_index/PREF_MINOR_SEG
verts.append( Vector(PREF_MAJOR_RAD+(cos(angle)*PREF_MINOR_RAD), 0, (sin(angle)*PREF_MINOR_RAD)) * mtx )
if minor_index+1==PREF_MINOR_SEG:
i2 = (major_index)*PREF_MINOR_SEG
i3 = i1 + PREF_MINOR_SEG
i4 = i2 + PREF_MINOR_SEG
else:
i2 = i1 + 1
i3 = i1 + PREF_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
faces.append( (i3,i4,i2,i1) )
i1+=1
return verts, faces
def main():
Draw = Blender.Draw
PREF_MAJOR_RAD = Draw.Create(1.0)
PREF_MINOR_RAD = Draw.Create(0.25)
PREF_MAJOR_SEG = Draw.Create(48)
PREF_MINOR_SEG = Draw.Create(16)
if not Draw.PupBlock('Add Torus', [\
('Major Radius:', PREF_MAJOR_RAD, 0.01, 100, 'Radius for the main ring of the torus'),\
('Minor Radius:', PREF_MINOR_RAD, 0.01, 100, 'Radius for the minor ring of the torus setting the thickness of the ring'),\
('Major Segments:', PREF_MAJOR_SEG, 3, 256, 'Number of segments for the main ring of the torus'),\
('Minor Segments:', PREF_MINOR_SEG, 3, 256, 'Number of segments for the minor ring of the torus'),\
]):
return
verts, faces = add_torus(PREF_MAJOR_RAD.val, PREF_MINOR_RAD.val, PREF_MAJOR_SEG.val, PREF_MINOR_SEG.val)
BPyAddMesh.add_mesh_simple('Torus', verts, [], faces)
if cos and sin and pi:
main()
else:
Blender.Draw.PupMenu("Error%t|This script requires a full python installation")

View File

@@ -0,0 +1,792 @@
#!BPY
"""
Name: 'Bake Constraints'
Blender: 246
Group: 'Animation'
Tooltip: 'Bake a Constrained object/rig to IPOs'
Fillename: 'Bake_Constraint.py'
"""
__author__ = "Roger Wickes (rogerwickes(at)yahoo.com)"
__script__ = "Animation Bake Constraints"
__version__ = "0.7"
__url__ = ["Communicate problems and errors, http://www.blenderartists.com/forum/private.php?do=newpm to PapaSmurf"]
__email__= ["Roger Wickes, rogerwickes@yahoo.com", "scripts"]
__bpydoc__ = """\
bake_constraints
This script bakes the real-world LocRot of an object (the net effect of any constraints -
(Copy, Limit, Track, Follow, - that affect Location, Rotation)
(usually one constrained to match another's location and/or Tracked to another)
and creates a clone with a set of Ipo Curves named Ipo<objname>
These curves control a non-constrained object and thus make it mimic the constrained object
Actions can be then be edited without the need for the drivers/constraining objects
Developed for use with MoCap data, where a bone is constrained to point at an empty
moving through space and time. This records the actual locrot of the armature
so that the motion can be edited, reoriented, scaled, and used as NLA Actions
see also wiki Scripts/Manual/ Tutorial/Motion Capture <br>
Usage: <br>
- Select the reference Object(s) you want to bake <br>
- Set the frame range to bake in the Anim Panel <br>
- Set the test code (if you want a self-test) in the RT field in the Anim Panel <br>
-- Set RT:1 to create a test armature <br>
-- Set RT: up to 100 for more debug messages and status updates <br>
<br>
- Run the script <br>
- The clone copy of the object is created and it has an IPO curve assigned to it. <br>
- The clone shadows the object by an offset locrot (see usrDelta) <br>
- That Object has Ipo Location and Rotation curves that make the clone mimic the movement <br>
of the selected object, but without using constraints. <br>
- If the object was an Armature, the clone's bones move identically in relation to the <br>
original armature, and an Action is created that drives the bone movements. <br>
Version History:
0.1: bakes Loc Rot for a constrained object
0.2: bakes Loc and Rot for the bones within Armature object
0.3: UI for setting options
0.3.1 add manual to script library
0.4: bake multiple objects
0.5: root bone worldspace rotation
0.6: re-integration with BPyArmature
0.7: bakes parents and leaves clones selected
License, Copyright, and Attribution:
by Roger WICKES May 2008, released under Blender Artistic Licence to Public Domain
feel free to add to any Blender Python Scripts Bundle.
Thanks to Jean-Baptiste PERIN, IdeasMan42 (Campbell Barton), Basil_Fawlty/Cage_drei (Andrew Cruse)
much lifted/learned from blender.org/documentation/245PytonDoc and wiki
some modules based on c3D_Import.py, PoseLib16.py and IPO/Armature code examples e.g. camera jitter
Pseudocode:
Initialize
If at least one object is selected
For each selected object,
create a cloned object
remove any constraints on the clone
create or reset an ipo curve named like the object
for each frame
set the clone's locrot key based on the reference object
if it's an armature,
create an action (which is an Ipo for each bone)
for each frame of the animation
for each bone in the armature
set the key
Else you're a smurf
Test Conditions and Regressions:
1. (v0.1) Non-armatures (the cube), with ipo curve and constraints at the object level
2. armatures, with ipo curve and constraints at the object level
3. armatures, with bones that have ipo curves and constraints
4. objects without parents, children with unselected parents, select children first.
Naming conventions:
arm = a specific objec type armature
bone = bones that make up the skeleton of an armature
ob = object, an instance of an object type
ebone = edit bone, a bone in edit mode
pbone = pose bone, a posed bone in an object
tst = testing, self-test routines
usr = user-entered or designated stuff
"""
########################################
import Blender
from Blender import *
from Blender.Mathutils import *
import struct
import string
import bpy
import BPyMessages
import BPyArmature
# reload(BPyArmature)
from BPyArmature import getBakedPoseData
Vector= Blender.Mathutils.Vector
Euler= Blender.Mathutils.Euler
Matrix= Blender.Mathutils.Matrix #invert() function at least
RotationMatrix = Blender.Mathutils.RotationMatrix
TranslationMatrix= Blender.Mathutils.TranslationMatrix
Quaternion = Blender.Mathutils.Quaternion
Vector = Blender.Mathutils.Vector
POSE_XFORM= [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT]
#=================
# Global Variables
#=================
# set senstitivity for displaying debug/console messages. 0=none, 100=max
# then call debug(num,string) to conditionally display status/info in console window
MODE=Blender.Get('rt') #execution mode: 0=run normal, 1=make test armature
DEBUG=Blender.Get('rt') #how much detail on internal processing for user to see. range 0-100
BATCH=False #called from command line? is someone there? Would you like some cake?
#there are two coordinate systems, the real, or absolute 3D space,
# and the local relative to a parent.
COORDINATE_SYSTEMS = ['local','real']
COORD_LOCAL = 0
COORD_REAL = 1
# User Settings - Change these options manually or via GUI (future TODO)
usrCoord = COORD_REAL # what the user wants
usrParent = False # True=clone keeps original parent, False = clone's parent is the clone of the original parent (if cloned)
usrFreeze = 2 #2=yes, 0=no. Freezes shadow object in place at current frame as origin
# delta is amount to offset/change from the reference object. future set in a ui, so technically not a constant
usrDelta = [10,10,0,0,0,0] #order specific - Loc xyz Rot xyz
usrACTION = True # Offset baked Action frames to start at frame 1
CURFRAME = 'curframe' #keyword to use when getting the frame number that the scene is presently on
ARMATURE = 'Armature' #en anglais
BONE_SPACES = ['ARMATURESPACE','BONESPACE']
# 'ARMATURESPACE' - this matrix of the bone in relation to the armature
# 'BONESPACE' - the matrix of the bone in relation to itself
#Ipo curves created are prefixed with a name, like Ipo_ or Bake_ followed by the object/bone name
#bakedArmName = "b." #used for both the armature class and object instance
usrObjectNamePrefix= ""
#ipoBoneNamePrefix = ""
# for example, if on entry an armature named Man was selected, and the object prefix was "a."
# on exit an armature and an IPO curve named a.Man exists for the object as a whole
# if that armature had bones (spine, neck, arm) and the bone prefix was "a."
# the bones and IPO curves will be (a.spine, a.neck, a.arm)
R2D = 18/3.141592653589793 # radian to grad
BLENDER_VERSION = Blender.Get('version')
# Gets the current scene, there can be many scenes in 1 blend file.
scn = Blender.Scene.GetCurrent()
#=================
# Methods
#=================
########################################
def debug(num,msg): #use log4j or just console here.
if DEBUG >= num:
if BATCH == False:
print 'debug: '[:num/10+7]+msg
#TODO: else write out to file (runs faster if it doesnt have to display details)
return
########################################
def error(str):
debug(0,'ERROR: '+str)
if BATCH == False:
Draw.PupMenu('ERROR%t|'+str)
return
########################################
def getRenderInfo():
context=scn.getRenderingContext()
staframe = context.startFrame()
endframe = context.endFrame()
if endframe<staframe: endframe=staframe
curframe = Blender.Get(CURFRAME)
debug(90,'Scene is on frame %i and frame range is %i to %i' % (curframe,staframe,endframe))
return (staframe,endframe,curframe)
########################################
def sortObjects(obs): #returns a list of objects sorted based on parent dependency
obClones= []
while len(obClones) < len(obs):
for ob in obs:
if not ob in obClones:
par= ob.getParent()
#if no parent, or the parent is not scheduled to be cloned
if par==None:
obClones.append(ob) # add the independent
elif par not in obs: # parent will not be cloned
obClones.append(ob) # add the child
elif par in obClones: # is it on the list?
obClones.append(ob) # add the child
# parent may be a child, so it will be caught next time thru
debug(100,'clone object order: \n%s' % obClones)
return obClones # ordered list of (ob, par) tuples
########################################
def sortBones(xbones): #returns a sorted list of bones that should be added,sorted based on parent dependency
# while there are bones to add,
# look thru the list of bones we need to add
# if we have not already added this bone
# if it does not have a parent
# add it
# else, it has a parent
# if we already added it's parent
# add it now.
# else #we need to keep cycling and catch its parent
# else it is a root bone
# add it
# else skip it, it's already in there
# endfor
# endwhile
xboneNames=[]
for xbone in xbones: xboneNames.append(xbone.name)
debug (80,'reference bone order: \n%s' % xboneNames)
eboneNames=[]
while len(eboneNames) < len(xboneNames):
for xbone in xbones:
if not xbone.name in eboneNames:
if not xbone.parent:
eboneNames.append(xbone.name)
else:
if xbone.parent.name in eboneNames:
eboneNames.append(xbone.name)
#else skip it
#endif
#else prego
#endfor
#endwhile
debug (80,'clone bone order: \n%s' % eboneNames)
return eboneNames
########################################
def dupliArmature(ob): #makes a copy in current scn of the armature used by ob and its bones
ob_mat = ob.matrixWorld
ob_data = ob.getData()
debug(49,'Reference object uses %s' % ob_data)
arm_ob = Armature.Get(ob_data.name) #the armature used by the passed object
arm = Blender.Armature.New()
debug(20,'Cloning Armature %s to create %s' % (arm_ob.name, arm.name))
arm.drawType = Armature.STICK #set the draw type
arm.makeEditable() #enter editmode
# for each bone in the object's armature,
xbones=ob.data.bones.values()
usrSpace = 0 #0=armature, 1=local
space=[BONE_SPACES[usrSpace]][0]
#we have to make a list of bones, then figure out our parents, then add to the arm
#when creating a child, we cannot link to a parent if it does not yet exist in our armature
ebones = [] #list of the bones I want to create for my arm
eboneNames = sortBones(xbones)
i=0
# error('bones sorted. continue?')
for abone in eboneNames: #set all editable attributes to fully define the bone.
for bone in xbones:
if bone.name == abone: break # get the reference bone
ebone = Armature.Editbone() #throw me a bone, bone-man!
ebones.append(ebone) #you're on my list, buddy
ebone.name = bone.name
ebone.headRadius = bone.headRadius
ebone.tailRadius = bone.tailRadius
ebone.weight = bone.weight
ebone.options = bone.options
ebone.head = bone.head[space] #dictionary lookups
ebone.tail = bone.tail[space]
ebone.matrix = bone.matrix[space]
ebone.roll = bone.roll[space]
debug(30,'Generating new %s as child of %s' % (bone,bone.parent))
if bone.hasParent():
# parent=bone.parent.name
# debug(100,'looking for %s' % parent)
# for parbone in xbones: if parbone.name == parent: break # get the parent bone
# ebone.parent = arm.bones[ebones[j].name]
ebone.parent = arm.bones[bone.parent.name]
# else:
# ebone.parent = None
debug(30,'Generating new editbone %s as child of %s' % (ebone,ebone.parent))
arm.bones[ebone.name] = ebone # i would have expected an append or add function, but this works
debug (100,'arm.bones: \n%s' % arm.bones)
debug (20,'Cloned %i bones now in armature %s' %(len(arm.bones),arm.name))
myob = scn.objects.new(arm) #interestingly, object must be created before
arm.update() #armature can be saved
debug(40,'dupArm finished %s instanced as object %s' % (arm.name,myob.getName()))
print ob.matrix
print myob.matrix
return myob
########################################
def scrub(): # scrubs to startframe
staFrame,endFrame,curFrame = getRenderInfo()
# eye-candy, go from current to start, fwd or back
if not BATCH:
debug(100, "Positioning to start...")
frameinc=(staFrame-curFrame)/10
if abs(frameinc) >= 1:
for i in range(10):
curFrame+=frameinc
Blender.Set(CURFRAME,curFrame) # computes the constrained location of the 'real' objects
Blender.Redraw()
Blender.Set(CURFRAME, staFrame)
return
########################################
def bakeBones(ref_ob,arm_ob): #copy pose from ref_ob to arm_ob
scrub()
staFrame,endFrame,curFrame = getRenderInfo()
act = getBakedPoseData(ref_ob, staFrame, endFrame, ACTION_BAKE = True, ACTION_BAKE_FIRST_FRAME = usrACTION) # bake the pose positions of the reference ob to the armature ob
arm_ob.action = act
scrub()
# user comprehension feature - change action name and channel ipo names to match the names of the bone they drive
debug (80,'Renaming each action ipo to match the bone they pose')
act.name = arm_ob.name
arm_channels = act.getAllChannelIpos()
pose= arm_ob.getPose()
pbones= pose.bones.values() #we want the bones themselves, not the dictionary lookup
for pbone in pbones:
debug (100,'Channel listing for %s: %s' % (pbone.name,arm_channels[pbone.name] ))
ipo=arm_channels[pbone.name]
ipo.name = pbone.name # since bone names are unique within an armature, the pose names can be the same since they are within an Action
return
########################################
def getOrCreateCurve(ipo, curvename):
"""
Retrieve or create a Blender Ipo Curve named C{curvename} in the C{ipo} Ipo
Either an ipo curve named C{curvename} exists before the call then this curve is returned,
Or such a curve doesn't exist before the call .. then it is created into the c{ipo} Ipo and returned
"""
try:
mycurve = ipo.getCurve(curvename)
if mycurve != None:
pass
else:
mycurve = ipo.addCurve(curvename)
except:
mycurve = ipo.addCurve(curvename)
return mycurve
########################################
def eraseCurve(ipo,numCurves):
debug(90,'Erasing %i curves for %' % (numCurves,ipo.GetName()))
for i in range(numCurves):
nbBezPoints= ipo.getNBezPoints(i)
for j in range(nbBezPoints):
ipo.delBezPoint(i)
return
########################################
def resetIPO(ipo):
debug(60,'Resetting ipo curve named %s' %ipo.name)
numCurves = ipo.getNcurves() #like LocX, LocY, etc
if numCurves > 0:
eraseCurve(ipo, numCurves) #erase data if one exists
return
########################################
def resetIPOs(ob): #resets all IPO curvess assocated with an object and its bones
debug(30,'Resetting any ipo curves linked to %s' %ob.getName())
ipo = ob.getIpo() #may be None
ipoName = ipo.getName() #name of the IPO that guides/controls this object
debug(70,'Object IPO is %s' %ipoName)
try:
ipo = Ipo.Get(ipoName)
except:
ipo = Ipo.New('Object', ipoName)
resetIPO(ipo)
if ob.getType() == ARMATURE:
arm_data=ob.getData()
bones=arm_data.bones.values()
for bone in bones:
#for each bone: get the name and check for a Pose IPO
debug(10,'Processing '+ bone.name)
return
########################################
def parse(string,delim):
index = string.find(delim) # -1 if not found, else pointer to delim
if index+1: return string[:index]
return string
########################################
def newIpo(ipoName): #add a new Ipo object to the Blender scene
ipo=Blender.Ipo.New('Object',ipoName)
ipo.addCurve('LocX')
ipo.addCurve('LocY')
ipo.addCurve('LocZ')
ipo.addCurve('RotX')
ipo.addCurve('RotY')
ipo.addCurve('RotZ')
return ipo
########################################
def makeUpaName(type,name): #i know this exists in Blender somewhere...
debug(90,'Making up a new %s name using %s as a basis.' % (type,name))
name = (parse(name,'.'))
if type == 'Ipo':
ipoName = name # maybe we get lucky today
ext = 0
extlen = 3 # 3 digit extensions, like hello.002
success = False
while not(success):
try:
debug(100,'Trying %s' % ipoName)
ipo = Ipo.Get(ipoName)
#that one exists if we get here. add on extension and keep trying
ext +=1
if ext>=10**extlen: extlen +=1 # go to more digits if 999 not found
ipoName = '%s.%s' % (name, str(ext).zfill(extlen))
except: # could not find it
success = True
name=ipoName
else:
debug (0,'FATAL ERROR: I dont know how to make up a new %s name based on %s' % (type,ob))
return None
return name
########################################
def createIpo(ob): #create an Ipo and curves and link them to this object
#first, we have to create a unique name
#try first with just the name of the object to keep things simple.
ipoName = makeUpaName('Ipo',ob.getName()) # make up a name for a new Ipo based on the object name
debug(20,'Ipo and LocRot curves called %s' % ipoName)
ipo=newIpo(ipoName)
ob.setIpo(ipo) #link them
return ipo
########################################
def getLocLocal(ob):
key = [
ob.LocX,
ob.LocY,
ob.LocZ,
ob.RotX*R2D, #get the curves in this order
ob.RotY*R2D,
ob.RotZ*R2D
]
return key
########################################
def getLocReal(ob):
obMatrix = ob.matrixWorld #Thank you IdeasMan42
loc = obMatrix.translationPart()
rot = obMatrix.toEuler()
key = [
loc.x,
loc.y,
loc.z,
rot.x/10,
rot.y/10,
rot.z/10
]
return key
########################################
def getLocRot(ob,space):
if space in xrange(len(COORDINATE_SYSTEMS)):
if space == COORD_LOCAL:
key = getLocLocal(ob)
return key
elif space == COORD_REAL:
key = getLocReal(ob)
return key
else: #hey, programmers make mistakes too.
debug(0,'Fatal Error: getLoc called with %i' % space)
return
########################################
def getCurves(ipo):
ipos = [
ipo[Ipo.OB_LOCX],
ipo[Ipo.OB_LOCY],
ipo[Ipo.OB_LOCZ],
ipo[Ipo.OB_ROTX], #get the curves in this order
ipo[Ipo.OB_ROTY],
ipo[Ipo.OB_ROTZ]
]
return ipos
########################################
def addPoint(time,keyLocRot,ipos):
if BLENDER_VERSION < 245:
debug(0,'WARNING: addPoint uses BezTriple')
for i in range(len(ipos)):
point = BezTriple.New() #this was new with Blender 2.45 API
point.pt = (time, keyLocRot[i])
point.handleTypes = [1,1]
ipos[i].append(point)
return ipos
########################################
def bakeFrames(ob,myipo): #bakes an object in a scene, returning the IPO containing the curves
myipoName = myipo.getName()
debug(20,'Baking frames for scene %s object %s to ipo %s' % (scn.getName(),ob.getName(),myipoName))
ipos = getCurves(myipo)
#TODO: Gui setup idea: myOffset
# reset action to start at frame 1 or at location
myOffset=0 #=1-staframe
#loop through frames in the animation. Often, there is rollup and the mocap starts late
staframe,endframe,curframe = getRenderInfo()
for frame in range(staframe, endframe+1):
debug(80,'Baking Frame %i' % frame)
#tell Blender to advace to frame
Blender.Set(CURFRAME,frame) # computes the constrained location of the 'real' objects
if not BATCH: Blender.Redraw() # no secrets, let user see what we are doing
#using the constrained Loc Rot of the object, set the location of the unconstrained clone. Yea! Clones are FreeMen
key = getLocRot(ob,usrCoord) #a key is a set of specifed exact channel values (LocRotScale) for a certain frame
key = [a+b for a,b in zip(key, usrDelta)] #offset to the new location
myframe= frame+myOffset
Blender.Set(CURFRAME,myframe)
time = Blender.Get('curtime') #for BezTriple
ipos = addPoint(time,key,ipos) #add this data at this time to the ipos
debug(100,'%s %i %.3f %.2f %.2f %.2f %.2f %.2f %.2f' % (myipoName, myframe, time, key[0], key[1], key[2], key[3], key[4], key[5]))
# eye-candy - smoothly rewind the animation, showing now how the clone match moves
if endframe-staframe <400 and not BATCH:
for frame in range (endframe,staframe,-1): #rewind
Blender.Set(CURFRAME,frame) # computes the constrained location of the 'real' objects
Blender.Redraw()
Blender.Set(CURFRAME,staframe)
Blender.Redraw()
return ipos
########################################
def duplicateLinked(ob):
obType = ob.type
debug(10,'Duplicating %s Object named %s' % (obType,ob.getName()))
scn.objects.selected = [ob]
## rdw: simplified by just duplicating armature. kept code as reference for creating armatures
## disadvantage is that you cant have clone as stick and original as octahedron
## since they share the same Armature. User can click Make Single User button.
## if obType == ARMATURE: #build a copy from scratch
## myob= dupliArmature(ob)
## else:
Blender.Object.Duplicate() # Duplicate linked, including pose constraints.
myobs = Object.GetSelected() #duplicate is top on the list
myob = myobs[0]
if usrParent == False:
myob.clrParent(usrFreeze)
debug(20,'=myob= was created as %s' % myob.getName())
return myob
########################################
def removeConstraints(ob):
for const in ob.constraints:
debug(90,'removed %s => %s' % (ob.name, const))
ob.constraints.remove(const)
return
########################################
def removeConstraintsOb(ob): # from object or armature
debug(40,'Removing constraints from '+ob.getName())
if BLENDER_VERSION > 241: #constraints module not available before 242
removeConstraints(ob)
if ob.getType() == ARMATURE:
pose = ob.getPose()
for pbone in pose.bones.values():
#bone = pose.bones[bonename]
removeConstraints(pbone)
#should also check if it is a deflector?
return
########################################
def deLinkOb(type,ob): #remove linkages
if type == 'Ipo':
success = ob.clearIpo() #true=there was one
if success: debug(80,'deLinked Ipo curve to %s' % ob.getName())
return
########################################
def bakeObject(ob): #bakes the core object locrot and assigns the Ipo to a Clone
if ob != None:
# Clone the object - duplicate it, clean the clone, and create an ipo curve for the clone
myob = duplicateLinked(ob) #clone it
myob.setName(usrObjectNamePrefix + ob.getName())
removeConstraintsOb(myob) #my object is a free man
deLinkOb('Ipo',myob) #kids, it's not nice to share. you've been lied to
if ob.getType() != ARMATURE: # baking armatures is based on bones, not object
myipo = createIpo(myob) #create own IPO and curves for the clone object
ipos = bakeFrames(ob,myipo) #bake the locrot for this obj for the scene frames
return myob
########################################
def bake(ob,par): #bakes an object of any type, linking it to parent
debug(0,'Baking %s object %s' % (ob.getType(), ob))
clone = bakeObject(ob) #creates and bakes the object motion
if par!= None:
par.makeParent([clone])
debug(20,"assigned object to parent %s" % par)
if ob.getType() == ARMATURE:
## error('Object baked. Continue with bones?')
bakeBones(ob,clone) #go into the bones and copy from -> to in frame range
#future idea: bakeMesh (net result of Shapekeys, Softbody, Cloth, Fluidsim,...)
return clone
########################################
def tstCreateArm(): #create a test armature in scene
# rip-off from http://www.blender.org/documentation/245PythonDoc/Pose-module.html - thank you!
debug(0,'Making Test Armature')
# New Armature
arm_data= Armature.New('myArmature')
print arm_data
arm_ob = scn.objects.new(arm_data)
arm_data.makeEditable()
# Add 4 bones
ebones = [Armature.Editbone(), Armature.Editbone(), Armature.Editbone(), Armature.Editbone()]
# Name the editbones
ebones[0].name = 'Bone.001'
ebones[1].name = 'Bone.002'
ebones[2].name = 'Bone.003'
ebones[3].name = 'Bone.004'
# Assign the editbones to the armature
for eb in ebones:
arm_data.bones[eb.name]= eb
# Set the locations of the bones
ebones[0].head= Mathutils.Vector(0,0,0)
ebones[0].tail= Mathutils.Vector(0,0,1) #tip
ebones[1].head= Mathutils.Vector(0,0,1)
ebones[1].tail= Mathutils.Vector(0,0,2)
ebones[2].head= Mathutils.Vector(0,0,2)
ebones[2].tail= Mathutils.Vector(0,0,3)
ebones[3].head= Mathutils.Vector(0,0,3)
ebones[3].tail= Mathutils.Vector(0,0,4)
ebones[1].parent= ebones[0]
ebones[2].parent= ebones[1]
ebones[3].parent= ebones[2]
arm_data.update()
# Done with editing the armature
# Assign the pose animation
arm_pose = arm_ob.getPose()
act = arm_ob.getAction()
if not act: # Add a pose action if we dont have one
act = Armature.NLA.NewAction()
act.setActive(arm_ob)
xbones=arm_ob.data.bones.values()
pbones = arm_pose.bones.values()
frame = 1
for pbone in pbones: # set bones to no rotation
pbone.quat[:] = 1.000,0.000,0.000,0.0000
pbone.insertKey(arm_ob, frame, Object.Pose.ROT)
# Set a different rotation at frame 25
pbones[0].quat[:] = 1.000,0.1000,0.2000,0.20000
pbones[1].quat[:] = 1.000,0.6000,0.5000,0.40000
pbones[2].quat[:] = 1.000,0.1000,0.3000,0.40000
pbones[3].quat[:] = 1.000,-0.2000,-0.3000,0.30000
frame = 25
for i in xrange(4):
pbones[i].insertKey(arm_ob, frame, Object.Pose.ROT)
pbones[0].quat[:] = 1.000,0.000,0.000,0.0000
pbones[1].quat[:] = 1.000,0.000,0.000,0.0000
pbones[2].quat[:] = 1.000,0.000,0.000,0.0000
pbones[3].quat[:] = 1.000,0.000,0.000,0.0000
frame = 50
for pbone in pbones: # set bones to no rotation
pbone.quat[:] = 1.000,0.000,0.000,0.0000
pbone.insertKey(arm_ob, frame, Object.Pose.ROT)
return arm_ob
########################################
def tstMoveOb(ob): # makes a simple LocRot animation of object in the scene
anim = [
#Loc Rot/10
#
( 0,0,0, 0, 0, 0), #frame 1 origin
( 1,0,0, 0, 0, 0), #frame 2
( 1,1,0, 0, 0, 0),
( 1,1,1, 0, 0, 0),
( 1,1,1,4.5, 0, 0),
( 1,1,1,4.5,4.5, 0),
( 1,1,1,4.5,4.5,4.5)
]
space = COORD_LOCAL
ipo = createIpo(ob) #create an Ipo and curves for this object
ipos = getCurves(ipo)
# span this motion over the currently set anim range
# to set points, i need time but do not know how it is computed, so will have to advance the animation
staframe,endframe,curframe = getRenderInfo()
frame = staframe #x position of new ipo datapoint. set to staframe if you want a match
frameDelta=(endframe-staframe)/(len(anim)) #accomplish the animation in frame range
for key in anim: #effectively does a getLocRot()
#tell Blender to advace to frame
Blender.Set('curframe',frame) # computes the constrained location of the 'real' objects
time = Blender.Get('curtime')
ipos = addPoint(time,key,ipos) #add this data at this time to the ipos
debug(100,'%s %i %.3f %.2f %.2f %.2f %.2f %.2f %.2f' % (ipo.name, frame, time, key[0], key[1], key[2], key[3], key[4], key[5]))
frame += frameDelta
Blender.Set(CURFRAME,curframe) # reset back to where we started
return
#=================
# Program Template
#=================
########################################
def main():
# return code set via rt button in Blender Buttons Scene Context Anim panel
if MODE == 1: #create test armature #1
ob = tstCreateArm() # make test arm and select it
tstMoveOb(ob)
scn.objects.selected = [ob]
obs= Blender.Object.GetSelected() #scn.objects.selected
obs= sortObjects(obs)
debug(0,'Baking %i objects' % len(obs))
if len(obs) >= 1: # user might have multiple objects selected
i= 0
clones=[] # my clone army
for ob in obs:
par= ob.getParent()
if not usrParent:
if par in obs:
par= clones[obs.index(par)]
clones.append(bake(ob,par))
scn.objects.selected = clones
else:
error('Please select at least one object')
return
########################################
def benchmark(): # This lets you benchmark (time) the script's running duration
Window.WaitCursor(1)
t = sys.time()
debug(60,'%s began at %.0f' %(__script__,sys.time()))
# Run the function on the active scene
in_editmode = Window.EditMode()
if in_editmode: Window.EditMode(0)
main()
if in_editmode: Window.EditMode(1)
# Timing the script is a good way to be aware on any speed hits when scripting
debug(0,'%s Script finished in %.2f seconds' % (__script__,sys.time()-t) )
Window.WaitCursor(0)
return
########################################
# This lets you can import the script without running it
if __name__ == '__main__':
debug(0, "------------------------------------")
debug(0, "%s %s Script begins with mode=%i debug=%i batch=%s" % (__script__,__version__,MODE,DEBUG,BATCH))
benchmark()

View File

@@ -0,0 +1,192 @@
#!BPY
"""
Name: 'Clean Animation Curves'
Blender: 249
Group: 'Animation'
Tooltip: 'Remove unused keyframes for ipo curves'
"""
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2008-2009: Blender Foundation
#
# 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,
# --------------------------------------------------------------------------
import bpy
from Blender import IpoCurve, Draw, Window
def clean_ipos(ipos):
eul = 0.001
def isflat(vec):
prev_y = vec[0][1]
mid_y = vec[1][1]
next_y = vec[2][1]
# flat status for prev and next
return abs(mid_y-prev_y) < eul, abs(mid_y-next_y) < eul
X=0
Y=1
PREV=0
MID=1
NEXT=2
LEFT = 0
RIGHT = 1
TOT = 0
TOTBEZ = 0
# for ipo in bpy.data.ipos:
for ipo in ipos:
if ipo.lib:
continue
# print ipo
for icu in ipo:
interp = icu.interpolation
extend = icu.extend
bezierPoints = icu.bezierPoints
bezierVecs = [bez.vec for bez in bezierPoints]
l = len(bezierPoints)
TOTBEZ += l
# our aim is to simplify this ipo as much as possible!
if interp == IpoCurve.InterpTypes.BEZIER or interp == interp == IpoCurve.InterpTypes.LINEAR:
#print "Not yet supported"
if interp == IpoCurve.InterpTypes.BEZIER:
flats = [isflat(bez) for bez in bezierVecs]
else:
# A bit of a waste but fake the locations for these so they will always be flats
# IS better then too much duplicate code.
flats = [(True, True)] * l
for v in bezierVecs:
v[PREV][Y] = v[NEXT][Y] = v[MID][Y]
# remove middle points
if l>2:
done_nothing = False
while not done_nothing and len(bezierVecs) > 2:
done_nothing = True
i = l-2
while i > 0:
#print i
#print i, len(bezierVecs)
if flats[i]==(True,True) and flats[i-1][RIGHT] and flats[i+1][LEFT]:
if abs(bezierVecs[i][MID][Y] - bezierVecs[i-1][MID][Y]) < eul and abs(bezierVecs[i][MID][Y] - bezierVecs[i+1][MID][Y]) < eul:
done_nothing = False
del flats[i]
del bezierVecs[i]
icu.delBezier(i)
TOT += 1
l-=1
i-=1
# remove endpoints
if extend == IpoCurve.ExtendTypes.CONST and len(bezierVecs) > 1:
#print l, len(bezierVecs)
# start
while l > 2 and (flats[0][RIGHT] and flats[1][LEFT] and (abs(bezierVecs[0][MID][Y] - bezierVecs[1][MID][Y]) < eul)):
print "\tremoving 1 point from start of the curve"
del flats[0]
del bezierVecs[0]
icu.delBezier(0)
TOT += 1
l-=1
# End
while l > 2 and flats[-2][RIGHT] and flats[-1][LEFT] and (abs(bezierVecs[-2][MID][Y] - bezierVecs[-1][MID][Y]) < eul):
print "\tremoving 1 point from end of the curve", l
del flats[l-1]
del bezierVecs[l-1]
icu.delBezier(l-1)
TOT += 1
l-=1
if l==2:
if isflat( bezierVecs[0] )[RIGHT] and isflat( bezierVecs[1] )[LEFT] and abs(bezierVecs[0][MID][Y] - bezierVecs[1][MID][Y]) < eul:
# remove the second point
print "\tremoving 1 point from 2 point bez curve"
# remove the second point
del flats[1]
del bezierVecs[1]
icu.delBezier(1)
TOT+=1
l-=1
# Change to linear for faster evaluation
'''
if l==1:
print 'Linear'
icu.interpolation = IpoCurve.InterpTypes.LINEAR
'''
if interp== IpoCurve.InterpTypes.CONST:
print "Not yet supported"
print 'total', TOT, TOTBEZ
return TOT, TOTBEZ
def main():
ret = Draw.PupMenu('Clean Selected Objects Ipos%t|Object IPO%x1|Object Action%x2|%l|All IPOs (be careful!)%x3')
sce = bpy.data.scenes.active
ipos = []
if ret == 3:
ipos.extend(list(bpy.data.ipos))
else:
for ob in sce.objects.context:
if ret == 1:
ipo = ob.ipo
if ipo:
ipos.append(ipo)
elif ret == 2:
action = ob.action
if action:
ipos.extend([ipo for ipo in action.getAllChannelIpos().values() if ipo])
if not ipos:
Draw.PupMenu('Error%t|No ipos found')
else:
total_removed, total = clean_ipos(ipos)
Draw.PupMenu('Done!%t|Removed ' + str(total_removed) + ' of ' + str(total) + ' points')
Window.RedrawAll()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,575 @@
#!BPY
""" Registration info for Blender menus: <- these words are ignored
Name: 'Trajectory'
Blender: 243
Group: 'Animation'
Tip: 'See Trajectory of selected object'
"""
__author__ = '3R - R3gis'
__version__ = '2.43'
__url__ = ["Script's site , http://blenderfrance.free.fr/python/Trajectory_en.htm","Author's site , http://cybercreator.free.fr", "French Blender support forum, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender"]
__email__=["3R, r3gis@free.fr"]
__bpydoc__ = """
Usage:
* Launch with alt+P (or put it in .script folder)
Allow to see in real time trajectory of selected object.
On first run, it ask you
- If you want that actually selected object have they trajectory always shown
- If you want to use Space Handler or a Scriptlink in Redraw mode
- Future and Past : it is the frame in past and future
of the beggining and the end of the path
- Width of line that represent the trajectory
Then the object's trajectory will be shown in all 3D areas.
When trajectory is red, you can modifiy it by moving object.
When trajectory is blue and you want to be able to modify it, inser a Key (I-Key)
Points appears on trajectory :
- Left Clic to modify position
- Right Clic to go to the frame it represents
Notes:<br>
In scriptlink mode, it create one script link so make sure that 'Enable Script Link' toogle is on
In SpaceHandler mode, you have to go in View>>SpaceHandlerScript menu to activate Trajectory
"""
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Copyright (C) 2004-2006: Regis Montoya
#
# 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 *****
# --------------------------------------------------------------------------
#################################
# by 3R - 26/08/05
# for any problem :
# r3gis@free.fr
# ou sur le newsgroup:
# http://zoo-logique.org/3D.Blender/
#################################
#Many thanks to cambo for his fixes
#################################
import Blender
scene= Blender.Scene.GetCurrent()
#Writing
def write_script(name, script):
global scene
#List texts and their name
#write : type of writing : 1->New, 2->Overwrite
scripting= None
for text in Blender.Text.Get():
if text.name==name and text.asLines()[1] != "#"+str(__version__):
scripting = text
scripting.clear()
scripting.write(script)
break
if not scripting:
scripting= Blender.Text.New(name)
scripting.write(script)
def link_script(name, type):
global scene
scriptlinks = scene.getScriptLinks(type) # none or list
if not scriptlinks or name not in scriptlinks:
scene.addScriptLink(name, type)
#Deleting of a text
def text_remove(name):
global scene
#try to delete text if already linked
try:
text= Blender.Text.Get(name)
# Texte.clear()
scene.clearScriptLinks([name])
Blender.Text.unlink(text)
except:
print('---Initialisation of Trajectory_'+str(__version__)+'.py---')
#Whether is already running, also check if it's the last version of the script : second line contain the version fo the script
ask_modif= 0 # Default
for text in Blender.Text.Get():
if text.name == 'Trajectory' and text.asLines()[1] == "#"+str(__version__):
#We ask if script modify his seetings, keep it or stop script
ask_modif= Blender.Draw.PupMenu("Script already launch %t|Modify settings%x0|Keep settings%x1|Stop script%x2|")
if ask_modif==-1: # user canceled.
ask_modif= 1
break
selection_mode= 0
future= 35
past= 20
width= 2
#In modify case
if ask_modif==0:
handle_mode= Blender.Draw.Create(0)
selection_mode= Blender.Draw.Create(0)
future= Blender.Draw.Create(35)
past= Blender.Draw.Create(20)
width= Blender.Draw.Create(2)
block= []
block.append(("Space Handlers", handle_mode, "You have to activate for each area by View>>SpaceHandler")) #You can delete this option...
block.append(("Always Draw", selection_mode, "Selected object will have their trajectory always shown"))
block.append(("Past :", past, 1, 900))
block.append(("Futur:", future, 1, 900))
block.append(("Width:", width, 1,5))
if not Blender.Draw.PupBlock("Trajectory seetings", block):
ask_modif=1
handle_mode= handle_mode.val
selection_mode= selection_mode.val
future= future.val
past= past.val
width= width.val
#put names of selected objects in objects_select if option choosen by user
if selection_mode==1:
objects_select= [ob.name for ob in scene.objects.context]
else:
objects_select= []
try:
if handle_mode==1:
DrawPart="#SPACEHANDLER.VIEW3D.DRAW\n"
else:
DrawPart="#!BPY\n"
except:DrawPart="#BadlyMade"
#Here is the script to write in Blender and to link, options are also written now
DrawPart=DrawPart+"#"+str(__version__)+"""
#This script is a part of Trajectory.py and have to be linked to the scene in Redraw if not in HANDLER mode.
#Author : 3R - Regis Montoya
#It's better to use the Trajectory_"version_number".py
#You can modify the two following value to change the path settings
future="""+str(future)+"""
past="""+str(past)+"""
object_init_names="""+str(objects_select)+"""
import Blender, math
from Blender import BGL, Draw, Ipo
from Blender.BGL import *
from Blender.Draw import *
from math import *
from Blender.Mathutils import Vector
#take actual frame
frameC=Blender.Get('curframe')
scene = Blender.Scene.GetCurrent()
render_context=scene.getRenderingContext()
#ajust number of frames with NewMap and OldMapvalue values
k=1.00*render_context.oldMapValue()/render_context.newMapValue()
if k<1:
tr=-1*int(log(k*0.1, 10))
else:
tr=-1*int(log(k, 10))
#The real and integer frame to compare to ipos keys frames
frameCtr=round(frameC*k, tr)
frameCr=frameC*k
frameC=int(round(frameC*k, 0))
#List objects that we have to show trajectory in $objects
# In this case, using a dict for unique objects is the fastest way.
object_dict= dict([(ob.name, ob) for ob in scene.objects.context])
for obname in object_init_names:
if not object_dict.has_key(obname):
try: # Object may be removed.
object_dict[obname]= Blender.Object.Get(obname)
except:
pass # object was removed.
#This fonction give the resulting matrix of all parents at a given frame
#parent_list is the list of all parents [object, matrix, locX_ipo, locY, Z, rotX, Y, Z, sizeX, Y, Z] of current object
def matrixForTraj(frame, parent_list):
DecMatC=Blender.Mathutils.Matrix([1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1])
for parent_data in parent_list:
parent_ob= parent_data[0]
try: X= parent_data[5][frame]*pi/18
except: X= parent_ob.RotX
try: Y= parent_data[6][frame]*pi/18
except: Y= parent_ob.RotY
try: Z= parent_data[7][frame]*pi/18
except: Z= parent_ob.RotZ
try: LX= parent_data[2][frame]
except: LX= parent_ob.LocX
try: LY= parent_data[3][frame]
except: LY= parent_ob.LocY
try: LZ= parent_data[4][frame]
except: LZ= parent_ob.LocZ
try: SX= parent_data[8][frame]
except: SX= parent_ob.SizeX
try: SY= parent_data[9][frame]
except: SY= parent_ob.SizeY
try: SZ= parent_data[10][frame]
except: SZ= parent_ob.SizeZ
NMat=Blender.Mathutils.Matrix([cos(Y)*cos(Z)*SX,SX*cos(Y)*sin(Z),-SX*sin(Y),0],
[(-cos(X)*sin(Z)+sin(Y)*sin(X)*cos(Z))*SY,(sin(X)*sin(Y)*sin(Z)+cos(X)*cos(Z))*SY,sin(X)*cos(Y)*SY,0],
[(cos(X)*sin(Y)*cos(Z)+sin(X)*sin(Z))*SZ,(cos(X)*sin(Y)*sin(Z)-sin(X)*cos(Z))*SZ,SZ*cos(X)*cos(Y),0],
[LX,LY,LZ,1])
DecMatC=DecMatC*parent_data[1]*NMat
return DecMatC
#####
TestLIST=[]
matview=Blender.Window.GetPerspMatrix()
###########
#Fonction to draw trajectories
###########
def Trace_Traj(ob):
global TestLIST, matview
#we draw trajectories for all objects in list
LocX=[]
LocY=[]
LocZ=[]
#List with trajectories' vertexs
vertexX=[]
contextIpo= ob.ipo
if contextIpo:
ipoLocX=contextIpo[Ipo.OB_LOCX]
ipoLocY=contextIpo[Ipo.OB_LOCY]
ipoLocZ=contextIpo[Ipo.OB_LOCZ]
ipoTime=contextIpo[Ipo.OB_TIME]
else: # only do if there is no IPO (if no ipo curves : return None object and don't go in this except)
ipoLocX= ipoLocY= ipoLocZ= ipoTime= None
if ipoTime:
return 0
#Get all parents of ob
parent=ob.parent
backup_ob= ob
child= ob
parent_list= []
#Get parents's infos :
#list of [name, initial matrix at make parent, ipo in X,Y,Z,rotX,rotY,rotZ,sizeX,Y,Z]
while parent:
Init_Mat=Blender.Mathutils.Matrix(child.getMatrix('worldspace')) #must be done like it (it isn't a matrix otherwise)
Init_Mat.invert()
Init_Mat=Init_Mat*child.getMatrix('localspace')
Init_Mat=parent.getMatrix()*Init_Mat
Init_Mat.invert()
contextIpo= parent.ipo # None or IPO
if contextIpo:
ipo_Parent_LocX=contextIpo[Ipo.OB_LOCX]
ipo_Parent_LocY=contextIpo[Ipo.OB_LOCY]
ipo_Parent_LocZ=contextIpo[Ipo.OB_LOCZ]
ipo_Parent_RotX=contextIpo[Ipo.OB_ROTX]
ipo_Parent_RotY=contextIpo[Ipo.OB_ROTY]
ipo_Parent_RotZ=contextIpo[Ipo.OB_ROTZ]
ipo_Parent_SizeX=contextIpo[Ipo.OB_SIZEX]
ipo_Parent_SizeY=contextIpo[Ipo.OB_SIZEY]
ipo_Parent_SizeZ=contextIpo[Ipo.OB_SIZEZ]
else:
ipo_Parent_LocX=ipo_Parent_LocY=ipo_Parent_LocZ=\
ipo_Parent_RotX=ipo_Parent_RotY=ipo_Parent_RotZ=\
ipo_Parent_SizeX=ipo_Parent_SizeY=ipo_Parent_SizeZ= None
parent_list.append([parent, Init_Mat, ipo_Parent_LocX, ipo_Parent_LocY, ipo_Parent_LocZ, ipo_Parent_RotX, ipo_Parent_RotY, ipo_Parent_RotZ, ipo_Parent_SizeX, ipo_Parent_SizeY, ipo_Parent_SizeZ])
child=parent
parent=parent.parent
#security : if one of parents object are a path>>follow : trajectory don't work properly so it have to draw nothing
for parent in parent_list:
if parent[0].type == 'Curve':
if parent[0].data.flag & 1<<4: # Follow path, 4th bit
return 1
#ob >> re-assign obj and not parent
ob= backup_ob
ob= backup_ob
if ipoLocX: LXC= ipoLocX[frameC]
else: LXC= ob.LocX
if ipoLocY: LYC= ipoLocY[frameC]
else: LYC= ob.LocY
if ipoLocZ: LZC= ipoLocZ[frameC]
else: LZC= ob.LocZ
vect= Vector([ob.LocX, ob.LocY, ob.LocZ, 1])
color=[0, 1]
#If trajectory is being modified and we are at a frame where a ipo key already exist
if round(ob.LocX, 5)!=round(LXC, 5):
for bez in ipoLocX.bezierPoints:
if round(bez.pt[0], tr)==frameCtr:
bez.pt = [frameCr, vect[0]]
ipoLocX.recalc()
if round(ob.LocY, 5)!=round(LYC, 5):
for bez in ipoLocY.bezierPoints:
if round(bez.pt[0], tr)==frameCtr:
bez.pt = [frameCr, vect[1]]
ipoLocY.recalc()
if round(ob.LocZ, 5)!=round(LZC, 5):
for bez in ipoLocZ.bezierPoints:
if round(bez.pt[0], tr)==frameCtr:
bez.pt = [frameCr, vect[2]]
ipoLocZ.recalc()
#change trajectory color if at an ipoKey
VertexFrame=[]
bezier_Coord=0
if ipoLocX: # FIXED like others it was just in case ipoLocX==None
for bez in ipoLocX.bezierPoints:
bezier_Coord=round(bez.pt[0], tr)
if bezier_Coord not in VertexFrame:
VertexFrame.append(bezier_Coord)
if bezier_Coord==frameCtr:
color=[1, color[1]-0.3]
if ipoLocY: # FIXED
for bez in ipoLocY.bezierPoints:
bezier_Coord=round(bez.pt[0], tr)
if bezier_Coord not in VertexFrame:
VertexFrame.append(bezier_Coord)
if round(bez.pt[0], tr)==frameCtr:
color=[1, color[1]-0.3]
if ipoLocZ: # FIXED
for bez in ipoLocZ.bezierPoints:
bezier_Coord=round(bez.pt[0], tr)
if bezier_Coord not in VertexFrame:
VertexFrame.append(bezier_Coord)
if round(bez.pt[0], tr)==frameCtr:
color=[1, color[1]-0.3]
#put in LocX, LocY and LocZ all points of trajectory
for frame in xrange(frameC-past, frameC+future):
DecMat=matrixForTraj(frame, parent_list)
if ipoLocX: LX= ipoLocX[frame]
else: LX= ob.LocX
if ipoLocY: LY= ipoLocY[frame]
else: LY= ob.LocY
if ipoLocZ: LZ= ipoLocZ[frame]
else: LZ= ob.LocZ
vect=Vector(LX, LY, LZ)*DecMat
LocX.append(vect[0])
LocY.append(vect[1])
LocZ.append(vect[2])
#draw part : get current view
MatPreBuff= [matview[i][j] for i in xrange(4) for j in xrange(4)]
MatBuff=BGL.Buffer(GL_FLOAT, 16, MatPreBuff)
glLoadIdentity()
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadMatrixf(MatBuff)
#draw trajectory line
glLineWidth("""+str(width)+""")
glBegin(GL_LINE_STRIP)
for i in xrange(len(LocX)):
glColor3f((i+1)*1.00/len(LocX)*color[0], 0, (i+1)*1.00/len(LocX)*color[1])
glVertex3f(LocX[i], LocY[i], LocZ[i])
glEnd()
#draw trajectory's "vertexs"
if not Blender.Window.EditMode():
glPointSize(5)
glBegin(GL_POINTS)
TestPOINTS=[]
TestFRAME=[]
i=0
for frame in VertexFrame:
ix=int(frame)-frameC+past
if ix>=0 and ix<len(LocX):
glColor3f(1, 0.7, 0.2)
glVertex3f(LocX[ix], LocY[ix], LocZ[ix])
TestPOINTS.append(Vector([LocX[ix], LocY[ix], LocZ[ix], 1]))
TestFRAME.append(int(frame))
i+=1
glEnd()
#this list contains info about where to check if we click over a "vertex" in 3D view
TestLIST.append((ob, TestPOINTS, TestFRAME))
glLineWidth(1)
return 0
for ob in object_dict.itervalues():
Trace_Traj(ob)
###########
#Fonction to handle trajectories
###########
def Manip():
#use TestLIST and matview defined by Trace_Traj
global TestLIST, matview
for screen in Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D):
if screen['id']==Blender.Window.GetAreaID():
x0, y0, x1, y1= screen['vertices']
break
#Projection of GL matrix in 3D view
glPushMatrix()
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
#Global coordinates' matrix
glOrtho(x0, x1, y0, y1, -1, 0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
#Test mouse clics and other events
if Blender.Window.QTest():
evt, val= Blender.Window.QRead()
if (evt==LEFTMOUSE or evt==RIGHTMOUSE) and not Blender.Window.EditMode():
mouse_co=Blender.Window.GetMouseCoords()
#if click on trajectory "vertexs"...
for ob, TestPOINTS, TestFRAME in TestLIST: # ob is now used, line 552 to know what object it had to select
for k, Vect in enumerate(TestPOINTS):
proj=Vect*matview
pt=[(proj[0]/proj[3])*(x1-x0)/2+(x1+x0)/2, (proj[1]/proj[3])*(y1-y0)/2+(y1+y0)/2]
if mouse_co[0]<pt[0]+4 and mouse_co[0]>pt[0]-4 and mouse_co[1]>pt[1]-4 and mouse_co[1]<pt[1]+4:
if evt==LEFTMOUSE:
#remember current selected object
object_names=[obj.name for obj in Blender.Object.GetSelected()]
#this script allow to simulate a GKey, but I have to write a script
#another way would made a infinit redraw or don't allow to move object
#it auto unlink and delete itself
script=\"\"\"
import Blender
from Blender import Draw, Window
from Blender.Window import *
from Blender.Draw import *
from Blender.Mathutils import Vector
# The following code is a bit of a hack, it allows clicking on the points and dragging directly
#It simulate user press GKey
#It also set the cursor position at center (because user have previously clic on area and moved the cursor):
#And I can't get previous cursor position : redraw appear after it has been moved
#If there is no better way you can remove this comments
f= GetAreaID()
SetCursorPos(0,0,0)
#SetKeyQualifiers(1) #FIXED : the bug in older versions seems to have been fixed
SetKeyQualifiers(0)
QAdd(f, Blender.Draw.GKEY, 1, 0)
QHandle(f)
Blender.Redraw()
done=0
while not done:
while Blender.Window.QTest():
ev=Blender.Window.QRead()[0]
if ev not in (4, 5, 18, 112, 213): #all event needed to move object
#SetKeyQualifiers(1) #FIXED too, same reason that above
#SetKeyQualifiers(0)
SetKeyQualifiers(Blender.Window.GetKeyQualifiers())
QAdd(f, ev, 1, 0)
QHandle(f)
Blender.Redraw()
if ev in (RIGHTMOUSE, LEFTMOUSE, ESCKEY):
done=1
Blender.Set('curframe',\"\"\"+str(Blender.Get('curframe'))+\"\"\")
Blender.Object.GetSelected()[0].sel= False
for obname in \"\"\"+str(object_names)+\"\"\":
ob=Blender.Object.Get(obname)
ob.sel= True
SetCursorPos(0,0,0)
scripting=Blender.Text.Get('Edit_Trajectory')
scripting.clear()
Blender.Text.unlink(scripting)
\"\"\"
#FIXED Edit_Trajectory was longer : all SetKeyQualifiers removed
scene=Blender.Scene.GetCurrent()
try:
scripting=Blender.Text.Get('Edit_Trajectory')
scripting.clear()
except:
scripting=Blender.Text.New('Edit_Trajectory')
scripting.write(script)
#script= scripting #FIXED seems not needed anymore
#Go to frame that correspond to selected "vertex"
Blender.Set('curframe', TestFRAME[k])
scene.objects.selected = [] #un select all objects
#FIXED TestLIST[j][0].sel=0, but no j. So ob.sel and above variable changed in obj
ob.sel= True
Blender.Run('Edit_Trajectory')
#work well now !!!
if evt==RIGHTMOUSE :
Blender.Set('curframe', TestFRAME[k])
Manip()
#retrieve a normal matrix
glPopMatrix()
glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)
"""
if ask_modif==0:
text_remove('Trajectory')
write_script('Trajectory', DrawPart)
if handle_mode==1:
Blender.UpdateMenus()
else:
link_script('Trajectory', 'Redraw')
if ask_modif==2:
text_remove('Trajectory')
print("---End of Trajectory_"+str(__version__)+".py---\n--- Thanks for use ---")

View File

@@ -0,0 +1,325 @@
#!BPY
"""
Name: 'Armature Symmetry'
Blender: 242
Group: 'Armature'
Tooltip: 'Make an Armature symmetrical'
"""
__author__ = "Campbell Barton"
__url__ = ("blender", "blenderartist")
__version__ = "1.0 2006-7-26"
__doc__ = """\
This script creates perfectly symmetrical armatures,
based on the best fit when comparing the mirrored locations of 2 bones.
Hidden bones are ignored, and optionally only operate on selected bones.
"""
# ***** BEGIN GPL LICENSE BLOCK *****
#
# Script copyright (C) Campbell J Barton 2006
#
# 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 *****
# --------------------------------------------------------------------------
import Blender
import bpy
Vector= Blender.Mathutils.Vector
def VecXFlip(vec):
'''
Return a copy of this vector x flipped.
'''
x,y,z= vec
return Vector(-x,y,z)
def editbone_mirror_diff(editbone1, editbone2):
'''
X Mirror bone compare
return a float representing the difference between the 2 bones
the smaller the better the match
'''
h1= editbone1.head
h2= editbone2.head
t1= editbone1.tail
t2= editbone2.tail
# Mirror bone2's location
h2= VecXFlip(h2)
t2= VecXFlip(t2)
#return (h1-h2).length + (t1-t2).length # returns the length only
# For this function its easier to return the bones also
return ((h1-h2).length + (t1-t2).length)/2, editbone1, editbone2
def editbone_mirror_merge(editbone1, editbone2, PREF_MODE_L2R, PREF_MODE_R2L):
'''
Merge these 2 bones to their mirrored locations
'''
h1= editbone1.head
h2= editbone2.head
t1= editbone1.tail
t2= editbone2.tail
if PREF_MODE_L2R and PREF_MODE_R2L:
# Median, flip bone2's locations and average, then apply to editbone1, flip and apply to editbone2
h2_f= VecXFlip(h2)
t2_f= VecXFlip(t2)
h_med= (h1+h2_f)*0.5 # middle between t1 and flipped t2
t_med= (t1+t2_f)*0.5 # middle between h1 and flipped h2
# Apply the median to editbone1
editbone1.head= h_med
editbone1.tail= t_med
# Flip in place for editbone2
h_med.x= -h_med.x
t_med.x= -t_med.x
# Apply the median to editbone2
editbone2.head= h_med
editbone2.tail= t_med
# Average the roll, this might need some logical work, but looks good for now.
r1= editbone1.roll
r2= -editbone2.roll
# print 'rolls are', r1,r2
r_med= (r1+r2)/2
# print 'new roll is', r_med
editbone1.roll= r_med
editbone2.roll= -r_med # mirror roll
else: # Copy from 1 side to another
# Crafty function we can use so L>R and R>L can use the same code
def IS_XMIRROR_SOURCE(xval):
'''Source means is this the value we want to copy from'''
if PREF_MODE_L2R:
if xval<0: return True
else: return False
else: # PREF_MODE_R2L
if xval<0: return False
else: return True
if IS_XMIRROR_SOURCE( h1.x ):# head bone 1s negative, so copy it to h2
editbone2.head= VecXFlip(h1)
else:
'''
assume h2.x<0 - not a big deal if were wrong,
its unlikely to ever happen because the bones would both be on the same side.
'''
# head bone 2s negative, so copy it to h1
editbone1.head= VecXFlip(h2)
# Same as above for tail
if IS_XMIRROR_SOURCE(t1.x):
editbone2.tail= VecXFlip(t1)
else:
editbone1.tail= VecXFlip(t2)
# Copy roll from 1 bone to another, use the head's location to decide which side it's on.
if IS_XMIRROR_SOURCE(editbone1.head):
editbone2.roll= -editbone1.roll
else:
editbone1.roll= -editbone2.roll
def armature_symetry(\
arm_ob,\
PREF_MAX_DIST,\
PREF_XMID_SNAP,\
PREF_XZERO_THRESH,\
PREF_MODE_L2R,\
PREF_MODE_R2L,\
PREF_SEL_ONLY):
'''
Main function that does all the work,
return the number of
'''
arm_data= arm_ob.data
arm_data.makeEditable()
# Get the bones
bones= []
HIDDEN_EDIT= Blender.Armature.HIDDEN_EDIT
BONE_SELECTED= Blender.Armature.BONE_SELECTED
if PREF_SEL_ONLY:
for eb in arm_data.bones.values():
options= eb.options
if HIDDEN_EDIT not in options and BONE_SELECTED in options:
bones.append(eb)
else:
# All non hidden bones
for eb in arm_data.bones.values():
options= eb.options
if HIDDEN_EDIT not in options:
bones.append(eb)
del HIDDEN_EDIT # remove temp variables
del BONE_SELECTED
# Store the numder of bones we have modified for a message
tot_editbones= len(bones)
tot_editbones_modified= 0
if PREF_XMID_SNAP:
# Remove bones that are in the middle (X Zero)
# reverse loop so we can remove items in the list.
for eb_idx in xrange(len(bones)-1, -1, -1):
edit_bone= bones[eb_idx]
if abs(edit_bone.head.x) + abs(edit_bone.tail.x) <= PREF_XZERO_THRESH/2:
# This is a center bone, clamp and remove from the bone list so we dont use again.
if edit_bone.tail.x or edit_bone.head.x:
tot_editbones_modified += 1
edit_bone.tail.x= edit_bone.head.x= 0
del bones[eb_idx]
bone_comparisons= []
# Compare every bone with every other bone, shouldn't be too slow.
# These 2 "for" loops only compare once
for eb_idx_a in xrange(len(bones)-1, -1, -1):
edit_bone_a= bones[eb_idx_a]
for eb_idx_b in xrange(eb_idx_a-1, -1, -1):
edit_bone_b= bones[eb_idx_b]
# Error float the first value from editbone_mirror_diff() so we can sort the resulting list.
bone_comparisons.append(editbone_mirror_diff(edit_bone_a, edit_bone_b))
bone_comparisons.sort() # best matches first
# Make a dict() of bone names that have been used so we dont mirror more then once
bone_mirrored= {}
for error, editbone1, editbone2 in bone_comparisons:
# print 'Trying to merge at error %.3f' % error
if error > PREF_MAX_DIST:
# print 'breaking, max error limit reached PREF_MAX_DIST: %.3f' % PREF_MAX_DIST
break
if not bone_mirrored.has_key(editbone1.name) and not bone_mirrored.has_key(editbone2.name):
# Were not used, execute the mirror
editbone_mirror_merge(editbone1, editbone2, PREF_MODE_L2R, PREF_MODE_R2L)
# print 'Merging bones'
# Add ourselves so we aren't touched again
bone_mirrored[editbone1.name] = None # dummy value, would use sets in python 2.4
bone_mirrored[editbone2.name] = None
# If both options are enabled, then we have changed 2 bones
tot_editbones_modified+= PREF_MODE_L2R + PREF_MODE_R2L
arm_data.update() # get out of armature editmode
return tot_editbones, tot_editbones_modified
def main():
'''
User interface function that gets the options and calls armature_symetry()
'''
scn= bpy.data.scenes.active
arm_ob= scn.objects.active
if not arm_ob or arm_ob.type!='Armature':
Blender.Draw.PupMenu('No Armature object selected.')
return
# Cant be in editmode for armature.makeEditable()
is_editmode= Blender.Window.EditMode()
if is_editmode: Blender.Window.EditMode(0)
Draw= Blender.Draw
# Defaults for the user input
PREF_XMID_SNAP= Draw.Create(1)
PREF_MAX_DIST= Draw.Create(0.4)
PREF_XZERO_THRESH= Draw.Create(0.02)
PREF_MODE_L2R= Draw.Create(1)
PREF_MODE_R2L= Draw.Create(0)
PREF_SEL_ONLY= Draw.Create(1)
pup_block = [\
'Left (-), Right (+)',\
('Left > Right', PREF_MODE_L2R, 'Copy from the Left to Right of the mesh. Enable Both for a mid loc.'),\
('Right > Left', PREF_MODE_R2L, 'Copy from the Right to Left of the mesh. Enable Both for a mid loc.'),\
'',\
('MaxDist:', PREF_MAX_DIST, 0.0, 4.0, 'Maximum difference in mirror bones to match up pairs.'),\
('XZero limit:', PREF_XZERO_THRESH, 0.0, 2.0, 'Tolerance for locking bones into the middle (X/zero).'),\
('XMidSnap Bones', PREF_XMID_SNAP, 'Snap middle verts to X Zero (uses XZero limit)'),\
('Selected Only', PREF_SEL_ONLY, 'Only xmirror selected bones.'),\
]
# Popup, exit if the user doesn't click OK
if not Draw.PupBlock("X Mirror mesh tool", pup_block):
return
# Replace the variables with their button values.
PREF_XMID_SNAP= PREF_XMID_SNAP.val
PREF_MAX_DIST= PREF_MAX_DIST.val
PREF_MODE_L2R= PREF_MODE_L2R.val
PREF_MODE_R2L= PREF_MODE_R2L.val
PREF_XZERO_THRESH= PREF_XZERO_THRESH.val
PREF_SEL_ONLY= PREF_SEL_ONLY.val
# If both are off assume mid-point and enable both
if not PREF_MODE_R2L and not PREF_MODE_L2R:
PREF_MODE_R2L= PREF_MODE_L2R= True
tot_editbones, tot_editbones_modified = armature_symetry(\
arm_ob,\
PREF_MAX_DIST,\
PREF_XMID_SNAP,\
PREF_XZERO_THRESH,\
PREF_MODE_L2R,\
PREF_MODE_R2L,\
PREF_SEL_ONLY)
if is_editmode: Blender.Window.EditMode(1)
# Redraw all views before popup
Blender.Window.RedrawAll()
# Print results
if PREF_SEL_ONLY:
msg= 'moved %i bones of %i selected' % (tot_editbones_modified, tot_editbones)
else:
msg= 'moved %i bones of %i visible' % (tot_editbones_modified, tot_editbones)
Blender.Draw.PupMenu(msg)
# Check for __main__ so this function can be imported by other scripts without running the script.
if __name__=='__main__':
main()

View File

@@ -0,0 +1,474 @@
#!BPY
# -*- coding: utf-8 -*-
""" Registration info for Blender menus
Name: 'Bevel Center'
Blender: 243
Group: 'Mesh'
Tip: 'Bevel selected faces, edges, and vertices'
"""
__author__ = "Loic BERTHE"
__url__ = ("blender", "blenderartists.org")
__version__ = "2.0"
__bpydoc__ = """\
This script implements vertex and edges bevelling in Blender.
Usage:
Select the mesh you want to work on, enter Edit Mode and select the edges
to bevel. Then run this script from the 3d View's Mesh->Scripts menu.
You can control the thickness of the bevel with the slider -- redefine the
end points for bigger or smaller ranges. The thickness can be changed even
after applying the bevel, as many times as needed.
For an extra smoothing after or instead of direct bevel, set the level of
recursiveness and use the "Recursive" button.
This "Recursive" Button, won't work in face select mode, unless you choose
"faces" in the select mode menu.
Notes:<br>
You can undo and redo your steps just like with normal mesh operations in
Blender.
"""
######################################################################
# Bevel Center v2.0 for Blender
# This script lets you bevel the selected vertices or edges and control the
# thickness of the bevel
# (c) 2004-2006 Loรฏc Berthe (loic+blender@lilotux.net)
# released under Blender Artistic License
######################################################################
import Blender
from Blender import NMesh, Window, Scene
from Blender.Draw import *
from Blender.Mathutils import *
from Blender.BGL import *
import BPyMessages
#PY23 NO SETS#
'''
try:
set()
except:
from sets import set
'''
######################################################################
# Functions to handle the global structures of the script NF, NE and NC
# which contain informations about faces and corners to be created
global E_selected
E_selected = NMesh.EdgeFlags['SELECT']
old_dist = None
def act_mesh_ob():
scn = Scene.GetCurrent()
ob = scn.objects.active
if ob == None or ob.type != 'Mesh':
BPyMessages.Error_NoMeshActive()
return
if ob.getData(mesh=1).multires:
BPyMessages.Error_NoMeshMultiresEdit()
return
return ob
def make_sel_vert(*co):
v= NMesh.Vert(*co)
v.sel = 1
me.verts.append(v)
return v
def make_sel_face(verts):
f = NMesh.Face(verts)
f.sel = 1
me.addFace(f)
def add_to_NV(old,dir,new):
try:
NV[old][dir] = new
except:
NV[old] = {dir:new}
def get_v(old, *neighbors):
# compute the direction of the new vert
if len(neighbors) == 1: dir = (neighbors[0].co - old.co).normalize()
#dir
else: dir = (neighbors[0].co - old.co).normalize() + (neighbors[1].co-old.co).normalize()
# look in NV if this vert already exists
key = tuple(dir)
if old in NV and key in NV[old] : return NV[old][key]
# else, create it
new = old.co + dist.val*dir
v = make_sel_vert(new.x,new.y,new.z)
add_to_NV(old,key,v)
return v
def make_faces():
""" Analyse the mesh, make the faces corresponding to selected faces and
fill the structures NE and NC """
# make the differents flags consistent
for e in me.edges:
if e.flag & E_selected :
e.v1.sel = 1
e.v2.sel = 1
NF =[] # NF : New faces
for f in me.faces:
V = f.v
nV = len(V)
enumV = range(nV)
E = [me.findEdge(V[i],V[(i+1) % nV]) for i in enumV]
Esel = [x.flag & E_selected for x in E]
# look for selected vertices and creates a list containing the new vertices
newV = V[:]
changes = False
for (i,v) in enumerate(V):
if v.sel :
changes = True
if Esel[i-1] == 0 and Esel[i] == 1 : newV[i] = get_v(v,V[i-1])
elif Esel[i-1] == 1 and Esel[i] == 0 : newV[i] = get_v(v,V[(i+1) % nV])
elif Esel[i-1] == 1 and Esel[i] == 1 : newV[i] = get_v(v,V[i-1],V[(i+1) % nV])
else : newV[i] = [get_v(v,V[i-1]),get_v(v,V[(i+1) % nV])]
if changes:
# determine and store the face to be created
lenV = [len(x) for x in newV]
if 2 not in lenV :
new_f = NMesh.Face(newV)
if sum(Esel) == nV : new_f.sel = 1
NF.append(new_f)
else :
nb2 = lenV.count(2)
if nV == 4 : # f is a quad
if nb2 == 1 :
ind2 = lenV.index(2)
NF.append(NMesh.Face([newV[ind2-1],newV[ind2][0],newV[ind2][1],newV[ind2-3]]))
NF.append(NMesh.Face([newV[ind2-1],newV[ind2-2],newV[ind2-3]]))
elif nb2 == 2 :
# We must know if the tuples are neighbours
ind2 = ''.join([str(x) for x in lenV+lenV[:1]]).find('22')
if ind2 != -1 : # They are
NF.append(NMesh.Face([newV[ind2][0],newV[ind2][1],newV[ind2-3][0],newV[ind2-3][1]]))
NF.append(NMesh.Face([newV[ind2][0],newV[ind2-1],newV[ind2-2],newV[ind2-3][1]]))
else: # They aren't
ind2 = lenV.index(2)
NF.append(NMesh.Face([newV[ind2][0],newV[ind2][1],newV[ind2-2][0],newV[ind2-2][1]]))
NF.append(NMesh.Face([newV[ind2][1],newV[ind2-3],newV[ind2-2][0]]))
NF.append(NMesh.Face([newV[ind2][0],newV[ind2-1],newV[ind2-2][1]]))
elif nb2 == 3 :
ind2 = lenV.index(3)
NF.append(NMesh.Face([newV[ind2-1][1],newV[ind2],newV[ind2-3][0]]))
NF.append(NMesh.Face([newV[ind2-1][0],newV[ind2-1][1],newV[ind2-3][0],newV[ind2-3][1]]))
NF.append(NMesh.Face([newV[ind2-3][1],newV[ind2-2][0],newV[ind2-2][1],newV[ind2-1][0]]))
else:
if (newV[0][1].co-newV[3][0].co).length + (newV[1][0].co-newV[2][1].co).length \
< (newV[0][0].co-newV[1][1].co).length + (newV[2][0].co-newV[3][1].co).length :
ind2 = 0
else :
ind2 = 1
NF.append(NMesh.Face([newV[ind2-1][0],newV[ind2-1][1],newV[ind2][0],newV[ind2][1]]))
NF.append(NMesh.Face([newV[ind2][1],newV[ind2-3][0],newV[ind2-2][1],newV[ind2-1][0]]))
NF.append(NMesh.Face([newV[ind2-3][0],newV[ind2-3][1],newV[ind2-2][0],newV[ind2-2][1]]))
else : # f is a tri
if nb2 == 1:
ind2 = lenV.index(2)
NF.append(NMesh.Face([newV[ind2-2],newV[ind2-1],newV[ind2][0],newV[ind2][1]]))
elif nb2 == 2:
ind2 = lenV.index(3)
NF.append(NMesh.Face([newV[ind2-1][1],newV[ind2],newV[ind2-2][0]]))
NF.append(NMesh.Face([newV[ind2-2][0],newV[ind2-2][1],newV[ind2-1][0],newV[ind2-1][1]]))
else:
ind2 = min( [((newV[i][1].co-newV[i-1][0].co).length, i) for i in enumV] )[1]
NF.append(NMesh.Face([newV[ind2-1][1],newV[ind2][0],newV[ind2][1],newV[ind2-2][0]]))
NF.append(NMesh.Face([newV[ind2-2][0],newV[ind2-2][1],newV[ind2-1][0],newV[ind2-1][1]]))
# Preparing the corners
for i in enumV:
if lenV[i] == 2 : NC.setdefault(V[i],[]).append(newV[i])
old_faces.append(f)
# Preparing the Edges
for i in enumV:
if Esel[i]:
verts = [newV[i],newV[(i+1) % nV]]
if V[i].index > V[(i+1) % nV].index : verts.reverse()
NE.setdefault(E[i],[]).append(verts)
# Create the faces
for f in NF: me.addFace(f)
def make_edges():
""" Make the faces corresponding to selected edges """
for old,new in NE.iteritems() :
if len(new) == 1 : # This edge was on a border
oldv = [old.v1, old.v2]
if old.v1.index < old.v2.index : oldv.reverse()
make_sel_face(oldv+new[0])
me.findEdge(*oldv).flag |= E_selected
me.findEdge(*new[0]).flag |= E_selected
#PY23 NO SETS# for v in oldv : NV_ext.add(v)
for v in oldv : NV_ext[v]= None
else:
make_sel_face(new[0] + new[1][::-1])
me.findEdge(*new[0]).flag |= E_selected
me.findEdge(*new[1]).flag |= E_selected
def make_corners():
""" Make the faces corresponding to corners """
for v in NV.iterkeys():
V = NV[v].values()
nV = len(V)
if nV == 1: pass
elif nV == 2 :
#PY23 NO SETS# if v in NV_ext:
if v in NV_ext.iterkeys():
make_sel_face(V+[v])
me.findEdge(*V).flag |= E_selected
else:
#PY23 NO SETS# if nV == 3 and v not in NV_ext : make_sel_face(V)
if nV == 3 and v not in NV_ext.iterkeys() : make_sel_face(V)
else :
# We need to know which are the edges around the corner.
# First, we look for the quads surrounding the corner.
eed = []
for old, new in NE.iteritems():
if v in (old.v1,old.v2) :
if v.index == min(old.v1.index,old.v2.index) : ind = 0
else : ind = 1
if len(new) == 1: eed.append([v,new[0][ind]])
else : eed.append([new[0][ind],new[1][ind]])
# We will add the edges coming from faces where only one vertice is selected.
# They are stored in NC.
if v in NC: eed = eed+NC[v]
# Now we have to sort these vertices
hc = {}
for (a,b) in eed :
hc.setdefault(a,[]).append(b)
hc.setdefault(b,[]).append(a)
for x0,edges in hc.iteritems():
if len(edges) == 1 : break
b = [x0] # b will contain the sorted list of vertices
for i in xrange(len(hc)-1):
for x in hc[x0] :
if x not in b : break
b.append(x)
x0 = x
b.append(b[0])
# Now we can create the faces
if len(b) == 5: make_sel_face(b[:4])
else:
New_V = Vector(0.0, 0.0,0.0)
New_d = [0.0, 0.0,0.0]
for x in hc.iterkeys(): New_V += x.co
for dir in NV[v] :
for i in xrange(3): New_d[i] += dir[i]
New_V *= 1./len(hc)
for i in xrange(3) : New_d[i] /= nV
center = make_sel_vert(New_V.x,New_V.y,New_V.z)
add_to_NV(v,tuple(New_d),center)
for k in xrange(len(b)-1): make_sel_face([center, b[k], b[k+1]])
if 2 < nV and v in NC :
for edge in NC[v] : me.findEdge(*edge).flag |= E_selected
def clear_old():
""" Erase old faces and vertices """
for f in old_faces: me.removeFace(f)
for v in NV.iterkeys():
#PY23 NO SETS# if v not in NV_ext : me.verts.remove(v)
if v not in NV_ext.iterkeys() : me.verts.remove(v)
for e in me.edges:
if e.flag & E_selected :
e.v1.sel = 1
e.v2.sel = 1
######################################################################
# Interface
global dist
dist = Create(0.2)
left = Create(0.0)
right = Create(1.0)
num = Create(2)
# Events
EVENT_NOEVENT = 1
EVENT_BEVEL = 2
EVENT_UPDATE = 3
EVENT_RECURS = 4
EVENT_EXIT = 5
def draw():
global dist, left, right, num, old_dist
global EVENT_NOEVENT, EVENT_BEVEL, EVENT_UPDATE, EVENT_RECURS, EVENT_EXIT
glClear(GL_COLOR_BUFFER_BIT)
Button("Bevel",EVENT_BEVEL,10,100,280,25)
BeginAlign()
left=Number('', EVENT_NOEVENT,10,70,45, 20,left.val,0,right.val,'Set the minimum of the slider')
dist=Slider("Thickness ",EVENT_UPDATE,60,70,180,20,dist.val,left.val,right.val,0, \
"Thickness of the bevel, can be changed even after bevelling")
right = Number("",EVENT_NOEVENT,245,70,45,20,right.val,left.val,200,"Set the maximum of the slider")
EndAlign()
glRasterPos2d(8,40)
Text('To finish, you can use recursive bevel to smooth it')
if old_dist != None:
num=Number('', EVENT_NOEVENT,10,10,40, 16,num.val,1,100,'Recursion level')
Button("Recursive",EVENT_RECURS,55,10,100,16)
Button("Exit",EVENT_EXIT,210,10,80,20)
def event(evt, val):
if ((evt == QKEY or evt == ESCKEY) and not val): Exit()
def bevent(evt):
if evt == EVENT_EXIT : Exit()
elif evt == EVENT_BEVEL : bevel()
elif evt == EVENT_UPDATE :
try: bevel_update()
except NameError : pass
elif evt == EVENT_RECURS : recursive()
Register(draw, event, bevent)
######################################################################
def bevel():
""" The main function, which creates the bevel """
global me,NV,NV_ext,NE,NC, old_faces,old_dist
ob = act_mesh_ob()
if not ob: return
Window.WaitCursor(1) # Change the Cursor
t= Blender.sys.time()
is_editmode = Window.EditMode()
if is_editmode: Window.EditMode(0)
me = ob.data
NV = {}
#PY23 NO SETS# NV_ext = set()
NV_ext= {}
NE = {}
NC = {}
old_faces = []
make_faces()
make_edges()
make_corners()
clear_old()
old_dist = dist.val
print '\tbevel in %.6f sec' % (Blender.sys.time()-t)
me.update(1)
if is_editmode: Window.EditMode(1)
Window.WaitCursor(0)
Blender.Redraw()
def bevel_update():
""" Use NV to update the bevel """
global dist, old_dist
if old_dist == None:
# PupMenu('Error%t|Must bevel first.')
return
is_editmode = Window.EditMode()
if is_editmode: Window.EditMode(0)
fac = dist.val - old_dist
old_dist = dist.val
for old_v in NV.iterkeys():
for dir in NV[old_v].iterkeys():
for i in xrange(3):
NV[old_v][dir].co[i] += fac*dir[i]
me.update(1)
if is_editmode: Window.EditMode(1)
Blender.Redraw()
def recursive():
""" Make a recursive bevel... still experimental """
global dist
from math import pi, sin
if num.val > 1:
a = pi/4
ang = []
for k in xrange(num.val):
ang.append(a)
a = (pi+2*a)/4
l = [2*(1-sin(x))/sin(2*x) for x in ang]
R = dist.val/sum(l)
l = [x*R for x in l]
dist.val = l[0]
bevel_update()
for x in l[1:]:
dist.val = x
bevel()

View File

@@ -0,0 +1,729 @@
#!BPY
# coding: utf-8
"""
Name: 'BlenderLipSynchro'
Blender: 242
Group: 'Animation'
Tooltip: 'Import phonemes from Papagayo or JLipSync for lip synchronization'
"""
__author__ = "Dienben: Benoit Foucque dienben_mail@yahoo.fr"
__url__ = ["blenderLipSynchro Blog, http://blenderlipsynchro.blogspot.com/",
"Papagayo (Python), http://www.lostmarble.com/papagayo/index.shtml",
"JLipSync (Java), http://jlipsync.sourceforge.net/"]
__version__ = "2.0"
__bpydoc__ = """\
Description:
This script imports Voice Export made by Papagayo or JLipSync and maps the export with your shapes.
Usage:
Import a Papagayo or JLipSync voice export file and link it with your shapes.
Note:<br>
- Naturally, you need files exported from one of the supported lip synching
programs. Check their sites to learn more and download them.
"""
# --------------------------------------------------------------------------
# BlenderLipSynchro
# --------------------------------------------------------------------------
# ***** 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 LICENCE BLOCK *****
# --------------------------------------------------------------------------
#il y a 3 etapes
#la deuxieme on charge le dictionnaire de correspondance
#la troisieme on fait le choix des correpondance
#la quatrieme on construit les cles a partir du fichiers frame
#there are 3 stages
#the second one load the mapping dictionnary
#the tird make the mapping
#the fourth make the key in the IPO Curve
#voici mes differents imports
#the imports
import os
import Blender
from Blender import Ipo
from Blender.Draw import *
from Blender.BGL import *
from Blender.sys import basename
#ici commencent mes fonctions
#here begin my functions
#cette fonction trace l'interface graphique
#this functions draw the User interface
def trace():
#voici mes variables pouvant etre modifie
#my variables
global nbr_phoneme, mon_fichier_dico
global let01, let02, let03, let04,let05, let06, let07, let08, let09, let10
global let11, let12, let13, let14,let15, let16, let17, let18, let19, let20
global let21, let22, let23, let24
global let01selectkey,let02selectkey,let03selectkey,let04selectkey,let05selectkey
global let06selectkey,let07selectkey,let08selectkey,let09selectkey,let10selectkey,let11selectkey
global let12selectkey,let13selectkey,let14selectkey,let15selectkey,let16selectkey,let17selectkey
global let18selectkey,let19selectkey,let20selectkey,let21selectkey,let22selectkey,let23selectkey
global let24selectkey
glClearColor(0.4,0.5,0.6 ,0.0)
glClear(GL_COLOR_BUFFER_BIT)
glColor3d(1,1,1)
glRasterPos2i(87, 375)
Text("Blendersynchro V 2.0")
glColor3d(1,1,1)
glRasterPos2i(84, 360)
Text("Programming: Dienben")
glColor3d(0,0,0)
glRasterPos2i(13, 342)
Text("Lip Synchronization Tool")
glColor3d(0,0,0)
glRasterPos2i(13, 326)
Text("Thanks to Chris Clawson and Liubomir Kovatchev")
glColor3d(1,1,1)
glRasterPos2i(5, 320)
Text("_______________________________________________________")
glColor3d(0,0,0)
glRasterPos2i(6, 318)
Text("_______________________________________________________")
if (etape==1):
#cette etape permet de choisi la correspondance entre les phonemes et les cles
#this stage offer the possibility to choose the mapping between phonems and shapes
glColor3d(1,1,1)
glRasterPos2i(140, 300)
Text("Objet: "+Blender.Object.GetSelected()[0].getName() )
glColor3d(1,1,1)
glRasterPos2i(5, 215)
Text("Assign phonems to shapes:")
#on mesure la taille de la liste de phonemes
#this is the lenght of the phonem list
nbr_phoneme=len(liste_phoneme)
#on dessine les listes de choix
#we draw the choice list
#
if (nbr_phoneme > 0):
let01 = String(" ", 4, 5, 185, 30, 16, liste_phoneme[0], 3)
glColor3d(0,0,0)
glRasterPos2i(40, 188)
Text("=")
let01selectkey = Menu(key_menu, 50, 50, 185, 70, 16, let01selectkey.val)
#
if (nbr_phoneme > 1):
let02 = String(" ", 4, 150, 185, 30, 16, liste_phoneme[1], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 188)
Text("=")
let02selectkey = Menu(key_menu, 51, 195, 185, 70, 16, let02selectkey.val)
#
if (nbr_phoneme > 2):
let03 = String(" ", 4, 5, 165, 30, 16, liste_phoneme[2], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 168)
Text("=")
let03selectkey = Menu(key_menu, 52, 50, 165, 70, 16, let03selectkey.val)
#
if (nbr_phoneme > 3):
let04 = String(" ", 4, 150, 165, 30, 16, liste_phoneme[3], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 168)
Text("=")
let04selectkey = Menu(key_menu, 53, 195, 165, 70, 16, let04selectkey.val)
#
if (nbr_phoneme > 4):
let05 = String(" ", 4, 5, 145, 30, 16, liste_phoneme[4], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 148)
Text("=")
let05selectkey = Menu(key_menu, 54, 50, 145, 70, 16, let05selectkey.val)
#
if (nbr_phoneme > 5):
let06 = String(" ", 4, 150, 145, 30, 16, liste_phoneme[5], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 148)
Text("=")
let06selectkey = Menu(key_menu, 55, 195, 145, 70, 16, let06selectkey.val)
#
if (nbr_phoneme > 6):
let07 = String(" ", 4, 5, 125, 30, 16, liste_phoneme[6], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 128)
Text("=")
let07selectkey = Menu(key_menu, 56, 50, 125, 70, 16, let07selectkey.val)
#
if (nbr_phoneme > 7):
let08 = String(" ", 4, 150, 125, 30, 16, liste_phoneme[7], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 128)
Text("=")
let08selectkey = Menu(key_menu, 57, 195, 125, 70, 16,let08selectkey.val)
#
if (nbr_phoneme > 8):
let09 = String(" ", 4, 5, 105, 30, 16, liste_phoneme[8], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 108)
Text("=")
let09selectkey = Menu(key_menu, 58, 50, 105, 70, 16,let09selectkey.val)
#
if (nbr_phoneme > 9):
let10 = String(" ", 4, 150, 105, 30, 16, liste_phoneme[9], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 108)
Text("=")
let10selectkey = Menu(key_menu, 59, 195, 105, 70, 16, let10selectkey.val)
#
if (nbr_phoneme > 10):
let11 = String(" ", 4, 5, 85, 30, 16, liste_phoneme[10], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 88)
Text("=")
let11selectkey = Menu(key_menu, 60, 50, 85, 70, 16, let11selectkey.val)
#
if (nbr_phoneme > 11):
let12 = String(" ", 4, 150, 85, 30, 16, liste_phoneme[11], 2)
glColor3d(0,0,0)
Text("=")
let12selectkey = Menu(key_menu, 61, 195, 85, 70, 16, let12selectkey.val)
#
if (nbr_phoneme > 12):
let13 = String(" ", 4, 5, 65, 30, 16, liste_phoneme[12], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 68)
Text("=")
let13selectkey = Menu(key_menu, 62, 50, 65, 70, 16, let13selectkey.val)
#
if (nbr_phoneme > 13):
let14 = String(" ", 4, 150, 65, 30, 16, liste_phoneme[13], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 68)
Text("=")
let14selectkey = Menu(key_menu, 63, 195, 65, 70, 16, let14selectkey.val)
#
if (nbr_phoneme > 14):
let15 = String(" ", 4, 5, 45, 30, 16, liste_phoneme[14], 2)
glColor3d(0,0,0)
glRasterPos2i(40, 48)
Text("=")
let15selectkey = Menu(key_menu, 64, 50, 45, 70, 16, let15selectkey.val)
#
if (nbr_phoneme > 15):
let16 = String(" ", 4, 150, 45, 30, 16, liste_phoneme[15], 2)
glColor3d(0,0,0)
glRasterPos2i(185, 48)
Text("=")
let16selectkey = Menu(key_menu, 65, 195, 45, 70, 16, let16selectkey.val)
#
if (nbr_phoneme > 16):
let17 = String(" ", 4, 295, 185, 30, 16, liste_phoneme[16], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 188)
Text("=")
let17selectkey = Menu(key_menu, 66, 340, 185, 70, 16, let17selectkey.val)
#
if (nbr_phoneme > 17):
let18 = String(" ", 4, 440, 185, 70, 16, liste_phoneme[17], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 188)
Text("=")
let18selectkey = Menu(key_menu, 67, 525, 185, 70, 16, let18selectkey.val)
#
if (nbr_phoneme > 18):
let19 = String(" ", 4, 295, 165, 30, 16, liste_phoneme[18], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 168)
Text("=")
let19selectkey = Menu(key_menu, 68, 340, 165, 70, 16, let19selectkey.val)
#
if (nbr_phoneme > 19):
let20 = String(" ", 4, 440, 165, 70, 16, liste_phoneme[19], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 168)
Text("=")
let20selectkey = Menu(key_menu, 69, 525, 165, 70, 16, let20selectkey.val)
#
if (nbr_phoneme > 20):
let21 = String(" ", 4, 295, 145, 30, 16, liste_phoneme[20], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 148)
Text("=")
let21selectkey = Menu(key_menu, 70, 340, 145, 70, 16, let21selectkey.val)
#
if (nbr_phoneme > 21):
let22 = String(" ", 4, 440, 145, 70, 16, liste_phoneme[21], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 148)
Text("=")
let22selectkey = Menu(key_menu, 71, 525, 145, 70, 16, let22selectkey.val)
#
if (nbr_phoneme > 22):
let23 = String(" ", 4, 295, 125, 30, 16, liste_phoneme[22], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 128)
Text("=")
let23selectkey = Menu(key_menu, 72, 340, 125, 70, 16,let23selectkey.val)
#
if (nbr_phoneme > 23):
let24 = String(" ", 4, 440, 125, 70, 16, liste_phoneme[23], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 128)
Text("=")
let24selectkey = Menu(key_menu, 73, 525, 125, 70, 16, let24selectkey.val)
#
if (nbr_phoneme > 24):
let25 = String(" ", 4, 295, 105, 30, 16, liste_phoneme[24], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 108)
Text("=")
let25selectkey = Menu(key_menu, 74, 340, 105, 70, 16, let25selectkey.val)
#
if (nbr_phoneme > 25):
let26 = String(" ", 4, 440, 105, 70, 16, liste_phoneme[25], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 108)
Text("=")
let26selectkey = Menu(key_menu, 75, 525, 105, 70, 16,let26selectkey.val)
#
if (nbr_phoneme > 26):
let27 = String(" ", 4, 295, 85, 30, 16, liste_phoneme[26], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 88)
Text("=")
let27selectkey = Menu(key_menu, 76, 340, 85, 70, 16, let27selectkey.val)
#
if (nbr_phoneme > 27):
let28 = String(" ", 4, 440, 85, 70, 16, liste_phoneme[27], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 88)
Text("=")
let28selectkey = Menu(key_menu, 77, 525, 85, 70, 16,let28selectkey.val)
#
if (nbr_phoneme > 28):
let29 = String(" ", 4, 295, 65, 30, 16, liste_phoneme[28], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 68)
Text("=")
let29selectkey = Menu(key_menu, 78, 340, 65, 70, 16, let29selectkey.val)
#
if (nbr_phoneme > 29):
let30 = String(" ", 4, 440, 65, 70, 16, liste_phoneme[29], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 68)
Text("=")
let30selectkey = Menu(key_menu, 79, 525, 65, 70, 16, let30selectkey.val)
#
if (nbr_phoneme > 30):
let31 = String(" ", 4, 295, 45, 30, 16, liste_phoneme[30], 2)
glColor3d(0,0,0)
glRasterPos2i(330, 48)
Text("=")
let31selectkey = Menu(key_menu, 80, 340, 45, 70, 16, let31selectkey.val)
#
if (nbr_phoneme > 31):
let32 = String(" ", 4, 440, 45, 70, 16, liste_phoneme[31], 8)
glColor3d(0,0,0)
glRasterPos2i(515, 48)
Text("=")
let32selectkey = Menu(key_menu, 81, 525, 45, 70, 16, let32selectkey.val)
Button("Go", 3, 155, 5, 145, 22)
if (etape==2):
glColor3d(1,1,1)
glRasterPos2i(125, 200)
Text("Operation Completed")
if (etape==0):
glColor3d(1,1,1)
glRasterPos2i(125, 200)
Text("Please select a Mesh'Object and Create all the IPO Curves for your Shapes")
if (etape==3):
#this stage permits to load a custom dictionnary
load_file_text = "Load File"
if mon_fichier_dico:
Button("Import Loaded File", 2, 5, 5, 145, 22)
glColor3d(1,1,1)
glRasterPos2i(6, 50)
Text("loaded file: %s" % basename(mon_fichier_dico))
load_file_text = "Choose Another File"
Button(load_file_text, 8, 125, 180, 145, 22)
glRasterPos2i(6, 40)
Text("_______________________________________________________")
glColor3d(0,0,0)
glRasterPos2i(6, 38)
Text("_______________________________________________________")
Button("Exit", 1, 305, 5, 80, 22)
#cette fonction sur evenement quite en cas d'ESC
#this functions catch the ESC event and quit
def event(evt,val):
if (evt == ESCKEY and not val): Exit()
#cette fonction gere les evenements
#the event functions
def bevent(evt):
global etape,soft_type,liste_phoneme,dico_phoneme_export
if (evt == 1):
Exit()
elif (evt == 2):
#c'est l'import du dictionnaire
#we create and import the dictionnary
lecture_chaine(mon_fichier_dico,dico_phoneme_export)
construction_dictionnaire_phoneme()
#we change the stage
etape=1
elif (evt == 3):
#c'est l'import
#we import
lecture_chaine(mon_fichier_export,dico_phoneme_export)
construction_dico_correspondance()
construction_lipsynchro()
#on change d'etape
#we change the stage
etape=2
elif (evt == 8):
#we choose the file
Blender.Window.FileSelector(selectionner_fichier,"Select File")
Blender.Redraw()
#cette fonction recupere le nom et le chemin du fichier dictionnaire
#we catch the name and the path of the dictionnary
def selectionner_fichier(filename):
global mon_fichier_dico,mon_fichier_export
mon_fichier_dico=filename
mon_fichier_export=filename
#fonction de lecture de la liste frame phoneme
#we read the frame and phonems
def lecture_chaine(fichier,liste):
mon_fichier=open(fichier)
#je lis la premiere ligne qui contiens la version de moho
#first, we read the moho version
mon_fichier.readline()
#je lis jusqu'a la fin
#then we read until the end of the file
while 1:
ma_ligne=mon_fichier.readline()
if ma_ligne=='':
break
decoup=ma_ligne.split()
liste[decoup[0]]=decoup[1]
print liste
#fonction qui construit la liste dictionnaire simple
#we make the dictionnary
def construction_dictionnaire_phoneme():
global liste_phoneme
index_liste=0
#je transforme mon dictionnaire en list de tulpes
#we transform the list in tulpes
ma_liste=dico_phoneme_export.items()
#je parcours ma liste a la recherche d'elements non existant
#we read the list to find non existing elements
print dico_phoneme
for index in range(len(ma_liste)):
if ma_liste[index][1] not in liste_phoneme:
liste_phoneme[index_liste:index_liste]=[ma_liste[index][1]]
index_liste=index_liste+1
print liste_phoneme
#cette fonction recupere les courbes cible
#this functon catch the IPO curve
def recuperation_courbe():
global key_menu,dico_key
#on recupere le nom des shapes
#we catch the shapes
key=Blender.Object.GetSelected()[0].getData().getKey().getBlocks()
for n in range(len(key)):
#on vire la premi<6D>re cle (en effet basic n'est pas une cle en tant que telle)
#we threw away the basic shapes
if (n>0):
key_menu=key_menu+key[n].name + " %x" + str(n-1) + "|"
dico_key[str(n-1)]=Blender.Object.GetSelected()[0].getData().getKey().getIpo().getCurves()[n-1]
print "dico_key"
print dico_key
print 'end dico_key'
#cette fonction construit un dictionnaire de correspondance entre les phonemes prononces et les cles a utiliser
#we make the dictionnary for the mapping between shapes and phonems
def construction_dico_correspondance():
global dico_correspondance
#je parcours les phonemes
#we read the phonems
if (nbr_phoneme>0):
dico_correspondance[liste_phoneme[0]]=dico_key[str(let01selectkey.val)]
if (nbr_phoneme>1):
dico_correspondance[liste_phoneme[1]]=dico_key[str(let02selectkey.val)]
if (nbr_phoneme>2):
dico_correspondance[liste_phoneme[2]]=dico_key[str(let03selectkey.val)]
if (nbr_phoneme>3):
dico_correspondance[liste_phoneme[3]]=dico_key[str(let04selectkey.val)]
if (nbr_phoneme>4):
dico_correspondance[liste_phoneme[4]]=dico_key[str(let05selectkey.val)]
if (nbr_phoneme>5):
dico_correspondance[liste_phoneme[5]]=dico_key[str(let06selectkey.val)]
if (nbr_phoneme>6):
dico_correspondance[liste_phoneme[6]]=dico_key[str(let07selectkey.val)]
if (nbr_phoneme>7):
dico_correspondance[liste_phoneme[7]]=dico_key[str(let08selectkey.val)]
if (nbr_phoneme>8):
dico_correspondance[liste_phoneme[8]]=dico_key[str(let09selectkey.val)]
if (nbr_phoneme>9):
dico_correspondance[liste_phoneme[9]]=dico_key[str(let10selectkey.val)]
if (nbr_phoneme>10):
dico_correspondance[liste_phoneme[10]]=dico_key[str(let11selectkey.val)]
if (nbr_phoneme>11):
dico_correspondance[liste_phoneme[11]]=dico_key[str(let12selectkey.val)]
if (nbr_phoneme>12):
dico_correspondance[liste_phoneme[12]]=dico_key[str(let13selectkey.val)]
if (nbr_phoneme>13):
dico_correspondance[liste_phoneme[13]]=dico_key[str(let14selectkey.val)]
if (nbr_phoneme>14):
dico_correspondance[liste_phoneme[14]]=dico_key[str(let15selectkey.val)]
if (nbr_phoneme>15):
dico_correspondance[liste_phoneme[15]]=dico_key[str(let16selectkey.val)]
if (nbr_phoneme>16):
dico_correspondance[liste_phoneme[16]]=dico_key[str(let17selectkey.val)]
if (nbr_phoneme>17):
dico_correspondance[liste_phoneme[17]]=dico_key[str(let18selectkey.val)]
if (nbr_phoneme>18):
dico_correspondance[liste_phoneme[18]]=dico_key[str(let19selectkey.val)]
if (nbr_phoneme>19):
dico_correspondance[liste_phoneme[19]]=dico_key[str(let20selectkey.val)]
if (nbr_phoneme>20):
dico_correspondance[liste_phoneme[20]]=dico_key[str(let21selectkey.val)]
if (nbr_phoneme>21):
dico_correspondance[liste_phoneme[21]]=dico_key[str(let22selectkey.val)]
if (nbr_phoneme>22):
dico_correspondance[liste_phoneme[22]]=dico_key[str(let23selectkey.val)]
if (nbr_phoneme>23):
dico_correspondance[liste_phoneme[23]]=dico_key[str(let24selectkey.val)]
if (nbr_phoneme>24):
dico_correspondance[liste_phoneme[24]]=dico_key[str(let25selectkey.val)]
if (nbr_phoneme>25):
dico_correspondance[liste_phoneme[25]]=dico_key[str(let26selectkey.val)]
if (nbr_phoneme>26):
dico_correspondance[liste_phoneme[26]]=dico_key[str(let27selectkey.val)]
if (nbr_phoneme>27):
dico_correspondance[liste_phoneme[27]]=dico_key[str(let28selectkey.val)]
if (nbr_phoneme>28):
dico_correspondance[liste_phoneme[28]]=dico_key[str(let29selectkey.val)]
if (nbr_phoneme>29):
dico_correspondance[liste_phoneme[29]]=dico_key[str(let30selectkey.val)]
if (nbr_phoneme>30):
dico_correspondance[liste_phoneme[30]]=dico_key[str(let31selectkey.val)]
if (nbr_phoneme>31):
dico_correspondance[liste_phoneme[31]]=dico_key[str(let32selectkey.val)]
print dico_correspondance
#cette fonction ajoute un points a la cle donnee a la frame donnee
#we add a point to the IPO curve Target
def ajoute_point(cle,frame,valeur):
cle.setInterpolation('Linear')
cle.append((frame,valeur))
cle.Recalc()
#cette fonction parcours le dictionnaire des frame <20> ajouter et construit les points
#we add all the point to the IPO Curve
def construction_lipsynchro():
print "je construit"
doublet_old=""
#construction de la liste des frame
cpt=0
liste_frame=[]
for frame in dico_phoneme_export:
liste_frame.append(int(frame))
cpt=cpt+1
liste_frame.sort()
print "listeframe"
print liste_frame
print "fini"
for doublet in liste_frame:
ajoute_point(dico_correspondance[dico_phoneme_export[str(doublet)]],doublet,1)
if (doublet_old==""):
ajoute_point(dico_correspondance[dico_phoneme_export[str(doublet)]],(doublet-2),0)
if (doublet_old!=''):
if (dico_correspondance[dico_phoneme_export[str(doublet)]]!=dico_correspondance[dico_phoneme_export[doublet_old]]):
print "doublet:"+str(doublet)
print "doublet old:"+doublet_old
ajoute_point(dico_correspondance[dico_phoneme_export[doublet_old]],(int(doublet_old)+2),0)
ajoute_point(dico_correspondance[dico_phoneme_export[str(doublet)]],(doublet-2),0)
doublet_old=str(doublet)
#end of my functions we begin the execution
#je commence l execution-----------------------------------------------------------------------------------------------
#voici mes variables
#declaration et instanciation
#decleration and instanciation
#voici mon objet de travail
objet_travail=Create(0)
#my soft type
soft_type=1
#voici la liste des phoneme effectivement utilise
#the phonems'list
#liste_phoneme_papagayo=['AI','E','O','U','FV','L','WQ','MBP','etc','rest']
#liste_phoneme_jlipsinch=['A','B','C','Closed','D','E','F','G','I','K','L','M','N','O','P','Q','R','S','SH','T','TH','U','V','W']
liste_phoneme=[]
#voici mon dictionnaire des frames o
dico_phoneme_export = Create(0)
dico_phoneme_export={}
dico_phoneme={}
#voici mes cle
key_menu=""
dico_key={}
#voici mes ipo
dico_bloc={}
iponame = Create(0)
#voici mon dictionnaire de correspondance
dico_correspondance={}
try:
#on verifie est bien une mesh et qu'il a des courbes
if ((Blender.Object.GetSelected()[0].getType()=='Mesh')):
#on verifie que l'objet a bien toute ses Courbes
if (len(Blender.Object.GetSelected()[0].getData().getKey().getBlocks())-1==Blender.Object.GetSelected()[0].getData().getKey().getIpo().getNcurves()):
etape=3
#on lance la creation du dictionnaire
recuperation_courbe()
else:
print "not the good number of IPO Curve"
etape = 0
else:
print "error: bad object Type:"
print Blender.Object.GetSelected()[0].getType()
etape = 0
except:
print 'error: exception'
etape = 0
#voici le fichier dictionnaire
mon_fichier_dico=""
#voici le fichier export pamela
mon_fichier_export=""
let01selectkey = Create(0)
let02selectkey = Create(0)
let03selectkey = Create(0)
let04selectkey = Create(0)
let05selectkey = Create(0)
let06selectkey = Create(0)
let07selectkey = Create(0)
let08selectkey = Create(0)
let09selectkey = Create(0)
let10selectkey = Create(0)
let11selectkey = Create(0)
let12selectkey = Create(0)
let13selectkey = Create(0)
let14selectkey = Create(0)
let15selectkey = Create(0)
let16selectkey = Create(0)
let17selectkey = Create(0)
let18selectkey = Create(0)
let19selectkey = Create(0)
let20selectkey = Create(0)
let21selectkey = Create(0)
let22selectkey = Create(0)
let23selectkey = Create(0)
let24selectkey = Create(0)
Register (trace,event,bevent)

View File

@@ -0,0 +1,121 @@
Version 3.233-2004
******************
Espanol
Sale del programa
Utilidades de...%t|Alinea objetos%x1|Creacion%x2|Edita mallas%x3|Edita objetos%x4
11
Mov
Esc
Encaja
Abarca
Separa
Alinea
Rota
Incr.
Crea nuevos objetos
Es+
Es*
Separar entre:%t|Origenes%x1|Centros geometricos%x2|Minimos%x3|Maximos%x4|Baricentro%x5|Objetos%x6
Crear%t|Arco (3 ptos.)%x1|Arco (interactivo)%x2|Circunferencia (3 ptos.)%x3
12
Puntos
Centro
Orden
Objeto
AngIni:
AngFin:
Angulo:
Radio:
Puntos:
Centro
Nombre:
Puntos
Modifica vertices%t|Subdivide%x1|Envia a un plano%x2|Aplica LocRotSize%x3
Partes
Proyectar en el plano:%t|Coordenado global...%x1|Coordenado local...%x2
Actuar sobre el plano%t|Yz%x1|Zx%x2|Xy%x3
En la direcci<63>n%t|X%x1|Y%x2|Z%x3|Ortogonal al plano%x4
Captura
Buffer%t|Copia vector diferencia%x1|Copia distancia%x2|Copia diferencia de rotacion%x3|Copia media LocRotSiz%x4|Ver buffer en consola%x5
Transformar LocRotSize%t|Hacia el obj. activo%x1|Aleatoriamente%x2
Poner a distancia fija%x1|Sumar (desp. absoluto)%x2|Multiplicar (desp. relativo)%x3
********************
English
Exit program
Utils about:%t|Align Objects%x1|Create%x2|Edit Meshes%x3|Edit Objects%x4
11
Mov
Sca
Fit
Embrace
Separate
Align
Rota
Incr.
Create new objects
Sc+
Sc*
Separate between:%t|Origins%x1|Geometric centers%x2|Minimum%x3|Maximum%x4|Baricenter%x5|Objects%x6
Create what%t|Arc (3 pts.)%x1|Arc (interactive)%x2|Circunference (3 pts.)%x3
12
Points
Centre
Sort
Object
AngIni:
AngEnd:
Angle:
Radius:
Points:
Centre
ObjName:
Points
Modify vertices%t|Subdivide edges%x1|Send to a plane%x2|Set LocRotSize%x3
Parts
Project onto the plane:%t|Global coordinated...%x1|Local coordinated...%x2
Act on plane%t|Yz%x1|Zx%x2|Xy%x3
In direction%t|X%x1|Y%x2|Z%x3|Ortogonal to plane%x4
Get
Buffer%t|Copy diference vector%x1|Copy distance%x2|Copy rot diference%x3|Copy LocRotSiz average%x4|Show Buffer in Console%x5
Transform LocRotSize%t|Close to active%x1|Randomly%x2
Set at fixed distance%x1|Add (absolute displ.)%x2|Multiply (relative displ.)%x3
********************
Catala
Surt del programa
Utilitats de...%t|Alinea objectes%x1|Creacio%x2|Edita malles%x3|Edita objetes%x4
11
Mov
Esc
Encaixa
Abarca
Separa
Alinea
Rotacio
Incr.
Crea objectes nous
Es+
Es*
Separa entra:%t|Origens%x1|Centres geometrics%x2|Minims%x3|Maxims%x4|Baricentre%x5|Objectes%x6
Crear%t|Arc (3 pts.)%x1|Arc (interactiu)%x2|Circumferencia (3 pts.)%x3
12
Punts
Centre
Ordre
Objecte
AngIni:
AngFi:
Angle:
Radi:
Punts:
Centre
Nom:
Punts
Modifica vertex%t|Subdivideix%x1|Envia a un pla%x2|Aplica LocRotSize%x3
Parts
Projectar en el pla:%t|Coordenacio global...%x1|Coordenacio local...%x2
Actuar sobre el pla%t|Yz%x1|Zx%x2|Xy%x3
En la direccio%t|X%x1|Y%x2|Z%x3|Ortogonal al pla%x4
Captura
Buffer%t|Copia vector diferencia%x1|Copia distancia%x2|Copia diferencia de rotacio%x3|Copia mitjana LocRotSiz%x4|Veure buffer en consola%x5
Transformar LocRotSize%t|Cap al obj. actiu%x1|Aleatoriamente%x2
Posar a distancia fixa%x1|Sumar (desp. absolut)%x2|Multiplicar (desp. relatiu)%x3

View File

@@ -0,0 +1,6 @@
This folder is for automatically saved scripts configuration data.
To use this feature scripts just need to set a proper Blender.Registry key.
To know more, check the API Reference doc (specifically the API_related and
Registry parts) and the documentation for the "Scripts Config Editor" script.

View File

@@ -0,0 +1,5 @@
vrml97_export = {
'selection_only': 1,
'compressed': 0,
'rotate_z_to_y': 0,
}

View File

@@ -0,0 +1,9 @@
This directory is the default place for scripts to put their data,
like internal files needed by the script and its saved configuration.
Scripts can find the path to this dir using Blender.Get("datadir").
Ex:
import Blender
print Blender.Get("datadir")

View File

@@ -0,0 +1,159 @@
import Blender
from Blender.Window import EditMode, GetCursorPos, GetViewQuat
import bpy
import BPyMessages
def add_mesh_simple(name, verts, edges, faces):
'''
Adds a mesh from verts, edges and faces
name - new object/mesh name
verts - list of 3d vectors
edges - list of int pairs
faces - list of int triplets/quads
'''
scn = bpy.data.scenes.active
if scn.lib: return
ob_act = scn.objects.active
is_editmode = EditMode()
cursor = GetCursorPos()
quat = None
if is_editmode or Blender.Get('add_view_align'): # Aligning seems odd for editmode, but blender does it, oh well
try: quat = Blender.Mathutils.Quaternion(GetViewQuat())
except: pass
# Exist editmode for non mesh types
if ob_act and ob_act.type != 'Mesh' and is_editmode:
EditMode(0)
# We are in mesh editmode
if EditMode():
me = ob_act.getData(mesh=1)
if me.multires:
BPyMessages.Error_NoMeshMultiresEdit()
return
# Add to existing mesh
# must exit editmode to modify mesh
EditMode(0)
me.sel = False
vert_offset = len(me.verts)
edge_offset = len(me.edges)
face_offset = len(me.faces)
# transform the verts
txmat = Blender.Mathutils.TranslationMatrix(Blender.Mathutils.Vector(cursor))
if quat:
mat = quat.toMatrix()
mat.invert()
mat.resize4x4()
txmat = mat * txmat
txmat = txmat * ob_act.matrixWorld.copy().invert()
me.verts.extend(verts)
# Transform the verts by the cursor and view rotation
me.transform(txmat, selected_only=True)
if vert_offset:
me.edges.extend([[i+vert_offset for i in e] for e in edges])
me.faces.extend([[i+vert_offset for i in f] for f in faces])
else:
# Mesh with no data, unlikely
me.edges.extend(edges)
me.faces.extend(faces)
else:
# Object mode add new
me = bpy.data.meshes.new(name)
me.verts.extend(verts)
me.edges.extend(edges)
me.faces.extend(faces)
me.sel = True
# Object creation and location
scn.objects.selected = []
ob_act = scn.objects.new(me, name)
scn.objects.active = ob_act
if quat:
mat = quat.toMatrix()
mat.invert()
mat.resize4x4()
ob_act.setMatrix(mat)
ob_act.loc = cursor
me.calcNormals()
if is_editmode or Blender.Get('add_editmode'):
EditMode(1)
def write_mesh_script(filepath, me):
'''
filepath - path to py file
me - mesh to write
'''
name = me.name
file = open(filepath, 'w')
file.write('#!BPY\n')
file.write('"""\n')
file.write('Name: \'%s\'\n' % name)
file.write('Blender: 245\n')
file.write('Group: \'AddMesh\'\n')
file.write('"""\n\n')
file.write('import BPyAddMesh\n')
file.write('from Blender.Mathutils import Vector\n\n')
file.write('verts = [\\\n')
for v in me.verts:
file.write('Vector(%f,%f,%f),\\\n' % tuple(v.co))
file.write(']\n')
file.write('edges = []\n') # TODO, write loose edges
file.write('faces = [\\\n')
for f in me.faces:
file.write('%s,\\\n' % str(tuple([v.index for v in f])))
file.write(']\n')
file.write('BPyAddMesh.add_mesh_simple("%s", verts, edges, faces)\n' % name)
# The script below can make a file from a mesh with teh above function...
'''
#!BPY
"""
Name: 'Mesh as AddMesh Script'
Blender: 242
Group: 'Mesh'
Tip: ''
"""
import BPyAddMesh
reload(BPyAddMesh)
import bpy
def main():
# Add error checking
scn = bpy.data.scenes.active
ob = scn.objects.active
me = ob.getData(mesh=1)
BPyAddMesh.write_mesh_script('/test.py', me)
main()
'''

View File

@@ -0,0 +1,152 @@
# 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 History:
# 1.0 original release bakes an armature into a matrix
# 1.1 optional params (ACTION_BAKE, ACTION_BAKE_FIRST_FRAME, direct function to key and return the Action
import Blender
from Blender import sys
import bpy
def getBakedPoseData(ob_arm, start_frame, end_frame, ACTION_BAKE = False, ACTION_BAKE_FIRST_FRAME = True):
'''
If you are currently getting IPO's this function can be used to
ACTION_BAKE==False: return a list of frame aligned bone dictionary's
ACTION_BAKE==True: return an action with keys aligned to bone constrained movement
if ACTION_BAKE_FIRST_FRAME is not supplied or is true: keys begin at frame 1
The data in these can be swaped in for the IPO loc and quat
If you want to bake an action, this is not as hard and the ipo hack can be removed.
'''
# --------------------------------- Dummy Action! Only for this functon
backup_action = ob_arm.action
backup_frame = Blender.Get('curframe')
DUMMY_ACTION_NAME = '~DONT_USE~'
# Get the dummy action if it has no users
try:
new_action = bpy.data.actions[DUMMY_ACTION_NAME]
if new_action.users:
new_action = None
except:
new_action = None
if not new_action:
new_action = bpy.data.actions.new(DUMMY_ACTION_NAME)
new_action.fakeUser = False
# ---------------------------------- Done
Matrix = Blender.Mathutils.Matrix
Quaternion = Blender.Mathutils.Quaternion
Vector = Blender.Mathutils.Vector
POSE_XFORM= [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT]
# Each dict a frame
bake_data = [{} for i in xrange(1+end_frame-start_frame)]
pose= ob_arm.getPose()
armature_data= ob_arm.getData();
pose_bones= pose.bones
# --------------------------------- Build a list of arma data for reuse
armature_bone_data = []
bones_index = {}
for bone_name, rest_bone in armature_data.bones.items():
pose_bone = pose_bones[bone_name]
rest_matrix = rest_bone.matrix['ARMATURESPACE']
rest_matrix_inv = rest_matrix.copy().invert()
armature_bone_data.append( [len(bones_index), -1, bone_name, rest_bone, rest_matrix, rest_matrix_inv, pose_bone, None ])
bones_index[bone_name] = len(bones_index)
# Set the parent ID's
for bone_name, pose_bone in pose_bones.items():
parent = pose_bone.parent
if parent:
bone_index= bones_index[bone_name]
parent_index= bones_index[parent.name]
armature_bone_data[ bone_index ][1]= parent_index
# ---------------------------------- Done
# --------------------------------- Main loop to collect IPO data
frame_index = 0
NvideoFrames= end_frame-start_frame
for current_frame in xrange(start_frame, end_frame+1):
if frame_index==0: start=sys.time()
elif frame_index==15: print NvideoFrames*(sys.time()-start),"seconds estimated..." #slows as it grows *3
elif frame_index >15:
percom= frame_index*100/NvideoFrames
print "Frame %i Overall %i percent complete\r" % (current_frame, percom),
ob_arm.action = backup_action
#pose.update() # not needed
Blender.Set('curframe', current_frame)
#Blender.Window.RedrawAll()
#frame_data = bake_data[frame_index]
ob_arm.action = new_action
###for i,pose_bone in enumerate(pose_bones):
for index, parent_index, bone_name, rest_bone, rest_matrix, rest_matrix_inv, pose_bone, ipo in armature_bone_data:
matrix= pose_bone.poseMatrix
parent_bone= rest_bone.parent
if parent_index != -1:
parent_pose_matrix = armature_bone_data[parent_index][6].poseMatrix
parent_bone_matrix_inv = armature_bone_data[parent_index][5]
matrix= matrix * parent_pose_matrix.copy().invert()
rest_matrix= rest_matrix * parent_bone_matrix_inv
matrix=matrix * rest_matrix.copy().invert()
pose_bone.quat= matrix.toQuat()
pose_bone.loc= matrix.translationPart()
if ACTION_BAKE==False:
pose_bone.insertKey(ob_arm, 1, POSE_XFORM) # always frame 1
# THIS IS A BAD HACK! IT SUCKS BIGTIME BUT THE RESULT ARE NICE
# - use a temp action and bake into that, always at the same frame
# so as not to make big IPO's, then collect the result from the IPOs
# Now get the data from the IPOs
if not ipo: ipo = armature_bone_data[index][7] = new_action.getChannelIpo(bone_name)
loc = Vector()
quat = Quaternion()
for curve in ipo:
val = curve.evaluate(1)
curve_name= curve.name
if curve_name == 'LocX': loc[0] = val
elif curve_name == 'LocY': loc[1] = val
elif curve_name == 'LocZ': loc[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
bake_data[frame_index][bone_name] = loc, quat
else:
if ACTION_BAKE_FIRST_FRAME: pose_bone.insertKey(ob_arm, frame_index+1, POSE_XFORM)
else: pose_bone.insertKey(ob_arm, current_frame , POSE_XFORM)
frame_index+=1
print "\nBaking Complete."
ob_arm.action = backup_action
if ACTION_BAKE==False:
Blender.Set('curframe', backup_frame)
return bake_data
elif ACTION_BAKE==True:
return new_action
else: print "ERROR: Invalid ACTION_BAKE %i sent to BPyArmature" % ACTION_BAKE

View File

@@ -0,0 +1,36 @@
# $Id: BPyBlender.py 5652 2005-10-30 19:19:38Z aphex $
#
# --------------------------------------------------------------------------
# BPyBlender.py version 0.3 Mar 20, 2005
# --------------------------------------------------------------------------
# helper functions to be used by other scripts
# --------------------------------------------------------------------------
# ***** 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 LICENCE BLOCK *****
# --------------------------------------------------------------------------
# Basic set of modules Blender should have in all supported platforms.
# The second and third lines are the contents of the Python23.zip file
# included with Windows Blender binaries along with zlib.pyd.
# Other platforms are assumed to have Python installed.
basic_modules = [
'Blender',
'chunk','colorsys','copy','copy_reg','gzip','os','random','repr','stat',
'string','StringIO','types','UserDict','webbrowser', 'zlib', 'math',
'BPyBlender', 'BPyRegistry'
]

View File

@@ -0,0 +1,79 @@
# --------------------------------------------------------------------------
# BPyImage.py version 0.15
# --------------------------------------------------------------------------
# helper functions to be used by other scripts
# --------------------------------------------------------------------------
# ***** 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 LICENCE BLOCK *****
# --------------------------------------------------------------------------
from Blender import *
def curve2vecs(ob, WORLDSPACE= True):
'''
Takes a curve object and retuirns a list of vec lists (polylines)
one list per curve
This is usefull as a way to get a polyline per curve
so as not to have to deal with the spline types directly
'''
if ob.type != 'Curve':
raise 'must be a curve object'
me_dummy = Mesh.New()
me_dummy.getFromObject(ob)
if WORLDSPACE:
me_dummy.transform(ob.matrixWorld)
# build an edge dict
edges = {} # should be a set
def sort_pair(i1, i2):
if i1 > i2: return i2, i1
else: return i1, i2
for ed in me_dummy.edges:
edges[sort_pair(ed.v1.index,ed.v2.index)] = None # dummy value
# now set the curves
first_time = True
current_vecs = []
vec_list = [current_vecs]
for v in me_dummy.verts:
if first_time:
first_time = False
current_vecs.append(v.co.copy())
last_index = v.index
else:
index = v.index
if edges.has_key(sort_pair(index, last_index)):
current_vecs.append( v.co.copy() )
else:
current_vecs = []
vec_list.append(current_vecs)
last_index = index
me_dummy.verts = None
return vec_list

View File

@@ -0,0 +1,318 @@
# --------------------------------------------------------------------------
# BPyImage.py version 0.15
# --------------------------------------------------------------------------
# helper functions to be used by other scripts
# --------------------------------------------------------------------------
# ***** 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 LICENCE BLOCK *****
# --------------------------------------------------------------------------
#===========================================================================#
# Comprehensive image loader, will search and find the image #
# Will return a blender image or a new image if the image is missing #
#===========================================================================#
import bpy
from Blender import sys
try:
import os
except:
os=None
#==============================================#
# Return directory, where the file is #
#==============================================#
def stripFile(path):
lastSlash = max(path.rfind('\\'), path.rfind('/'))
if lastSlash != -1:
path = path[:lastSlash]
newpath= '%s%s' % (path, sys.sep)
else:
newpath= path
return newpath
#==============================================#
# Strips the slashes from the back of a string #
#==============================================#
def stripPath(path):
return path.split('/')[-1].split('\\')[-1]
#====================================================#
# Strips the prefix off the name before writing #
#====================================================#
def stripExt(name): # name is a string
index = name.rfind('.')
if index != -1:
return name[ : index ]
else:
return name
def getExt(name):
index = name.rfind('.')
if index != -1:
return name[index+1:]
return name
#====================================================#
# Adds a slash to the end of a path if its not there #
#====================================================#
def addSlash(path):
if not path:
return ''
elif path.endswith('\\') or path.endswith('/'):
return path
return path + sys.sep
def comprehensiveImageLoad(imagePath, filePath, PLACE_HOLDER= True, RECURSIVE=True, VERBOSE=False, CONVERT_CALLBACK=None):
'''
imagePath: The image filename
If a path precedes it, this will be searched as well.
filePath: is the directory where the image may be located - any file at teh end will be ignored.
PLACE_HOLDER: if True a new place holder image will be created.
this is usefull so later you can relink the image to its original data.
VERBOSE: If True debug info will be printed.
RECURSIVE: If True, directories will be recursivly searched.
Be carefull with this if you have files in your root directory because it may take a long time.
CASE_INSENSITIVE: for non win32 systems, find the correct case for the file.
CONVERT_CALLBACK: a function that takes an existing path and returns a new one.
Use this when loading image formats blender may not support, the CONVERT_CALLBACK
can take the path for a GIF (for example), convert it to a PNG and return the PNG's path.
For formats blender can read, simply return the path that is given.
'''
# VERBOSE = True
if VERBOSE: print 'img:', imagePath, 'file:', filePath
if os == None and CASE_INSENSITIVE:
CASE_INSENSITIVE = True
# When we have the file load it with this. try/except niceness.
def imageLoad(path):
#if path.endswith('\\') or path.endswith('/'):
# raise 'INVALID PATH'
if CONVERT_CALLBACK:
path = CONVERT_CALLBACK(path)
try:
img = bpy.data.images.new(filename=path)
if VERBOSE: print '\t\tImage loaded "%s"' % path
return img
except:
if VERBOSE:
if sys.exists(path): print '\t\tImage failed loading "%s", mabe its not a format blender can read.' % (path)
else: print '\t\tImage not found, making a place holder "%s"' % (path)
if PLACE_HOLDER:
img= bpy.data.images.new(stripPath(path),4,4)
img.filename= path
return img #blank image
else:
return None
# Image formats blender can read
IMAGE_EXT = ['jpg', 'jpeg', 'png', 'tga', 'bmp', 'rgb', 'sgi', 'bw', 'iff', 'lbm', # Blender Internal
'gif', 'psd', 'tif', 'tiff', 'pct', 'pict', 'pntg', 'qtif'] # Quacktime, worth a try.
imageFileName = stripPath(imagePath) # image path only
imageFileName_lower = imageFileName.lower() # image path only
if VERBOSE: print '\tSearchingExisting Images for "%s"' % imagePath
for i in bpy.data.images:
if stripPath(i.filename.lower()) == imageFileName_lower:
if VERBOSE: print '\t\tUsing existing image.'
return i
if VERBOSE: print '\tAttempting to load "%s"' % imagePath
if sys.exists(imagePath):
if VERBOSE: print '\t\tFile found where expected "%s".' % imagePath
return imageLoad(imagePath)
imageFileName_noext = stripExt(imageFileName) # With no extension.
imageFileName_noext_lower = stripExt(imageFileName_lower) # With no extension.
imageFilePath = stripFile(imagePath)
# Remove relative path from image path
if imageFilePath.startswith('./') or imageFilePath.startswith('.\\'):
imageFilePath = imageFilePath[2:]
# Attempt to load from obj path.
tmpPath = stripFile(filePath) + stripPath(imageFileName)
if sys.exists(tmpPath):
if VERBOSE: print '\t\tFile found in path (1)"%s".' % tmpPath
return imageLoad(tmpPath)
# os needed if we go any further.
if not os:
if VERBOSE: print '\t\tCreating a placeholder with a face path: "%s".' % imagePath
return imageLoad(imagePath) # Will jus treturn a placeholder.
# We have os.
# GATHER PATHS.
paths = {} # Store possible paths we may use, dict for no doubles.
tmpPath = addSlash(sys.expandpath('//')) # Blenders path
if sys.exists(tmpPath):
if VERBOSE: print '\t\tSearching in %s' % tmpPath
paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading
paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
else:
if VERBOSE: print '\tNo Path: "%s"' % tmpPath
tmpPath = imageFilePath
if sys.exists(tmpPath):
if VERBOSE: print '\t\tSearching in %s' % tmpPath
paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading
paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
else:
if VERBOSE: print '\tNo Path: "%s"' % tmpPath
tmpPath = stripFile(filePath)
if sys.exists(tmpPath):
if VERBOSE: print '\t\tSearching in %s' % tmpPath
paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading
paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
else:
if VERBOSE: print '\tNo Path: "%s"' % tmpPath
tmpPath = addSlash(bpy.config.textureDir)
if tmpPath and sys.exists(tmpPath):
if VERBOSE: print '\t\tSearching in %s' % tmpPath
paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading
paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
else:
if VERBOSE: print '\tNo Path: "%s"' % tmpPath
# Add path if relative image patrh was given.
tmp_paths= paths.keys()
for k in tmp_paths:
tmpPath = k + imageFilePath
if sys.exists(tmpPath):
paths[tmpPath] = [os.listdir(tmpPath)] # Orig name for loading
paths[tmpPath].append([f.lower() for f in paths[tmpPath][0]]) # Lower case list.
paths[tmpPath].append([stripExt(f) for f in paths[tmpPath][1]]) # Lower case no ext
else:
if VERBOSE: print '\tNo Path: "%s"' % tmpPath
# DONE
#
for path, files in paths.iteritems():
if sys.exists(path + imageFileName):
if VERBOSE: print '\tFound image at path: "%s" file" "%s"' % (path, imageFileName)
return imageLoad(path + imageFileName)
# If the files not there then well do a case insensitive seek.
filesOrigCase = files[0]
filesLower = files[1]
filesLowerNoExt = files[2]
# We are going to try in index the file directly, if its not there just keep on
index = None
try:
# Is it just a case mismatch?
index = filesLower.index(imageFileName_lower)
except:
try:
# Have the extensions changed?
index = filesLowerNoExt.index(imageFileName_noext_lower)
ext = getExt( filesLower[index] ) # Get the extension of the file that matches all but ext.
# Check that the ext is useable eg- not a 3ds file :)
if ext.lower() not in IMAGE_EXT:
index = None
except:
index = None
if index != None:
tmpPath = path + filesOrigCase[index]
img = imageLoad( tmpPath )
if img != None:
if VERBOSE: print '\t\tImage Found "%s"' % tmpPath
return img
if RECURSIVE:
# IMAGE NOT FOUND IN ANY OF THE DIRS!, DO A RECURSIVE SEARCH.
if VERBOSE: print '\t\tImage Not Found in any of the dirs, doing a recusrive search'
for path in paths.iterkeys():
# Were not going to use files
if path == '/' or len(path) == 3 and path[1:] == ':\\':
continue
# print path , 'ASS'
#------------------
# finds the file starting at the root.
# def findImage(findRoot, imagePath):
#W---------------
# ROOT, DIRS, FILES
pathWalk = os.walk(path)
pathList = [True]
matchList = [] # Store a list of (match, size), choose the biggest.
while True:
try:
pathList = pathWalk.next()
except:
break
for file in pathList[2]:
file_lower = file.lower()
# FOUND A MATCH
if (file_lower == imageFileName_lower) or\
(stripExt(file_lower) == imageFileName_noext_lower and getExt(file_lower) in IMAGE_EXT):
name = pathList[0] + sys.sep + file
size = os.path.getsize(name)
if VERBOSE: print '\t\t\tfound:', name
matchList.append( (name, size) )
if matchList:
# Sort by file size
matchList.sort(lambda A, B: cmp(B[1], A[1]) )
if VERBOSE: print '\t\tFound "%s"' % matchList[0][0]
# Loop through all we have found
img = None
for match in matchList:
img = imageLoad(match[0]) # 0 - first, 0 - pathname
if img != None:
break
return img
# No go.
if VERBOSE: print '\t\tImage Not Found after looking everywhere! "%s"' % imagePath
return imageLoad(imagePath) # Will jus treturn a placeholder.

View File

@@ -0,0 +1,228 @@
# $Id: BPyMathutils.py 20333 2009-05-22 03:45:46Z campbellbarton $
#
# --------------------------------------------------------------------------
# helper functions to be used by other scripts
# --------------------------------------------------------------------------
# ***** 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 LICENCE BLOCK *****
# --------------------------------------------------------------------------
import Blender
from Blender.Mathutils import *
# ------ Mersenne Twister - start
# Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura.
# Any feedback is very welcome. For any question, comments,
# see http://www.math.keio.ac.jp/matumoto/emt.html or email
# matumoto@math.keio.ac.jp
# The link above is dead, this is the new one:
# http://www.math.sci.hiroshima-u.ac.jp/m-mat/MT/emt.html
# And here the license info, from Mr. Matsumoto's site:
# Until 2001/4/6, MT had been distributed under GNU Public License,
# but after 2001/4/6, we decided to let MT be used for any purpose, including
# commercial use. 2002-versions mt19937ar.c, mt19937ar-cok.c are considered
# to be usable freely.
#
# So from the year above (1997), this code is under GPL.
# Period parameters
N = 624
M = 397
MATRIX_A = 0x9908b0dfL # constant vector a
UPPER_MASK = 0x80000000L # most significant w-r bits
LOWER_MASK = 0x7fffffffL # least significant r bits
# Tempering parameters
TEMPERING_MASK_B = 0x9d2c5680L
TEMPERING_MASK_C = 0xefc60000L
def TEMPERING_SHIFT_U(y):
return (y >> 11)
def TEMPERING_SHIFT_S(y):
return (y << 7)
def TEMPERING_SHIFT_T(y):
return (y << 15)
def TEMPERING_SHIFT_L(y):
return (y >> 18)
mt = [] # the array for the state vector
mti = N+1 # mti==N+1 means mt[N] is not initialized
# initializing the array with a NONZERO seed
def sgenrand(seed):
# setting initial seeds to mt[N] using
# the generator Line 25 of Table 1 in
# [KNUTH 1981, The Art of Computer Programming
# Vol. 2 (2nd Ed.), pp102]
global mt, mti
mt = []
mt.append(seed & 0xffffffffL)
for i in xrange(1, N + 1):
mt.append((69069 * mt[i-1]) & 0xffffffffL)
mti = i
# end sgenrand
def genrand():
global mt, mti
mag01 = [0x0L, MATRIX_A]
# mag01[x] = x * MATRIX_A for x=0,1
y = 0
if mti >= N: # generate N words at one time
if mti == N+1: # if sgenrand() has not been called,
sgenrand(4357) # a default initial seed is used
for kk in xrange((N-M) + 1):
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK)
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1]
for kk in xrange(kk, N):
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK)
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1]
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK)
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1]
mti = 0
y = mt[mti]
mti += 1
y ^= TEMPERING_SHIFT_U(y)
y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B
y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C
y ^= TEMPERING_SHIFT_L(y)
return ( float(y) / 0xffffffffL ) # reals
#------ Mersenne Twister -- end
""" 2d convexhull
Based from Dinu C. Gherman's work,
modified for Blender/Mathutils by Campell Barton
"""
######################################################################
# Public interface
######################################################################
def convexHull(point_list_2d):
"""Calculate the convex hull of a set of vectors
The vectors can be 3 or 4d but only the Xand Y are used.
returns a list of convex hull indicies to the given point list
"""
######################################################################
# Helpers
######################################################################
def _myDet(p, q, r):
"""Calc. determinant of a special matrix with three 2D points.
The sign, "-" or "+", determines the side, right or left,
respectivly, on which the point r lies, when measured against
a directed vector from p to q.
"""
return (q.x*r.y + p.x*q.y + r.x*p.y) - (q.x*p.y + r.x*q.y + p.x*r.y)
def _isRightTurn((p, q, r)):
"Do the vectors pq:qr form a right turn, or not?"
#assert p[0] != q[0] and q[0] != r[0] and p[0] != r[0]
if _myDet(p[0], q[0], r[0]) < 0:
return 1
else:
return 0
# Get a local list copy of the points and sort them lexically.
points = [(p, i) for i, p in enumerate(point_list_2d)]
try: points.sort(key = lambda a: (a[0].x, a[0].y))
except: points.sort(lambda a,b: cmp((a[0].x, a[0].y), (b[0].x, b[0].y)))
# Build upper half of the hull.
upper = [points[0], points[1]] # cant remove these.
for i in xrange(len(points)-2):
upper.append(points[i+2])
while len(upper) > 2 and not _isRightTurn(upper[-3:]):
del upper[-2]
# Build lower half of the hull.
points.reverse()
lower = [points.pop(0), points.pop(1)]
for p in points:
lower.append(p)
while len(lower) > 2 and not _isRightTurn(lower[-3:]):
del lower[-2]
# Concatenate both halfs and return.
return [p[1] for ls in (upper, lower) for p in ls]
def plane2mat(plane, normalize= False):
'''
Takes a plane and converts to a matrix
points between 0 and 1 are up
1 and 2 are right
assumes the plane has 90d corners
'''
cent= (plane[0]+plane[1]+plane[2]+plane[3] ) /4.0
up= cent - ((plane[0]+plane[1])/2.0)
right= cent - ((plane[1]+plane[2])/2.0)
z= up.cross(right)
if normalize:
up.normalize()
right.normalize()
z.normalize()
mat= Matrix(up, right, z)
# translate
mat.resize4x4()
tmat= Blender.Mathutils.TranslationMatrix(cent)
return mat * tmat
# Used for mesh_solidify.py and mesh_wire.py
# returns a length from an angle
# Imaging a 2d space.
# there is a hoz line at Y1 going to inf on both X ends, never moves (LINEA)
# down at Y0 is a unit length line point up at (angle) from X0,Y0 (LINEB)
# This function returns the length of LINEB at the point it would intersect LINEA
# - Use this for working out how long to make the vector - differencing it from surrounding faces,
# import math
from math import pi, sin, cos, sqrt
def angleToLength(angle):
# Alredy accounted for
if angle < 0.000001: return 1.0
else: return abs(1.0 / cos(pi*angle/180));

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,652 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# (C) Copyright 2006 MetaVR, Inc.
# http://www.metavr.com
# Written by Campbell 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 *****
# --------------------------------------------------------------------------
import Blender
import bpy
Vector= Blender.Mathutils.Vector
Ang= Blender.Mathutils.AngleBetweenVecs
MidpointVecs= Blender.Mathutils.MidpointVecs
import BPyMesh
# If python version is less than 2.4, try to get set stuff from module
try:
set
except:
try:
from sets import Set as set
except:
set= None
def uv_key(uv):
return round(uv.x, 5), round(uv.y, 5)
def uv_key_mix(uv1, uv2, w1, w2):
# Weighted mix. w1+w2==1.0
return w1*uv1[0]+w2*uv2[0], w1*uv1[1]+w2*uv2[1]
def col_key(col):
return col.r, col.g, col.b
def col_key_mix(col1, col2, w1, w2):
# Weighted mix. w1+w2==1.0
return int(w1*col1[0] + w2*col2[0]), int(w1*col1[1] + w2*col2[1]), int(w1*col1[2]+col2[2]*w2)
def redux(ob, REDUX=0.5, BOUNDRY_WEIGHT=2.0, REMOVE_DOUBLES=False, FACE_AREA_WEIGHT=1.0, FACE_TRIANGULATE=True, DO_UV=True, DO_VCOL=True, DO_WEIGHTS=True, VGROUP_INF_REDUX= None, VGROUP_INF_WEIGHT=0.5):
"""
BOUNDRY_WEIGHT - 0 is no boundry weighting. 2.0 will make them twice as unlikely to collapse.
FACE_AREA_WEIGHT - 0 is no weight. 1 is normal, 2.0 is higher.
"""
if REDUX<0 or REDUX>1.0:
raise 'Error, factor must be between 0 and 1.0'
elif not set:
raise 'Error, this function requires Python 2.4 or a full install of Python 2.3'
BOUNDRY_WEIGHT= 1+BOUNDRY_WEIGHT
""" # DEBUG!
if Blender.Get('rt') == 1000:
DEBUG=True
else:
DEBUG= False
"""
me= ob.getData(mesh=1)
me.hide= False # unhide all data,.
if len(me.faces)<5:
return
if FACE_TRIANGULATE or REMOVE_DOUBLES:
me.sel= True
if FACE_TRIANGULATE:
me.quadToTriangle()
if REMOVE_DOUBLES:
me.remDoubles(0.0001)
vgroups= me.getVertGroupNames()
if not me.getVertGroupNames():
DO_WEIGHTS= False
if (VGROUP_INF_REDUX!= None and VGROUP_INF_REDUX not in vgroups) or\
VGROUP_INF_WEIGHT==0.0:
VGROUP_INF_REDUX= None
try:
VGROUP_INF_REDUX_INDEX= vgroups.index(VGROUP_INF_REDUX)
except:
VGROUP_INF_REDUX_INDEX= -1
# del vgroups
len_vgroups= len(vgroups)
OLD_MESH_MODE= Blender.Mesh.Mode()
Blender.Mesh.Mode(Blender.Mesh.SelectModes.VERTEX)
if DO_UV and not me.faceUV:
DO_UV= False
if DO_VCOL and not me.vertexColors:
DO_VCOL = False
current_face_count= len(me.faces)
target_face_count= int(current_face_count * REDUX)
# % of the collapseable faces to collapse per pass.
#collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
collapse_per_pass= 0.333 # between 0.1 - lots of small nibbles, slow but high q. and 0.9 - big passes and faster.
"""# DEBUG!
if DEBUG:
COUNT= [0]
def rd():
if COUNT[0]< 330:
COUNT[0]+=1
return
me.update()
Blender.Window.RedrawAll()
print 'Press key for next, count "%s"' % COUNT[0]
try: input()
except KeyboardInterrupt:
raise "Error"
except:
pass
COUNT[0]+=1
"""
class collapseEdge(object):
__slots__ = 'length', 'key', 'faces', 'collapse_loc', 'v1', 'v2','uv1', 'uv2', 'col1', 'col2', 'collapse_weight'
def __init__(self, ed):
self.init_from_edge(ed) # So we can re-use the classes without using more memory.
def init_from_edge(self, ed):
self.key= ed.key
self.length= ed.length
self.faces= []
self.v1= ed.v1
self.v2= ed.v2
if DO_UV or DO_VCOL:
self.uv1= []
self.uv2= []
self.col1= []
self.col2= []
# self.collapse_loc= None # new collapse location.
# Basic weighting.
#self.collapse_weight= self.length * (1+ ((ed.v1.no-ed.v2.no).length**2))
self.collapse_weight= 1.0
def collapse_locations(self, w1, w2):
'''
Generate a smart location for this edge to collapse to
w1 and w2 are vertex location bias
'''
v1co= self.v1.co
v2co= self.v2.co
v1no= self.v1.no
v2no= self.v2.no
# Basic operation, works fine but not as good as predicting the best place.
#between= ((v1co*w1) + (v2co*w2))
#self.collapse_loc= between