diff options
author | Willian Padovani Germano <wpgermano@gmail.com> | 2004-11-30 05:27:46 +0300 |
---|---|---|
committer | Willian Padovani Germano <wpgermano@gmail.com> | 2004-11-30 05:27:46 +0300 |
commit | 6d9c02be4cfd61b985af789e167f7fa78dbc0868 (patch) | |
tree | 1bb8758f454cb90e0659e6122f11ec13b02a7b3f | |
parent | e4562134d28af07b6115436fdfaa4523020ea733 (diff) |
Scripts:
- 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, 986 insertions, 1082 deletions
diff --git a/release/scripts/blender2cal3d.py b/release/scripts/blender2cal3d.py index 3d7d148a55c..835ec16ae1f 100644 --- a/release/scripts/blender2cal3d.py +++ b/release/scripts/blender2cal3d.py @@ -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) + + diff --git a/release/scripts/lightwave_export.py b/release/scripts/lightwave_export.py index 1df2cec66fa..99d7deab440 100644 --- a/release/scripts/lightwave_export.py +++ b/release/scripts/lightwave_export.py @@ -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$ diff --git a/release/scripts/lightwave_import.py b/release/scripts/lightwave_import.py index 758c3b0229a..c795358c734 100644 --- a/release/scripts/lightwave_import.py +++ b/release/scripts/lightwave_import.py @@ -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$ diff --git a/release/scripts/mod_ai2obj.py b/release/scripts/mod_ai2obj.py index 07342737aad..8be6c5e18eb 100644 --- a/release/scripts/mod_ai2obj.py +++ b/release/scripts/mod_ai2obj.py @@ -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 diff --git a/release/scripts/mod_meshtools.py b/release/scripts/mod_meshtools.py index 6409f7ad72b..772b7549a11 100644 --- a/release/scripts/mod_meshtools.py +++ b/release/scripts/mod_meshtools.py @@ -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('.') diff --git a/release/scripts/mod_svg2obj.py b/release/scripts/mod_svg2obj.py index d180b0d26c5..7445b13a4a7 100644 --- a/release/scripts/mod_svg2obj.py +++ b/release/scripts/mod_svg2obj.py @@ -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 diff --git a/release/scripts/nendo_export.py b/release/scripts/nendo_export.py index 4f0aac905fa..1dd84bb6d81 100644 --- a/release/scripts/nendo_export.py +++ b/release/scripts/nendo_export.py @@ -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$ # # +---------------------------------------------------------+ diff --git a/release/scripts/nendo_import.py b/release/scripts/nendo_import.py index 2d041aadd53..2af19ff5a5d 100644 --- a/release/scripts/nendo_import.py +++ b/release/scripts/nendo_import.py @@ -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$ # # +---------------------------------------------------------+ diff --git a/release/scripts/obj_import.py b/release/scripts/obj_import.py index c860a9707df..c6cb032a632 100644 --- a/release/scripts/obj_import.py +++ b/release/scripts/obj_import.py @@ -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 diff --git a/release/scripts/off_export.py b/release/scripts/off_export.py index 328854d39f8..332d553b389 100644 --- a/release/scripts/off_export.py +++ b/release/scripts/off_export.py @@ -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. """ # +---------------------------------------------------------+ diff --git a/release/scripts/off_import.py b/release/scripts/off_import.py index b70a935b4a9..94b6423149a 100644 --- a/release/scripts/off_import.py +++ b/release/scripts/off_import.py @@ -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() diff --git a/release/scripts/radiosity_export.py b/release/scripts/radiosity_export.py index 27a32c810d0..c9d5128b367 100644 --- a/release/scripts/radiosity_export.py +++ b/release/scripts/radiosity_export.py @@ -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$ # diff --git a/release/scripts/radiosity_import.py b/release/scripts/radiosity_import.py index 3966a9be88a..f3e14d669a6 100644 --- a/release/scripts/radiosity_import.py +++ b/release/scripts/radiosity_import.py @@ -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$ # # +---------------------------------------------------------+ diff --git a/release/scripts/raw_export.py b/release/scripts/raw_export.py index 843c3f6d32c..4cc49baff1e 100644 --- a/release/scripts/raw_export.py +++ b/release/scripts/raw_export.py @@ -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. """ diff --git a/release/scripts/raw_import.py b/release/scripts/raw_import.py index d650d11a23c..13b2e187826 100644 --- a/release/scripts/raw_import.py +++ b/release/scripts/raw_import.py @@ -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$ # diff --git a/release/scripts/slp_import.py b/release/scripts/slp_import.py index f9ab52b3724..e8c84f62ba2 100644 --- a/release/scripts/slp_import.py +++ b/release/scripts/slp_import.py @@ -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$ diff --git a/release/scripts/truespace_export.py b/release/scripts/truespace_export.py index df96552467f..d9cd0b1ad99 100644 --- a/release/scripts/truespace_export.py +++ b/release/scripts/truespace_export.py @@ -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. """ diff --git a/release/scripts/truespace_import.py b/release/scripts/truespace_import.py index 5250c5e24af..d1bfd3ab6bf 100644 --- a/release/scripts/truespace_import.py +++ b/release/scripts/truespace_import.py @@ -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. diff --git a/release/scripts/videoscape_export.py b/release/scripts/videoscape_export.py index 362ecdc0d60..26bd7d08b01 100644 --- a/release/scripts/videoscape_export.py +++ b/release/scripts/videoscape_export.py @@ -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 diff --git a/release/scripts/wings_export.py b/release/scripts/wings_export.py index 3fb02673ed3..c35b141cc97 100644 --- a/release/scripts/wings_export.py +++ b/release/scripts/wings_export.py @@ -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$ # diff --git a/release/scripts/wings_import.py b/release/scripts/wings_import.py index 9633c94355e..dc28e978aae 100644 --- a/release/scripts/wings_import.py +++ b/release/scripts/wings_import.py @@ -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$ diff --git a/source/blender/python/api2_2x/Ipocurve.c b/source/blender/python/api2_2x/Ipocurve.c index 9a50345fb39..8bebce34529 100644 --- a/source/blender/python/api2_2x/Ipocurve.c +++ b/source/blender/python/api2_2x/Ipocurve.c @@ -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; } /*****************************************************************************/ diff --git a/source/blender/python/api2_2x/Object.c b/source/blender/python/api2_2x/Object.c index e9048ffad3a..ec352061268 100644 --- a/source/blender/python/api2_2x/Object.c +++ b/source/blender/python/api2_2x/Object.c @@ -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} }; @@ -517,20 +511,8 @@ 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 ) { @@ -584,19 +566,6 @@ static PyObject *M_Object_GetSelected( PyObject * self, PyObject * args ) } /*****************************************************************************/ -/* 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 */ /*****************************************************************************/ PyObject *Object_Init( void ) diff --git a/source/blender/python/api2_2x/doc/Mathutils.py b/source/blender/python/api2_2x/doc/Mathutils.py index b5586fb2bfe..70427b6c074 100644 --- a/source/blender/python/api2_2x/doc/Mathutils.py +++ b/source/blender/python/api2_2x/doc/Mathutils.py @@ -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 a 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 """ diff --git a/source/blender/python/api2_2x/quat.c b/source/blender/python/api2_2x/quat.c index a6974ef5086..e87050b29ea 100644 --- a/source/blender/python/api2_2x/quat.c +++ b/source/blender/python/api2_2x/quat.c @@ -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]; |