Scripts:
authorWillian Padovani Germano <wpgermano@gmail.com>
Tue, 30 Nov 2004 02:27:46 +0000 (02:27 +0000)
committerWillian Padovani Germano <wpgermano@gmail.com>
Tue, 30 Nov 2004 02:27:46 +0000 (02:27 +0000)
- Fixes by Jean-Michel Soler: mod_ai2obj.py, mod_svg2obj.py;
- Fixes by Campbell Barton: obj_import.py;
- Small fix to mod_meshtools.py (fixes bug #1605: http://projects.blender.org/tracker/?func=detail&atid=125&aid=1605&group_id=9);
- Updates by Jean-Baptiste (Jiba) to his blender2cal3d.py;
- Updates to all his import / export scripts (added doc data) by Anthony D'Agostino;
- Update to off_import: support for uv data, by Arne Schmitz.

BPython:
- Removed Object.get and .getSelected (deprecated long ago, we use .Get and .GetSelected) -- fixes #1861: http://projects.blender.org/tracker/?func=detail&atid=125&aid=1861&group_id=9
- Applied patch by Michael Reimpell: quat.c - fix for wrong initialization with newQuaternionObject; Mathutils documentation improvements.
- Stani reported a wrong return msg in IpoCurve.Get (that is unimplemented).

Thanks to all coders mentioned above!

25 files changed:
release/scripts/blender2cal3d.py
release/scripts/lightwave_export.py
release/scripts/lightwave_import.py
release/scripts/mod_ai2obj.py
release/scripts/mod_meshtools.py
release/scripts/mod_svg2obj.py
release/scripts/nendo_export.py
release/scripts/nendo_import.py
release/scripts/obj_import.py
release/scripts/off_export.py
release/scripts/off_import.py
release/scripts/radiosity_export.py
release/scripts/radiosity_import.py
release/scripts/raw_export.py
release/scripts/raw_import.py
release/scripts/slp_import.py
release/scripts/truespace_export.py
release/scripts/truespace_import.py
release/scripts/videoscape_export.py
release/scripts/wings_export.py
release/scripts/wings_import.py
source/blender/python/api2_2x/Ipocurve.c
source/blender/python/api2_2x/Object.c
source/blender/python/api2_2x/doc/Mathutils.py
source/blender/python/api2_2x/quat.c

index 3d7d148a55cedec76fc5a3ae862f3e85b84697f7..835ec16ae1f63f2cc796049d96988cc2bd20d9cb 100644 (file)
@@ -1,56 +1,15 @@
 #!BPY
 
 """
-Name: 'Cal3D Exporter V0.7'
-Blender: 234
+Name: 'Cal3D v0.9'
+Blender: 235
 Group: 'Export'
-Tip: 'Export armature/bone data to the Cal3D library.'
+Tip: 'Export armature/bone/mesh/action data to the Cal3D format.'
 """
 
-__author__ = ["Jean-Baptiste Lamy (Jiba)", "Chris Montijin", "Damien McGinnes"]
-__url__ = ("blender", "elysiun", "Cal3D, http://cal3d.sf.net")
-__version__ = "0.7"
-
-__bpydoc__ = """\
-This script exports armature / bone data to the well known open source Cal3D
-library.
-
-Usage:
-
-Simply run the script to export available armatures.
-
-Supported:<br>
-    Cal3D versions 0.7 -> 0.9.
-
-Known issues:<br>
-    Material color is not supported yet;<br>
-    Cal3D springs (for clothes and hair) are not supported yet;<br>
-    Cal3d has a bug in that a cycle that doesn't have a root bone channel
-will segfault cal3d.  Until cal3d supports this, add a keyframe for the
-root bone;<br>
-    When you finish an animation and run the script you can get an error
-(something with KeyError). Just save your work and reload the model. This is
-usually caused by deleted items hanging around;<br>
-    If a vertex is assigned to one or more bones, but has for each bone a
-weight of zero, there used to be a subdivision by zero error somewhere.  As a
-workaround, if sum is 0.0 then sum becomes 1.0.  It's recommended that you give
-weights to all bones to avoid problems.
-
-Notes:<br>
-    Objects/bones/actions whose names start by "_" are not exported so call IK
-and null bones _LegIK, for example;<br>
-    All your armature's exported bones must be connected to another bone
-    (except for the root bone). Contrary to Blender, Cal3D doesn't support
-"floating" bones.<br>
-    Actions that start with '@' will be exported as actions, others will be
-exported as cycles.
-"""
-
-# $Id$
-#
-# Copyright (C) 2003 Jean-Baptiste LAMY -- jiba@tuxfamily.org
-# Copyright (C) 2004 Chris Montijin
-# Copyright (C) 2004 Damien McGinnes
+# 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
@@ -67,150 +26,111 @@ exported as cycles.
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
-# This script is a Blender 2.34 => Cal3D 0.7/0.8/0.9 converter.
-# (See http://blender.org and http://cal3d.sourceforge.net)
-#
-
-# This script was written by Jiba, modified by Chris and later modified by Damien
-
-# Changes:
-#
-# 0.7 Damien McGinnes <mcginnes at netspeed com au>
-#    Added NLA functionality for IPOs - this simplifies
-#      the animation export and speeds it up significantly
-#      it also removes the constraints on channel names -
-#      they no longer have to match the bone or action and
-#      .L .R etc are supported
-#    bones starting with _ are not exported
-#    textures no longer flipped vertically
-#    fixed a filename bug for .csf and .cfg
-#    actions that are prefixed with '@' go into the cfg file
-#      as actions rather than cycles    
-#    works with baked IK actions, unbaked ones wont work well
-#      because you wont have the constraints evaluated
-#    added an FPS slider into the gui
-#    added registry saving for gui state. 
-#    
-# 0.6 Chris Montjin
-#    Updated for Blender 2.32, 2.33
-#    added basic GUI
-#    generally improved flexibility
-#
-# 0.5 Jiba <jiba@tuxfamily.org>
-#    Initial Release for Blender 2.28
+__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.
 
-# HOW TO USE :
-# 1 - load the script in Blender's text editor
-# 2 - type M-P (meta/alt + P) and wait until script execution is finished
-# or install it in .scripts and access from the export menu
+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, ...).
 
-# ADVICE
-# - Objects/bones/actions whose names start by "_" are not exported
-#   so call IK and null bones _LegIK for example
-# - All your armature's exported bones must be connected to another bone (except
-#   for the rootbone). Contrary to Blender, Cal3D doesn't support "floating" bones.
-# - Actions that start with '@' will be exported as actions, others will be
-#   exported as cycles
+You should be able to open the .cfg file in cal3d_miniviewer.
 
-# BUGS / TODO :
-# - Material color is not supported yet
-# - Cal3D springs (for clothes and hair) are not supported yet
-# - Cal3d has a bug in that a cycle that doesnt have as rootbone channel
-#    will segfault cal3d. until cal3d supports this, add a keyframe for the rootbone
 
+NOT (YET) SUPPORTED:
 
-# REMARKS
-# 1. When you finished an animation and run the script
-#    you can get an error (something with KeyError). Just save your work,
-#    and reload the model. This is usualy caused by deleted items hanging around
-# 2. If a vertex is assigned to one or more bones, but is has a for each
-#    bone a weight of zero, there was a subdivision by zero somewhere
-#    Made a workaround (if sum is 0.0 then sum becomes 1.0).
-#    I have not checked what the outcome of that is, so you better nail 'm,
-#    and give it some weight...
+  - Rotation, translation, or stretching Blender objects is still quite
+buggy, so AVOID MOVING / ROTATING / RESIZE OBJECTS (either mesh or armature) !
+Instead, edit the object (with tab), select all points / bones (with "a"),
+and move / rotate / resize them.<br>
+  - no support for exporting springs yet<br>
+  - no support for exporting material colors (most games should only use images
+I think...)
 
 
-# Parameters :
+KNOWN ISSUES:
 
-# The directory where the data are saved.
-SAVE_TO_DIR = "/tmp/tutorial/"
+  - Cal3D versions <=0.9.1 have a bug where animations aren't played when the root bone
+is not animated;<br>
+  - Cal3D versions <=0.9.1 have a bug where objects that aren't influenced by any bones
+are not drawn (fixed in Cal3D CVS).
 
-# Delete all existing Cal3D files in directory?
-DELETE_ALL_FILES = 0
 
-# What do you wanna export? If all are true then a .cfg file is created,
-# otherwise no .cfg file is made. You have to make one by hand.
-EXPORT_SKELETON = 1
-EXPORT_ANIMATION = 0
-EXPORT_MESH = 1
-EXPORT_MATERIAL = 0
+NOTES:
 
-# Prefix for all created files
-FILE_PREFIX = "Test"
+It requires a very recent version of Blender (>= 2.35).
 
-# Remove path from imagelocation
-REMOVE_PATH_FROM_IMAGE = 0
+Build a model following a few rules:<br>
+  - Use only a single armature;<br>
+  - Use only a single rootbone (Cal3D doesn't support floating bones);<br>
+  - Use only locrot keys (Cal3D doesn't support bone's size change);<br>
+  - Don't try to create child/parent constructs in blender object, that gets exported
+incorrectly at the moment;<br>
+  - 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).
 
-# prefix or subdir for imagepathname (if you place your textures in a
-# subdir or just need a prefix or something). Only used when
-# REMOVE_PATH_FROM_IMAGE = 1. Set to "" if none.
-IMAGE_PREFIX = "textures/"
+It can be run in batch mode, as following :<br>
+    blender model.blend -P blender2cal3d.py --blender2cal3d FILENAME=model.cfg EXPORT_FOR_SOYA=1
 
-# Export to new (>= 900) Cal3D XML-format
-EXPORT_TO_XML = 0
+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.
+"""
 
-# Set scalefactor for model
-SCALE = 0.5
+# Parameters :
 
-# frames per second - used to convert blender frames to times
-FPS = 25
+# Filename to export to (if "", display a file selector dialog).
+FILENAME = ""
 
-# Use this dictionary to rename animations, as their name is lost at the 
-# exportation.
-RENAME_ANIMATIONS = {
-  # "OldName" : "NewName",
-  
-  }
-
-# True (=1) to export for the Soya 3D engine 
-# (http://oomadness.tuxfamily.org/en/soya).
+# 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
 
-# See also BASE_MATRIX below, if you want to rotate/scale/translate the model at
-# the exportation.
-
-
-# Enables LODs computation. LODs computation is quite slow, and the algo is 
+# Enables LODs computation. LODs computation is quite slow, and the algo is
 # surely not optimal :-(
 LODS = 0
 
-#remove the word '.BAKED' from exported baked animations
-REMOVE_BAKED = 1
+# 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 format
+XML = 1
+
+
+MESSAGES = ""
+
+# 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.
+# 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, string
 import Blender
-from Blender.BGL import *
-from Blender.Draw import *
-from Blender.Armature import *
-from Blender.Registry import *
 
 # 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 = ["???"]
 
 
-# Math stuff
+# 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]
@@ -222,12 +142,10 @@ def quaternion2matrix(q):
   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]
-    ]
+  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]))
@@ -249,25 +167,14 @@ 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 notation
 def quaternion_multiply(q1, q2):
-  r = [
+  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],
     ]
-  d = math.sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2] + r[3] * r[3])
-  if d == 0:
-    r[0] = d
-    r[1] = d
-    r[2] = d
-    r[3] = d
-  else:
-    r[0] /= d
-    r[1] /= d
-    r[2] /= d
-    r[3] /= d
-  return r
 
 def matrix_translate(m, v):
   m[3][0] += v[0]
@@ -369,9 +276,9 @@ def matrix_rotate(axis, 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],
+    [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],
     ]
 
@@ -389,9 +296,14 @@ def point_by_matrix(p, m):
           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)
+  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],
@@ -438,46 +350,49 @@ def blender_bone2matrix(head, tail, roll):
     bMatrix = matrix_rotate(axis, theta)
     
   else:
-    if vector_crossproduct(target, nor) > 0.0: updown =  1.0
-    else:                                      updown = -1.0
+    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],
+      [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 structures
 
-CAL3D_VERSION = 700
-CAL3D_XML_VERSION = 900
+CAL3D_VERSION = 910
 
 NEXT_MATERIAL_ID = 0
 class 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.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 = []
+    if map_filename: self.maps_filenames = [map_filename]
+    else:            self.maps_filenames = []
     
     MATERIALS[map_filename] = self
     
@@ -485,37 +400,35 @@ class Material:
     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))
+    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 = "<HEADER MAGIC=\"XRF\" VERSION=\"%i\"/>\n" % CAL3D_XML_VERSION
-    s += "  <MATERIAL NUMMAPS=\"%i\">\n" % len(self.maps_filenames)
-    s += "  <AMBIENT>%f %f %f %f</AMBIENT>\n" % \
-         (self.ambient_r, self.ambient_g, self.ambient_b, self.ambient_a)   
-    s += "  <DIFFUSE>%f %f %f %f</DIFFUSE>\n" % \
-         (self.diffuse_r, self.diffuse_g, self.diffuse_b, self.diffuse_a)
-    s += "  <SPECULAR>%f %f %f %f</SPECULAR>\n" % \
-         (self.specular_r, self.specular_g, self.specular_b, self.specular_a)
-    s += "  <SHININESS>%f</SHININESS>\n" % self.shininess
+    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>%s</MAP>\n" % map_filename
-    s += "</MATERIAL>\n"
+      s += "  <MAP>" + map_filename + "</MAP>\n";
+      
+    s += "</MATERIAL>\n";
+        
     return s
   
 MATERIALS = {}
 
 class Mesh:
   def __init__(self, name):
-    self.name = name
+    self.name      = name
     self.submeshes = []
     
     self.next_submesh_id = 0
@@ -524,21 +437,22 @@ class Mesh:
     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 = "<HEADER MAGIC=\"XMF\" VERSION=\"%i\"/>\n" % CAL3D_XML_VERSION
+    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"
+    s += "</MESH>\n"                                                  
     return s
-  
+
 class SubMesh:
   def __init__(self, mesh, material):
-    self.material = material
-    self.vertices = []
-    self.faces = []
+    self.material   = material
+    self.vertices   = []
+    self.faces      = []
     self.nb_lodsteps = 0
-    self.springs = []
+    self.springs    = []
     
     self.next_vertex_id = 0
     
@@ -555,41 +469,34 @@ class SubMesh:
     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)
+        if not l: vertex2faces[vertex] = [face]
+        else: l.append(face)
         
-    couple_treated = {}
+    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)):
+      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 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
+          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 = {}
+    collapsed    = {}
     new_vertices = []
-    new_faces = []
+    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
+      # 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
+      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
         
@@ -597,13 +504,12 @@ class SubMesh:
         collapsed[v1] = 1
         collapsed[v2] = 1
         
-        # Check if v2 is already collapsed
-        while v2.collapse_to:
-          v2 = v2.collapse_to
+        # 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.collapse_to         = v2
         v1.face_collapse_count = len(common_faces)
         
         for clone in v1.clones:
@@ -622,11 +528,9 @@ class SubMesh:
           clone.face_collapse_count = 0
           new_vertices.append(clone)
 
-        # HACK -- all faces get collapsed with v1 
-        # (and no faces are collapsed with v1's
+        # 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
+        # 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)
         
@@ -642,8 +546,7 @@ class SubMesh:
           vertex2faces[face.vertex3].remove(face)
         vertex2faces[v2].extend(vertex2faces[v1])
         
-    new_vertices.extend(filter(lambda vertex: not vertex.collapse_to, 
-      self.vertices))
+    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[i].id = i
     self.vertices = new_vertices
@@ -652,24 +555,18 @@ 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))
+    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[i].id = i
+    """Rename (change ID) of all vertices, such as self.vertices == new_vertices."""
+    for i in range(len(new_vertices)): new_vertices[i].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 =  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))
+    s += "".join(map(Face  .to_cal3d, self.faces))
     return s
 
   def to_cal3d_xml(self):
@@ -686,16 +583,16 @@ class SubMesh:
 
 class Vertex:
   def __init__(self, submesh, loc, normal):
-    self.loc = loc
+    self.loc    = loc
     self.normal = normal
-    self.collapse_to = None
+    self.collapse_to         = None
     self.face_collapse_count = 0
-    self.maps = []
+    self.maps       = []
     self.influences = []
     self.weight = None
     
     self.cloned_from = None
-    self.clones = []
+    self.clones      = []
     
     self.submesh = submesh
     self.id = submesh.next_vertex_id
@@ -703,20 +600,15 @@ class Vertex:
     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)
+    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))
+    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
@@ -737,7 +629,7 @@ class Vertex:
       s += "      <PHYSIQUE>%f</PHYSIQUE>\n" % len(self.weight)
     s += "    </VERTEX>\n"
     return s
-  
 class Map:
   def __init__(self, u, v):
     self.u = u
@@ -745,22 +637,22 @@ class Map:
     
   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)
-    
+    return "      <TEXCOORD>%f %f</TEXCOORD>\n" % (self.u, self.v)    
+
 class Influence:
   def __init__(self, bone, weight):
-    self.bone = bone
+    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
@@ -769,8 +661,7 @@ class Spring:
     self.idlelength = 0.0
     
   def to_cal3d(self):
-    return struct.pack("iiff", self.vertex1.id, self.vertex2.id,
-           self.spring_coefficient, self.idlelength)
+    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" % \
@@ -790,11 +681,11 @@ class Face:
     
   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 = []
@@ -807,7 +698,8 @@ class Skeleton:
     return s
 
   def to_cal3d_xml(self):
-    s = "<HEADER MAGIC=\"XSF\" VERSION=\"%i\"/>\n" % CAL3D_XML_VERSION
+    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"
@@ -818,7 +710,7 @@ BONES = {}
 class Bone:
   def __init__(self, skeleton, parent, name, loc, rot):
     self.parent = parent
-    self.name = name
+    self.name   = name
     self.loc = loc
     self.rot = rot
     self.children = []
@@ -828,8 +720,8 @@ class Bone:
       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.
+    # 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)
@@ -842,23 +734,18 @@ class Bone:
     BONES[name] = self
     
   def to_cal3d(self):
-    s = struct.pack("i", len(self.name) + 1) + self.name + "\0"
+    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("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\" NUMCHILDS=\"%i\">\n" % \
+    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" % \
@@ -877,31 +764,30 @@ class Bone:
          self.children))
     s += "  </BONE>\n"
     return s
-  
+
 class Animation:
-  def __init__(self, name, action, duration = 0.0):
-    self.name = name
-    self.action = action
+  def __init__(self, name, duration = 0.0):
+    self.name     = name
     self.duration = duration
-    self.tracks = {} # Map bone names to tracks
+    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 = "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 = "<HEADER MAGIC=\"XAF\" VERSION=\"%i\"/>\n" % CAL3D_XML_VERSION
+    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))
+         (self.duration, len(self.tracks))                            
     s += "".join(map(Track.to_cal3d_xml, self.tracks.values()))
     s += "</ANIMATION>\n"
-    return s
-    
+    return s                                                          
 class Track:
   def __init__(self, animation, bone):
-    self.bone = bone
+    self.bone      = bone
     self.keyframes = []
     
     self.animation = animation
@@ -911,7 +797,7 @@ class Track:
     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))
@@ -922,16 +808,15 @@ class Track:
 class KeyFrame:
   def __init__(self, track, time, loc, rot):
     self.time = time
-    self.loc = loc
-    self.rot = rot
+    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])
+    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
@@ -941,633 +826,394 @@ class KeyFrame:
     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():
-  global STATUS
-  STATUS = "Start export..."
-  Draw()
-
-  # Hack for having the model rotated right.
-  # Put in BASE_MATRIX your own rotation if you need some.
+    return s                                                      
 
+def export(filename):
+  global MESSAGES
+  
   if EXPORT_FOR_SOYA:
+    global BASE_MATRIX
     BASE_MATRIX = matrix_rotate_x(-math.pi / 2.0)
-  else:
-    BASE_MATRIX = None
-
+    
   # Get the scene
-  
   scene = Blender.Scene.getCurrent()
   
-  
-  # Export skeleton (=armature)
-  
-  STATUS = "Calculate skeleton"
-  Draw()
+  # ---- Export skeleton (=armature) ----------------------------------------
 
   skeleton = Skeleton()
   
+  foundarmature = False
   for obj in Blender.Object.Get():
     data = obj.getData()
-    if type(data) is Blender.Types.ArmatureType:
-      matrix = obj.getMatrix()
-      if BASE_MATRIX: matrix = matrix_multiply(BASE_MATRIX, matrix)
+    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.getHead()
+      tail = b.getTail()
+      
+      # Turns the Blender's head-tail-roll notation into a quaternion
+      quat = matrix2quaternion(blender_bone2matrix(head, tail, b.getRoll()))
       
-      def treat_bone(b, parent = None):
-        #skip bones that start with _
-        #also skips children of that bone so be careful
-        if b.getName()[0] == '_' : return
-        head = b.getHead()
-        tail = b.getTail()
+      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.
         
-        # Turns the Blender's head-tail-roll notation into a quaternion
-        quat = matrix2quaternion(blender_bone2matrix(head, tail, b.getRoll()))
+        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
         
-        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)
-          
-          bone = Bone(skeleton, parent, b.getName(), 
-                 [parent_tail[0] - parent_head[0] + head[0], 
-                  parent_tail[1] - parent_head[1] + head[1],
-                  parent_tail[2] - parent_head[2] + head[2]], quat)
-        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
-          
-          # Here, the translation is simply the head vector
-          bone = Bone(skeleton, parent, b.getName(), head, quat)
-          
-        bone.head = head
-        bone.tail = tail
+        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
         
-        for child in b.getChildren():
-          treat_bone(child, bone)
+        # Here, the translation is simply the head vector
+        bone = Bone(skeleton, None, b.getName(), loc, rot)
         
-      for b in data.getBones():
-        # treat this bone if not already treated as a child bone
-        if not BONES.has_key(b.getName()):
-               treat_bone(b)
+      bone.head = head
+      bone.tail = tail
       
-      # Only one armature / skeleton
-      break
-    
-    
-  # Export Mesh data
-  
-  if EXPORT_MESH or EXPORT_MATERIAL:
-
-      STATUS = "Calculate mesh and materials"
-      Draw()
+      for child in b.getChildren():
+        treat_bone(child, bone)
+     
+    foundroot = False
+    for b in data.getBones():
+      # child bones are handled in treat_bone
+      if b.getParent() != 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
 
-      meshes = []
+  # ---- Export Mesh data ---------------------------------------------------
+  
+  meshes = []
+  
+  for obj in Blender.Object.Get():
+    data = obj.getData()
+    if (type(data) is Blender.Types.NMeshType) and data.faces:
+      mesh_name = obj.getName()
+      mesh = Mesh(mesh_name)
+      meshes.append(mesh)
       
-      for obj in Blender.Object.Get():
-        data = obj.getData()
-        if (type(data) is Blender.Types.NMeshType) and data.faces and EXPORT_MESH:
-          mesh = Mesh(obj.name)
-
-          if mesh.name[0] == '_' :
-              print "skipping object ", mesh.name
-              continue
-
-          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
-            # for windows
-            image_filename_t = str(image_filename)
-            #print image_filename_t
-            # end for windows
-            if REMOVE_PATH_FROM_IMAGE:
-              if image_filename_t == "None":
-                print "Something wrong with material (is none), set name to none..."
-                image_file = "none.tga"
-              else:
-                # for windows
-                if image_filename_t[0] == "/":
-                  tmplist = image_filename_t.split("/")
-                else:
-                  tmplist = image_filename_t.split("\\")
-                #print "tmplist: " + repr(tmplist)
-                image_file = IMAGE_PREFIX + tmplist[-1]          
-                # end for windows
-                # for linux
-                # image_file = IMAGE_PREFIX + os.path.basename(image_filename)
-            else:
-              image_file = image_filename
-            material = MATERIALS.get(image_file) or Material(image_file)
-            
-            # TODO add material color support here
+      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)
             
-            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[i].index)
+              if not vertex:
+                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)
+
+                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
+                # 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
                 
-                if not face.smooth:
-                  #if len(face.v) < 3 :
-                  #   print "mesh contains a dodgy face, skipping it"
-                  #   continue
-                  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))
+                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))
                   
-                face_vertices = []
-                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)
-                    if face.smooth:
-                      normal = vector_normalize(vector_by_matrix(face.v[i].no, 
-                               matrix))
-                    vertex = vertices[face.v[i].index] = Vertex(submesh, coord, 
-                             normal)
-                    
-                    influences = data.getVertexInfluences(face.v[i].index)
-                    if not influences:
-                      print "Warning: vertex %i (%i) has no influence !" % \
-                            (face.v[i].index, face.v[i].sel)
-                    
-                    # sum of influences is not always 1.0 in Blender ?!?!
-                    sum = 0.0
-                    for bone_name, weight in influences:
-                      sum += weight
-                    
-                    # Select vertex with no weight at all (sum = 0.0).
-                    # To find out which one it is, select part of vertices in mesh,
-                    # exit editmode and see if value between brackets is 1. If so,
-                    # the vertex is in selection. You can narrow the selection
-                    # this way, to find the offending vertex...
-                    if sum == 0.0:
-                      print "Warning: vertex %i in mesh %s (selected: %i) has influence sum of 0.0!" % \
-                            (face.v[i].index, mesh.name, face.v[i].sel)
-                      print "Set the sum to 1.0, otherwise there will " + \
-                            "be a division by zero. Better find the offending " + \
-                            "vertex..."
-                      # face.v[i].sel = 1 # does not work???
-                      sum = 1.0
-                    if face.v[i].sel:
-                      print "Vertex %i is selected" % (face.v[i].index)
-
-                    for bone_name, weight in influences:
-                      #print "bone: %s, weight: %f, sum: %f" % (bone_name, weight, sum)
-                      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.
-                    
+              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[i][0], 1.0 - face.uv[i][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, normal)
+                    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)
                     
-                  if data.hasFaceUV():
-                    uv = [face.uv[i][0], face.uv[i][1]] #1.0 - face.uv[i][1]]
-                    if not vertex.maps:
-                      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
-                        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[i],
-                       face_vertices[i + 1])
-                  
-            # Computes LODs info
-            if LODS:
-              submesh.compute_lods()
-            
-  # Export animations
+              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[i], face_vertices[i + 1])
               
-  if EXPORT_ANIMATION:
+        # Computes LODs info
+        if LODS:
+          submesh.compute_lods()
+        
+  # ---- Export animations --------------------------------------------------
+  ANIMATIONS = {}
 
-      ipoCurveType = ['LocX', 'LocY', 'LocZ', 'QuatX', 'QuatY', 'QuatZ', 'QuatW']
-      
-      STATUS = "Calculate animations"
-      Draw()
+  for a in Blender.Armature.NLA.GetActions().iteritems():
+    animation_name = a[0]
+    animation = Animation(animation_name)
+    animation.duration = 0.0
+
+    for b in a[1].getAllChannelIpos().iteritems():
+      bone_name = b[0]
+      if bone_name not in BONES:
+        MESSAGES += "No Bone '" + bone_name + "' defined (from Animation '" \
+            + animation_name + "' ?!?\n"
+        continue                                            
+
+      bone = BONES[bone_name]
 
-      ANIMATIONS = {}
+      track = Track(animation, bone)
+      track.finished = 0
+      animation.tracks[bone_name] = track
+
+      ipo = b[1]
       
-      actions = Blender.Armature.NLA.GetActions()
-
-      for a in actions:
-
-        #skip actions beginning with _
-        if a[0] == '_' : continue
-
-        #create the animation object
-        animation_name = a
-
-        #if the name starts with @ then it is a oneshot action otherwise its a cycle
-        if a[0] == '@':
-           animation_name = a.split("@")[1]
-           aact = 1
-        else:
-           aact = 0
-
-        print "Animationname: %s" % (animation_name)
-
-        if REMOVE_BAKED:
-           tmp = animation_name.split('.BAKED')
-           animation_name = "".join(tmp)
-
-        #check for duplicate animation names and work around
-        test = animation_name
-        suffix = 1
-        while ANIMATIONS.get(test):
-           print "Warning %s already exists!! renaming" % animation_name
-           test = "%s__%i" % (animation_name, suffix)
-           suffix += 1
-        animation_name = test
-           
-        animation = ANIMATIONS[animation_name] = Animation(animation_name, aact)
-
-        ipos = actions[a].getAllChannelIpos()
-        for bone_name in ipos:
-          #skip bones that start with _
-          if bone_name[0] == '_' :
-             continue
-
-          ipo = ipos[bone_name]
-          try: nbez = ipo.getNBezPoints(0)
-          except TypeError:
-            print "No key frame for action %s, ipo %s, skipping..." % (a, bone_name)
-            nbez = 0
-
-          bone = BONES[bone_name]
-          track = animation.tracks.get(bone_name)
-          if not track:
-            track = animation.tracks[bone_name] = Track(animation, bone)
-            track.finished = 0
+      times = []
       
-          curve = []
-          for ctype in ipoCurveType:
-            curve.append(ipo.getCurve(ctype))
-
-          for bez in range(nbez):
-            time1 = ipo.getCurveBeztriple(0, bez)[3]           
-            time = (time1 - 1.0) / FPS
-   
-            if animation.duration < time:
-              animation.duration = time
-
-            loc = bone.loc
-            rot = bone.rot
-           
-            if (curve[0]):
-              trans = vector_by_matrix((
-                curve[0].evaluate(time1),
-                curve[1].evaluate(time1),
-                curve[2].evaluate(time1)), bone.matrix)
-
-              loc = [
-                bone.loc[0] + trans[0],
-                bone.loc[1] + trans[1],
-                bone.loc[2] + trans[2]]
-
-            if (curve[3]):
-             
-              ipo_rot = [
-                curve[3].evaluate(time1),
-                curve[4].evaluate(time1),
-                curve[5].evaluate(time1),
-                curve[6].evaluate(time1)]
-              
-              # We need to blend the rotation from the bone rest state 
-              # (=bone.rot) with ipo_rot.
-              
-              rot = quaternion_multiply(ipo_rot, bone.rot)
+      # SideNote: MatzeB: Ipo.getCurve(curvename) is broken in blender 2.33 and
+      # below if the Ipo comes from an Action, so only use Ipo.getCurves()!
+      # also blender upto 2.33a had a bug where IpoCurve.evaluate was not
+      # exposed to the python interface :-/
+      
+      #run 1: we need to find all time values where we need to produce keyframes
+      for curve in ipo.getCurves():
+        curve_name = curve.getName()
 
-            KeyFrame(track, time, loc, rot)
+        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)
+        
+        for p in curve.getPoints():
+          time = p.getPoints() [0]
+          if time not in times:
+            times.append(time)
+      
+      times.sort()
+
+      # run2: now create keyframes
+      for time in times:
+        cal3dtime = (time-1) / 25.0 # assume 25FPS by default
+        if cal3dtime > animation.duration:
+          animation.duration = cal3dtime
+        trans = [0, 0, 0]
+        quat  = [0, 0, 0, 0]
         
+        for curve in ipo.getCurves():
+          val = curve.evaluate(time)
+          if curve.getName() == "LocX": trans[0] = val
+          if curve.getName() == "LocY": trans[1] = val
+          if curve.getName() == "LocZ": trans[2] = val
+          if curve.getName() == "QuatW": quat[3] = val
+          if curve.getName() == "QuatX": quat[0] = val
+          if curve.getName() == "QuatY": quat[1] = val
+          if curve.getName() == "QuatZ": quat[2] = val
           
+        transt = vector_by_matrix(trans, bone.matrix)
+        loc = vector_add(bone.loc, transt)
+        rot = quaternion_multiply(quat, bone.rot)
+        rot = quaternion_normalize(rot)
+        
+        KeyFrame(track, cal3dtime, loc, rot)
+        
+    if animation.duration <= 0:
+      MESSAGES += "Ignoring Animation '" + animation_name + \
+                  "': duration is 0.\n"
+      continue
+    ANIMATIONS[animation_name] = animation
+    
   # Save all data
+  if filename.endswith(".cfg"):
+    filename = os.path.splitext(filename)[0]
+  BASENAME = os.path.basename(filename)         
+  DIRNAME  = os.path.dirname(filename)
+  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
+  
+  cfg = open(os.path.join(DIRNAME, BASENAME + ".cfg"), "wb")
+  print >> cfg, "# Cal3D model exported from Blender with blender2cal3d.py"
+  print >> cfg
 
-  STATUS = "Save files"
-  Draw()
-
-  EXPORT_ALL = EXPORT_SKELETON and EXPORT_ANIMATION and \
-               EXPORT_MESH and EXPORT_MATERIAL
-  cfg_buffer = ""
-
-  if FILE_PREFIX == "":
-    std_fname = "cal3d"
-  else:
-    std_fname = ""
+  if SCALE != 1.0:
+    print >> cfg, "scale=%s" % SCALE
+    print >> cfg
+    
+  filename = BASENAME + "." + FORMAT_PREFIX + "sf"
+  open(os.path.join(DIRNAME, filename), "wb").write(encode(skeleton))
+  print >> cfg, "skeleton=%s" % filename
+  print >> cfg
   
-  if not os.path.exists(SAVE_TO_DIR):
-    os.makedirs(SAVE_TO_DIR)
-  else:
-    if DELETE_ALL_FILES:
-      for file in os.listdir(SAVE_TO_DIR):
-        if file.endswith(".cfg") or file.endswith(".caf") or \
-           file.endswith(".cmf") or file.endswith(".csf") or \
-           file.endswith(".crf") or file.endswith(".xsf") or \
-           file.endswith(".xaf") or file.endswith(".xmf") or \
-           file.endswith(".xrf"):
-           os.unlink(os.path.join(SAVE_TO_DIR, file))
+  for animation in ANIMATIONS.values():
+    if not animation.name.startswith("_"):
+      if animation.duration: # Cal3D does not support animation with only one state
+        filename = PREFIX + animation.name + "." + FORMAT_PREFIX + "af"
+        open(os.path.join(DIRNAME, filename), "wb").write(encode(animation))
+        print >> cfg, "animation=%s" % filename
         
-  cfg_buffer += "# Cal3D model exported from Blender with blender2cal3d.py\n\n"
-  if EXPORT_ALL:
-    cfg_buffer += "# --- Scale of model ---\n"
-    cfg_buffer += "scale=%f\n\n" % SCALE
-  else:
-    cfg_buffer += "# Append this file to the model configuration file\n\n"
+  print >> cfg
+  
+  for mesh in meshes:
+    if not mesh.name.startswith("_"):
+      filename = PREFIX + mesh.name + "." + FORMAT_PREFIX + "mf"
+      open(os.path.join(DIRNAME, filename), "wb").write(encode(mesh))
+      print >> cfg, "mesh=%s" % filename
+  print >> cfg
   
-  if EXPORT_SKELETON:
-    cfg_buffer += "# --- Skeleton ---\n"
-    if EXPORT_TO_XML:  
-      open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + std_fname + \
-           os.path.basename(SAVE_TO_DIR) +".xsf"),
-          "wb").write(skeleton.to_cal3d_xml())
-      cfg_buffer += "skeleton=%s.xsf\n" %  (FILE_PREFIX + std_fname +\
-               os.path.basename(SAVE_TO_DIR))
+  materials = MATERIALS.values()
+  materials.sort(lambda a, b: cmp(a.id, b.id))
+  for material in materials:
+    if material.maps_filenames:
+      filename = PREFIX + os.path.splitext(os.path.basename(material.maps_filenames[0]))[0] + "." + FORMAT_PREFIX + "rf"
     else:
-      open(os.path.join(SAVE_TO_DIR,  FILE_PREFIX + std_fname + \
-           os.path.basename(SAVE_TO_DIR) + ".csf"),
-          "wb").write(skeleton.to_cal3d())
-      cfg_buffer += "skeleton=%s.csf\n" %  (FILE_PREFIX + std_fname +\
-               os.path.basename(SAVE_TO_DIR))
-    cfg_buffer += "\n"
+      filename = PREFIX + "plain." + FORMAT_PREFIX + "rf"
+    open(os.path.join(DIRNAME, filename), "wb").write(encode(material))
+    print >> cfg, "material=%s" % filename
+  print >> cfg
   
-  if EXPORT_ANIMATION:
-    cfg_buffer += "# --- Animations ---\n"
-    for animation in ANIMATIONS.values():
-      # Cal3D does not support animation with only one state
-      if animation.duration:
-        animation.name = RENAME_ANIMATIONS.get(animation.name) or animation.name
-
-        action_suffix=""
-        if animation.action:
-           action_suffix = "_action"
-
-        if EXPORT_TO_XML:
-          open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + \
-               animation.name + ".xaf"), "wb").write(animation.to_cal3d_xml())
-          cfg_buffer += "animation%s=%s.xaf\n" % (action_suffix, (FILE_PREFIX + animation.name))
-        else:
-          open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + \
-               animation.name + ".caf"), "wb").write(animation.to_cal3d())
-          cfg_buffer += "animation%s=%s.caf\n" % (action_suffix, (FILE_PREFIX + animation.name))
-
-        # Prints animation names and durations
-        print animation.name, "duration", animation.duration * FPS + 1.0
-    cfg_buffer += "\n"
-
-  if EXPORT_MESH:
-    cfg_buffer += "# --- Meshes ---\n"
-    for mesh in meshes:
-      if not mesh.name.startswith("_"):
-        if EXPORT_TO_XML:
-          open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + mesh.name + ".xmf"),
-              "wb").write(mesh.to_cal3d_xml())
-          cfg_buffer += "mesh=%s.xmf\n" % (FILE_PREFIX + mesh.name)
-        else:
-          open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + mesh.name + ".cmf"),
-              "wb").write(mesh.to_cal3d())
-          cfg_buffer += "mesh=%s.cmf\n" % (FILE_PREFIX + mesh.name)
-    cfg_buffer += "\n"
+  MESSAGES += "Saved to '%s.cfg'\n" % BASENAME
+  MESSAGES += "Done."
   
-  if EXPORT_MATERIAL:
-    cfg_buffer += "# --- Materials ---\n"
-    materials = MATERIALS.values()
-    materials.sort(lambda a, b: cmp(a.id, b.id))
-    for material in materials:
-      if material.maps_filenames:
-        fname = os.path.splitext(os.path.basename(material.maps_filenames[0]))[0]
-      else:
-        fname = "plain"
-      if EXPORT_TO_XML:
-        open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + fname + ".xrf"),
-            "wb").write(material.to_cal3d_xml())
-        cfg_buffer += "material=%s.xrf\n" % (FILE_PREFIX + fname)
-      else:
-        open(os.path.join(SAVE_TO_DIR, FILE_PREFIX + fname + ".crf"),
-            "wb").write(material.to_cal3d())
-        cfg_buffer += "material=%s.crf\n" % (FILE_PREFIX + fname)
-    cfg_buffer += "\n"
-
-  if EXPORT_ALL:
-    cfg_prefix = ""
-  else:
-    cfg_prefix = "append_to_"
-
-  cfg = open(os.path.join(SAVE_TO_DIR, cfg_prefix + FILE_PREFIX + std_fname +\
-             os.path.basename(SAVE_TO_DIR) + ".cfg"), "wb")
-  print >> cfg, cfg_buffer
-  cfg.close()
-
-  print "Saved to", SAVE_TO_DIR
-  print "Done."
-
-  STATUS = "Export finished."
-  Draw()
-
-
-# ::: GUI around the whole thing, not very clean, but it works for me...
-
-_save_dir = Create(SAVE_TO_DIR)
-_file_prefix = Create(FILE_PREFIX)
-_image_prefix = Create(IMAGE_PREFIX)
-_scale = Create(SCALE)
-_framepsec = Create(FPS)
-STATUS = "Done nothing yet"
+  # show messages
+  print MESSAGES
 
+# some (ugly) gui to show the error messages - no scrollbar or other luxury,
+# please improve this if you know how
 def gui():
-  global EXPORT_TO_XML, EXPORT_SKELETON, EXPORT_ANIMATION, EXPORT_MESH, \
-         EXPORT_MATERIAL, SAVE_TO_DIR, _save_dir, _scale, SCALE, \
-         EXPORT_FOR_SOYA, REMOVE_PATH_FROM_IMAGE, LODS, _file_prefix, \
-         FILE_PREFIX, _image_prefix, IMAGE_PREFIX, DELETE_ALL_FILES, STATUS, \
-         _framepsec, FPS
-
-  glRasterPos2i(8, 14)
-  Text("Status: %s" % STATUS)
-
-  _export_button = Button("Export (E)", 1, 8, 36, 100, 20, 
-                          "Start export to Cal3D format")
-  _quit_button = Button("Quit (Q)", 5, 108, 36, 100, 20, "Exit from script")
-
-  _delete_toggle = Toggle("X", 15, 8, 64, 20, 20, DELETE_ALL_FILES, 
-         "Delete all existing Cal3D files in export directory")
-  _SF_toggle = Toggle("_SF", 6, 28, 64, 45, 20, EXPORT_SKELETON, 
-         "Export skeleton (CSF/XSF)")
-  _AF_toggle = Toggle("_AF", 7, 73, 64, 45, 20, EXPORT_ANIMATION, 
-         "Export animations (CAF/XAF)")
-  _MF_toggle = Toggle("_MF", 8, 118, 64, 45, 20, EXPORT_MESH, 
-         "Export mesh (CMF/XMF)")
-  _RF_toggle = Toggle("_RF", 9, 163, 64, 45, 20, EXPORT_MATERIAL, 
-         "Export materials (CRF/XRF)")
-
-  _XML_toggle = Toggle("Export to XML", 2, 8, 84, 100, 20, EXPORT_TO_XML, 
-         "Export to Cal3D XML or binary fileformat")
-  _soya_toggle = Toggle("Export for Soya", 10, 108, 84, 100, 20, 
-                        EXPORT_FOR_SOYA, "Export for Soya 3D Engine")
-
-  _imagepath_toggle = Toggle("X imagepath", 11, 8, 104, 100, 20, 
-                        REMOVE_PATH_FROM_IMAGE, "Remove path from imagename")
-  _lods_toggle = Toggle("Calculate LODS", 12, 108, 104, 100, 20, 
-                        LODS, "Calculate LODS, quit slow and not optimal")
-
-  _scale = Slider("S:", 4, 8, 132, 100, 20, SCALE, 0.00, 10.00, 0, \
-                  "Sets the scale of the model (small number will scale up)")
-
-  _framepsec = Slider("F:", 16, 108, 132, 100, 20, FPS, 0.00, 100.0, 0, \
-                  "Sets the export framerate (FPS)")
-
-  _image_prefix = String("Image prefix: ", 13, 8, 160, 200, 20, IMAGE_PREFIX, \
-                         256, "Prefix used for imagename (if you have the " + \
-                         "textures in a subdirectory called textures, " + \
-                         "the prefix would be \"textures\\\\\")")
-
-  _file_prefix = String("File prefix: ", 14, 8, 180, 200, 20, FILE_PREFIX, \
-                        256, "Prefix to all exported Cal3D files "+ \
-                        "(f.e. \"model_\")")
-
-  _save_dir = String("Export to: ", 3, 8, 200, 200, 20, _save_dir.val, 256, \
-                     "Directory to save files to")
-
-
+  global MESSAGES
+  button = Blender.Draw.Button("Ok", 1, 0, 0, 50, 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):
-  global STATUS
-
-  if (evt == QKEY or evt == ESCKEY):
-    Exit()
+  if evt == Blender.Draw.ESCKEY:
+    Blender.Draw.Exit()
     return
-  if evt == EKEY:
-    update_reg()
-    export()
-
-def bevent(evt):
-  global EXPORT_TO_XML, EXPORT_SKELETON, EXPORT_ANIMATION, EXPORT_MESH, \
-         EXPORT_MATERIAL, _save_dir, SAVE_TO_DIR, _scale, SCALE, \
-         EXPORT_FOR_SOYA, REMOVE_PATH_FROM_IMAGE, LODS, _file_prefix, \
-         FILE_PREFIX, _image_prefix, IMAGE_PREFIX, DELETE_ALL_FILES, STATUS, \
-         _framepsec, FPS
 
+def button_event(evt):
   if evt == 1:
-    update_reg()
-    export()
-  if evt == 2:
-    EXPORT_TO_XML = 1 - EXPORT_TO_XML
-  if evt == 3:
-    SAVE_TO_DIR = _save_dir.val
-  if evt == 4:
-    SCALE = _scale.val
-  if evt == 5:
-    Exit()
+    Blender.Draw.Exit()
     return
-  if evt == 6:
-    EXPORT_SKELETON = 1 - EXPORT_SKELETON
-  if evt == 7:
-    EXPORT_ANIMATION = 1 - EXPORT_ANIMATION
-  if evt == 8:
-    EXPORT_MESH = 1 - EXPORT_MESH
-  if evt == 9:
-    EXPORT_MATERIAL = 1 - EXPORT_MATERIAL
-  if evt == 10:
-    EXPORT_FOR_SOYA = 1 - EXPORT_FOR_SOYA
-  if evt == 11:
-    REMOVE_PATH_FROM_IMAGE = 1 - REMOVE_PATH_FROM_IMAGE
-  if evt == 12:
-    LODS = 1 - LODS
-  if evt == 13:
-    IMAGE_PREFIX = _image_prefix.val
-  if evt == 14:
-    FILE_PREFIX = _file_prefix.val
-  if evt == 15:
-    DELETE_ALL_FILES = 1 - DELETE_ALL_FILES
-  if evt == 16:
-     FPS = _framepsec.val
-  Draw()
-
-def update_reg():
-   x = {}
-   x['sd'] = SAVE_TO_DIR
-   x['da'] = DELETE_ALL_FILES
-   x['es'] = EXPORT_SKELETON
-   x['ea'] = EXPORT_ANIMATION
-   x['em'] = EXPORT_MESH
-   x['emat'] = EXPORT_MATERIAL
-   x['fp'] = FILE_PREFIX
-   x['rp'] = REMOVE_PATH_FROM_IMAGE
-   x['ip'] = IMAGE_PREFIX
-   x['ex'] = EXPORT_TO_XML
-   x['sc'] = SCALE
-   x['fps'] = FPS
-   x['soya'] = EXPORT_FOR_SOYA
-   x['lod'] = LODS
-   Blender.Registry.SetKey('Cal3dExporter', x)
-
-def get_from_reg():
-   global SAVE_TO_DIR, DELETE_ALL_FILES, EXPORT_SKELETON, \
-     EXPORT_ANIMATION, EXPORT_MESH, EXPORT_MATERIAL, FILE_PREFIX,  \
-     REMOVE_PATH_FROM_IMAGE, IMAGE_PREFIX, EXPORT_TO_XML, SCALE, \
-     FPS, EXPORT_FOR_SOYA, LODS
-
-   tmp = Blender.Registry.GetKey("Cal3dExporter")
-   if tmp: 
-     SAVE_TO_DIR = tmp['sd']
-     #DELETE_ALL_FILES = tmp['da']
-     EXPORT_SKELETON = tmp['es']
-     EXPORT_ANIMATION = tmp['ea']
-     EXPORT_MESH = tmp['em']
-     EXPORT_MATERIAL = tmp['emat']
-     FILE_PREFIX = tmp['fp']
-     REMOVE_PATH_FROM_IMAGE = tmp['rp']
-     IMAGE_PREFIX = tmp['ip']
-     EXPORT_TO_XML = tmp['ex']
-     SCALE = tmp['sc']
-     FPS = tmp['fps']
-     EXPORT_FOR_SOYA = tmp['soya']
-     LODS = tmp['lod']
-
-get_from_reg()
-Register(gui, event, bevent)
+
+# Main script
+def fs_callback(filename):
+  export(filename)
+  Blender.Draw.Register(gui, event, button_event)
+
+
+# Check for batch mode
+if "--blender2cal3d" in sys.argv:
+  args = sys.argv[sys.argv.index("--blender2cal3d") + 1:]
+  for arg in args:
+    attr, val = arg.split("=")
+    try: val = int(val)
+    except:
+      try: val = float(val)
+      except: pass
+    globals()[attr] = val
+  export(FILENAME)
+  Blender.Quit()
+  
+else:
+  if FILENAME: fs_callback(FILENAME)
+  else:
+    defaultname = Blender.Get("filename")
+    if defaultname.endswith(".blend"):
+      defaultname = defaultname[0:len(defaultname)-len(".blend")] + ".cfg"
+    Blender.Window.FileSelector(fs_callback, "Cal3D Export", defaultname)
+
+
index 1df2cec66faba949be65e6a2a6402878df72d84a..99d7deab4405e620db4fa64e16c457ab2722afba 100644 (file)
@@ -15,9 +15,28 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script exports meshes to LightWave file format.
 
-Usage:
+LightWave is a full-featured commercial modeling and rendering
+application. The lwo file format is composed of 'chunks,' is well
+defined, and easy to read and write. It is similar in structure to the
+trueSpace cob format.
 
-Select meshes to be exported and run this script from "File->Export" menu.
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Supported:<br>
+       UV Coordinates, Meshes, Materials, Material Indices, Specular
+Highlights, and Vertex Colors. For added functionality, each object is
+placed on its own layer.
+
+Missing:<br>
+       Not too much, I hope! :).
+
+Known issues:<br>
+       Empty objects crash has been fixed.
+
+Notes:<br>
+       For compatibility reasons, it also reads lwo files in the old LW
+v5.5 format.
 """
 
 # $Id$
index 758c3b0229a2a60ad7e3bc9daafc94cf416a3309..c795358c734bb7cd7258e769d68d426fb414012d 100644 (file)
@@ -15,10 +15,28 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script imports LightWave files to Blender.
 
-Usage:
+LightWave is a full-featured commercial modeling and rendering
+application. The lwo file format is composed of 'chunks,' is well
+defined, and easy to read and write. It is similar in structure to the
+trueSpace cob format.
 
-Execute this script from the "File->Import" menu and choose a LightWave file to
-open.
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose a LightWave
+file to open.
+
+Supported:<br>
+       Meshes only.
+
+Missing:<br>
+    Materials, UV Coordinates, and Vertex Color info will be ignored.
+
+Known issues:<br>
+       Triangulation of convex polygons works fine, and uses a very simple
+fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
+require a different algorithm, and will be triagulated incorrectly.
+
+Notes:<br>
+       Also reads lwo files in the old LW v5.5 format.
 """
 
 # $Id$
index 07342737aad6a4917015786934f27ec55b03738a..8be6c5e18ebc68090e9dd5ae74f5e07c72315afb 100644 (file)
@@ -58,20 +58,17 @@ os.split=split
 os.join=join
 
 def filtreFICHIER(nom):
-     f=open(nom,'r')
+     f=open(nom,'rU')
      t=f.readlines()
      f.close()
      
-     if len(t)==1 and t[0].find('\r'):
-              t=t[0].split('\r')
-
      if len(t)>1: 
           return t   
      else:
          name = "OK?%t| Not a valid file or an empty file ... "  # if no %xN int is set, indices start from 1
          result = Draw.PupMenu(name)
           
-         return 'false' 
+         return 'false'
         
 #===============================
 # Data
@@ -140,6 +137,8 @@ def test_egalitedespositions(f1,f2):
 
 def Open_GEOfile(dir,nom):
     if BLversion>=233:
+       in_editmode = Blender.Window.EditMode()
+       if in_editmode: Blender.Window.EditMode(0)
        Blender.Load(dir+nom+'OOO.obj', 1)
        BO=Blender.Object.Get()
        BO[-1].RotY=0.0
index 6409f7ad72b85d60086589da4e0d274ae0753b85..772b7549a11681f0dae3474fd0b0992f3c1e0c7d 100644 (file)
@@ -94,7 +94,8 @@ def versioned_name(objname):
        existing_names = []
        for object in Blender.Object.Get():
                existing_names.append(object.name)
-               existing_names.append(object.data.name)
+               data = object.data
+               if data: existing_names.append(data.name)
        if objname in existing_names: # don't over-write other names
                try:
                        name, ext = objname.split('.')
index d180b0d26c59832801f09b2ae685e4d38cad4d70..7445b13a4a7e0517de48e65bdfcbe70e84585dc5 100644 (file)
@@ -1,6 +1,7 @@
 """
-(c) jm soler juillet 2004, released under Blender Artistic Licence 
-    for the Blender 2.34 Python Scripts Bundle.
+SVG 2 OBJ translater, 0.2.6
+(c) jm soler juillet/novembre 2004, released under Blender Artistic Licence 
+    for the Blender 2.34/35 Python Scripts Bundle.
 #---------------------------------------------------------------------------
 # Page officielle :
 #   http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_import_svg.htm
@@ -9,7 +10,6 @@
 #---------------------------------------------------------------------------
 
 -- Concept : translate SVG file in GEO .obj file and try to load it. 
--- Real problem  :  the name of the blender file is changed ...
 -- Curiousity : the original matrix must be :
 
                          0.0 0.0 1.0 0.0
@@ -55,14 +55,17 @@ Changelog:
       0.2.3 : - read a few new relative displacements
       0.2.4 : - better hash for command with followed by a lone data 
                 (h,v) or uncommun number (a) 
+      0.2.5 : - correction for gimp import 
+      0.2.6 : - correction for illustrator 10 SVG  
+                
 ==================================================================================   
 =================================================================================="""
 
 SHARP_IMPORT=0
 SCALE=1
 scale=1
-DEBUG =0
-DEVELOPPEMENT=1
+DEBUG =0 #print
+DEVELOPPEMENT=0
     
 import sys
 #oldpath=sys.path
@@ -107,16 +110,21 @@ os.split=split
 os.join=join
 
 def filtreFICHIER(nom):
-     f=open(nom,'r')
+     f=open(nom,'rU')
      t=f.read()
      f.close()
+     
      t=t.replace('\r','')
      t=t.replace('\n','')
      
-     if t.upper().find('<SVG')==-1:
+     if t.upper().find('<SVG')==-1 :
          name = "OK?%t| Not a valid file or an empty file ... "  # if no %xN int is set, indices start from 1
          result = Blender.Draw.PupMenu(name)
          return "false"
+     elif  t.upper().find('<PATH')==-1:
+         name = "OK?%t| Sorry, no Path in this file ... "  # if no %xN int is set, indices start from 1
+         result = Blender.Draw.PupMenu(name)
+         return "false"
      else:
           return t
 
@@ -329,7 +337,7 @@ def courbe_vers_s(c,D,n0,CP):  #S,s
     if DEBUG==1: print B.co,BP.co
     CP=[l[4],l[5]]    
 
-    if D[c[1]+3] not in TAGcourbe :
+    if len(D)<c[1]+3 and D[c[1]+3] not in TAGcourbe :
         c[1]+=2
         courbe_vers_c(c, D, n0,CP)
     return  courbes,n0,CP
@@ -379,8 +387,10 @@ def courbe_vers_c(c, D, n0,CP): #c,C
     if DEBUG==1: print B.co,BP.co
 
     CP=[l[4],l[5]]
-    
-    if D[c[1]+4] not in TAGcourbe :
+    if DEBUG==1:
+       print 'D[c[1]]', D[c[1]], c
+       print D
+    if len(D)<c[1]+4 and D[c[1]+4] not in TAGcourbe :
         c[1]+=3
         courbe_vers_c(c, D, n0,CP)
 
@@ -402,7 +412,7 @@ def ligne_tracee_l(c, D, n0,CP): #L,l
 
     CP=[l[0],l[1]]
 
-    if D[c[1]+2] not in TAGcourbe :
+    if len(D)<c[1]+2 and D[c[1]+2] not in TAGcourbe :
         c[1]+=1
         ligne_tracee_l(c, D, n0,CP) #L
             
@@ -557,6 +567,13 @@ def format_PATH(t):
     #print "D0= :",D
        
     D=D.split(' ')
+    
+    try:
+      while D.index(''):
+         del D[D.index('')]
+    except:
+      pass
+    
     #print len(D)
     #for D0 in D:
         #print "  ---->  D  = :", D0
@@ -600,16 +617,12 @@ def scan_FILE(nom):
      #print t
 
      while t.find('path')!=-1:
-
          t,D=format_PATH(t)
-
          cursor=0
          for cell in D: 
             if DEBUG==2 : print 'cell : ',cell ,' --'                   
-            #print 'cell',cell
             if len(cell)>=1 and cell[0] in TAGcourbe:
-                   courbes,n0,CP=Actions[cell]([cell,cursor], D, n0,CP)
-            
+                   courbes,n0,CP=Actions[cell]([cell,cursor], D, n0,CP)            
             cursor+=1
 
   courbes.number_of_items=len(courbes.ITEM.keys())
@@ -642,6 +655,6 @@ def  ajustement(v,s):
 def fonctionSELECT(nom):
     scan_FILE(nom)
 
-if DEVELOPPEMENT==0:
+if DEVELOPPEMENT==1:
    Blender.Window.FileSelector (fonctionSELECT, 'SELECT a .SVG FILE')
    #sys.path=oldpath
index 4f0aac905fa01818b0444eb4b54fb73ec923e325..1dd84bb6d813ab79987e0326ec0e581204d8ce8f 100644 (file)
@@ -15,12 +15,37 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script exports meshes to Nendo file format.
 
-Usage:
-
-Select meshes to be exported and run this script from "File->Export" menu.
+Nendo is (was) a commercial polygon modeler that has about half of the
+features found in Wings. The .ndo file format is a simple, uncompressed,
+memory dump of structures that represent the mesh objects, uv coords,
+and image textures.
+
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Supported:<br>
+       1. Exports meshes only. Hint: use ALT-C to convert non-mesh objects,
+and CTRL-ALT-A if you have "dupliverts" objects.<br>
+       2. Exports Vertex Colors & Radiosity Solutions.
+
+Missing:<br>
+       Materials and UV Coordinates info will be ignored.
+
+Known issues:<br>
+       Exports only well-behaved and topologically correct meshes (i.e,
+closed meshes, manifolds, meshes imported from wings, etc). The mesh
+cannot have duplicate vertices, missing faces (holes), open edges, etc.<br>
+       PowerUser Hint: In editmode, if CTRL-ALT-SHIFT-M results in a selection,
+then your mesh is not a manifold and most likely will not export.
+
+Notes:<br>
+       Blender & Wings can read/write ndo files with a maximum of 65,535 edges.
+Nendo can read/write ndo files with a maximum of 32,767 edges.<br>
+       If you have a very large mesh that you want to import into nendo, modify
+the 'write_edge_table' function to use a signed integer (i.e., ">h") for the 'len(edge_table)'
+field.
 """
 
-
 # $Id$
 #
 # +---------------------------------------------------------+
index 2d041aadd5353287d7aa547d2e36d672234cc3af..2af19ff5a5dc966c66c3672645f3ededccf33cf0 100644 (file)
@@ -15,13 +15,32 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script imports Nendo files to Blender.
 
-Usage:
-
-Execute this script from the "File->Import" menu and choose a Nendo file to
-open.
+Nendo is (was) a commercial polygon modeler that has about half of the
+features found in Wings. The .ndo file format is a simple, uncompressed,
+memory dump of structures that represent the mesh objects, uv coords,
+and image textures.
+
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose a Nendo file
+to open.
+
+Supported:<br>
+       Meshes only.
+
+Missing:<br>
+    Materials, UV Coordinates, and Vertex Color info will be ignored.
+
+Known issues:<br>
+       Triangulation of convex polygons works fine, and uses a very simple
+fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
+require a different algorithm, and will be triagulated incorrectly.
+
+Notes:<br>
+       Last tested with Wings 3D 0.98.25 & Nendo 1.1.6. Some models cannot be
+imported due to the fact that Nendo erroneously creates doubled back
+edges during the course of modeling.
 """
 
-
 # $Id$
 #
 # +---------------------------------------------------------+
index c860a9707df07e60f165589949a9f9d889e0e483..c6cb032a632947e825d905256dfb6d3d52f369d9 100644 (file)
@@ -202,8 +202,11 @@ def getUniqueName(name):
                        name += '.' + str(uniqueInt)
                        uniqueInt +=1
                except:
-                       return name
-
+                       if NMesh.GetRaw(name) == None:
+                               return name
+                       else:
+                               name += '.' + str(uniqueInt)
+                               uniqueInt +=1
 
 #==================================================================================#
 # This loads data from .obj file                                                   #
@@ -215,16 +218,16 @@ def load_obj(file):
                if len( meshList[objectName][0].materials ) >= MATLIMIT:
                        print 'Warning, max material limit reached, using an existing material'
                        return meshList[objectName][0]
-    
+               
                mIdx = 0
                for m in meshList[objectName][0].materials:
                        if m.getName() == mat.getName():
                                break
                        mIdx+=1
-    
+               
                if mIdx == len(mesh.materials):
                        meshList[objectName][0].addMaterial(mat)
-    
+               
                f.mat = mIdx
                return f
 
@@ -263,25 +266,26 @@ def load_obj(file):
        # Load all verts first (texture verts too)                                         #
        #==================================================================================#
        lIdx = 0
+       print len(fileLines)
        while lIdx < len(fileLines):
-               l = fileLines[lIdx]
-
-               # EMPTY LINE
-               if len(l) == 0 or l[0] == '#':
-                       pass
                
-               # VERTEX
+               l = fileLines[lIdx]
+               if len(l) == 0:
+                       fileLines.pop(lIdx)
+                       lIdx-=1                 
+                       
                elif l[0] == 'v':
                        # This is a new vert, make a new mesh
                        vertList.append( NMesh.Vert(float(l[1]), float(l[2]), float(l[3]) ) )
-                       fileLines.remove(fileLines[lIdx])
+                       fileLines.pop(lIdx)
                        lIdx-=1
+
                
                # UV COORDINATE
                elif l[0] == 'vt':
                        # This is a new vert, make a new mesh
                        uvMapList.append( (float(l[1]), float(l[2])) )
-                       fileLines.remove(fileLines[lIdx])
+                       fileLines.pop(lIdx)
                        lIdx-=1
                lIdx+=1
        
@@ -298,6 +302,7 @@ def load_obj(file):
        meshList = {}
        meshList[objectName] = (NMesh.GetRaw(), [-1]*len(vertList)) # Mesh/meshList[objectName][1]
        meshList[objectName][0].verts.append(vertList[0])
+       meshList[objectName][0].hasFaceUV(1)
 
        #==================================================================================#
        # Load all faces into objects, main loop                                           #
@@ -306,15 +311,15 @@ def load_obj(file):
        # Face and Object loading LOOP
        while lIdx < len(fileLines):
                l = fileLines[lIdx]
-
-               # COMMENTS AND EMPTY LINES
-               if len(l) == 0 or l[0] == '#':
-                       pass
-                       
+               
                # VERTEX
-               elif l[0] == 'v':
+               if l[0] == 'v':
                        pass
                        
+               # Comment
+               if l[0] == '#':
+                       pass                    
+               
                # VERTEX NORMAL
                elif l[0] == 'vn':
                        pass
@@ -374,8 +379,9 @@ def load_obj(file):
                                
                                # UV MAPPING
                                if fHasUV:
-                                       for i in [0,1,2,3]:
-                                               f.uv.append( uvMapList[ vtIdxLs[i] ] )
+                                       f.uv.extend([uvMapList[ vtIdxLs[0] ],uvMapList[ vtIdxLs[1] ],uvMapList[ vtIdxLs[2] ],uvMapList[ vtIdxLs[3] ]])
+                                       #for i in [0,1,2,3]:
+                                       #       f.uv.append( uvMapList[ vtIdxLs[i] ] )
 
                                if f.v > 0:
                                        f = applyMat(meshList[objectName][0], f, currentMat)
@@ -400,9 +406,7 @@ def load_obj(file):
 
                                        # UV MAPPING
                                        if fHasUV:
-                                               f.uv.append( uvMapList[ vtIdxLs[0] ] )
-                                               f.uv.append( uvMapList[ vtIdxLs[i+1] ] )
-                                               f.uv.append( uvMapList[ vtIdxLs[i+2] ] )
+                                               f.uv.extent([uvMapList[ vtIdxLs[0] ], uvMapList[ vtIdxLs[i+1] ], uvMapList[ vtIdxLs[i+2] ]])
 
                                        if f.v > 0:
                                                f = applyMat(meshList[objectName][0], f, currentMat)
@@ -450,6 +454,7 @@ def load_obj(file):
                                # if we have then we'll just keep appending to it, this is required for soem files.
                                if objectName not in meshList.keys():
                                        meshList[objectName] = (NMesh.GetRaw(), [-1]*len(vertList))
+                                       meshList[objectName][0].hasFaceUV(1)
                                        meshList[objectName][0].verts.append( vertList[0] )
                                
 
@@ -466,7 +471,7 @@ def load_obj(file):
                                currentImg = NULL_IMG
                        else:
                                currentImg = getImg(DIR + ' '.join(l[1:])) # Use join in case of spaces 
-
+               
                # MATERIAL FILE
                elif l[0] == 'mtllib':
                        mtl_fileName = ' '.join(l[1:])
@@ -482,12 +487,24 @@ def load_obj(file):
                if mtl_fileName != '':
                        load_mtl(DIR, mtl_fileName, meshList[mk][0])
                if len(meshList[mk][0].verts) >1:
-                       meshList[mk][0].verts.remove(meshList[mk][0].verts[0])
+                       meshList[mk][0].verts.pop(0)
                        
                        name = getUniqueName(mk)
-                       ob = NMesh.PutRaw(meshList[mk][0], mk)
-                       ob.name = mk
+                       ob = NMesh.PutRaw(meshList[mk][0], name)
+                       ob.name = name
 
        print "obj import time: ", sys.time() - time1
 
 Window.FileSelector(load_obj, 'Import Wavefront OBJ')
+
+'''
+# For testing compatability
+import os
+for obj in os.listdir('/obj/'):
+       if obj[-3:] == 'obj':
+               print obj
+               newScn = Scene.New(obj)
+               newScn.makeCurrent()
+               load_obj('/obj/' + obj)
+       
+'''
\ No newline at end of file
index 328854d39f8030dafcad729c8c8883127f1941c9..332d553b3899c8b1af255a34e0d0524c25ee35c4 100644 (file)
@@ -1,10 +1,10 @@
 #!BPY
 
 """
-Name: 'Object File Format (.off)...'
+Name: 'DEC Object File Format (.off)...'
 Blender: 232
 Group: 'Export'
-Tooltip: 'Export selected mesh to Object File Format (*.off)'
+Tooltip: 'Export selected mesh to DEC Object File Format (*.off)'
 """
 
 __author__ = "Anthony D'Agostino (Scorpius)"
@@ -13,11 +13,19 @@ __url__ = ("blender", "elysiun",
 __version__ = "Part of IOSuite 0.5"
 
 __bpydoc__ = """\
-This script exports meshes to Object File Format.
+This script exports meshes to DEC Object File Format.
 
-Usage:
+The DEC (Digital Equipment Corporation) OFF format is very old and
+almost identical to Wavefront's OBJ. I wrote this so I could get my huge
+meshes into Moonlight Atelier. (DXF can also be used but the file size
+is five times larger than OFF!) Blender/Moonlight users might find this
+script to be very useful.
 
-Select meshes to be exported and run this script from "File->Export" menu.
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Notes:<br>
+       Only exports a single selected mesh.
 """
 
 # +---------------------------------------------------------+
index b70a935b4a94fe6dafb0317d946eb7835a19b46a..94b6423149a98c489b302800208c38b949a2c2d5 100644 (file)
@@ -1,10 +1,10 @@
 #!BPY
 
 """
-Name: 'Object File Format (.off)...'
+Name: 'DEC Object File Format (.off)...'
 Blender: 232
 Group: 'Import'
-Tooltip: 'Import Object File Format (*.off)'
+Tooltip: 'Import DEC Object File Format (*.off)'
 """
 
 __author__ = "Anthony D'Agostino (Scorpius)"
@@ -13,12 +13,20 @@ __url__ = ("blender", "elysiun",
 __version__ = "Part of IOSuite 0.5"
 
 __bpydoc__ = """\
-This script imports Object File Format files to Blender.
+This script imports DEC Object File Format files to Blender.
 
-Usage:
+The DEC (Digital Equipment Corporation) OFF format is very old and
+almost identical to Wavefront's OBJ. I wrote this so I could get my huge
+meshes into Moonlight Atelier. (DXF can also be used but the file size
+is five times larger than OFF!) Blender/Moonlight users might find this
+script to be very useful.
 
-Execute this script from the "File->Import" menu and choose an OFF file to
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose an OFF file to
 open.
+
+Notes:<br>
+       UV Coordinate support has been added.
 """
 
 
@@ -45,19 +53,27 @@ def read(filename):
 
        verts = []
        faces = []
+       uv = []
 
        # === OFF Header ===
        offheader = file.readline()
        numverts, numfaces, null = file.readline().split()
        numverts = int(numverts)
        numfaces = int(numfaces)
+       if offheader.find('ST') >= 0:
+               has_uv = True
+       else:
+               has_uv = False
 
        # === Vertex List ===
        for i in range(numverts):
                if not i%100 and mod_meshtools.show_progress:
                        Blender.Window.DrawProgressBar(float(i)/numverts, "Reading Verts")
-               x, y, z = file.readline().split()
-               x, y, z = float(x), float(y), float(z)
+               if has_uv:
+                       x, y, z, u, v = map(float, file.readline().split())
+                       uv.append((u, v))
+               else:
+                       x, y, z = map(float, file.readline().split())
                verts.append((x, y, z))
 
        # === Face List ===
@@ -75,7 +91,7 @@ def read(filename):
 
        objname = Blender.sys.splitext(Blender.sys.basename(filename))[0]
 
-       mod_meshtools.create_mesh(verts, faces, objname)
+       mod_meshtools.create_mesh(verts, faces, objname, faces, uv)
        Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
        file.close()
        #end = time.clock()
index 27a32c810d0d24febda38a88d2e3ef9e6d62d6d3..c9d5128b367368e5573072bfbbda944c7440d0cb 100644 (file)
@@ -15,11 +15,29 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script exports meshes to Radiosity file format.
 
-Usage:
+The Radiosity file format is my own personal format. I created it to
+learn how meshes and vertex colors were stored. See IO-Examples.zip, the
+example *.radio files on my web page.
 
-Select meshes to be exported and run this script from "File->Export" menu.
-"""
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Notes:<br>
+       Before exporting to .radio format, the mesh must have vertex colors.
+Here's how to assign them:
+
+1. Use radiosity!
+
+2. Set up lights and materials, select a mesh, switch the drawing mode
+to "textured," press the VKEY.
 
+3. Press the VKEY and paint manually.
+
+4. Use a custom script to calculate and apply simple diffuse shading and
+specular highlights to the vertex colors.
+
+5. The Videoscape format also allows vertex colors to be specified.
+"""
 
 # $Id$
 #
index 3966a9be88a95dfbf19ffbd2eebab35615969be2..f3e14d669a6c3cad591cb2a6b48a83a1e6e4488f 100644 (file)
@@ -15,13 +15,15 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script imports Radiosity files to Blender.
 
-Usage:
+The Radiosity file format is my own personal format. I created it to
+learn how meshes and vertex colors were stored. See IO-Examples.zip, the
+example *.radio files on my web page.
 
-Execute this script from the "File->Import" menu and choose a Radiosity file to
-open.
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose a Radiosity
+file to open.
 """
 
-
 # $Id$
 #
 # +---------------------------------------------------------+
index 843c3f6d32c844b1ecc907a72dd7cf4d65a31677..4cc49baff1e728bbe830b44f3aef84853ed08cca 100644 (file)
@@ -15,9 +15,14 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script exports meshes to Raw Triangle file format.
 
-Usage:
-
-Select meshes to be exported and run this script from "File->Export" menu.
+The raw triangle format is very simple; it has no verts or faces lists.
+It's just a simple ascii text file with the vertices of each triangle
+listed on each line. There were some very old utilities (when the PovRay
+forum was in existence on CompuServe) that preformed operations on these
+files.
+
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
 """
 
 
index d650d11a23ce48c54a330f43b98cbc24d1010724..13b2e18782629de5f90257f7a2511d9cf51ec403 100644 (file)
@@ -15,12 +15,21 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script imports Raw Triangle File format files to Blender.
 
-Usage:
+The raw triangle format is very simple; it has no verts or faces lists.
+It's just a simple ascii text file with the vertices of each triangle
+listed on each line. There were some very old utilities (when the PovRay
+forum was in existence on CompuServe) that preformed operations on these
+files.
 
-Execute this script from the "File->Import" menu and choose a Raw file to
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose a Raw file to
 open.
-"""
 
+Notes:<br>
+       Generates the standard verts and faces lists, but without duplicate
+verts. Only *exact* duplicates are removed, there is no way to specify a
+tolerance.
+"""
 
 # $Id$
 #
index f9ab52b37241598951752ac53746c236dc97977f..e8c84f62ba2c7d176b3d069c4c9e0312734db913 100644 (file)
@@ -15,10 +15,18 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script imports Pro Engineer files to Blender.
 
-Usage:
+This format can be exported from Pro/Engineer and most other CAD
+applications. Written at the request of a Blender user. It is almost
+identical to RAW format.
 
-Execute this script from the "File->Import" menu and choose an SLP file to
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose an SLP file to
 open.
+
+Notes:<br>
+       Generates the standard verts and faces lists, but without duplicate
+verts. Only *exact* duplicates are removed, there is no way to specify a
+tolerance.
 """
 
 # $Id$
index df96552467f1c0e3b9076029cbf897ee761af3a6..d9cd0b1ad99ba942eebd226873644fecb69320d6 100644 (file)
@@ -15,9 +15,38 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script exports meshes to TrueSpace file format.
 
-Usage:
-
-Select meshes to be exported and run this script from "File->Export" menu.
+TrueSpace is a commercial modeling and rendering application. The .cob
+file format is composed of 'chunks,' is well defined, and easy to read and
+write. It's very similar to LightWave's lwo format.
+
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Supported:<br>
+       Vertex colors will be exported, if they are present.
+
+Known issues:<br>
+       Before exporting to .cob format, the mesh must have real-time UV
+coordinates.  Press the FKEY to assign them.
+
+Notes:<br>
+       There are a few differences between how Blender & TrueSpace represent
+their objects' transformation matrices. Blender simply uses a 4x4 matrix,
+and trueSpace splits it into the following two fields.
+
+       For the 'Local Axes' values: The x, y, and z-axis represent a simple
+rotation matrix.  This is equivalent to Blender's object matrix before
+it was combined with the object's scaling matrix. Dividing each value by
+the appropriate scaling factor (and transposing at the same time)
+produces the original rotation matrix.
+
+       For the 'Current Position' values:      This is equivalent to Blender's
+object matrix except that the last row is omitted and the xyz location
+is used in the last column. Binary format uses a 4x3 matrix, ascii
+format uses a 4x4 matrix.
+
+For Cameras: The matrix here gets a little confusing, and I'm not sure of 
+how to handle it.
 """
 
 
index 5250c5e24af607a515e66913149e31e1eed0bf30..d1bfd3ab6bfd15092825ea4d633d902d5878066b 100644 (file)
@@ -13,12 +13,46 @@ __url__ = ("blender", "elysiun",
 __version__ = "Part of IOSuite 0.5"
 
 __bpydoc__ = """\
-This script imports TrueSpace files to Blender.
+This script imports TrueSpace files to Blender
 
-Usage:
+TrueSpace is a commercial modeling and rendering application. The .cob
+file format is composed of 'chunks,' is well defined, and easy to read and
+write. It's very similar to LightWave's lwo format.
 
-Execute this script from the "File->Import" menu and choose a TrueSpace file to
-open.
+Usage:<br>
+       Execute this script from the "File->Import" menu and choose a TrueSpace
+file to open.
+
+Supported:<br>
+       Meshes only. Supports UV Coordinates. COB files in ascii format can't be
+read.
+
+Missing:<br>
+       Materials, and Vertex Color info will be ignored.
+
+Known issues:<br>
+       Triangulation of convex polygons works fine, and uses a very simple
+fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
+require a different algorithm, and will be triagulated incorrectly.
+
+Notes:<br>
+       There are a few differences between how Blender & TrueSpace represent
+their objects' transformation matrices. Blender simply uses a 4x4 matrix,
+and trueSpace splits it into the following two fields.
+
+       For the 'Local Axes' values: The x, y, and z-axis represent a simple
+rotation matrix.  This is equivalent to Blender's object matrix before
+it was combined with the object's scaling matrix. Dividing each value by
+the appropriate scaling factor (and transposing at the same time)
+produces the original rotation matrix.
+
+       For the 'Current Position' values:  This is equivalent to Blender's
+object matrix except that the last row is omitted and the xyz location
+is used in the last column. Binary format uses a 4x3 matrix, ascii
+format uses a 4x4 matrix.
+
+For Cameras: The matrix here gets a little confusing, and I'm not sure of
+how to handle it.
 """
 
 # $Id$
@@ -227,19 +261,3 @@ def fs_callback(filename):
 
 Blender.Window.FileSelector(fs_callback, "Import COB")
 
-#      === Matrix Differences between Blender & trueSpace ===
-#
-#      For the 'Local Axes' values:
-#      The x, y, and z-axis represent a simple rotation matrix.
-#      This is equivalent to Blender's object matrix before it was
-#      combined with the object's scaling matrix.  Dividing each value
-#      by the appropriate scaling factor (and transposing at the same
-#      time) produces the original rotation matrix.
-#
-#      For the 'Current Position' values:
-#      This is equivalent to Blender's object matrix except that the
-#      last row is omitted and the xyz location is used in the last
-#      column.  Binary format uses a 4x3 matrix, ascii format uses a 4x4
-#      matrix.
-#
-#      For Cameras: The matrix is a little confusing.
index 362ecdc0d60e6e32d58adcbdabbba68f3c01533f..26bd7d08b015f28579c716142cb7b3433763cb1f 100644 (file)
@@ -15,9 +15,32 @@ __version__ = "Part of IOSuite 0.5"
 __bpydoc__ = """\
 This script exports meshes (including vertex colors) to VideoScape File Format.
 
-Usage:
+The VideoScape file format is a simple format that is natively supported
+in Blender. I wrote this module because Blender's internal exporter
+doesn't export vertex colors correctly. Check the source for a *fast* algorithm for
+averaging vertex colors.
 
-Select meshes to be exported and run this script from "File->Export" menu.
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Supported:<br>
+       Exports meshes only. Hint: use ALT-C to convert non-mesh objects,
+and CTRL-ALT-A if you have "dupliverts" objects.
+
+Notes:<br>
+       Before exporting, the mesh must have vertex colors. Here's how to assign them:
+
+1. Use radiosity!
+
+2. Set up lights and materials, select a mesh, switch the drawing mode
+to "textured," press the VKEY.
+
+3. Press the VKEY and paint manually.
+
+4. Use a custom script to calculate and apply simple diffuse shading and
+specular highlights to the vertex colors.
+
+5. The Videoscape format also allows vertex colors to be specified.
 """
 
 
@@ -32,8 +55,6 @@ Select meshes to be exported and run this script from "File->Export" menu.
 # | Import Export Suite v0.5                                |
 # +---------------------------------------------------------+
 # | Write Videoscape File Format (*.obj NOT WAVEFRONT OBJ)  |
-# | Includes a *fast* algorithm for averaging vertex colors |
-# | Blender's a|w doesn't export proper vertex colors       |
 # +---------------------------------------------------------+
 
 import Blender, mod_meshtools
index 3fb02673ed30741a406f59c8a717866ac2d47d61..c35b141cc97c7f1f92a5eddf4bafd82a1f17ceb2 100644 (file)
@@ -14,13 +14,34 @@ __url__ = ("blender", "elysiun",
 __version__ = "Part of IOSuite 0.5"
 
 __bpydoc__ = """\
-This script exports meshes to Wings 3D file format.
+This script exports meshes to Wings3D file format.
 
-Usage:
+Wings3D is an open source polygon modeler written in Erlang, a
+language similar to Lisp. The .wings file format is a binary
+representation of erlang terms (lists, tuples, atoms, etc.) and is
+compressed with zlib.
 
-Select meshes to be exported and run this script from "File->Export" menu.
-"""
+Usage:<br>
+       Select meshes to be exported and run this script from "File->Export" menu.
+
+Supported:<br>
+       1. Exports meshes only. Hint: use ALT-C to convert non-mesh objects,
+and CTRL-ALT-A if you have "dupliverts" objects.<br>
+       2. Exports Vertex Colors & Radiosity Solutions.
+
+Missing:<br>
+       Materials and UV Coordinates info will be ignored.
 
+Known issues:<br>
+       Exports only well-behaved and topologically correct meshes (i.e,
+closed meshes, manifolds, meshes imported from wings, etc). The mesh
+cannot have duplicate vertices, missing faces (holes), open edges, etc.<br>
+       PowerUser Hint: In editmode, if CTRL-ALT-SHIFT-M results in a selection,
+then your mesh is not a manifold and most likely will not export.
+
+Notes:<br>
+       Last tested with Wings 3D 0.98.25 & Blender 2.35a.
+"""
 
 # $Id$
 #
index 9633c94355eeed87d1e6ab881b819e82fd2ea775..dc28e978aaebd1a168f7e43ad7699cd195d6948c 100644 (file)
@@ -14,12 +14,30 @@ __url__ = ("blender", "elysiun",
 __version__ = "Part of IOSuite 0.5"
 
 __bpydoc__ = """\
-This script imports Wings 3d files to Blender.
+This script imports Wings3D files to Blender.
 
-Usage:
+Wings3D is an open source polygon modeler written in Erlang, a
+language similar to Lisp. The .wings file format is a binary
+representation of erlang terms (lists, tuples, atoms, etc.) and is
+compressed with zlib.
 
-Execute this script from the "File->Import" menu and choose a Wings file to
-open.
+Usage:<br>
+  Execute this script from the "File->Import" menu and choose a Wings file
+to open.
+
+Supported:<br>
+       Meshes only. Not guaranteed to work in all situations.
+
+Missing:<br>
+  Materials, UV Coordinates, and Vertex Color info will be ignored.
+
+Known issues:<br>
+       Triangulation of convex polygons works fine, and uses a very simple
+fanning algorithm. Convex polygons (i.e., shaped like the letter "U")
+require a different algorithm, and will be triagulated incorrectly.
+
+Notes:<br>
+       Last tested with Wings 3D 0.98.25 & Blender 2.35a.
 """
 
 # $Id$
index 9a50345fb39ba0b9055357c83b3642bad3edbf4c..8bebce3452993626c42ca2361c4788141750b44f 100644 (file)
@@ -195,7 +195,8 @@ PyObject *IpoCurve_Init( void )
 /*****************************************************************************/
 static PyObject *M_IpoCurve_Get( PyObject * self, PyObject * args )
 {
-       return 0;
+       Py_INCREF(Py_None);
+       return Py_None;
 }
 
 /*****************************************************************************/
index e9048ffad3a30d7de427881faea88a1cd97362ee..ec352061268e2395f8e16637919b420590dc4ac6 100644 (file)
@@ -57,9 +57,7 @@
 /*****************************************************************************/
 static PyObject *M_Object_New( PyObject * self, PyObject * args );
 PyObject *M_Object_Get( PyObject * self, PyObject * args );
-PyObject *M_Object_get( PyObject * self, PyObject * args );
 static PyObject *M_Object_GetSelected( PyObject * self, PyObject * args );
-static PyObject *M_Object_getSelected( PyObject * self, PyObject * args );
 
 /*****************************************************************************/
 /* The following string definitions are used for documentation strings.         */
@@ -90,12 +88,8 @@ struct PyMethodDef M_Object_methods[] = {
         M_Object_New_doc},
        {"Get", ( PyCFunction ) M_Object_Get, METH_VARARGS,
         M_Object_Get_doc},
-       {"get", ( PyCFunction ) M_Object_get, METH_VARARGS,
-        M_Object_Get_doc},
        {"GetSelected", ( PyCFunction ) M_Object_GetSelected, METH_VARARGS,
         M_Object_GetSelected_doc},
-       {"getSelected", ( PyCFunction ) M_Object_getSelected, METH_VARARGS,
-        M_Object_GetSelected_doc},
        {NULL, NULL, 0, NULL}
 };
 
@@ -516,21 +510,9 @@ PyObject *M_Object_Get( PyObject * self, PyObject * args )
        }
 }
 
-/*****************************************************************************/
-/* Function:    M_Object_get                                   */
-/* Python equivalent:    Blender.Object.get                    */
-/*****************************************************************************/
-PyObject *M_Object_get( PyObject * self, PyObject * args )
-{
-       PyErr_Warn( PyExc_DeprecationWarning,
-                   "The Object.get() function will be removed in Blender 2.29\n"
-                   "Please update the script to use Object.Get" );
-       return ( M_Object_Get( self, args ) );
-}
-
 /*****************************************************************************/
 /* Function:     M_Object_GetSelected                          */
-/* Python equivalent:    Blender.Object.getSelected            */
+/* Python equivalent:    Blender.Object.GetSelected            */
 /*****************************************************************************/
 static PyObject *M_Object_GetSelected( PyObject * self, PyObject * args )
 {
@@ -583,19 +565,6 @@ static PyObject *M_Object_GetSelected( PyObject * self, PyObject * args )
        return ( list );
 }
 
-/*****************************************************************************/
-/* Function:     M_Object_getSelected                                  */
-/* Python equivalent:    Blender.Object.getSelected                    */
-/*****************************************************************************/
-static PyObject *M_Object_getSelected( PyObject * self, PyObject * args )
-{
-       PyErr_Warn( PyExc_DeprecationWarning,
-                   "The Object.getSelected() function will be removed in "
-                   "Blender 2.29\n"
-                   "Please update the script to use Object.GetSelected" );
-       return ( M_Object_GetSelected( self, args ) );
-}
-
 /*****************************************************************************/
 /* Function:    initObject                                             */
 /*****************************************************************************/
index b5586fb2bfe572ef81df928f21d4ebefcfba5de9..70427b6c07414b4d723c79bf43d1ef3ca996b068 100644 (file)
@@ -319,7 +319,7 @@ class Vector:
       v = Blender.Mathutils.Vector([1,0,0])
     @type list: PyList of float or int
     @param list: The list of values for the Vector object.
-    Must be 2, 3, or 4 values.
+    Must be 2, 3, or 4 values. The list is mapped to the parameters as [x,y,z,w].
     @rtype: Vector object.
     @return: It depends wheter a parameter was passed:
         - (list): Vector object initialized with the given values;
@@ -420,16 +420,16 @@ class Quaternion:
   in degrees.
   """
 
-  def __init__(list = None, angle = None):
+  def __init__(list, angle = None):
     """  
     Create a new quaternion object from initialized values.
 
     Example::
-      quat = Mathutils.Quaternion()
+      quat = Mathutils.Quaternion([1.0,0.0,0.0])
 
     @type list: PyList of int/float
     @param list: A 3d or 4d list to initialize quaternion.
-        4d if intializing, 3d if will be used as an axis of rotation.
+        4d if intializing [w,x,y,z], 3d if used as an axis of rotation.
     @type angle: float (optional)
     @param angle: An arbitrary rotation amount around 'list'.
         List is used as an axis of rotation in this case.
@@ -527,7 +527,7 @@ class Matrix:
 
   def determinant():
     """
-    Return the determinant of a matrix.
+    Return the determinant of a matrix.
     @rtype: int
     @return: Return a the determinant of a matrix.
 
@@ -540,9 +540,12 @@ class Matrix:
 
   def rotationPart():
     """
-    Return the 3d rotation matrix.
+    Return the 3d submatrix corresponding to the linear term of the 
+    embedded affine transformation in 3d. This matrix represents rotation
+    and scale. Note that the (4,4) element of a matrix can be used for uniform
+    scaling, too.
     @rtype: Matrix object.
-    @return: Return the 3d rotation matrix.
+    @return: Return the 3d matrix for rotation and scale.
 
     """
 
@@ -561,17 +564,17 @@ class Matrix:
   
   def toEuler():
     """
-    Return a euler representing the rotation matrix.
+    Return an Euler representation of the rotation matrix.
     @rtype: Euler object
-    @return: Return a euler representing the rotation matrix.
+    @return: Euler representation of the rotation matrix.
 
     """
 
   def toQuat():
     """
-    Return a quaternion representation the rotation matrix
+    Return a quaternion representation of the rotation matrix
     @rtype: Quaternion object
-    @return: Quaternion representation the rotation matrix
+    @return: Quaternion representation of the rotation matrix
 
     """
 
index a6974ef5086c1dad208d2a7851c67aebc8ee9ace..e87050b29eac35e488a3414f686b69a09e5e9b78 100644 (file)
@@ -531,13 +531,17 @@ PyTypeObject quaternion_Type = {
        &Quaternion_SeqMethods, /*tp_as_sequence */
 };
 
-/* 
- newQuaternionObject
- if the quat arg is not null, this method allocates memory and copies *quat into it.
- we will free the memory in the dealloc routine.
-*/
-
+/** Creates a new quaternion object.
+ * 
+ * Memory for a new quaternion is allocated. The quaternion copies the given 
+ * list of parameters or initializes to the identity, if a <code>NULL</code>
+ * pointer is given as parameter. The memory will be freed in the dealloc
+ * routine.
+ * 
+ * @param quat Pointer to a list of floats for the quanternion parameters w, x, y, z.
+ * @return Quaternion Python object.
+ * @see Quaternion_Identity
+ */
 PyObject *newQuaternionObject( float *quat )
 {
        QuaternionObject *self;
@@ -550,10 +554,7 @@ PyObject *newQuaternionObject( float *quat )
        self->quat = PyMem_Malloc( 4 * sizeof( float ) );
 
        if( !quat ) {
-               for( x = 0; x < 4; x++ ) {
-                       self->quat[x] = 0.0f;
-               }
-               self->quat[3] = 1.0f;
+               Quaternion_Identity(self);
        } else {
                for( x = 0; x < 4; x++ ) {
                        self->quat[x] = quat[x];