diff --git a/release/scripts/bpymodules/BPyArmature.py b/release/scripts/bpymodules/BPyArmature.py
new file mode 100644
index 00000000000..d0b41dc35c5
--- /dev/null
+++ b/release/scripts/bpymodules/BPyArmature.py
@@ -0,0 +1,137 @@
+# 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
+
+import Blender
+import bpy
+def getBakedPoseData(ob_arm, start_frame, end_frame):
+ '''
+ If you are currently getting IPO's this function can be used to
+ return a list of frame aligned bone dictionary's
+
+ 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
+ for current_frame in xrange(start_frame, end_frame+1):
+ 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()
+ 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
+
+
+ frame_index+=1
+
+ ob_arm.action = backup_action
+ Blender.Set('curframe', backup_frame)
+ return bake_data
+
+
+
diff --git a/release/scripts/bpymodules/BPyObject.py b/release/scripts/bpymodules/BPyObject.py
index 278ce7fe270..7de2c4113f9 100644
--- a/release/scripts/bpymodules/BPyObject.py
+++ b/release/scripts/bpymodules/BPyObject.py
@@ -5,6 +5,9 @@ def getObjectArmature(ob):
This returns the first armature the mesh uses.
remember there can be more then 1 armature but most people dont do that.
'''
+ if ob.type != 'Mesh':
+ return None
+
arm = ob.parent
if arm and arm.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.ARMATURE:
arm
diff --git a/release/scripts/export_cal3d.py b/release/scripts/export_cal3d.py
index 113b20b4bc3..0ac395b82fe 100644
--- a/release/scripts/export_cal3d.py
+++ b/release/scripts/export_cal3d.py
@@ -103,7 +103,8 @@ import sys, os, os.path, struct, math, string
import Blender
import BPyMesh
import BPySys
-
+import BPyArmature
+import BPyObject
def best_armature_root(armature):
@@ -184,7 +185,7 @@ def matrix2quaternion(m):
0.5 * s,
])
q.normalize()
- print q
+ #print q
return q
def vector_by_matrix_3x3(p, m):
@@ -260,7 +261,8 @@ BASE_MATRIX = None
CAL3D_VERSION = 910
MATERIALS = {}
-class Cal3DMaterial:
+class Cal3DMaterial(object):
+ __slots__ = 'amb', 'diff', 'spec', 'shininess', 'maps_filenames', 'id'
def __init__(self, map_filename = None):
self.amb = (255,255,255,255)
self.diff = (255,255,255,255)
@@ -292,12 +294,14 @@ class Cal3DMaterial:
file.write('\n')
-class Cal3DMesh:
+class Cal3DMesh(object):
+ __slots__ = 'name', 'submeshes'
def __init__(self, ob, blend_mesh):
self.name = ob.name
self.submeshes = []
matrix = ob.matrixWorld
+ matrix_no = matrix.copy().rotationPart()
#if BASE_MATRIX:
# matrix = matrix_multiply(BASE_MATRIX, matrix)
@@ -317,7 +321,7 @@ class Cal3DMesh:
faces.remove(face)
if not face.smooth:
- normal = face.no * matrix
+ normal = face.no * matrix_no
normal.normalize()
face_vertices = []
@@ -325,12 +329,11 @@ class Cal3DMesh:
for i, blend_vert in enumerate(face_v):
vertex = vertices.get(blend_vert.index)
if not vertex:
- #coord = blend_vert.co * matrix
- coord = blend_vert.co
+ coord = blend_vert.co * matrix
+
if face.smooth:
- #normal = blend_vert.no * matrix
- normal = blend_vert.no
- #normal.normalize()
+ normal = blend_vert.no * matrix_no
+ normal.normalize()
vertex = vertices[blend_vert.index] = Cal3DVertex(coord, normal, len(submesh.vertices))
submesh.vertices.append(vertex)
@@ -354,7 +357,7 @@ class Cal3DMesh:
print 'Couldnt find bone "%s" which influences object "%s"' % (bone_name, ob.name)
continue
if weight:
- vertex.influences.append(Influence(BONES[bone_name], weight / sum))
+ vertex.influences.append(Cal3DInfluence(BONES[bone_name], weight / sum))
elif not face.smooth:
# We cannot share vertex for non-smooth faces, since Cal3D does not
@@ -372,7 +375,7 @@ class Cal3DMesh:
if blend_mesh.faceUV:
uv = [face.uv[i][0], 1.0 - face.uv[i][1]]
if not vertex.maps:
- if outputuv: vertex.maps.append(Map(*uv))
+ if outputuv: vertex.maps.append(Cal3DMap(*uv))
elif (vertex.maps[0].u != uv[0]) or (vertex.maps[0].v != uv[1]):
# This vertex can be shared for Blender, but not for Cal3D !!!
# Cal3D does not support vertex sharing for 2 vertices with
@@ -390,14 +393,14 @@ class Cal3DMesh:
vertex.cloned_from = old_vertex
vertex.influences = old_vertex.influences
- if outputuv: vertex.maps.append(Map(*uv))
+ if outputuv: vertex.maps.append(Cal3DMap(*uv))
old_vertex.clones.append(vertex)
face_vertices.append(vertex)
# Split faces with more than 3 vertices
for i in xrange(1, len(face.v) - 1):
- submesh.faces.append(Face(face_vertices[0], face_vertices[i], face_vertices[i + 1]))
+ submesh.faces.append(Cal3DFace(face_vertices[0], face_vertices[i], face_vertices[i + 1]))
# Computes LODs info
if LODS:
@@ -411,7 +414,8 @@ class Cal3DMesh:
submesh.writeCal3D(file)
file.write('\n')
-class Cal3DSubMesh:
+class Cal3DSubMesh(object):
+ __slots__ = 'material', 'vertices', 'faces', 'nb_lodsteps', 'springs', 'id'
def __init__(self, mesh, material, id):
self.material = material
self.vertices = []
@@ -419,7 +423,7 @@ class Cal3DSubMesh:
self.nb_lodsteps = 0
self.springs = []
self.id = id
-
+
def compute_lods(self):
"""Computes LODs info for Cal3D (there's no Blender related stuff here)."""
@@ -486,7 +490,7 @@ class Cal3DSubMesh:
clone.face_collapse_count = 0
new_vertices.append(clone)
-
+
# HACK -- all faces get collapsed with v1 (and no faces are collapsed with v1's
# clones). This is why we add v1 in new_vertices after v1's clones.
# This hack has no other incidence that consuming a little few memory for the
@@ -534,20 +538,8 @@ class Cal3DSubMesh:
file.write('\t\n')
-class Cal3DVertex:
- """
- __slots__ =\
- 'loc',# vertex location, worldspace
- 'normal',# vertex normal, worldspace
- 'collapse_to',# ?
- 'face_collapse_count',# ?
- 'maps',# uv coords, must support Multi UV's eventually
- 'influences',# Bone influences
- 'weight',# ?
- 'cloned_from',# ?
- 'clones',# ?
- 'id'# index
- """
+class Cal3DVertex(object):
+ __slots__ = 'loc','normal','collapse_to','face_collapse_count','maps','influences','weight','cloned_from','clones','id'
def __init__(self, loc, normal, id):
self.loc = loc
self.normal = normal
@@ -588,7 +580,7 @@ class Cal3DVertex:
file.write('\t\t\n')
-class Map(object):
+class Cal3DMap(object):
__slots__ = 'u', 'v'
def __init__(self, u, v):
self.u = u
@@ -597,7 +589,7 @@ class Map(object):
def writeCal3D(self, file):
file.write('\t\t\t%.6f %.6f\n' % (self.u, self.v))
-class Influence(object):
+class Cal3DInfluence(object):
__slots__ = 'bone', 'weight'
def __init__(self, bone, weight):
self.bone = bone
@@ -607,7 +599,7 @@ class Influence(object):
file.write('\t\t\t%.6f\n' % \
(self.bone.id, self.weight))
-class Spring(object):
+class Cal3DSpring(object):
__slots__ = 'vertex1', 'vertex2', 'spring_coefficient', 'idlelength'
def __init__(self, vertex1, vertex2):
self.vertex1 = vertex1
@@ -619,7 +611,7 @@ class Spring(object):
file.write('\t\t\n' % \
(self.vertex1.id, self.vertex2.id, self.spring_coefficient, self.idlelength))
-class Face(object):
+class Cal3DFace(object):
__slots__ = 'vertex1', 'vertex2', 'vertex3', 'can_collapse',
def __init__(self, vertex1, vertex2, vertex3):
self.vertex1 = vertex1
@@ -648,13 +640,13 @@ class Cal3DSkeleton(object):
BONES = {}
POSEBONES= {}
class Cal3DBone(object):
- __slots__ = 'head', 'tail', 'name', 'cal3d_parent', 'loc', 'rot', 'children', 'matrix', 'lloc', 'lrot', 'id'
+ __slots__ = 'head', 'tail', 'name', 'cal3d_parent', 'loc', 'quat', 'children', 'matrix', 'lloc', 'lquat', 'id'
def __init__(self, skeleton, blend_bone, arm_matrix, cal3d_parent=None):
# def treat_bone(b, parent = None):
head = blend_bone.head['BONESPACE']
tail = blend_bone.tail['BONESPACE']
- #print parent.rot
+ #print parent.quat
# Turns the Blender's head-tail-roll notation into a quaternion
#quat = matrix2quaternion(blender_bone2matrix(head, tail, blend_bone.roll['BONESPACE']))
quat = matrix2quaternion(blend_bone.matrix['BONESPACE'].copy().resize4x4())
@@ -669,7 +661,7 @@ class Cal3DBone(object):
# but parent_tail and parent_head must be converted from the parent's parent
# system coordinate into the parent system coordinate.
- parent_invert_transform = matrix_invert(quaternion2matrix(cal3d_parent.rot))
+ parent_invert_transform = matrix_invert(quaternion2matrix(cal3d_parent.quat))
parent_head = vector_by_matrix_3x3(cal3d_parent.head, parent_invert_transform)
parent_tail = vector_by_matrix_3x3(cal3d_parent.tail, parent_invert_transform)
ploc = vector_add(ploc, blend_bone.head['BONESPACE'])
@@ -682,21 +674,15 @@ class Cal3DBone(object):
parentheadtotail = vector_sub(parent_tail, parent_head)
# hmm this should be handled by the IPos, but isn't for non-animated
# bones which are transformed in the pose mode...
- #loc = vector_add(ploc, parentheadtotail)
- #rot = quaternion_multiply(blender2cal3dquat(blend_bone.getQuat()), quat)
loc = parentheadtotail
- rot = quat
else:
# Apply the armature's matrix to the root bones
head = point_by_matrix(head, arm_matrix)
tail = point_by_matrix(tail, arm_matrix)
- quat = matrix2quaternion(matrix_multiply(arm_matrix, quaternion2matrix(quat))) # Probably not optimal
- # loc = vector_add(head, blend_bone.getLoc())
- # rot = quaternion_multiply(blender2cal3dquat(blend_bone.getQuat()), quat)
loc = head
- rot = quat
+ quat = matrix2quaternion(matrix_multiply(arm_matrix, quaternion2matrix(quat))) # Probably not optimal
self.head = head
self.tail = tail
@@ -704,18 +690,18 @@ class Cal3DBone(object):
self.cal3d_parent = cal3d_parent
self.name = blend_bone.name
self.loc = loc
- self.rot = rot
+ self.quat = quat
self.children = []
- self.matrix = matrix_translate(quaternion2matrix(rot), loc)
+ self.matrix = matrix_translate(quaternion2matrix(quat), loc)
if cal3d_parent:
self.matrix = matrix_multiply(cal3d_parent.matrix, self.matrix)
- # lloc and lrot are the bone => model space transformation (translation and rotation).
+ # lloc and lquat are the bone => model space transformation (translation and rotation).
# They are probably specific to Cal3D.
m = matrix_invert(self.matrix)
self.lloc = m[3][0], m[3][1], m[3][2]
- self.lrot = matrix2quaternion(m)
+ self.lquat = matrix2quaternion(m)
self.id = len(skeleton.bones)
skeleton.bones.append(self)
@@ -733,11 +719,11 @@ class Cal3DBone(object):
file.write('\t\t%.6f %.6f %.6f\n' % \
(self.loc[0], self.loc[1], self.loc[2]))
file.write('\t\t%.6f %.6f %.6f %.6f\n' % \
- (self.rot[0], self.rot[1], self.rot[2], -self.rot[3]))
+ (self.quat[0], self.quat[1], self.quat[2], -self.quat[3]))
file.write('\t\t%.6f %.6f %.6f\n' % \
(self.lloc[0], self.lloc[1], self.lloc[2]))
file.write('\t\t%.6f %.6f %.6f %.6f\n' % \
- (self.lrot[0], self.lrot[1], self.lrot[2], -self.lrot[3]))
+ (self.lquat[0], self.lquat[1], self.lquat[2], -self.lquat[3]))
if self.cal3d_parent:
file.write('\t\t%i\n' % self.cal3d_parent.id)
else:
@@ -765,28 +751,25 @@ class Cal3DAnimation:
file.write('\n')
-class Cal3DTrack:
+class Cal3DTrack(object):
+ __slots__ = 'bone', 'keyframes'
def __init__(self, bone):
self.bone = bone
self.keyframes = []
def writeCal3D(self, file):
- file.write('\t