Need Python exporter for Blender (Blender to Cal3d)

Started by
11 comments, last by Raduprv 18 years, 2 months ago
(template doesn't apply) The new Blender 2.4 changed some internal format things, and as such, the compatibility with many previous plug ins was lost. We would like someone to rewrite the Cal3d exporter plug in so that it works with this Blender version. The original plug in (that doesn't work with the version) can be found here. While you are at it, we'd also appreciate if you can fix this problem here. Such a plug in is important for many teams that use Blender and Cal3d, so it would be a nice service to a lot of people. If you want to be paid for such a task, please make a resonable offer and I will consider it.
Advertisement
Agreed, this doesn't fit into the template.

May I just point out the anyone reading this that as a tool that will be of use to a great many people, helping out with this could make a great way to perhaps raise your profile a little, and would be a good project to be able to list when applying for future positions.

- Jason Astle-Adams

So I guess no one is interested in making some money?
The last version of blender already comes with the cal3d exporter. I used it the other day, just once, and it apeared to work fine. I mean, I was able to load the mesh and the animation with a program of my own. What's the problem exactly?

The discussion you linked seems to point a solution.

EDIT: Hello... It seems there is a new release.. :P I'm downloading it right now.
[size="2"]I like the Walrus best.
Yes, it includes the solution but I have no idea how to program in Python, or where to add those lines in the plug in, etc.
I'm taking a look at it. I'll need that script too.

I already fixed some parts, but new crap keeps comming out. I'll let you know if I was able to fix it all.
[size="2"]I like the Walrus best.
Okey.

[ fixed this problem. Still doesn't work. Some guy at the Blender forums told me that 2.41 is about to be released and that it will be easier to make this script work with that version, so I'll wait until then. ]

The following is the code with my modifications. If someone wants to help.

#!BPY"""Name: 'Cal3D v0.9'Blender: 235Group: 'Export'Tip: 'Export armature/bone/mesh/action data to the Cal3D format.'"""# blender2cal3D.py# Copyright (C) 2003-2004 Jean-Baptiste LAMY -- jibalamy@free.fr# Copyright (C) 2004 Matthias Braun -- matze@braunis.de## This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2 of the License, or# (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA__version__ = "0.11"__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.(See http://blender.org and http://cal3d.sourceforge.net)USAGE:To install it, place the script in your $HOME/.blender/scripts directory.Then open the File->Export->Cal3d v0.9 menu. And select the filename of the .cfg file.The exporter will create a set of other files with same prefix (ie. bla.cfg, bla.xsf,bla_Action1.xaf, bla_Action2.xaf, ...).You should be able to open the .cfg file in cal3d_miniviewer.NOT (YET) SUPPORTED:  - Rotation, translation, or stretching Blender objects is still quitebuggy, so AVOID MOVING / ROTATING / RESIZE OBJECTS (either mesh or armature) !Instead, edit the object (with tab), select all points / bones (with "a"),and move / rotate / resize them.<br>  - no support for exporting springs yet<br>  - no support for exporting material colors (most games should only use imagesI think...)KNOWN ISSUES:  - Cal3D versions <=0.9.1 have a bug where animations aren't played when the root boneis not animated;<br>  - Cal3D versions <=0.9.1 have a bug where objects that aren't influenced by any bonesare not drawn (fixed in Cal3D CVS).NOTES:It requires a very recent version of Blender (>= 2.35).Build a model following a few rules:<br>  - Use only a single armature;<br>  - Use only a single rootbone (Cal3D doesn't support floating bones);<br>  - Use only locrot keys (Cal3D doesn't support bone's size change);<br>  - Don't try to create child/parent constructs in blender object, that gets exportedincorrectly 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).It can be run in batch mode, as following :<br>    blender model.blend -P blender2cal3d.py --blender2cal3d FILENAME=model.cfg EXPORT_FOR_SOYA=1You can pass as many parameters as you want at the end, "EXPORT_FOR_SOYA=1" is just anexample. The parameters are the same as below."""# Parameters :# Filename to export to (if "", display a file selector dialog).FILENAME = ""# True (=1) to export for the Soya 3D engine#     (http://oomadness.tuxfamily.org/en/soya).# (=> rotate meshes and skeletons so as X is right, Y is top and -Z is front)EXPORT_FOR_SOYA = 0# Enables LODs computation. LODs computation is quite slow, and the algo is# surely not optimal :-(LODS = 0# Scale the model (not supported by Soya).SCALE = 1.0# Set to 1 if you want to prefix all filename with the model name# (e.g. knight_walk.xaf instead of walk.xaf)PREFIX_FILE_WITH_MODEL_NAME = 0# Set to 0 to use Cal3D binary formatXML = 1MESSAGES = ""# See also BASE_MATRIX below, if you want to rotate/scale/translate the model at# the exportation.########################################################################################## Code starts here.# The script should be quite re-useable for writing another Blender animation exporter.# Most of the hell of it is to deal with Blender's head-tail-roll bone's definition.import sys, os, os.path, struct, math, stringimport Blender# 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 = ["???"]# transforms a blender to a cal3d quaternion notation (x,y,z,w)def blender2cal3dquat(q):  return [q.x, q.y, q.z, q.w]def quaternion2matrix(q):  xx = q[0] * q[0]  yy = q[1] * q[1]  zz = q[2] * q[2]  xy = q[0] * q[1]  xz = q[0] * q[2]  yz = q[1] * q[2]  wx = q[3] * q[0]  wy = q[3] * q[1]  wz = q[3] * q[2]  return [[1.0 - 2.0 * (yy + zz),       2.0 * (xy + wz),       2.0 * (xz - wy), 0.0],          [      2.0 * (xy - wz), 1.0 - 2.0 * (xx + zz),       2.0 * (yz + wx), 0.0],          [      2.0 * (xz + wy),       2.0 * (yz - wx), 1.0 - 2.0 * (xx + yy), 0.0],          [0.0                  , 0.0                  , 0.0                  , 1.0]]def 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 quaternion_normalize(q):  l = math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3])  return q[0] / l, q[1] / l, q[2] / l, q[3] / l# multiplies 2 quaternions in x,y,z,w notationdef quaternion_multiply(q1, q2):  return [    q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1],    q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2],    q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0],    q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2],    ]def matrix_translate(m, v):  m[3][0] += v[0]  m[3][1] += v[1]  m[3][2] += v[2]  return mdef matrix_multiply(b, a):  return [ [    a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0],    a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1],    a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2],    0.0,    ], [    a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0],    a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1],    a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2],    0.0,    ], [    a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0],    a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1],    a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2],     0.0,    ], [    a[3][0] * b[0][0] + a[3][1] * b[1][0] + a[3][2] * b[2][0] + b[3][0],    a[3][0] * b[0][1] + a[3][1] * b[1][1] + a[3][2] * b[2][1] + b[3][1],    a[3][0] * b[0][2] + a[3][1] * b[1][2] + a[3][2] * b[2][2] + b[3][2],    1.0,    ] ]def matrix_invert(m):  det = (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])       - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])       + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]))  if det == 0.0: return None  det = 1.0 / det  r = [ [      det * (m[1][1] * m[2][2] - m[2][1] * m[1][2]),    - det * (m[0][1] * m[2][2] - m[2][1] * m[0][2]),      det * (m[0][1] * m[1][2] - m[1][1] * m[0][2]),      0.0,    ], [    - det * (m[1][0] * m[2][2] - m[2][0] * m[1][2]),      det * (m[0][0] * m[2][2] - m[2][0] * m[0][2]),    - det * (m[0][0] * m[1][2] - m[1][0] * m[0][2]),      0.0    ], [      det * (m[1][0] * m[2][1] - m[2][0] * m[1][1]),    - det * (m[0][0] * m[2][1] - m[2][0] * m[0][1]),      det * (m[0][0] * m[1][1] - m[1][0] * m[0][1]),      0.0,    ] ]  r.append([    -(m[3][0] * r[0][0] + m[3][1] * r[1][0] + m[3][2] * r[2][0]),    -(m[3][0] * r[0][1] + m[3][1] * r[1][1] + m[3][2] * r[2][1]),    -(m[3][0] * r[0][2] + m[3][1] * r[1][2] + m[3][2] * r[2][2]),    1.0,    ])  return rdef matrix_rotate_x(angle):  cos = math.cos(angle)  sin = math.sin(angle)  return [    [1.0,  0.0, 0.0, 0.0],    [0.0,  cos, sin, 0.0],    [0.0, -sin, cos, 0.0],    [0.0,  0.0, 0.0, 1.0],    ]def matrix_rotate_y(angle):  cos = math.cos(angle)  sin = math.sin(angle)  return [    [cos, 0.0, -sin, 0.0],    [0.0, 1.0,  0.0, 0.0],    [sin, 0.0,  cos, 0.0],    [0.0, 0.0,  0.0, 1.0],    ]def matrix_rotate_z(angle):  cos = math.cos(angle)  sin = math.sin(angle)  return [    [ cos, sin, 0.0, 0.0],    [-sin, cos, 0.0, 0.0],    [ 0.0, 0.0, 1.0, 0.0],    [ 0.0, 0.0, 0.0, 1.0],    ]def matrix_rotate(axis, angle):  vx  = axis[0]  vy  = axis[1]  vz  = axis[2]  vx2 = vx * vx  vy2 = vy * vy  vz2 = vz * vz  cos = math.cos(angle)  sin = math.sin(angle)  co1 = 1.0 - cos  return [    [vx2 * co1 + cos,          vx * vy * co1 + vz * sin, vz * vx * co1 - vy * sin, 0.0],    [vx * vy * co1 - vz * sin, vy2 * co1 + cos,          vy * vz * co1 + vx * sin, 0.0],    [vz * vx * co1 + vy * sin, vy * vz * co1 - vx * sin, vz2 * co1 + cos,          0.0],    [0.0, 0.0, 0.0, 1.0],    ]def matrix_scale(fx, fy, fz):  return [    [ fx, 0.0, 0.0, 0.0],    [0.0,  fy, 0.0, 0.0],    [0.0, 0.0,  fz, 0.0],    [0.0, 0.0, 0.0, 1.0],    ]  def point_by_matrix(p, m):  return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0] + m[3][0],          p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1] + m[3][1],          p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2] + m[3][2]]def point_distance(p1, p2):  return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2 + (p2[2] - p1[2]) ** 2)def vector_add(v1, v2):  return [v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]]def vector_sub(v1, v2):  return [v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]]    def vector_by_matrix(p, m):  return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0],          p[0] * m[0][1] + p[1] * m[1][1] + p[2] * m[2][1],          p[0] * m[0][2] + p[1] * m[1][2] + p[2] * m[2][2]]def vector_length(v):  return math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])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] / ldef vector_dotproduct(v1, v2):  return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]def vector_crossproduct(v1, v2):  return [    v1[1] * v2[2] - v1[2] * v2[1],    v1[2] * v2[0] - v1[0] * v2[2],    v1[0] * v2[1] - v1[1] * v2[0],    ]def vector_angle(v1, v2):  s = vector_length(v1) * vector_length(v2)  f = vector_dotproduct(v1, v2) / s  if f >=  1.0: return 0.0  if f <= -1.0: return math.pi / 2.0  return math.atan(-f / math.sqrt(1.0 - f * f)) + math.pi / 2.0def blender_bone2matrix(head, tail, roll):  # Convert bone rest state (defined by bone.head, bone.tail and bone.roll)  # to a matrix (the more standard notation).  # Taken from blenkernel/intern/armature.c in Blender source.  # See also DNA_armature_types.h:47.  target = [0.0, 1.0, 0.0]  delta  = [tail.values()[0][0] - head.values()[0][0], tail.values()[0][1] - head.values()[0][1], tail.values()[0][2] - head.values()[0][2]]  nor    = vector_normalize(delta)  axis   = vector_crossproduct(target, nor)  if vector_dotproduct(axis, axis) > 0.0000000000001:    axis    = vector_normalize(axis)    theta   = math.acos(vector_dotproduct(target, nor))    bMatrix = matrix_rotate(axis, theta)  else:    if vector_dotproduct(target, nor) > 0.0: updown =  1.0    else:                                    updown = -1.0        # Quoted from Blender source : "I think this should work ..."    bMatrix = [      [updown, 0.0,    0.0, 0.0],      [0.0,    updown, 0.0, 0.0],      [0.0,    0.0,    1.0, 0.0],      [0.0,    0.0,    0.0, 1.0],      ]    rMatrix = matrix_rotate(nor, roll)  return matrix_multiply(rMatrix, bMatrix)# Hack for having the model rotated right.# Put in BASE_MATRIX your own rotation if you need some.BASE_MATRIX = None# Cal3D data structuresCAL3D_VERSION = 910NEXT_MATERIAL_ID = 0class Material:  def __init__(self, map_filename = None):    self.ambient_r  = 255    self.ambient_g  = 255    self.ambient_b  = 255    self.ambient_a  = 255    self.diffuse_r  = 255    self.diffuse_g  = 255    self.diffuse_b  = 255    self.diffuse_a  = 255    self.specular_r = 255    self.specular_g = 255    self.specular_b = 255    self.specular_a = 255    self.shininess = 1.0    if map_filename: self.maps_filenames = [map_filename]    else:            self.maps_filenames = []        MATERIALS[map_filename] = self        global NEXT_MATERIAL_ID    self.id = NEXT_MATERIAL_ID    NEXT_MATERIAL_ID += 1      # old cal3d format  def to_cal3d(self):    s = "CRF\0" + struct.pack("iBBBBBBBBBBBBfi", CAL3D_VERSION, self.ambient_r, self.ambient_g, self.ambient_b, self.ambient_a, self.diffuse_r, self.diffuse_g, self.diffuse_b, self.diffuse_a, self.specular_r, self.specular_g, self.specular_b, self.specular_a, self.shininess, len(self.maps_filenames))    for map_filename in self.maps_filenames:      s += struct.pack("i", len(map_filename) + 1)      s += map_filename + "\0"    return s   # new xml format  def to_cal3d_xml(self):    s = "<?xml version=\"1.0\"?>\n"    s += "<HEADER MAGIC=\"XRF\" VERSION=\"%i\"/>\n" % CAL3D_VERSION    s += "<MATERIAL NUMMAPS=\"" + str(len(self.maps_filenames)) + "\">\n"    s += "  <AMBIENT>" + str(self.ambient_r) + " " + str(self.ambient_g) + " " + str(self.ambient_b) + " " + str(self.ambient_a) + "</AMBIENT>\n";    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";          s += "</MATERIAL>\n";            return s  MATERIALS = {}class Mesh:  def __init__(self, name):    self.name      = name    self.submeshes = []        self.next_submesh_id = 0      def to_cal3d(self):    s = "CMF\0" + struct.pack("ii", CAL3D_VERSION, len(self.submeshes))    s += "".join(map(SubMesh.to_cal3d, self.submeshes))    return s  def to_cal3d_xml(self):    s = "<?xml version=\"1.0\"?>\n"    s += "<HEADER MAGIC=\"XMF\" VERSION=\"%i\"/>\n" % CAL3D_VERSION    s += "<MESH NUMSUBMESH=\"%i\">\n" % len(self.submeshes)    s += "".join(map(SubMesh.to_cal3d_xml, self.submeshes))    s += "</MESH>\n"                                                      return sclass SubMesh:  def __init__(self, mesh, material):    self.material   = material    self.vertices   = []    self.faces      = []    self.nb_lodsteps = 0    self.springs    = []        self.next_vertex_id = 0        self.mesh = mesh    self.id = mesh.next_submesh_id    mesh.next_submesh_id += 1    mesh.submeshes.append(self)      def compute_lods(self):    """Computes LODs info for Cal3D (there's no Blender related stuff here)."""        print "Start LODs computation..."    vertex2faces = {}    for face in self.faces:      for vertex in (face.vertex1, face.vertex2, face.vertex3):        l = vertex2faces.get(vertex)        if not l: vertex2faces[vertex] = [face]        else: l.append(face)            couple_treated         = {}    couple_collapse_factor = []    for face in self.faces:      for a, b in ((face.vertex1, face.vertex2), (face.vertex1, face.vertex3), (face.vertex2, face.vertex3)):        a = a.cloned_from or a        b = b.cloned_from or b        if a.id > b.id: a, b = b, a        if not couple_treated.has_key((a, b)):          # The collapse factor is simply the distance between the 2 points :-(          # This should be improved !!          if vector_dotproduct(a.normal, b.normal) < 0.9: continue          couple_collapse_factor.append((point_distance(a.loc, b.loc), a, b))          couple_treated[a, b] = 1          couple_collapse_factor.sort()        collapsed    = {}    new_vertices = []    new_faces    = []    for factor, v1, v2 in couple_collapse_factor:      # Determines if v1 collapses to v2 or v2 to v1.      # We choose to keep the vertex which is on the smaller number of faces, since      # this one has more chance of being in an extrimity of the body.      # Though heuristic, this rule yields very good results in practice.      if   len(vertex2faces[v1]) <  len(vertex2faces[v2]): v2, v1 = v1, v2      elif len(vertex2faces[v1]) == len(vertex2faces[v2]):        if collapsed.get(v1, 0): v2, v1 = v1, v2 # v1 already collapsed, try v2              if (not collapsed.get(v1, 0)) and (not collapsed.get(v2, 0)):        collapsed[v1] = 1        collapsed[v2] = 1                # Check if v2 is already colapsed        while v2.collapse_to: v2 = v2.collapse_to                common_faces = filter(vertex2faces[v1].__contains__, vertex2faces[v2])                v1.collapse_to         = v2        v1.face_collapse_count = len(common_faces)                for clone in v1.clones:          # Find the clone of v2 that correspond to this clone of v1          possibles = []          for face in vertex2faces[clone]:            possibles.append(face.vertex1)            possibles.append(face.vertex2)            possibles.append(face.vertex3)          clone.collapse_to = v2          for vertex in v2.clones:            if vertex in possibles:              clone.collapse_to = vertex              break                      clone.face_collapse_count = 0          new_vertices.append(clone)        # HACK -- all faces get collapsed with v1 (and no faces are collapsed with v1's        # clones). This is why we add v1 in new_vertices after v1's clones.        # This hack has no other incidence that consuming a little few memory for the        # extra faces if some v1's clone are collapsed but v1 is not.        new_vertices.append(v1)                self.nb_lodsteps += 1 + len(v1.clones)                new_faces.extend(common_faces)        for face in common_faces:          face.can_collapse = 1                    # Updates vertex2faces          vertex2faces[face.vertex1].remove(face)          vertex2faces[face.vertex2].remove(face)          vertex2faces[face.vertex3].remove(face)        vertex2faces[v2].extend(vertex2faces[v1])            new_vertices.extend(filter(lambda vertex: not vertex.collapse_to, self.vertices))    new_vertices.reverse() # Cal3D want LODed vertices at the end    for i in range(len(new_vertices)): new_vertices.id = i    self.vertices = new_vertices        new_faces.extend(filter(lambda face: not face.can_collapse, self.faces))    new_faces.reverse() # Cal3D want LODed faces at the end    self.faces = new_faces        print "LODs computed : %s vertices can be removed (from a total of %s)." % (self.nb_lodsteps, len(self.vertices))      def rename_vertices(self, new_vertices):    """Rename (change ID) of all vertices, such as self.vertices == new_vertices."""    for i in range(len(new_vertices)): new_vertices.id = i    self.vertices = new_vertices      def to_cal3d(self):    s =  struct.pack("iiiiii", self.material.id, len(self.vertices), len(self.faces), self.nb_lodsteps, len(self.springs), len(self.material.maps_filenames))    s += "".join(map(Vertex.to_cal3d, self.vertices))    s += "".join(map(Spring.to_cal3d, self.springs))    s += "".join(map(Face  .to_cal3d, self.faces))    return s  def to_cal3d_xml(self):    s = "  <SUBMESH NUMVERTICES=\"%i\" NUMFACES=\"%i\" MATERIAL=\"%i\" " %         (len(self.vertices), len(self.faces), self.material.id)    s += "NUMLODSTEPS=\"%i\" NUMSPRINGS=\"%i\" NUMTEXCOORDS=\"%i\">\n" %          (self.nb_lodsteps, len(self.springs),         len(self.material.maps_filenames))    s += "".join(map(Vertex.to_cal3d_xml, self.vertices))    s += "".join(map(Spring.to_cal3d_xml, self.springs))    s += "".join(map(Face.to_cal3d_xml, self.faces))    s += "  </SUBMESH>\n"    return sclass Vertex:  def __init__(self, submesh, loc, normal):    self.loc    = loc    self.normal = normal    self.collapse_to         = None    self.face_collapse_count = 0    self.maps       = []    self.influences = []    self.weight = None        self.cloned_from = None    self.clones      = []        self.submesh = submesh    self.id = submesh.next_vertex_id    submesh.next_vertex_id += 1    submesh.vertices.append(self)      def to_cal3d(self):    if self.collapse_to: collapse_id = self.collapse_to.id    else:                collapse_id = -1    s =  struct.pack("ffffffii", self.loc[0], self.loc[1], self.loc[2], self.normal[0], self.normal[1], self.normal[2], collapse_id, self.face_collapse_count)    s += "".join(map(Map.to_cal3d, self.maps))    s += struct.pack("i", len(self.influences))    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    else:      collapse_id = -1    s = "    <VERTEX ID=\"%i\" NUMINFLUENCES=\"%i\">\n" %         (self.id, len(self.influences))    s += "      <POS>%f %f %f</POS>\n" % (self.loc[0], self.loc[1], self.loc[2])    s += "      <NORM>%f %f %f</NORM>\n" %          (self.normal[0], self.normal[1], self.normal[2])    if collapse_id != -1:      s += "      <COLLAPSEID>%i</COLLAPSEID>\n" % collapse_id      s += "      <COLLAPSECOUNT>%i</COLLAPSECOUNT>\n" %            self.face_collapse_count    s += "".join(map(Map.to_cal3d_xml, self.maps))    s += "".join(map(Influence.to_cal3d_xml, self.influences))    if not self.weight is None:      s += "      <PHYSIQUE>%f</PHYSIQUE>\n" % len(self.weight)    s += "    </VERTEX>\n"    return s class Map:  def __init__(self, u, v):    self.u = u    self.v = v      def to_cal3d(self):    return struct.pack("ff", self.u, self.v)  def to_cal3d_xml(self):    return "      <TEXCOORD>%f %f</TEXCOORD>\n" % (self.u, self.v)    class Influence:  def __init__(self, bone, weight):    self.bone   = bone    self.weight = weight      def to_cal3d(self):    return struct.pack("if", self.bone.id, self.weight)  def to_cal3d_xml(self):    return "      <INFLUENCE ID=\"%i\">%f</INFLUENCE>\n" %            (self.bone.id, self.weight) class Spring:  def __init__(self, vertex1, vertex2):    self.vertex1 = vertex1    self.vertex2 = vertex2    self.spring_coefficient = 0.0    self.idlelength = 0.0      def to_cal3d(self):    return struct.pack("iiff", self.vertex1.id, self.vertex2.id, self.spring_coefficient, self.idlelength)  def to_cal3d_xml(self):    return "    <SPRING VERTEXID=\"%i %i\" COEF=\"%f\" LENGTH=\"%f\"/>\n" %            (self.vertex1.id, self.vertex2.id, self.spring_coefficient,           self.idlelength)class Face:  def __init__(self, submesh, vertex1, vertex2, vertex3):    self.vertex1 = vertex1    self.vertex2 = vertex2    self.vertex3 = vertex3        self.can_collapse = 0        self.submesh = submesh    submesh.faces.append(self)      def to_cal3d(self):    return struct.pack("iii", self.vertex1.id, self.vertex2.id, self.vertex3.id)  def to_cal3d_xml(self):    return "    <FACE VERTEXID=\"%i %i %i\"/>\n" %            (self.vertex1.id, self.vertex2.id, self.vertex3.id) class Skeleton:  def __init__(self):    self.bones = []        self.next_bone_id = 0      def to_cal3d(self):    s = "CSF\0" + struct.pack("ii", CAL3D_VERSION, len(self.bones))    s += "".join(map(Bone.to_cal3d, self.bones))    return s  def to_cal3d_xml(self):    s = "<?xml version=\"1.0\"?>\n"    s += "<HEADER MAGIC=\"XSF\" VERSION=\"%i\"/>\n" % CAL3D_VERSION    s += "<SKELETON NUMBONES=\"%i\">\n" % len(self.bones)    s += "".join(map(Bone.to_cal3d_xml, self.bones))    s += "</SKELETON>\n"    return sBONES = {}class Bone:  def __init__(self, skeleton, parent, name, loc, rot):    self.parent = parent    self.name   = name    self.loc = loc    self.rot = rot    self.children = []        self.matrix = matrix_translate(quaternion2matrix(rot), loc)    if parent:      self.matrix = matrix_multiply(parent.matrix, self.matrix)      parent.children.append(self)        # lloc and lrot 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.skeleton = skeleton    self.id = skeleton.next_bone_id    skeleton.next_bone_id += 1    skeleton.bones.append(self)        BONES[name] = self      def to_cal3d(self):    s =  struct.pack("i", len(self.name) + 1) + self.name + "\0"        # We need to negate quaternion W value, but why ?    s += struct.pack("ffffffffffffff", self.loc[0], self.loc[1], self.loc[2], self.rot[0], self.rot[1], self.rot[2], -self.rot[3], self.lloc[0], self.lloc[1], self.lloc[2], self.lrot[0], self.lrot[1], self.lrot[2], -self.lrot[3])    if self.parent: s += struct.pack("i", self.parent.id)    else:           s += struct.pack("i", -1)    s += struct.pack("i", len(self.children))    s += "".join(map(lambda bone: struct.pack("i", bone.id), self.children))    return s  def to_cal3d_xml(self):    s = "  <BONE ID=\"%i\" NAME=\"%s\" NUMCHILD=\"%i\">\n" %         (self.id, self.name, len(self.children))    # We need to negate quaternion W value, but why ?    s += "    <TRANSLATION>%f %f %f</TRANSLATION>\n" %          (self.loc[0], self.loc[1], self.loc[2])    s += "    <ROTATION>%f %f %f %f</ROTATION>\n" %          (self.rot[0], self.rot[1], self.rot[2], -self.rot[3])    s += "    <LOCALTRANSLATION>%f %f %f</LOCALTRANSLATION>\n" %          (self.lloc[0], self.lloc[1], self.lloc[2])    s += "    <LOCALROTATION>%f %f %f %f</LOCALROTATION>\n" %          (self.lrot[0], self.lrot[1], self.lrot[2], -self.lrot[3])    if self.parent:      s += "    <PARENTID>%i</PARENTID>\n" % self.parent.id    else:      s += "    <PARENTID>%i</PARENTID>\n" % -1    s += "".join(map(lambda bone: "    <CHILDID>%i</CHILDID>\n" % bone.id,         self.children))    s += "  </BONE>\n"    return sclass Animation:  def __init__(self, name, duration = 0.0):    self.name     = name    self.duration = duration    self.tracks   = {} # Map bone names to tracks      def to_cal3d(self):    s = "CAF\0" + struct.pack("ifi", CAL3D_VERSION, self.duration, len(self.tracks))    s += "".join(map(Track.to_cal3d, self.tracks.values()))    return s  def to_cal3d_xml(self):    s = "<?xml version=\"1.0\"?>\n"    s += "<HEADER MAGIC=\"XAF\" VERSION=\"%i\"/>\n" % CAL3D_VERSION    s += "<ANIMATION DURATION=\"%f\" NUMTRACKS=\"%i\">\n" %          (self.duration, len(self.tracks))                                s += "".join(map(Track.to_cal3d_xml, self.tracks.values()))    s += "</ANIMATION>\n"    return s                                                           class Track:  def __init__(self, animation, bone):    self.bone      = bone    self.keyframes = []        self.animation = animation    animation.tracks[bone.name] = self      def to_cal3d(self):    s = struct.pack("ii", self.bone.id, len(self.keyframes))    s += "".join(map(KeyFrame.to_cal3d, self.keyframes))    return s  def to_cal3d_xml(self):    s = "  <TRACK BONEID=\"%i\" NUMKEYFRAMES=\"%i\">\n" %         (self.bone.id, len(self.keyframes))    s += "".join(map(KeyFrame.to_cal3d_xml, self.keyframes))    s += "  </TRACK>\n"    return s    class KeyFrame:  def __init__(self, track, time, loc, rot):    self.time = time    self.loc  = loc    self.rot  = rot        self.track = track    track.keyframes.append(self)      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" %          (self.loc[0], self.loc[1], self.loc[2])    # We need to negate quaternion W value, but why ?    s += "      <ROTATION>%f %f %f %f</ROTATION>\n" %          (self.rot[0], self.rot[1], self.rot[2], -self.rot[3])    s += "    </KEYFRAME>\n"    return s                                                      def export(filename):  global MESSAGES    if EXPORT_FOR_SOYA:    global BASE_MATRIX    BASE_MATRIX = matrix_rotate_x(-math.pi / 2.0)      # Get the scene  scene = Blender.Scene.getCurrent()    # ---- Export skeleton (=armature) ----------------------------------------  skeleton = Skeleton()    foundarmature = False  for obj in Blender.Object.Get():    data = obj.getData()    if type(data) is not Blender.Types.ArmatureType:      continue        if foundarmature == True:      MESSAGES += "Found multiple armatures! '" + obj.getName() + "' ignored.\n"      continue    foundarmature = True    matrix = obj.getMatrix()    if BASE_MATRIX:      matrix = matrix_multiply(BASE_MATRIX, matrix)        def treat_bone(b, parent = None):      head = b.head      tail = b.tail            # Turns the Blender's head-tail-roll notation into a quaternion      quat = matrix2quaternion(blender_bone2matrix(head, tail, b.roll['BONESPACE']))            if parent:        # Compute the translation from the parent bone's head to the child        # bone's head, in the parent bone coordinate system.        # The translation is parent_tail - parent_head + child_head,        # but parent_tail and parent_head must be converted from the parent's parent        # system coordinate into the parent system coordinate.                parent_invert_transform = matrix_invert(quaternion2matrix(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())        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(b.getQuat()), quat)        loc = parentheadtotail        rot = quat                bone = Bone(skeleton, parent, b.getName(), loc, rot)      else:        # Apply the armature's matrix to the root bones        head = point_by_matrix(head, matrix)        tail = point_by_matrix(tail, matrix)        quat = matrix2quaternion(matrix_multiply(matrix, quaternion2matrix(quat))) # Probably not optimal                # loc = vector_add(head, b.getLoc())        # rot = quaternion_multiply(blender2cal3dquat(b.getQuat()), quat)        loc = head        rot = quat                # Here, the translation is simply the head vector        bone = Bone(skeleton, None, b.getName(), loc, rot)              bone.head = head      bone.tail = tail            for child in b.getChildren():        treat_bone(child, bone)         foundroot = False    for b in data.bones.values():      # child bones are handled in treat_bone      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."        #continue              treat_bone(b)      foundroot = True  # ---- Export Mesh data ---------------------------------------------------    meshes = []    for obj in Blender.Object.Get():    data = obj.bones.values()    if (type(data) is Blender.Types.NMeshType) and data.faces:      mesh_name = obj.getName()      mesh = Mesh(mesh_name)      meshes.append(mesh)            matrix = obj.getMatrix()      if BASE_MATRIX:        matrix = matrix_multiply(BASE_MATRIX, matrix)              faces = data.faces      while faces:        image          = faces[0].image        image_filename = image and image.filename        material       = MATERIALS.get(image_filename) or Material(image_filename)        outputuv       = len(material.maps_filenames) > 0                # TODO add material color support here                submesh  = SubMesh(mesh, material)        vertices = {}        for face in faces[:]:          if (face.image and face.image.filename) == image_filename:            faces.remove(face)                        if not face.smooth:              p1 = face.v[0].co              p2 = face.v[1].co              p3 = face.v[2].co              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]],                ), matrix))                          face_vertices = []            for i in range(len(face.v)):              vertex = vertices.get(face.v.index)              if not vertex:                coord  = point_by_matrix (face.v.co, matrix)                if face.smooth:                  normal = vector_normalize(vector_by_matrix(face.v.no, matrix))                vertex  = vertices[face.v.index] = Vertex(submesh, coord, normal)                influences = data.getVertexInfluences(face.v.index)                # should this really be a warning? (well currently enabled,                # because blender has some bugs where it doesn't return                # influences in python api though they are set, and because                # cal3d<=0.9.1 had bugs where objects without influences                # aren't drawn.                if not 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()                                # sum of influences is not always 1.0 in Blender ?!?!                sum = 0.0                for bone_name, weight in influences:                  sum += weight                                for bone_name, weight in influences:                  if bone_name not in BONES:                    MESSAGES += "Couldn't find bone '%s' which influences"                                 "object '%s'.\n" % (bone_name, obj.getName())                    continue                  vertex.influences.append(Influence(BONES[bone_name], weight / sum))                                elif not face.smooth:                # We cannot share vertex for non-smooth faces, since Cal3D does not                # support vertex sharing for 2 vertices with different normals.                # => we must clone the vertex.                                old_vertex = vertex                vertex = Vertex(submesh, vertex.loc, normal)                vertex.cloned_from = old_vertex                vertex.influences = old_vertex.influences                old_vertex.clones.append(vertex)                              if data.hasFaceUV():                uv = [face.uv[0], 1.0 - face.uv[1]]                if not vertex.maps:                  if outputuv: vertex.maps.append(Map(*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                  # different UV texture coodinates.                  # => we must clone the vertex.                                    for clone in vertex.clones:                    if (clone.maps[0].u == uv[0]) and (clone.maps[0].v == uv[1]):                      vertex = clone                      break                  else: # Not yet cloned...                    old_vertex = vertex                    vertex = Vertex(submesh, vertex.loc, vertex.normal)                    vertex.cloned_from = old_vertex                    vertex.influences = old_vertex.influences                    if outputuv: vertex.maps.append(Map(*uv))                    old_vertex.clones.append(vertex)                                  face_vertices.append(vertex)                          # Split faces with more than 3 vertices            for i in range(1, len(face.v) - 1):              Face(submesh, face_vertices[0], face_vertices, face_vertices)<br>              <br>        # Computes LODs info<br>        <span class="cpp-keyword">if</span> LODS:<br>          submesh.compute_lods()<br>        <br>  # —- Export animations ————————————————–<br>  ANIMATIONS = {}<br><br>  <span class="cpp-keyword">for</span> a in Blender.Armature.NLA.GetActions().iteritems():<br>    animation_name = a[<span class="cpp-number">0</span>]<br>    animation = Animation(animation_name)<br>    animation.duration = <span class="cpp-number">0</span>.<span class="cpp-number">0</span><br><br>    <span class="cpp-keyword">for</span> b in a[<span class="cpp-number">1</span>].getAllChannelIpos().iteritems():<br>      bone_name = b[<span class="cpp-number">0</span>]<br>      <span class="cpp-keyword">if</span> bone_name not in BONES:<br>        MESSAGES += <span class="cpp-literal">"No Bone '"</span> + bone_name + <span class="cpp-literal">"' defined (from Animation '"</span>             + animation_name + <span class="cpp-literal">"' ?!?\n"</span><br>        <span class="cpp-keyword">continue</span>                                            <br><br>      bone = BONES[bone_name]<br><br>      track = Track(animation, bone)<br>      track.finished = <span class="cpp-number">0</span><br>      animation.tracks[bone_name] = track<br><br>      ipo = b[<span class="cpp-number">1</span>]<br>      <br>      times = []<br>      <br>      # SideNote: MatzeB: Ipo.getCurve(curvename) is broken in blender <span class="cpp-number">2</span>.<span class="cpp-number">33</span> and<br>      # below <span class="cpp-keyword">if</span> the Ipo comes from an Action, so only use Ipo.getCurves()!<br>      # also blender upto <span class="cpp-number">2</span>.33a had a bug where IpoCurve.evaluate was not<br>      # exposed to the python interface :-/<br>      <br>      #run <span class="cpp-number">1</span>: we need to find all time values where we need to produce keyframes<br>      <span class="cpp-keyword">for</span> curve in ipo.getCurves():<br>        curve_name = curve.getName()<br><br>        <span class="cpp-keyword">if</span> curve_name not in [<span class="cpp-literal">"QuatW"</span>, <span class="cpp-literal">"QuatX"</span>, <span class="cpp-literal">"QuatY"</span>, <span class="cpp-literal">"QuatZ"</span>, <span class="cpp-literal">"LocX"</span>, <span class="cpp-literal">"LocY"</span>, <span class="cpp-literal">"LocZ"</span>]:<br>          MESSAGES += <span class="cpp-literal">"Curve type %s not supported in Action '%s' Bone '%s'.\n"</span>                    % (curve_name, animation_name, bone_name)<br>        <br>        <span class="cpp-keyword">for</span> p in curve.getPoints():<br>          time = p.getPoints() [<span class="cpp-number">0</span>]<br>          <span class="cpp-keyword">if</span> time not in times:<br>            times.append(time)<br>      <br>      times.sort()<br><br>      # run2: now create keyframes<br>      <span class="cpp-keyword">for</span> time in times:<br>        cal3dtime = (time-<span class="cpp-number">1</span>) / <span class="cpp-number">25</span>.<span class="cpp-number">0</span> # assume 25FPS by <span class="cpp-keyword">default</span><br>        <span class="cpp-keyword">if</span> cal3dtime &gt; animation.duration:<br>          animation.duration = cal3dtime<br>        trans = [<span class="cpp-number">0</span>, <span class="cpp-number">0</span>, <span class="cpp-number">0</span>]<br>        quat  = [<span class="cpp-number">0</span>, <span class="cpp-number">0</span>, <span class="cpp-number">0</span>, <span class="cpp-number">0</span>]<br>        <br>        <span class="cpp-keyword">for</span> curve in ipo.getCurves():<br>          val = curve.evaluate(time)<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"LocX"</span>: trans[<span class="cpp-number">0</span>] = val<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"LocY"</span>: trans[<span class="cpp-number">1</span>] = val<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"LocZ"</span>: trans[<span class="cpp-number">2</span>] = val<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"QuatW"</span>: quat[<span class="cpp-number">3</span>] = val<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"QuatX"</span>: quat[<span class="cpp-number">0</span>] = val<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"QuatY"</span>: quat[<span class="cpp-number">1</span>] = val<br>          <span class="cpp-keyword">if</span> curve.getName() == <span class="cpp-literal">"QuatZ"</span>: quat[<span class="cpp-number">2</span>] = val<br>          <br>        transt = vector_by_matrix(trans, bone.matrix)<br>        loc = vector_add(bone.loc, transt)<br>        rot = quaternion_multiply(quat, bone.rot)<br>        rot = quaternion_normalize(rot)<br>        <br>        KeyFrame(track, cal3dtime, loc, rot)<br>        <br>    <span class="cpp-keyword">if</span> animation.duration &lt;= <span class="cpp-number">0</span>:<br>      MESSAGES += <span class="cpp-literal">"Ignoring Animation '"</span> + animation_name +                   <span class="cpp-literal">"': duration is 0.\n"</span><br>      <span class="cpp-keyword">continue</span><br>    ANIMATIONS[animation_name] = animation<br>    <br>  # Save all data<br>  <span class="cpp-keyword">if</span> filename.endswith(<span class="cpp-literal">".cfg"</span>):<br>    filename = os.path.splitext(filename)[<span class="cpp-number">0</span>]<br>  BASENAME = os.path.basename(filename)         <br>  DIRNAME  = os.path.dirname(filename)<br>  <span class="cpp-keyword">if</span> PREFIX_FILE_WITH_MODEL_NAME: PREFIX = BASENAME + <span class="cpp-literal">"_"</span><br>  <span class="cpp-keyword">else</span>:                           PREFIX = <span class="cpp-literal">""</span><br>  <span class="cpp-keyword">if</span> XML: FORMAT_PREFIX = <span class="cpp-literal">"x"</span>; encode = lambda x: x.to_cal3d_xml()<br>  <span class="cpp-keyword">else</span>:   FORMAT_PREFIX = <span class="cpp-literal">"c"</span>; encode = lambda x: x.to_cal3d()<br>  print DIRNAME + <span class="cpp-literal">" - "</span> + BASENAME<br>  <br>  cfg = open(os.path.join(DIRNAME, BASENAME + <span class="cpp-literal">".cfg"</span>), <span class="cpp-literal">"wb"</span>)<br>  print &gt;&gt; cfg, <span class="cpp-literal">"# Cal3D model exported from Blender with blender2cal3d.py"</span><br>  print &gt;&gt; cfg<br><br>  <span class="cpp-keyword">if</span> SCALE != <span class="cpp-number">1</span>.<span class="cpp-number">0</span>:<br>    print &gt;&gt; cfg, <span class="cpp-literal">"scale=%s"</span> % SCALE<br>    print &gt;&gt; cfg<br>    <br>  filename = BASENAME + <span class="cpp-literal">"."</span> + FORMAT_PREFIX + <span class="cpp-literal">"sf"</span><br>  open(os.path.join(DIRNAME, filename), <span class="cpp-literal">"wb"</span>).write(encode(skeleton))<br>  print &gt;&gt; cfg, <span class="cpp-literal">"skeleton=%s"</span> % filename<br>  print &gt;&gt; cfg<br>  <br>  <span class="cpp-keyword">for</span> animation in ANIMATIONS.values():<br>    <span class="cpp-keyword">if</span> not animation.name.startswith(<span class="cpp-literal">"_"</span>):<br>      <span class="cpp-keyword">if</span> animation.duration: # Cal3D does not support animation with only one state<br>        filename = PREFIX + animation.name + <span class="cpp-literal">"."</span> + FORMAT_PREFIX + <span class="cpp-literal">"af"</span><br>        open(os.path.join(DIRNAME, filename), <span class="cpp-literal">"wb"</span>).write(encode(animation))<br>        print &gt;&gt; cfg, <span class="cpp-literal">"animation=%s"</span> % filename<br>        <br>  print &gt;&gt; cfg<br>  <br>  <span class="cpp-keyword">for</span> mesh in meshes:<br>    <span class="cpp-keyword">if</span> not mesh.name.startswith(<span class="cpp-literal">"_"</span>):<br>      filename = PREFIX + mesh.name + <span class="cpp-literal">"."</span> + FORMAT_PREFIX + <span class="cpp-literal">"mf"</span><br>      open(os.path.join(DIRNAME, filename), <span class="cpp-literal">"wb"</span>).write(encode(mesh))<br>      print &gt;&gt; cfg, <span class="cpp-literal">"mesh=%s"</span> % filename<br>  print &gt;&gt; cfg<br>  <br>  materials = MATERIALS.values()<br>  materials.sort(lambda a, b: cmp(a.id, b.id))<br>  <span class="cpp-keyword">for</span> material in materials:<br>    <span class="cpp-keyword">if</span> material.maps_filenames:<br>      filename = PREFIX + os.path.splitext(os.path.basename(material.maps_filenames[<span class="cpp-number">0</span>]))[<span class="cpp-number">0</span>] + <span class="cpp-literal">"."</span> + FORMAT_PREFIX + <span class="cpp-literal">"rf"</span><br>    <span class="cpp-keyword">else</span>:<br>      filename = PREFIX + <span class="cpp-literal">"plain."</span> + FORMAT_PREFIX + <span class="cpp-literal">"rf"</span><br>    open(os.path.join(DIRNAME, filename), <span class="cpp-literal">"wb"</span>).write(encode(material))<br>    print &gt;&gt; cfg, <span class="cpp-literal">"material=%s"</span> % filename<br>  print &gt;&gt; cfg<br>  <br>  MESSAGES += <span class="cpp-literal">"Saved to '%s.cfg'\n"</span> % BASENAME<br>  MESSAGES += <span class="cpp-literal">"Done."</span><br>  <br>  # show messages<br>  print MESSAGES<br><br># some (ugly) gui to show the error messages - no scrollbar or other luxury,<br># please improve <span class="cpp-keyword">this</span> <span class="cpp-keyword">if</span> you know how<br>def gui():<br>  global MESSAGES<br>  button = Blender.Draw.Button(<span class="cpp-literal">"Ok"</span>, <span class="cpp-number">1</span>, <span class="cpp-number">0</span>, <span class="cpp-number">0</span>, <span class="cpp-number">50</span>, <span class="cpp-number">20</span>, <span class="cpp-literal">"Close Window"</span>)<br>    <br>  lines = MESSAGES.split(<span class="cpp-literal">"\n"</span>)<br>  <span class="cpp-keyword">if</span> len(lines) &gt; <span class="cpp-number">15</span>:<br>    lines.append(<span class="cpp-literal">"Please also take a look at your console"</span>)<br>  pos = len(lines) * <span class="cpp-number">15</span> + <span class="cpp-number">20</span><br>  <span class="cpp-keyword">for</span> line in lines:<br>    Blender.BGL.glRasterPos2i(<span class="cpp-number">0</span>, pos)<br>    Blender.Draw.Text(line)<br>    pos -= <span class="cpp-number">15</span><br><br>def event(evt, val):<br>  <span class="cpp-keyword">if</span> evt == Blender.Draw.ESCKEY:<br>    Blender.Draw.Exit()<br>    <span class="cpp-keyword">return</span><br><br>def button_event(evt):<br>  <span class="cpp-keyword">if</span> evt == <span class="cpp-number">1</span>:<br>    Blender.Draw.Exit()<br>    <span class="cpp-keyword">return</span><br><br># Main script<br>def fs_callback(filename):<br>  export(filename)<br>  Blender.Draw.<span class="cpp-keyword">Register</span>(gui, event, button_event)<br><br><br># Check <span class="cpp-keyword">for</span> batch mode<br><span class="cpp-keyword">if</span> <span class="cpp-literal">"–blender2cal3d"</span> in sys.argv:<br>  args = sys.argv[sys.argv.index(<span class="cpp-literal">"–blender2cal3d"</span>) + <span class="cpp-number">1</span>:]<br>  <span class="cpp-keyword">for</span> arg in args:<br>    attr, val = arg.split(<span class="cpp-literal">"="</span>)<br>    <span class="cpp-keyword">try</span>: val = <span class="cpp-keyword">int</span>(val)<br>    except:<br>      <span class="cpp-keyword">try</span>: val = <span class="cpp-keyword">float</span>(val)<br>      except: pass<br>    globals()[attr] = val<br>  export(FILENAME)<br>  Blender.Quit()<br>  <br><span class="cpp-keyword">else</span>:<br>  <span class="cpp-keyword">if</span> FILENAME: fs_callback(FILENAME)<br>  <span class="cpp-keyword">else</span>:<br>    defaultname = Blender.Get(<span class="cpp-literal">"filename"</span>)<br>    <span class="cpp-keyword">if</span> defaultname.endswith(<span class="cpp-literal">".blend"</span>):<br>      defaultname = defaultname[<span class="cpp-number">0</span>:len(defaultname)-len(<span class="cpp-literal">".blend"</span>)] + <span class="cpp-literal">".cfg"</span><br>    Blender.Window.FileSelector(fs_callback, <span class="cpp-literal">"Cal3D Export"</span>, defaultname)<br><br></pre></div><!–ENDSCRIPT–> <br><br><!–EDIT–><span class=editedby><!–/EDIT–>[Edited by - owl on January 19, 2006 4:44:54 AM]<!–EDIT–></span><!–/EDIT–>
[size="2"]I like the Walrus best.
Hello, I changed some part of exporter and it appeared to work fine for me. I tested it with CVS build of Blender 2.41 as it will be released soon. But unfortunately I have only 1 model to test it on. Could anybody test it, please, on models that previously were exported into cal3d format succesfully.

Thank you.
Kostya.
http://www.geocities.com/kostyabkg/blender2cal3d_4.zip
Hi
Do you come from the Blender forum?
If so, I think my wife tried the plug in already and we have issues with it (might be because it exports for a different cal3d version?)
We use version 0.10
If you are not from the Blender forum, nvm about that, we'll try it tomorrow.

And thanks for all the effort.
Yes, you are right,
I just decided to post it here and there to get more feedback, and I already got some issues posted about in Blender forum, will be working on that today. I am just a beginner programmer, so, please, don't be expecting miracles :o) I'll just try to do what I can.

This topic is closed to new replies.

Advertisement