Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Musgrove <LetterRip@gmail.com>2006-04-19 04:54:21 +0400
committerTom Musgrove <LetterRip@gmail.com>2006-04-19 04:54:21 +0400
commit98b64dc93b1474f72bfe4549d751e02aff3a6d9a (patch)
tree9daf22f29ce329b363cecd0cfd7574db901b0e41 /release/scripts
parent9bfd86730d5e77aea8ef640c38e013a3f8c048f5 (diff)
==scripts ==
latest update blender2cal3d by Jean-Baptiste LAMY, fixes script to give correct output with current CVS
Diffstat (limited to 'release/scripts')
-rw-r--r--release/scripts/blender2cal3d.py530
1 files changed, 413 insertions, 117 deletions
diff --git a/release/scripts/blender2cal3d.py b/release/scripts/blender2cal3d.py
index 835ec16ae1f..cef63528a1d 100644
--- a/release/scripts/blender2cal3d.py
+++ b/release/scripts/blender2cal3d.py
@@ -1,7 +1,7 @@
#!BPY
"""
-Name: 'Cal3D v0.9'
+Name: 'Cal3D'
Blender: 235
Group: 'Export'
Tip: 'Export armature/bone/mesh/action data to the Cal3D format.'
@@ -26,65 +26,78 @@ Tip: 'Export armature/bone/mesh/action data to the Cal3D format.'
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-__version__ = "0.11"
+__version__ = "0.13"
__author__ = "Jean-Baptiste 'Jiba' Lamy"
-__email__ = ["Author's email, jibalamy:free*fr"]
-__url__ = ["Soya3d's homepage, http://home.gna.org/oomadness/en/soya/",
- "Cal3d, http://cal3d.sourceforge.net"]
-__bpydoc__ = """\
-This script is a Blender => Cal3D converter.
+__email__ = "jibalamy@free.fr"
+__url__ = "Soya3d's homepage http://home.gna.org/oomadness/en/soya/"
+__bpydoc__ = """This script is a Blender => Cal3D converter.
(See http://blender.org and http://cal3d.sourceforge.net)
-USAGE:
+USAGE
To install it, place the script in your $HOME/.blender/scripts directory.
-
-Then open the File->Export->Cal3d v0.9 menu. And select the filename of the .cfg file.
+Then open the File->Export->Cal3d menu. And select the filename of the .cfg file.
The exporter will create a set of other files with same prefix (ie. bla.cfg, bla.xsf,
bla_Action1.xaf, bla_Action2.xaf, ...).
-
You should be able to open the .cfg file in cal3d_miniviewer.
-NOT (YET) SUPPORTED:
+NOT (YET) SUPPORTED
- Rotation, translation, or stretching Blender objects is still quite
-buggy, so AVOID MOVING / ROTATING / RESIZE OBJECTS (either mesh or armature) !
-Instead, edit the object (with tab), select all points / bones (with "a"),
-and move / rotate / resize them.<br>
- - no support for exporting springs yet<br>
+ buggy, so AVOID MOVING / ROTATING / RESIZE OBJECTS (either mesh or armature) !
+ Instead, edit the object (with tab), select all points / bones (with "a"),
+ and move / rotate / resize them.
+ - no support for exporting springs yet
- no support for exporting material colors (most games should only use images
-I think...)
+ I think...)
-KNOWN ISSUES:
+KNOWN ISSUES
- Cal3D versions <=0.9.1 have a bug where animations aren't played when the root bone
-is not animated;<br>
+ is not animated
- Cal3D versions <=0.9.1 have a bug where objects that aren't influenced by any bones
-are not drawn (fixed in Cal3D CVS).
+ are not drawn (fixed in Cal3D CVS)
-NOTES:
+NOTES
-It requires a very recent version of Blender (>= 2.35).
+It requires a very recent version of Blender (tested with 2.41).
-Build a model following a few rules:<br>
- - Use only a single armature;<br>
- - Use only a single rootbone (Cal3D doesn't support floating bones);<br>
- - Use only locrot keys (Cal3D doesn't support bone's size change);<br>
+Build a model follows a few rules:
+ - Use only a single armature
+ - Use only a single rootbone (Cal3D doesn't support floating bones)
+ - Use only locrot keys (Cal3D doesn't support bone's size change)
- Don't try to create child/parent constructs in blender object, that gets exported
-incorrectly at the moment;<br>
- - Don't put "." in action or bone names, and do not start these names by a figure;<br>
- - Objects or animations whose names start by "_" are not exported (hidden object).
+ incorrectly at the moment
+ - Objects or animations whose names start by "_" are not exported (hidden object)
-It can be run in batch mode, as following :<br>
+It can be run in batch mode, as following :
blender model.blend -P blender2cal3d.py --blender2cal3d FILENAME=model.cfg EXPORT_FOR_SOYA=1
-
You can pass as many parameters as you want at the end, "EXPORT_FOR_SOYA=1" is just an
-example. The parameters are the same as below.
+exemple. The parameters are the same than below.
+
+
+Logging is sent through python logger instance. you can control verbosity by changing
+log.set_level(DEBUG|WARNING|ERROR|CRITICAL). to print to it use log.debug(),
+log.warning(), log.error() and log.critical(). the logger breaks normal operation
+by printing _all_ info messages. ( log.info() ). Output is sent to stdout and to
+a blender text.
+
+Psyco support, turned off by default. can speed up export by 25% on my tests.
+Turned off by default.
+
+hotshot_export function to see profile. just replace export with hotshot export
+to see.
+
+Any vertices found without influence are placed into a vertex group called
+"_no_inf". [FIXME] i need to stop this for batch more
+running under gui mode the last export dir is saved into the blender registry
+and called back again.
"""
+
# Parameters :
# Filename to export to (if "", display a file selector dialog).
@@ -109,6 +122,12 @@ PREFIX_FILE_WITH_MODEL_NAME = 0
# Set to 0 to use Cal3D binary format
XML = 1
+# Configuration text buffer
+CONFIG_TEXT = ""
+
+# Allows to replace a material by another
+MATERIAL_MAP = {}
+
MESSAGES = ""
@@ -121,12 +140,112 @@ MESSAGES = ""
# Most of the hell of it is to deal with Blender's head-tail-roll bone's definition.
import sys, os, os.path, struct, math, string
+
+try:
+ import psyco
+ psyco.full()
+except:
+ print "* Blender2Cal3D * (Psyco not found)"
+
import Blender
+from Blender import Registry
+from Blender.Window import DrawProgressBar
+from Blender import Draw, BGL
+
+import logging
+reload(logging)
+
+import types
+
+import textwrap
# HACK -- it seems that some Blender versions don't define sys.argv,
# which may crash Python if a warning occurs.
if not hasattr(sys, "argv"): sys.argv = ["???"]
+# our own logger class. it works just the same as a normal logger except
+# all info messages get show.
+class Logger(logging.Logger):
+ def __init__(self,name,level=logging.NOTSET):
+ logging.Logger.__init__(self,name,level)
+
+ self.has_warnings=False
+ self.has_errors=False
+ self.has_critical=False
+
+ def info(self,msg,*args,**kwargs):
+ apply(self._log,(logging.INFO,msg,args),kwargs)
+
+ def warning(self,msg,*args,**kwargs):
+ logging.Logger.warning(self,msg,*args,**kwargs)
+ self.has_warnings=True
+
+ def error(self,msg,*args,**kwargs):
+ logging.Logger.error(self,msg,*args,**kwargs)
+ self.has_errors=True
+
+ def critical(self,msg,*args,**kwargs):
+ logging.Logger.critical(self,msg,*args,**kwargs)
+ self.has_errors=True
+
+
+# should be able to make this print to stdout in realtime and save MESSAGES
+# as well. perhaps also have a log to file option
+class LogHandler(logging.StreamHandler):
+ def __init__(self):
+ logging.StreamHandler.__init__(self,sys.stdout)
+
+ if "blender2cal3d_log" not in Blender.Text.Get():
+ self.outtext=Blender.Text.New("blender2cal3d_log")
+ else:
+ self.outtext=Blender.Text.Get('blender2cal3d_log')
+ self.outtext.clear()
+
+ self.lastmsg=''
+
+ def emit(self,record):
+ # print to stdout and to a new blender text object
+
+ msg=self.format(record)
+
+ if msg==self.lastmsg:
+ return
+
+ self.lastmsg=msg
+
+ self.outtext.write("%s\n" %msg)
+
+ logging.StreamHandler.emit(self,record)
+
+ """
+ try:
+ msg=self.format(record)
+ if not hasattr(types,"UnicodeType"):
+ self.stream.write("%s\n" % msg)
+ else:
+ try:
+ self.stream.write("%s\n" % msg)
+ except UnicodeError:
+ self.stream.write("%s\n" % msg.encode("UTF-8"))
+
+ self.flush()
+ except:
+ self.handleError(record)
+ """
+logging.setLoggerClass(Logger)
+log=logging.getLogger('blender2cal3d')
+
+handler=LogHandler()
+formatter=logging.Formatter('%(levelname)s %(message)s')
+handler.setFormatter(formatter)
+
+log.addHandler(handler)
+# set this to minimum output level. eg. logging.DEBUG, logging.WARNING, logging.ERROR
+# logging.CRITICAL. logging.INFO will make little difference as these always get
+# output'd
+log.setLevel(logging.WARNING)
+
+log.info("Starting...")
# transforms a blender to a cal3d quaternion notation (x,y,z,w)
def blender2cal3dquat(q):
@@ -147,21 +266,57 @@ def quaternion2matrix(q):
[ 2.0 * (xz + wy), 2.0 * (yz - wx), 1.0 - 2.0 * (xx + yy), 0.0],
[0.0 , 0.0 , 0.0 , 1.0]]
+# def matrix2quaternion(m):
+# s = math.sqrt(abs(m[0][0] + m[1][1] + m[2][2] + m[3][3]))
+# if s == 0.0:
+# x = abs(m[2][1] - m[1][2])
+# y = abs(m[0][2] - m[2][0])
+# z = abs(m[1][0] - m[0][1])
+# if (x >= y) and (x >= z): return 1.0, 0.0, 0.0, 0.0
+# elif (y >= x) and (y >= z): return 0.0, 1.0, 0.0, 0.0
+# else: return 0.0, 0.0, 1.0, 0.0
+# return quaternion_normalize([
+# -(m[2][1] - m[1][2]) / (2.0 * s),
+# -(m[0][2] - m[2][0]) / (2.0 * s),
+# -(m[1][0] - m[0][1]) / (2.0 * s),
+# 0.5 * s,
+# ])
+
def matrix2quaternion(m):
- s = math.sqrt(abs(m[0][0] + m[1][1] + m[2][2] + m[3][3]))
- if s == 0.0:
- x = abs(m[2][1] - m[1][2])
- y = abs(m[0][2] - m[2][0])
- z = abs(m[1][0] - m[0][1])
- if (x >= y) and (x >= z): return 1.0, 0.0, 0.0, 0.0
- elif (y >= x) and (y >= z): return 0.0, 1.0, 0.0, 0.0
- else: return 0.0, 0.0, 1.0, 0.0
- return quaternion_normalize([
- -(m[2][1] - m[1][2]) / (2.0 * s),
- -(m[0][2] - m[2][0]) / (2.0 * s),
- -(m[1][0] - m[0][1]) / (2.0 * s),
- 0.5 * s,
+ t = m[0][0] + m[1][1] + m[2][2] + m[3][3]
+ if t > 0.00000001:
+ s = math.sqrt(t) * 2
+ return quaternion_normalize([
+ -(m[2][1] - m[1][2]) / s,
+ -(m[0][2] - m[2][0]) / s,
+ -(m[1][0] - m[0][1]) / s,
+ 0.25 * s,
])
+ else:
+ if (m[0][0] > m[1][1]) and (m[0][0] > m[2][2]):
+ s = math.sqrt(1.0 + m[0][0] - m[1][1] - m[2][2]) * 2
+ return quaternion_normalize([
+ 0.25 * s,
+ -(m[1][0] + m[0][1]) / s,
+ -(m[0][2] + m[2][0]) / s,
+ -(m[2][1] - m[1][2]) / s,
+ ])
+ elif m[1][1] > m[2][2]:
+ s = math.sqrt(1.0 + m[1][1] - m[0][0] - m[2][2]) * 2
+ return quaternion_normalize([
+ -(m[1][0] + m[0][1]) / s,
+ 0.25 * s,
+ -(m[2][1] + m[1][2]) / s,
+ -(m[0][2] - m[2][0]) / s,
+ ])
+ else:
+ s = math.sqrt(1.0 + m[2][2] - m[0][0] - m[1][1]) * 2
+ return quaternion_normalize([
+ -(m[0][2] + m[2][0]) / s,
+ -(m[2][1] + m[1][2]) / s,
+ 0.25 * s,
+ -(m[1][0] - m[0][1]) / s,
+ ])
def quaternion_normalize(q):
l = math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3])
@@ -314,7 +469,8 @@ def vector_length(v):
def vector_normalize(v):
l = math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
- return v[0] / l, v[1] / l, v[2] / l
+ if l: return v[0] / l, v[1] / l, v[2] / l
+ else: return v
def vector_dotproduct(v1, v2):
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
@@ -391,6 +547,9 @@ class Material:
self.specular_b = 255
self.specular_a = 255
self.shininess = 1.0
+
+ log.debug("Material with name %s",map_filename)
+
if map_filename: self.maps_filenames = [map_filename]
else: self.maps_filenames = []
@@ -417,6 +576,7 @@ class Material:
s += " <DIFFUSE>" + str(self.diffuse_r) + " " + str(self.diffuse_g) + " " + str(self.diffuse_b) + " " + str(self.diffuse_a) + "</DIFFUSE>\n";
s += " <SPECULAR>" + str(self.specular_r) + " " + str(self.specular_g) + " " + str(self.specular_b) + " " + str(self.specular_a) + "</SPECULAR>\n";
s += " <SHININESS>" + str(self.shininess) + "</SHININESS>\n";
+
for map_filename in self.maps_filenames:
s += " <MAP>" + map_filename + "</MAP>\n";
@@ -428,6 +588,7 @@ MATERIALS = {}
class Mesh:
def __init__(self, name):
+ name=string.replace(name,'.','_')
self.name = name
self.submeshes = []
@@ -464,7 +625,8 @@ class SubMesh:
def compute_lods(self):
"""Computes LODs info for Cal3D (there's no Blender related stuff here)."""
- print "Start LODs computation..."
+ log.info("Start LODs computation...")
+
vertex2faces = {}
for face in self.faces:
for vertex in (face.vertex1, face.vertex2, face.vertex3):
@@ -555,7 +717,7 @@ class SubMesh:
new_faces.reverse() # Cal3D want LODed faces at the end
self.faces = new_faces
- print "LODs computed : %s vertices can be removed (from a total of %s)." % (self.nb_lodsteps, len(self.vertices))
+ log.info("LODs computed : %s vertices can be removed (from a total of %s)." % (self.nb_lodsteps, len(self.vertices)))
def rename_vertices(self, new_vertices):
"""Rename (change ID) of all vertices, such as self.vertices == new_vertices."""
@@ -608,7 +770,7 @@ class Vertex:
s += "".join(map(Influence.to_cal3d, self.influences))
if not self.weight is None: s += struct.pack("f", len(self.weight))
return s
-
+
def to_cal3d_xml(self):
if self.collapse_to:
collapse_id = self.collapse_to.id
@@ -710,12 +872,14 @@ BONES = {}
class Bone:
def __init__(self, skeleton, parent, name, loc, rot):
self.parent = parent
+ name=string.replace(name,'.','_')
self.name = name
self.loc = loc
self.rot = rot
self.children = []
- self.matrix = matrix_translate(quaternion2matrix(rot), loc)
+ self.matrix = self.local_matrix = matrix_translate(quaternion2matrix(rot), loc)
+
if parent:
self.matrix = matrix_multiply(parent.matrix, self.matrix)
parent.children.append(self)
@@ -767,6 +931,7 @@ class Bone:
class Animation:
def __init__(self, name, duration = 0.0):
+ name=string.replace(name,'.','_')
self.name = name
self.duration = duration
self.tracks = {} # Map bone names to tracks
@@ -817,7 +982,7 @@ class KeyFrame:
def to_cal3d(self):
# We need to negate quaternion W value, but why ?
return struct.pack("ffffffff", self.time, self.loc[0], self.loc[1], self.loc[2], self.rot[0], self.rot[1], self.rot[2], -self.rot[3])
-
+
def to_cal3d_xml(self):
s = " <KEYFRAME TIME=\"%f\">\n" % self.time
s += " <TRANSLATION>%f %f %f</TRANSLATION>\n" % \
@@ -827,7 +992,7 @@ class KeyFrame:
(self.rot[0], self.rot[1], self.rot[2], -self.rot[3])
s += " </KEYFRAME>\n"
return s
-
+
def export(filename):
global MESSAGES
@@ -839,9 +1004,11 @@ def export(filename):
scene = Blender.Scene.getCurrent()
# ---- Export skeleton (=armature) ----------------------------------------
+
+ if Blender.mode == 'interactive': DrawProgressBar(0.0,'Exporting skeleton...')
skeleton = Skeleton()
-
+
foundarmature = False
for obj in Blender.Object.Get():
data = obj.getData()
@@ -849,7 +1016,7 @@ def export(filename):
continue
if foundarmature == True:
- MESSAGES += "Found multiple armatures! '" + obj.getName() + "' ignored.\n"
+ log.error("Found multiple armatures! '" + obj.getName() + "' ignored.\n")
continue
foundarmature = True
@@ -858,11 +1025,11 @@ def export(filename):
matrix = matrix_multiply(BASE_MATRIX, matrix)
def treat_bone(b, parent = None):
- head = b.getHead()
- tail = b.getTail()
+ head = b.head["BONESPACE"]
+ tail = b.tail["BONESPACE"]
# Turns the Blender's head-tail-roll notation into a quaternion
- quat = matrix2quaternion(blender_bone2matrix(head, tail, b.getRoll()))
+ quat = matrix2quaternion(blender_bone2matrix(head, tail, b.roll["BONESPACE"]))
if parent:
# Compute the translation from the parent bone's head to the child
@@ -874,8 +1041,10 @@ def export(filename):
parent_invert_transform = matrix_invert(quaternion2matrix(parent.rot))
parent_head = vector_by_matrix(parent.head, parent_invert_transform)
parent_tail = vector_by_matrix(parent.tail, parent_invert_transform)
-
- ploc = vector_add(head, b.getLoc())
+
+
+
+ #ploc = vector_add(head, b.getLoc())
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...
@@ -884,7 +1053,9 @@ def export(filename):
loc = parentheadtotail
rot = quat
- bone = Bone(skeleton, parent, b.getName(), loc, rot)
+ log.debug("Parented Bone: %s",b.name)
+
+ bone = Bone(skeleton, parent, b.name, loc, rot)
else:
# Apply the armature's matrix to the root bones
head = point_by_matrix(head, matrix)
@@ -896,29 +1067,34 @@ def export(filename):
loc = head
rot = quat
+ log.debug("Non Parented Bone: %s",b.name)
+
# Here, the translation is simply the head vector
- bone = Bone(skeleton, None, b.getName(), loc, rot)
+ bone = Bone(skeleton, None, b.name, loc, rot)
bone.head = head
bone.tail = tail
- for child in b.getChildren():
- treat_bone(child, bone)
-
+ if b.hasChildren():
+ for child in b.children:
+ treat_bone(child, bone)
+
foundroot = False
- for b in data.getBones():
+ for b in data.bones.values():
# child bones are handled in treat_bone
- if b.getParent() != None:
+ if b.parent != None:
continue
if foundroot == True:
- print "Warning: Found multiple root-bones, this may not be supported in cal3d."
- #print "Ignoring bone '" + b.getName() + "' and it's childs."
+ log.warning("Warning: Found multiple root-bones, this may not be supported in cal3d.")
+ #print "Ignoring bone '" + b.name + "' and it's childs."
#continue
treat_bone(b)
foundroot = True
# ---- Export Mesh data ---------------------------------------------------
+
+ if Blender.mode == 'interactive': DrawProgressBar(0.3,'Exporting meshes...')
meshes = []
@@ -926,6 +1102,10 @@ def export(filename):
data = obj.getData()
if (type(data) is Blender.Types.NMeshType) and data.faces:
mesh_name = obj.getName()
+ if mesh_name[0]=='_': continue
+
+ log.debug("Mesh: %s",mesh_name)
+
mesh = Mesh(mesh_name)
meshes.append(mesh)
@@ -937,7 +1117,13 @@ def export(filename):
while faces:
image = faces[0].image
image_filename = image and image.filename
- material = MATERIALS.get(image_filename) or Material(image_filename)
+ image_name = os.path.splitext(os.path.basename(image_filename))[0]
+ #print "MATERIAL", image_filename, image_name
+ if MATERIAL_MAP.has_key(image_name):
+ image_filename2 = os.path.join(os.path.dirname(image_filename), MATERIAL_MAP[image_name] + os.path.splitext(image_filename)[1])
+ #print "=>", image_filename
+ else: image_filename2 = image_filename
+ material = MATERIALS.get(image_filename2) or Material(image_filename2)
outputuv = len(material.maps_filenames) > 0
# TODO add material color support here
@@ -949,9 +1135,14 @@ def export(filename):
faces.remove(face)
if not face.smooth:
- p1 = face.v[0].co
- p2 = face.v[1].co
- p3 = face.v[2].co
+ try:
+ p1 = face.v[0].co
+ p2 = face.v[1].co
+ p3 = face.v[2].co
+ except IndexError:
+ log.error("You have faces with less that three verticies!")
+ continue
+
normal = vector_normalize(vector_by_matrix(vector_crossproduct(
[p3[0] - p2[0], p3[1] - p2[1], p3[2] - p2[2]],
[p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]],
@@ -961,11 +1152,11 @@ def export(filename):
for i in range(len(face.v)):
vertex = vertices.get(face.v[i].index)
if not vertex:
- coord = point_by_matrix (face.v[i].co, matrix)
+ coord = point_by_matrix (face.v[i].co, matrix)
if face.smooth:
normal = vector_normalize(vector_by_matrix(face.v[i].no, matrix))
- vertex = vertices[face.v[i].index] = Vertex(submesh, coord, normal)
-
+ vertex = vertices[face.v[i].index] = Vertex(submesh, coord, normal)
+
influences = data.getVertexInfluences(face.v[i].index)
# should this really be a warning? (well currently enabled,
# because blender has some bugs where it doesn't return
@@ -973,8 +1164,11 @@ def export(filename):
# cal3d<=0.9.1 had bugs where objects without influences
# aren't drawn.
if not influences:
- MESSAGES += "A vertex of object '%s' has no influences.\n(This occurs on objects placed in an invisible layer, you can fix it by using a single layer)\n" \
- % obj.getName()
+ log.error("A vertex of object '%s' has no influences.\n(This occurs on objects placed in an invisible layer, you can fix it by using a single layer)\n. The vertex has been added to a vertex group called _no_inf" % obj.getName())
+ if '_no_inf' not in data.getVertGroupNames():
+ data.addVertGroup('_no_inf')
+
+ data.assignVertsToGroup('_no_inf',[face.v[i].index],0.5,'add')
# sum of influences is not always 1.0 in Blender ?!?!
sum = 0.0
@@ -982,9 +1176,12 @@ def export(filename):
sum += weight
for bone_name, weight in influences:
+ bone_name=string.replace(bone_name,'.','_')
+ if bone_name=='':
+ log.critical('Found bone with no name which influences %s' % obj.getName())
+ continue
if bone_name not in BONES:
- MESSAGES += "Couldn't find bone '%s' which influences" \
- "object '%s'.\n" % (bone_name, obj.getName())
+ log.error("Couldn't find bone '%s' which influences object '%s'.\n" % (bone_name, obj.getName()))
continue
vertex.influences.append(Influence(BONES[bone_name], weight / sum))
@@ -1032,18 +1229,23 @@ def export(filename):
submesh.compute_lods()
# ---- Export animations --------------------------------------------------
+
+ if Blender.mode == 'interactive': DrawProgressBar(0.7,'Exporting animations...')
+
ANIMATIONS = {}
for a in Blender.Armature.NLA.GetActions().iteritems():
animation_name = a[0]
+
+ log.debug( "Animation: %s",animation_name)
+
animation = Animation(animation_name)
animation.duration = 0.0
for b in a[1].getAllChannelIpos().iteritems():
- bone_name = b[0]
+ bone_name = string.replace(b[0],'.','_')
if bone_name not in BONES:
- MESSAGES += "No Bone '" + bone_name + "' defined (from Animation '" \
- + animation_name + "' ?!?\n"
+ log.error("No Bone '" + bone_name + "' defined (from Animation '" + animation_name + "' ?!?\n")
continue
bone = BONES[bone_name]
@@ -1066,8 +1268,8 @@ def export(filename):
curve_name = curve.getName()
if curve_name not in ["QuatW", "QuatX", "QuatY", "QuatZ", "LocX", "LocY", "LocZ"]:
- MESSAGES += "Curve type %s not supported in Action '%s' Bone '%s'.\n"\
- % (curve_name, animation_name, bone_name)
+ log.error("Curve type %s not supported in Action '%s' Bone '%s'.\n"\
+ % (curve_name, animation_name, bone_name))
for p in curve.getPoints():
time = p.getPoints() [0]
@@ -1093,17 +1295,28 @@ def export(filename):
if curve.getName() == "QuatX": quat[0] = val
if curve.getName() == "QuatY": quat[1] = val
if curve.getName() == "QuatZ": quat[2] = val
+ log.debug('Curve: %s' % curve.getName())
+
+ if quat==[0,0,0,0]:
+ log.critical('You are using just Loc keys. You must use LocRot keys instead.')
+ continue
+
+ transt = vector_by_matrix(trans, bone.local_matrix)
- transt = vector_by_matrix(trans, bone.matrix)
loc = vector_add(bone.loc, transt)
rot = quaternion_multiply(quat, bone.rot)
- rot = quaternion_normalize(rot)
+ try:
+ rot = quaternion_normalize(rot)
+ except:
+ import traceback
+ log.error('quaternion_normalize failed')
+
KeyFrame(track, cal3dtime, loc, rot)
if animation.duration <= 0:
- MESSAGES += "Ignoring Animation '" + animation_name + \
- "': duration is 0.\n"
+ log.warning("Ignoring Animation '" + animation_name + \
+ "': duration is 0.")
continue
ANIMATIONS[animation_name] = animation
@@ -1112,11 +1325,15 @@ def export(filename):
filename = os.path.splitext(filename)[0]
BASENAME = os.path.basename(filename)
DIRNAME = os.path.dirname(filename)
+
+ try: os.makedirs(DIRNAME)
+ except: pass
+
if PREFIX_FILE_WITH_MODEL_NAME: PREFIX = BASENAME + "_"
else: PREFIX = ""
if XML: FORMAT_PREFIX = "x"; encode = lambda x: x.to_cal3d_xml()
else: FORMAT_PREFIX = "c"; encode = lambda x: x.to_cal3d()
- print DIRNAME + " - " + BASENAME
+ #print DIRNAME + " - " + BASENAME
cfg = open(os.path.join(DIRNAME, BASENAME + ".cfg"), "wb")
print >> cfg, "# Cal3D model exported from Blender with blender2cal3d.py"
@@ -1158,42 +1375,106 @@ def export(filename):
print >> cfg, "material=%s" % filename
print >> cfg
- MESSAGES += "Saved to '%s.cfg'\n" % BASENAME
- MESSAGES += "Done."
+ try:
+ glob_params = Blender.Text.get("soya_params").asLines()
+ for glob_param in glob_params:
+ print >> cfg, glob_param
+ except: pass
- # show messages
- print MESSAGES
+ if CONFIG_TEXT:
+ for line in Blender.Text.get(CONFIG_TEXT).asLines():
+ if not line.startswith("material_"):
+ print >> cfg, line
+
+
+ # Remove soya cached data -- they need to be re-computed, since the model have changed
+ for filename in os.listdir(DIRNAME):
+ if filename.startswith("neighbors"):
+ os.remove(os.path.join(DIRNAME, filename))
+
+ log.info("Saved to '%s.cfg'" % BASENAME)
+ log.info("Done.")
+
+ if Blender.mode == 'interactive': DrawProgressBar(1.0,'Done!')
+
+class BlenderGui:
+ def __init__(self):
+ text="""A log has been written to a blender text window. Change this window type to
+a text window and you will be able to select the file."""
-# some (ugly) gui to show the error messages - no scrollbar or other luxury,
-# please improve this if you know how
-def gui():
- global MESSAGES
- button = Blender.Draw.Button("Ok", 1, 0, 0, 50, 20, "Close Window")
+ text=textwrap.wrap(text,40)
+
+ text+=['']
+
+ if log.has_critical:
+ text+=['There were critical errors!!!!']
+
+ elif log.has_errors:
+ text+=['There were errors!']
+
+ elif log.has_warnings:
+ text+=['There were warnings']
+
+ # add any more text before here
+ text.reverse()
+
+ self.msg=text
+
+ Blender.Draw.Register(self.gui, self.event, self.button_event)
+
+ def gui(self,):
+ quitbutton = Blender.Draw.Button("Exit", 1, 0, 0, 100, 20, "Close Window")
- lines = MESSAGES.split("\n")
- if len(lines) > 15:
- lines.append("Please also take a look at your console")
- pos = len(lines) * 15 + 20
- for line in lines:
- Blender.BGL.glRasterPos2i(0, pos)
- Blender.Draw.Text(line)
- pos -= 15
-
-def event(evt, val):
- if evt == Blender.Draw.ESCKEY:
- Blender.Draw.Exit()
- return
-
-def button_event(evt):
- if evt == 1:
- Blender.Draw.Exit()
- return
+ y=35
+
+ for line in self.msg:
+ BGL.glRasterPos2i(10,y)
+ Blender.Draw.Text(line)
+ y+=15
+
+ def event(self,evt, val):
+ if evt == Blender.Draw.ESCKEY:
+ Blender.Draw.Exit()
+ return
+
+ def button_event(self,evt):
+ if evt == 1:
+ Blender.Draw.Exit()
+ return
+
+def hotshot_export(filename):
+ import hotshot,hotshot.stats
+ prof=hotshot.Profile('blender2cal3d.prof')
+ print prof.runcall(export,filename)
+ prof.close()
+ stats=hotshot.stats.load('blender2cal3d.prof')
+ stats.strip_dirs()
+ stats.sort_stats('time','calls')
+ stats.print_stats()
# Main script
def fs_callback(filename):
+ save_to_registry(filename)
+ #hotshot_export(filename)
export(filename)
- Blender.Draw.Register(gui, event, button_event)
+ BlenderGui()
+
+def save_to_registry(filename):
+ dir,name=os.path.split(filename)
+ d={'default_path':dir,
+ }
+
+ log.info('storing %s to registry' % str(d))
+ Registry.SetKey('blender2cal3d',d)
+
+def get_from_registry():
+ d=Registry.GetKey('blender2cal3d')
+ if d:
+ log.info('got %s from registry' % str(d))
+ return d['default_path']
+ else:
+ return ''
# Check for batch mode
if "--blender2cal3d" in sys.argv:
@@ -1205,6 +1486,14 @@ if "--blender2cal3d" in sys.argv:
try: val = float(val)
except: pass
globals()[attr] = val
+
+ if CONFIG_TEXT:
+ for line in Blender.Text.get(CONFIG_TEXT).asLines():
+ if line.startswith("material_"):
+ old, new = line[9:].split("=")
+ MATERIAL_MAP[old] = new
+ log.info("Maps material %s to %s" % (old, new))
+
export(FILENAME)
Blender.Quit()
@@ -1212,8 +1501,15 @@ else:
if FILENAME: fs_callback(FILENAME)
else:
defaultname = Blender.Get("filename")
+
if defaultname.endswith(".blend"):
defaultname = defaultname[0:len(defaultname)-len(".blend")] + ".cfg"
+
+ dir,name=os.path.split(defaultname)
+
+ lastpath=get_from_registry()
+ defaultname=os.path.join(lastpath,name)
+
Blender.Window.FileSelector(fs_callback, "Cal3D Export", defaultname)