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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWillian Padovani Germano <wpgermano@gmail.com>2004-07-27 07:13:45 +0400
committerWillian Padovani Germano <wpgermano@gmail.com>2004-07-27 07:13:45 +0400
commit7b943f2a6d8ddcb58580896b3526c14c5f776b59 (patch)
tree6d8c9977ec932f16922f172112bbabf47a476a1f /release
parentd0da285547071aa51291ef3a07ec41d1fd4b0ac1 (diff)
Scripts:
- Ben Omari sent an update version of his DirectX8.py, Jean-Michel Soler updated his disp_paint.py and Campbell Barton contributed a new one: sel_same.py (for now it's in the UV menu). Thanks all, great scripts; - small updates in some other scripts. BPython: - Finished wrapping radiosity functions for the Radio submodule; - doc updates.
Diffstat (limited to 'release')
-rw-r--r--release/scripts/DirectX8Exporter.py330
-rw-r--r--release/scripts/DirectXExporter.py2
-rw-r--r--release/scripts/ac3d_import.py24
-rw-r--r--release/scripts/blender2cal3d.py1253
-rw-r--r--release/scripts/disp_paint.py248
-rw-r--r--release/scripts/fixfromarmature.py5
-rw-r--r--release/scripts/hotkeys.py2
-rw-r--r--release/scripts/mod_blender.py20
-rw-r--r--release/scripts/sel_same.py368
-rw-r--r--release/scripts/sysinfo.py6
10 files changed, 1610 insertions, 648 deletions
diff --git a/release/scripts/DirectX8Exporter.py b/release/scripts/DirectX8Exporter.py
index df2c564a10f..c37f6dceba6 100644
--- a/release/scripts/DirectX8Exporter.py
+++ b/release/scripts/DirectX8Exporter.py
@@ -1,7 +1,7 @@
#!BPY
""" Registration info for Blender menus:
-Name: 'DirectX8'
+Name: 'DirectX8 (.x)...'
Blender: 234
Group: 'Export'
Submenu: 'Export to DX8 file format' export
@@ -33,8 +33,8 @@ import Blender
from Blender import Types, Object, NMesh, Material,Armature
from Blender.Mathutils import *
-global bon_list, new_bon,mat_flip
-bon_list = []
+global new_bon,mat_flip,index_list
+index_list = []
new_bon = {}
mat_flip = Matrix([1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1])
@@ -66,11 +66,11 @@ def draw():
Blender.BGL.glRasterPos2d(10, 215)
Blender.Draw.Text(" (select them and press Ctrl + A)")
Blender.BGL.glRasterPos2d(10, 195)
- Blender.Draw.Text("3.Flip Normals ")
+ Blender.Draw.Text("3.Set the number of the animation frames to export ")
Blender.BGL.glRasterPos2d(10, 175)
- Blender.Draw.Text("4.Set the number of the animation frames to export ")
- Blender.BGL.glRasterPos2d(10, 155)
Blender.Draw.Text("5.Read warnings in console(if any)")
+
+
def event(evt, val):
if evt == Blender.Draw.ESCKEY and not val: Blender.Draw.Exit()
@@ -95,24 +95,24 @@ class xExport:
#***********************************************
#Export Animation
#***********************************************
- def exportMesh(self,armat,tex):
- global bon_list
+ def exportMesh(self,arm,arm_ob,tex):
+
for name in Object.Get():
obj = name.getData()
if type(obj) == Types.NMeshType :
- self.writeMeshcoord(name, obj,armat)
+ self.writeMeshcoord(name, obj,arm_ob)
self.writeMeshMaterialList(name, obj, tex)
self.writeMeshNormals(name, obj)
self.writeMeshTextureCoords(name, obj)
- self.writeSkinWeights(bon_list,obj)
+ self.writeSkinWeights(arm,obj)
self.file.write(" }\n")
self.file.write("}\n")
- self.writeAnimation(name, obj, bon_list,armat)
+ self.writeAnimation(name, obj,arm)
#***********************************************
#Export Root Bone
#***********************************************
def writeRootBone(self):
- global bon_list, new_bon,mat_flip
+ global new_bon,mat_flip
space = 0
tex = []
print "exporting ..."
@@ -126,27 +126,21 @@ class xExport:
Blender.Set('curframe',1)
am_ob = Object.Get(name.name)
mat_ob = mat_flip * am_ob.getMatrix()
- mat_o = Matrix([mat_ob[0][0],mat_ob[0][1],mat_ob[0][2],mat_ob[0][3]],
- [mat_ob[1][0],mat_ob[1][1],mat_ob[1][2],mat_ob[1][3]],
- [mat_ob[2][0],mat_ob[2][1],mat_ob[2][2],mat_ob[2][3]],
- [0, 0, 0, 1])
-
- self.writeArmFrames(mat_o, "RootFrame", 0)
+ self.writeArmFrames(mat_ob, "RootFrame", 0)
root_bon = arm.getBones()
- bon_list.append(root_bon[0])
mat_r = self.writeCombineMatrix(root_bon[0])
name_r = root_bon[0].getName()
new_bon[name_r] = len(root_bon[0].getChildren())
self.writeArmFrames(mat_r, name_r, 1)
- self.writeListOfChildrens(root_bon[0],2)
+ self.writeListOfChildrens(root_bon[0],2,arm)
self.file.write("}\n")
- self.exportMesh(am_ob, tex)
+ self.exportMesh(arm,am_ob, tex)
self.writeEnd()
#***********************************************
#Export Children Bones
#***********************************************
- def writeListOfChildrens(self,bon,space):
- global bon_list, new_bon
+ def writeListOfChildrens(self,bon,space,arm):
+ global new_bon
bon_c = bon.getChildren()
Blender.Set('curframe',1)
for n in range(len(bon_c)):
@@ -155,14 +149,13 @@ class xExport:
new_bon[name_h] = len(chi_h)
if bon_c == [] :
- self.CloseBrackets(bon, new_bon, space, bon_list[0])
+ self.CloseBrackets(bon, new_bon, space, arm.getBones()[0])
for nch in range(len(bon_c)):
- bon_list.append(bon_c[nch])
mat = self.writeCombineMatrix(bon_c[nch])
name_ch = bon_c[nch].getName()
self.writeArmFrames(mat, name_ch,space)
- self.findChildrens(bon_c[nch],space)
+ self.findChildrens(bon_c[nch],space,arm)
#***********************************************
@@ -190,10 +183,10 @@ class xExport:
#***********************************************
#Create Children structure
#***********************************************
- def findChildrens(self,bon_c,space):
+ def findChildrens(self,bon_c,space,arm):
bon_cc = bon_c
space += 1
- self.writeListOfChildrens(bon_cc,space)
+ self.writeListOfChildrens(bon_cc,space,arm)
#***********************************************
@@ -243,12 +236,13 @@ class xExport:
#***********************************************
#Write SkinWeights
#***********************************************
- def writeSkinWeights(self, bon_list, mesh):
- global mat_dict
+ def writeSkinWeights(self, arm, mesh):
+ global index_list
+
Blender.Set('curframe',1)
self.file.write(" XSkinMeshHeader {\n")
max_infl = 0
- for bo in bon_list :
+ for bo in arm.getBones() :
name = bo.getName()
try :
vertx_list = mesh.getVertsFromGroup(name,1)
@@ -263,35 +257,47 @@ class xExport:
self.file.write(" %s; \n" % (max_infl))
self.file.write(" %s; \n" % (max_infl * 3))
- self.file.write(" %s; \n" % (len(bon_list)))
+ self.file.write(" %s; \n" % (len(arm.getBones())))
self.file.write(" }\n")
- for bo in bon_list :
+ for bo in arm.getBones() :
+ bo_list = []
+ weight_list = []
name = bo.getName()
try :
vert_list = mesh.getVertsFromGroup(name,1)
+ le = 0
+ for indx in vert_list:
+ ver_infl = mesh.getVertexInfluences(indx[0])
+ len_infl = float(len(ver_infl))
+ infl = 1 / len_infl
+ i = -1
+ for el in index_list :
+ i += 1
+ if el == indx[0] :
+ le +=1
+ bo_list.append(i)
+ weight_list.append(infl)
+
+
self.file.write(" SkinWeights {\n")
self.file.write(' "%s"; \n' % (name))
- self.file.write(' %s; \n' % (len(vert_list)))
+ self.file.write(' %s; \n' % (le))
count = 0
- for ind in vert_list :
+ for ind in bo_list :
count += 1
- if count == len(vert_list):
- self.file.write(" %s; \n" % (ind[0]))
+ if count == len(bo_list):
+ self.file.write(" %s; \n" % (ind))
else :
- self.file.write(" %s, \n" % (ind[0]))
+ self.file.write(" %s, \n" % (ind))
cou = 0
- for ind in vert_list :
+ for wegh in weight_list :
cou += 1
- ver_infl = mesh.getVertexInfluences(ind[0])
-
- len_infl = float(len(ver_infl))
- infl = 1 / len_infl
-
- if cou == len(vert_list):
- self.file.write(" %s; \n" % (round(infl,6)))
+
+ if cou == len(weight_list):
+ self.file.write(" %s; \n" % (round(wegh,6)))
else :
- self.file.write(" %s, \n" % (round(infl,6)))
+ self.file.write(" %s, \n" % (round(wegh,6)))
matx = self.writeMatrixOffset(bo)
@@ -393,54 +399,109 @@ template SkinWeights {\n\
#EXPORT MESH DATA
#***********************************************
def writeMeshcoord(self, name, mesh,armat):
- global mat_flip
-
+ global index_list
#ROTATION
mat_ob = name.getMatrix()
mat_ar = armat.getInverseMatrix()
mat_f = mat_ob * mat_ar
-
self.writeArmFrames(mat_f, "body", 1)
- self.file.write(" Mesh object {\n")
- numfaces=0
+ self.file.write("Mesh {\n")
+ numfaces=len(mesh.faces)
#VERTICES NUMBER
- self.file.write(" %s;\n" % (len(mesh.verts)))
+ numvert = 0
+ for face in mesh.faces:
+ numvert = numvert + len(face.v)
+ self.file.write("%s;\n" % (numvert))
#VERTICES COORDINATES
- numvert=0
- for vertex in mesh.verts:
- numvert=numvert+1
- self.file.write(" %s; %s; %s;" % (round(vertex[0],6), round(vertex[1],6), round((vertex[2]),6)))
- if numvert == len(mesh.verts):
- self.file.write(";\n")
- else:
- self.file.write(",\n")
-
- #FACES NUMBER
- numfaces=len(mesh.faces)
- self.file.write(" %s;\n" % (numfaces))
- #FACES INDEX
counter = 0
- for face in mesh.faces :
+ for face in mesh.faces:
counter += 1
- if counter == numfaces:
+ for n in range(len(face.v)):
+ index_list.append(face.v[n].index)
+ self.file.write("%s; %s; %s;" % (face.v[n].co[0], face.v[n].co[1], face.v[n].co[2]))
+ if counter == numfaces :
+ if n == len(face.v)-1 :
+ self.file.write(";\n")
+ else :
+ self.file.write(",\n")
+ else :
+ self.file.write(",\n")
+
+ #FACES NUMBER
+ self.file.write("%s;\n" % (numfaces))
+ #FACES INDEX
+ numface=len(mesh.faces)
+ coun,counter = 0, 0
+ for face in mesh.faces :
+ coun += 1
+ if coun == numface:
if len(face.v) == 3:
- self.file.write(" 3; %s, %s, %s;;\n\n" % (face[0].index, face[1].index, face[2].index))
- elif len(face.v) == 4:
- self.file.write(" 4; %s, %s, %s, %s;;\n\n" % (face[0].index, face[1].index, face[2].index, face[3].index))
- elif len(face.v) == 2:
- print "WARNING:the mesh has faces with less then 3 vertices"
+ self.file.write("3; %s, %s, %s;;\n" % (counter, counter + 2, counter + 1))
+ counter += 3
+ else :
+ self.file.write("4; %s, %s, %s, %s;;\n" % (counter, counter + 3, counter + 2, counter + 1))
+ counter += 4
else:
+
if len(face.v) == 3:
- self.file.write(" 3; %s, %s, %s;,\n" % (face[0].index, face[1].index, face[2].index))
- elif len(face.v) == 4 :
- self.file.write(" 4; %s, %s, %s, %s;,\n" % (face[0].index, face[1].index, face[2].index, face[3].index))
- elif len(face.v) == 2:
- print "WARNING:the mesh has faces with less then 3 vertices"
+ self.file.write("3; %s, %s, %s;,\n" % (counter, counter + 2, counter + 1))
+ counter += 3
+ else :
+ self.file.write("4; %s, %s, %s, %s;,\n" % (counter, counter + 3, counter + 2, counter + 1))
+ counter += 4
+
+ #***********************************************
+ #VERTEX DUPLICATION INDEX
+ #***********************************************
+ def writeVertDupInd(self, mesh):
+ self.file.write(" VertexDuplicationIndices {\n")
+ numvert = 0
+ numfaces=len(mesh.faces)
+ for face in mesh.faces:
+ numvert = numvert + len(face.v)
+ self.file.write(" %s;\n" % (numvert+len(mesh.verts)))
+ self.file.write(" %s;\n" % (len(mesh.verts)))
+ #VERTICES INDEX
+ cou = 0
+ for vert in mesh.verts:
+ cou += 1
+ self.file.write(" %s" % ((vert.index)))
+ if cou == len(mesh.verts):
+ self.file.write(";\n")
+ else:
+ self.file.write(",\n")
+
+ counter = 0
+ for face in mesh.faces:
+ counter += 1
+ if counter == numfaces:
+ if len(face.v) == 4:
+ self.file.write(" %s,\n" % ((face.v[0].index)))
+ self.file.write(" %s,\n" % ((face.v[1].index)))
+ self.file.write(" %s,\n" % ((face.v[2].index)))
+ self.file.write(" %s;\n" % ((face.v[3].index)))
+ elif len(face.v) == 3 :
+ self.file.write(" %s,\n" % ((face.v[0].index)))
+ self.file.write(" %s,\n" % ((face.v[1].index)))
+ self.file.write(" %s;\n" % ((face.v[2].index)))
+
+ else :
+ if len(face.v) == 4:
+ self.file.write(" %s,\n" % ((face.v[0].index)))
+ self.file.write(" %s,\n" % ((face.v[1].index)))
+ self.file.write(" %s,\n" % ((face.v[2].index)))
+ self.file.write(" %s,\n" % ((face.v[3].index)))
+ elif len(face.v) == 3 :
+ self.file.write(" %s,\n" % ((face.v[0].index)))
+ self.file.write(" %s,\n" % ((face.v[1].index)))
+ self.file.write(" %s,\n" % ((face.v[2].index)))
+
+ self.file.write(" }\n")
+
-
#***********************************************
#MESH MATERIAL LIST
@@ -503,67 +564,88 @@ template SkinWeights {\n\
#***********************************************
#MESH NORMALS
#***********************************************
- def writeMeshNormals(self,name,obj):
+ def writeMeshNormals(self,name,mesh):
self.file.write(" MeshNormals {\n")
#VERTICES NUMBER
- numvert=len(obj.verts)
- self.file.write(" %s;\n" % (numvert))
+ numvert = 0
+ for face in mesh.faces:
+ numvert = numvert + len(face.v)
+ self.file.write("%s;\n" % (numvert))
+ numfaces=len(mesh.faces)
+
#VERTICES NORMAL
counter = 0
- for vert in obj.verts:
+ for face in mesh.faces:
counter += 1
- if counter == numvert:
- self.file.write(" %s; %s; %s;;\n" % ((round(vert.no[0],6)),(round(vert.no[1],6)),(round(vert.no[2],6))))
- else :
- self.file.write(" %s; %s; %s;,\n" % ((round(vert.no[0],6)), (round(vert.no[1],6)),(round(vert.no[2],6))))
+ for n in range(len(face.v)):
+ self.file.write(" %s; %s; %s;" % ((round(face.v[n].no[0],6)),(round(face.v[n].no[1],6)),(round(face.v[n].no[2],6))))
+ if counter == numfaces :
+ if n == len(face.v)-1 :
+ self.file.write(";\n")
+ else :
+ self.file.write(",\n")
+ else :
+ self.file.write(",\n")
+
+
+
#FACES NUMBER
- numfaces=len(obj.faces)
- self.file.write(" %s;\n" % (numfaces))
- #FACES INDEX
- counter = 0
- for face in obj.faces :
- counter += 1
- if counter == numfaces:
+ self.file.write("%s;\n" % (numfaces))
+ coun,counter = 0, 0
+ for face in mesh.faces :
+ coun += 1
+ if coun == numfaces:
if len(face.v) == 3:
- self.file.write(" 3; %s, %s, %s;;\n" % (face[0].index, face[1].index, face[2].index))
- elif len(face.v) == 4:
- self.file.write(" 4; %s, %s, %s, %s;;\n" % (face[0].index, face[1].index, face[2].index, face[3].index))
+ self.file.write("3; %s, %s, %s;;\n" % (counter, counter + 2, counter + 1))
+ counter += 3
+ else :
+ self.file.write("4; %s, %s, %s, %s;;\n" % (counter, counter + 3, counter + 2, counter + 1))
+ counter += 4
else:
+
if len(face.v) == 3:
- self.file.write(" 3; %s, %s, %s;,\n" % (face[0].index, face[1].index, face[2].index))
- elif len(face.v) == 4 :
- self.file.write(" 4; %s, %s, %s, %s;,\n" % (face[0].index, face[1].index, face[2].index, face[3].index))
+ self.file.write("3; %s, %s, %s;,\n" % (counter, counter + 2, counter + 1))
+ counter += 3
+ else :
+ self.file.write("4; %s, %s, %s, %s;,\n" % (counter, counter + 3, counter + 2, counter + 1))
+ counter += 4
self.file.write("}\n")
#***********************************************
#MESH TEXTURE COORDS
#***********************************************
- def writeMeshTextureCoords(self, name, obj):
- uv_list = {}
- if obj.hasFaceUV():
- self.file.write(" MeshTextureCoords {\n")
- #VERTICES NUMBER
- mesh = name.data
- numvert = 0
- for face in mesh.faces:
- numvert = numvert + len(face.v)
- self.file.write(" %s;\n" % (len(mesh.verts)))
- #UV COORDS
- counter = -1
- for face in mesh.faces:
- counter += 1
- nrvx = len(face.uv)
- for n in range(nrvx) :
- uv_list[face.v[n].index] = face.uv[n][0], face.uv[n][1]
- for vert in mesh.verts:
- self.file.write(" %s; %s;,\n" % (uv_list[vert.index][0],1 - (uv_list[vert.index][1])))
-
- self.file.write("}\n")
+ def writeMeshTextureCoords(self, name, mesh):
+ if mesh.hasFaceUV():
+ self.file.write("MeshTextureCoords {\n")
+ #VERTICES NUMBER
+ numvert = 0
+ for face in mesh.faces:
+ numvert += len(face.v)
+ self.file.write("%s;\n" % (numvert))
+ #UV COORDS
+ numfaces = len(mesh.faces)
+ counter = -1
+ co = 0
+ for face in mesh.faces:
+ counter += 1
+ co += 1
+ for n in range(len(face.v)):
+ self.file.write("%s;%s;" % (mesh.faces[counter].uv[n][0], -mesh.faces[counter].uv[n][1]))
+ if co == numfaces :
+ if n == len(face.v) - 1 :
+ self.file.write(";\n")
+ else :
+ self.file.write(",\n")
+ else :
+ self.file.write(",\n")
+
+ self.file.write("}\n")
#***********************************************#***********************************************#***********************************************
#***********************************************
#FRAMES
#***********************************************
def writeFrames(self, matx):
+
self.file.write("%s,%s,%s,%s," %
(round(matx[0][0],4),round(matx[0][1],4),round(matx[0][2],4),round(matx[0][3],6)))
self.file.write("%s,%s,%s,%s," %
@@ -579,11 +661,11 @@ template SkinWeights {\n\
#***********************************************
#WRITE ANIMATION KEYS
#***********************************************
- def writeAnimation(self, name, obj, bone_list, arm):
+ def writeAnimation(self, name, obj, arm):
self.file.write("AnimationSet {\n")
startFr = Blender.Get('staframe')
endFr = Blender.Get('endframe')
- for bon in bon_list :
+ for bon in arm.getBones() :
self.file.write(" Animation { \n")
self.file.write(" {%s}\n" %(bon.getName()))
@@ -633,4 +715,4 @@ if arg == 'help':
Blender.Draw.Register(draw,event,bevent)
else:
fname = Blender.sys.makename(ext = ".x")
- Blender.Window.FileSelector(my_callback, "Export DirectX8", fname)
+ Blender.Window.FileSelector(my_callback, "Export DirectX8", fname)
diff --git a/release/scripts/DirectXExporter.py b/release/scripts/DirectXExporter.py
index 4fa0d1c83f6..c2cee3a4c11 100644
--- a/release/scripts/DirectXExporter.py
+++ b/release/scripts/DirectXExporter.py
@@ -1,7 +1,7 @@
#!BPY
""" Registration info for Blender menus:
-Name: 'DirectX'
+Name: 'DirectX (.x)...'
Blender: 234
Group: 'Export'
Submenu: 'Only mesh data...' mesh
diff --git a/release/scripts/ac3d_import.py b/release/scripts/ac3d_import.py
index a9c855b4a0a..82bc1b2335b 100644
--- a/release/scripts/ac3d_import.py
+++ b/release/scripts/ac3d_import.py
@@ -6,11 +6,13 @@ Blender: 232
Group: 'Import'
Tip: 'Import an AC3D (.ac) file.'
"""
+
# $Id$
#
# --------------------------------------------------------------------------
-# AC3DImport version 2.32-1 Jan 21, 2004
+# AC3DImport version 2.34 Jul 26, 2004
# Program versions: Blender 2.32+ and AC3Db files (means version 0xb)
+# small update to allow a default path for textures, see TEXDIR below.
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
@@ -39,8 +41,9 @@ Tip: 'Import an AC3D (.ac) file.'
# fixing. Avoiding or triangulating concave n-gons in AC3D is a simple way to
# avoid problems.
-# Default folder for AC3D models, change to your liking or leave as "":
-BASEDIR = ""
+# Default folder for AC3D textures, to override wrong paths, change to your
+# liking or leave as "":
+TEXDIR = ""
# Set 'GROUP' to 1 to make Blender group imported objects using Empties,
# to reproduce the object hierarchy in the .ac file
@@ -48,9 +51,9 @@ GROUP = 0
import Blender
-if BASEDIR:
- BASEDIR = BASEDIR.replace('\\','/')
- if BASEDIR[-1] != '/': BASEDIR += '/'
+if TEXDIR:
+ TEXDIR = TEXDIR.replace('\\','/')
+ if TEXDIR[-1] != '/': TEXDIR += '/'
errmsg = ""
@@ -361,11 +364,16 @@ class AC3DImport:
#tex.xrep = int(obj.texrep[0])
#tex.yrep = int(obj.texrep[1])
except:
+ basetexname = Blender.sys.basename(obj.tex)
try:
- obj.tex = self.importdir + '/' + obj.tex
+ obj.tex = self.importdir + '/' + basetexname
tex = Blender.Image.Load(obj.tex)
except:
- print "Couldn't load texture: %s" % obj.tex
+ try:
+ obj.tex = TEXDIR + basetexname
+ tex = Blender.Image.Load(obj.tex)
+ except:
+ print "Couldn't load texture: %s" % basetexname
for v in obj.vlist:
bvert = Blender.NMesh.Vert(v[0],v[1],v[2])
diff --git a/release/scripts/blender2cal3d.py b/release/scripts/blender2cal3d.py
index a6c04d8cb54..07728df50b3 100644
--- a/release/scripts/blender2cal3d.py
+++ b/release/scripts/blender2cal3d.py
@@ -1,15 +1,16 @@
#!BPY
"""
-Name: 'Cal3D v0.5...'
-Blender: 232
+Name: 'Cal3D Exporter V0.7'
+Blender: 234
Group: 'Export'
Tip: 'Export armature/bone data to the Cal3D library.'
"""
# $Id$
#
-# blender2cal3D.py version 0.5
# Copyright (C) 2003 Jean-Baptiste LAMY -- jiba@tuxfamily.org
+# Copyright (C) 2004 Chris Montijin
+# Copyright (C) 2004 Damien McGinnes
#
# 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
@@ -26,80 +27,146 @@ Tip: 'Export armature/bone data to the Cal3D library.'
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-# This script is a Blender 2.28 => Cal3D 0.7/0.8 converter.
+# This script is a Blender 2.34 => Cal3D 0.7/0.8/0.9 converter.
# (See http://blender.org and http://cal3d.sourceforge.net)
#
-# Grab the latest version here :
-# http://oomadness.tuxfamily.org/en/blender2cal3d
+
+# 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
+
+
# HOW TO USE :
# 1 - load the script in Blender's text editor
-# 2 - modify the parameters below (e.g. the file name)
-# 3 - type M-P (meta/alt + P) and wait until script execution is finished
+# 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
-# ADVICES :
-# - Use only locrot keys in Blender's action
-# - Do not put "." in action or bone names, and do not start these names by a figure
-# - Objects whose names start by "_" are not exported (hidden object)
-# - All your armature's bones must be connected to another bone (except for the root
-# bone). Contrary to Blender, Cal3D doesn't support "floating" bones.
-# - Only Linux has been tested
+# 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
# BUGS / TODO :
-# - Animation names ARE LOST when exporting (this is due to Blender Python API and cannot
-# be fixed until the API change). See parameters for how to rename your animations
-# - Rotation, translation, or stretch (size changing) of Blender object is still quite
-# bugged, 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.
# - Material color is not supported yet
# - Cal3D springs (for clothes and hair) are not supported yet
-# - Optimization tips : almost all the time is spent on scene.makeCurrent(), called for
-# updating the IPO curve's values. Updating a single IPO and not the whole scene
-# would speed up a lot.
+# - 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
-# Questions and comments are welcome at jiba@tuxfamily.org
+
+# 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...
# Parameters :
# The directory where the data are saved.
-# blender2cal3d.py will create all files in this directory,
-# including a .cfg file.
-# WARNING: As Cal3D stores model in directory and not in a single file,
-# you MUST avoid putting other file in this directory !
-# Please give an empty directory (or an unexistant one).
-# Files may be deleted from this directoty !
-SAVE_TO_DIR = "cal3d"
-
-# Use this dictionary to rename animations, as their name is lost at the exportation.
+SAVE_TO_DIR = "/tmp/tutorial/"
+
+# 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
+
+# Prefix for all created files
+FILE_PREFIX = "Test"
+
+# Remove path from imagelocation
+REMOVE_PATH_FROM_IMAGE = 0
+
+# 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/"
+
+# Export to new (>= 900) Cal3D XML-format
+EXPORT_TO_XML = 0
+
+# Set scalefactor for model
+SCALE = 0.5
+
+# frames per second - used to convert blender frames to times
+FPS = 25
+
+# 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
-# Enables LODs computation. LODs computation is quite slow, and the algo is surely
-# not optimal :-(
-LODS = 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
+# surely not optimal :-(
+LODS = 0
+
+#remove the word '.BAKED' from exported baked animations
+REMOVE_BAKED = 1
+
+
+################################################################################
# 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 = ["???"]
@@ -115,10 +182,12 @@ 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]))
@@ -148,10 +217,16 @@ def quaternion_multiply(q1, q2):
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])
- r[0] /= d
- r[1] /= d
- r[2] /= d
- r[3] /= d
+ 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):
@@ -254,9 +329,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],
]
@@ -274,7 +349,8 @@ 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_by_matrix(p, m):
return [p[0] * m[0][0] + p[1] * m[1][0] + p[2] * m[2][0],
@@ -337,38 +413,31 @@ def blender_bone2matrix(head, tail, roll):
return matrix_multiply(rMatrix, bMatrix)
-# Hack for having the model rotated right.
-# Put in BASE_MATRIX your own rotation if you need some.
-
-if EXPORT_FOR_SOYA:
- BASE_MATRIX = matrix_rotate_x(-math.pi / 2.0)
-
-else:
- BASE_MATRIX = None
-
-
# Cal3D data structures
CAL3D_VERSION = 700
+CAL3D_XML_VERSION = 900
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
@@ -377,17 +446,36 @@ class Material:
NEXT_MATERIAL_ID += 1
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
+ 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
+ for map_filename in self.maps_filenames:
+ s += " <MAP>%s</MAP>\n" % map_filename
+ 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
@@ -397,13 +485,20 @@ class Mesh:
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 += "<MESH NUMSUBMESH=\"%i\">\n" % len(self.submeshes)
+ s += "".join(map(SubMesh.to_cal3d_xml, self.submeshes))
+ 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
@@ -420,34 +515,41 @@ 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
@@ -455,12 +557,13 @@ class SubMesh:
collapsed[v1] = 1
collapsed[v2] = 1
- # Check if v2 is already colapsed
- while v2.collapse_to: v2 = v2.collapse_to
+ # Check if v2 is already collapsed
+ 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:
@@ -479,9 +582,11 @@ 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)
@@ -497,7 +602,8 @@ 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
@@ -506,32 +612,50 @@ 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):
+ s = " <SUBMESH NUMVERTICES=\"%i\" NUMFACES=\"%i\" MATERIAL=\"%i\" " % \
+ (len(self.vertices), len(self.faces), self.material.id)
+ s += "NUMLODSTEPS=\"%i\" NUMSPRINGS=\"%i\" NUMTEXCOORDS=\"%i\">\n" % \
+ (self.nb_lodsteps, len(self.springs),
+ len(self.material.maps_filenames))
+ s += "".join(map(Vertex.to_cal3d_xml, self.vertices))
+ s += "".join(map(Spring.to_cal3d_xml, self.springs))
+ s += "".join(map(Face.to_cal3d_xml, self.faces))
+ s += " </SUBMESH>\n"
return s
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
@@ -539,13 +663,39 @@ 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
+ else:
+ collapse_id = -1
+ s = " <VERTEX ID=\"%i\" NUMINFLUENCES=\"%i\">\n" % \
+ (self.id, len(self.influences))
+ s += " <POS>%f %f %f</POS>\n" % (self.loc[0], self.loc[1], self.loc[2])
+ s += " <NORM>%f %f %f</NORM>\n" % \
+ (self.normal[0], self.normal[1], self.normal[2])
+ if collapse_id != -1:
+ s += " <COLLAPSEID>%i</COLLAPSEID>\n" % collapse_id
+ s += " <COLLAPSECOUNT>%i</COLLAPSECOUNT>\n" % \
+ self.face_collapse_count
+ s += "".join(map(Map.to_cal3d_xml, self.maps))
+ s += "".join(map(Influence.to_cal3d_xml, self.influences))
+ if not self.weight is None:
+ s += " <PHYSIQUE>%f</PHYSIQUE>\n" % len(self.weight)
+ s += " </VERTEX>\n"
return s
class Map:
@@ -556,14 +706,21 @@ 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)
+
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
@@ -572,7 +729,13 @@ 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" % \
+ (self.vertex1.id, self.vertex2.id, self.spring_coefficient,
+ self.idlelength)
class Face:
def __init__(self, submesh, vertex1, vertex2, vertex3):
@@ -588,6 +751,10 @@ 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 = []
@@ -599,12 +766,19 @@ class Skeleton:
s += "".join(map(Bone.to_cal3d, self.bones))
return s
+ def to_cal3d_xml(self):
+ s = "<HEADER MAGIC=\"XSF\" VERSION=\"%i\"/>\n" % CAL3D_XML_VERSION
+ s += "<SKELETON NUMBONES=\"%i\">\n" % len(self.bones)
+ s += "".join(map(Bone.to_cal3d_xml, self.bones))
+ s += "</SKELETON>\n"
+ return s
+
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 = []
@@ -614,8 +788,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)
@@ -628,30 +802,66 @@ 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" % \
+ (self.id, self.name, len(self.children))
+ # We need to negate quaternion W value, but why ?
+ s += " <TRANSLATION>%f %f %f</TRANSLATION>\n" % \
+ (self.loc[0], self.loc[1], self.loc[2])
+ s += " <ROTATION>%f %f %f %f</ROTATION>\n" % \
+ (self.rot[0], self.rot[1], self.rot[2], -self.rot[3])
+ s += " <LOCALTRANSLATION>%f %f %f</LOCALTRANSLATION>\n" % \
+ (self.lloc[0], self.lloc[1], self.lloc[2])
+ s += " <LOCALROTATION>%f %f %f %f</LOCALROTATION>\n" % \
+ (self.lrot[0], self.lrot[1], self.lrot[2], -self.lrot[3])
+ if self.parent:
+ s += " <PARENTID>%i</PARENTID>\n" % self.parent.id
+ else:
+ s += " <PARENTID>%i</PARENTID>\n" % -1
+ s += "".join(map(lambda bone: " <CHILDID>%i</CHILDID>\n" % bone.id,
+ self.children))
+ s += " </BONE>\n"
+ return s
+
class Animation:
- def __init__(self, name, duration = 0.0):
- self.name = name
+ def __init__(self, name, action, duration = 0.0):
+ self.name = name
+ self.action = action
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 += "<ANIMATION DURATION=\"%f\" NUMTRACKS=\"%i\">\n" % \
+ (self.duration, len(self.tracks))
+ s += "".join(map(Track.to_cal3d_xml, self.tracks.values()))
+ s += "</ANIMATION>\n"
+ return s
+
class Track:
def __init__(self, animation, bone):
- self.bone = bone
+ self.bone = bone
self.keyframes = []
self.animation = animation
@@ -662,21 +872,51 @@ class Track:
s += "".join(map(KeyFrame.to_cal3d, self.keyframes))
return s
+ def to_cal3d_xml(self):
+ s = " <TRACK BONEID=\"%i\" NUMKEYFRAMES=\"%i\">\n" % \
+ (self.bone.id, len(self.keyframes))
+ s += "".join(map(KeyFrame.to_cal3d_xml, self.keyframes))
+ s += " </TRACK>\n"
+ return s
+
class KeyFrame:
def __init__(self, track, time, loc, rot):
self.time = time
- self.loc = loc
- self.rot = rot
+ self.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
+ s += " <TRANSLATION>%f %f %f</TRANSLATION>\n" % \
+ (self.loc[0], self.loc[1], self.loc[2])
+ # We need to negate quaternion W value, but why ?
+ s += " <ROTATION>%f %f %f %f</ROTATION>\n" % \
+ (self.rot[0], self.rot[1], self.rot[2], -self.rot[3])
+ s += " </KEYFRAME>\n"
+ return s
def export():
+ global STATUS
+ STATUS = "Start export..."
+ Draw()
+
+ # Hack for having the model rotated right.
+ # Put in BASE_MATRIX your own rotation if you need some.
+
+ if EXPORT_FOR_SOYA:
+ BASE_MATRIX = matrix_rotate_x(-math.pi / 2.0)
+ else:
+ BASE_MATRIX = None
+
# Get the scene
scene = Blender.Scene.getCurrent()
@@ -684,6 +924,9 @@ def export():
# Export skeleton (=armature)
+ STATUS = "Calculate skeleton"
+ Draw()
+
skeleton = Skeleton()
for obj in Blender.Object.Get():
@@ -693,6 +936,9 @@ def export():
if BASE_MATRIX: matrix = matrix_multiply(BASE_MATRIX, matrix)
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()
@@ -703,19 +949,23 @@ def export():
# 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.
+ # 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)
+ 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
+ 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)
@@ -723,9 +973,13 @@ def export():
bone.head = head
bone.tail = tail
- for child in b.getChildren(): treat_bone(child, bone)
+ for child in b.getChildren():
+ treat_bone(child, bone)
- for b in data.getBones(): treat_bone(b)
+ 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)
# Only one armature / skeleton
break
@@ -733,252 +987,547 @@ def export():
# Export Mesh data
- meshes = []
-
- for obj in Blender.Object.Get():
- data = obj.getData()
- if (type(data) is Blender.Types.NMeshType) and data.faces:
- mesh = Mesh(obj.name)
- meshes.append(mesh)
-
- matrix = obj.getMatrix()
- if BASE_MATRIX: matrix = matrix_multiply(BASE_MATRIX, matrix)
+ if EXPORT_MESH or EXPORT_MATERIAL:
+
+ STATUS = "Calculate mesh and materials"
+ Draw()
+
+ meshes = []
- faces = data.faces
- while faces:
- image = faces[0].image
- image_filename = image and image.filename
- material = MATERIALS.get(image_filename) or Material(image_filename)
-
- # 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)
+ 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)
- 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)
- if not influences: print "Warning ! A vertex has no influence !"
-
- # sum of influences is not always 1.0 in Blender ?!?!
- sum = 0.0
- for bone_name, weight in influences: sum += weight
-
- for bone_name, weight in influences:
- vertex.influences.append(Influence(BONES[bone_name], weight / sum))
-
- elif not face.smooth:
- # We cannot share vertex for non-smooth faces, since Cal3D does not
- # support vertex sharing for 2 vertices with different normals.
- # => we must clone the vertex.
-
- old_vertex = vertex
- vertex = Vertex(submesh, vertex.loc, normal)
- vertex.cloned_from = old_vertex
- vertex.influences = old_vertex.influences
- old_vertex.clones.append(vertex)
+ # TODO add material color support here
+
+ submesh = SubMesh(mesh, material)
+ vertices = {}
+ for face in faces[:]:
+ if (face.image and face.image.filename) == image_filename:
+ faces.remove(face)
- if data.hasFaceUV():
- uv = [face.uv[i][0], 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.
+ 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 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...
+ 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.
+
old_vertex = vertex
- vertex = Vertex(submesh, vertex.loc, vertex.normal)
+ vertex = Vertex(submesh, vertex.loc, 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()
-
+ 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
- ANIMATIONS = {}
-
- for ipo in Blender.Ipo.Get():
- name = ipo.getName()
-
- # Try to extract the animation name and the bone name from the IPO name.
- # THIS MAY NOT WORK !!!
- # The animation name extracted here is usually NOT the name of the action in Blender
-
- splitted = name.split(".")
- if len(splitted) == 2:
- animation_name, bone_name = splitted
- animation_name += ".000"
- elif len(splitted) == 3:
- animation_name, a, b = splitted
- if a[0] in string.digits:
- animation_name += "." + a
- bone_name = b
- elif b[0] in string.digits:
- animation_name += "." + b
- bone_name = a
- else:
- print "Un-analysable IPO name :", name
- continue
- else:
- print "Un-analysable IPO name :", name
- continue
-
- animation = ANIMATIONS.get(animation_name)
- if not animation:
- animation = ANIMATIONS[animation_name] = Animation(animation_name)
-
- bone = BONES[bone_name]
- track = animation.tracks.get(bone_name)
- if not track:
- track = animation.tracks[bone_name] = Track(animation, bone)
- track.finished = 0
-
- nb_curve = ipo.getNcurves()
- has_loc = nb_curve in (3, 7)
- has_rot = nb_curve in (4, 7)
-
- # TODO support size here
- # Cal3D does not support it yet.
-
- try: nb_bez_pts = ipo.getNBezPoints(0)
- except TypeError:
- print "No key frame for animation %s, bone %s, skipping..." % (animation_name, bone_name)
- nb_bez_pts = 0
+ if EXPORT_ANIMATION:
+
+ ipoCurveType = ['LocX', 'LocY', 'LocZ', 'QuatX', 'QuatY', 'QuatZ', 'QuatW']
- for bez in range(nb_bez_pts): # WARNING ! May not work if not loc !!!
- time = ipo.getCurveBeztriple(0, bez)[3]
- scene.currentFrame(int(time))
+ STATUS = "Calculate animations"
+ Draw()
- # Needed to update IPO's value, but probably not the best way for that...
- scene.makeCurrent()
+ ANIMATIONS = {}
+
+ actions = Blender.Armature.NLA.GetActions()
- # Convert time units from Blender's frame (starting at 1) to second
- # (using default FPS of 25)
- time = (time - 1.0) / 25.0
+ for a in actions:
- if animation.duration < time: animation.duration = time
-
- loc = bone.loc
- rot = bone.rot
-
- curves = ipo.getCurves()
- print curves
- curve_id = 0
- while curve_id < len(curves):
- curve_name = curves[curve_id].getName()
- if curve_name == "LocX":
- # Get the translation
- # We need to blend the translation from the bone rest state (=bone.loc) with
- # the translation due to IPO.
- trans = vector_by_matrix((
- ipo.getCurveCurval(curve_id),
- ipo.getCurveCurval(curve_id + 1),
- ipo.getCurveCurval(curve_id + 2),
- ), bone.matrix)
- loc = [
- bone.loc[0] + trans[0],
- bone.loc[1] + trans[1],
- bone.loc[2] + trans[2],
- ]
- curve_id += 3
-
- elif curve_name == "RotX":
- # Get the rotation of the IPO
- ipo_rot = [
- ipo.getCurveCurval(curve_id),
- ipo.getCurveCurval(curve_id + 1),
- ipo.getCurveCurval(curve_id + 2),
- ipo.getCurveCurval(curve_id + 3),
- ]
- curve_id += 3 # XXX Strange !!!
- # We need to blend the rotation from the bone rest state (=bone.rot) with
- # ipo_rot.
- rot = quaternion_multiply(ipo_rot, bone.rot)
-
+ #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:
- print "Unknown IPO curve : %s" % curve_name
- break #Unknown curves
+ 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
- KeyFrame(track, time, loc, rot)
+ 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)
+
+ KeyFrame(track, time, loc, rot)
-
+
# Save all data
+
+ 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 not os.path.exists(SAVE_TO_DIR): os.makedirs(SAVE_TO_DIR)
+ if not os.path.exists(SAVE_TO_DIR):
+ os.makedirs(SAVE_TO_DIR)
else:
- 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"):
- os.unlink(os.path.join(SAVE_TO_DIR, file))
+ 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))
- cfg = open(os.path.join(SAVE_TO_DIR, os.path.basename(SAVE_TO_DIR) + ".cfg"), "wb")
- print >> cfg, "# Cal3D model exported from Blender with blender2cal3d.py"
- print >> cfg
+ 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"
- open(os.path.join(SAVE_TO_DIR, os.path.basename(SAVE_TO_DIR) + ".csf"), "wb").write(skeleton.to_cal3d())
- print >> cfg, "skeleton=%s.csf" % os.path.basename(SAVE_TO_DIR)
- 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))
+ 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"
- for animation in ANIMATIONS.values():
- if animation.duration: # Cal3D does not support animation with only one state
- animation.name = RENAME_ANIMATIONS.get(animation.name) or animation.name
- open(os.path.join(SAVE_TO_DIR, animation.name + ".caf"), "wb").write(animation.to_cal3d())
- print >> cfg, "animation=%s.caf" % animation.name
-
- # Prints animation names and durations, in order to help identifying animation
- # (since their name are lost).
- print animation.name, "duration", animation.duration * 25.0 + 1.0
-
- 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
- for mesh in meshes:
- if not mesh.name.startswith("_"):
- open(os.path.join(SAVE_TO_DIR, mesh.name + ".cmf"), "wb").write(mesh.to_cal3d())
- print >> cfg, "mesh=%s.cmf" % mesh.name
- print >> cfg
-
- materials = MATERIALS.values()
- materials.sort(lambda a, b: cmp(a.id, b.id))
- for material in materials:
- if material.maps_filenames: filename = os.path.splitext(os.path.basename(material.maps_filenames[0]))[0]
- else: filename = "plain"
- open(os.path.join(SAVE_TO_DIR, filename + ".crf"), "wb").write(material.to_cal3d())
- print >> cfg, "material=%s.crf" % filename
- print >> cfg
+ 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"
+ 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."
-export()
+ 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"
+
+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")
+
+
+
+def event(evt, val):
+ global STATUS
+
+ if (evt == QKEY or evt == ESCKEY):
+ 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
+
+ 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()
+ 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)
diff --git a/release/scripts/disp_paint.py b/release/scripts/disp_paint.py
index de852c2cbe2..934ab6f9c54 100644
--- a/release/scripts/disp_paint.py
+++ b/release/scripts/disp_paint.py
@@ -14,10 +14,10 @@ Tip: 'Use vertex paint color value to modify shape displacing vertices along nor
# Terrain Noise added suugered by Jimmy Haze
#----------------------------------------------
# Page officielle :
-# http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_displacementpainting.htm
+# http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_displacementpainting.htm
# Communiquer les problemes et erreurs sur:
-# http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender
-#---------------------------------------------
+# http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender
+# --------------------------------------------------------------------------
# ce script est proposé sous licence GPL pour etre associe
# a la distribution de Blender 2.33
# --------------------------------------------------------------------------
@@ -45,7 +45,7 @@ Tip: 'Use vertex paint color value to modify shape displacing vertices along nor
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
# 09/07/04 : Noise functions based on a piece of script by Jimmy Haze.
-#----------------------------------------------
+# --------------------------------------------------------------------------
import Blender
from Blender import *
from Blender.Draw import *
@@ -74,31 +74,6 @@ NEWName=''
ERROR=0
TextERROR=''
-E_EXIT = 1
-E_MODE = 2
-E_ORIENT = 3
-E_NSIZE = 4
-E_REPEAT = 5
-E_ACTION = 16
-E_CREATE = 17
-E_DOCMAT = 24
-E_MATVAL = [32,33,34,35,36,37,38,39,40,41,42,43,44]
-E_AXESEL = 45
-E_AXESELX = 46
-E_AXESELY = 47
-E_AXESELZ = 48
-
-E_NOISEME = 49
-E_NOISEH = 50
-E_NOISELAC = 51
-E_NOISEOCT = 52
-E_NOISEOFF = 53
-E_NOISEBAS = 54
-E_NOISEVAL=[E_NOISEH,E_NOISELAC,E_NOISEOCT,E_NOISEOFF,E_NOISEBAS]
-E_NOISEDIM = 55
-
-
-
def copy_transform(ozero,Obis):
Obis.setSize(ozero.getSize());
Obis.setEuler(ozero.getEuler());
@@ -106,7 +81,7 @@ def copy_transform(ozero,Obis):
return Obis
def traite_face(f):
- global vindexm, ng, NOISE, NOISEDIM
+ global vindexm, ng, NOISE
global H,lacunarity,octaves,offset,basis
if ORIENTMenu.val==1:
@@ -136,10 +111,10 @@ def traite_face(f):
for z in range(len(f.v)):
c=0.0
if vindex[f.v[z].index]!=0:
- nx=f.v[z].co[0]/NOISEDIM
- ny=f.v[z].co[1]/NOISEDIM
- nz=f.v[z].co[2]/NOISEDIM
- nn = ng * noise((nx,ny,nz),NOISE)
+ nx=f.v[z].co[0]/4
+ ny=f.v[z].co[1]/4
+ nz=f.v[z].co[2]/4
+ nn = ng + noise((nx,ny,nz),NOISE)
c=float(f.col[z].r+f.col[z].b+f.col[z].g)/maxcol*nn/vindex[f.v[z].index]
else:
c=0
@@ -151,10 +126,10 @@ def traite_face(f):
for z in range(len(f.v)):
c=0.0
if vindex[f.v[z].index]!=0:
- nx=f.v[z].co[0]/NOISEDIM
- ny=f.v[z].co[1]/NOISEDIM
- nz=f.v[z].co[2]/NOISEDIM
- nn = ng * cellNoise((nx,ny,nz))
+ nx=f.v[z].co[0]/4
+ ny=f.v[z].co[1]/4
+ nz=f.v[z].co[2]/4
+ nn = ng + cellNoise((nx,ny,nz))
c=float(f.col[z].r+f.col[z].b+f.col[z].g)/maxcol*nn/vindex[f.v[z].index]
else:
c=0
@@ -166,10 +141,10 @@ def traite_face(f):
for z in range(len(f.v)):
c=0.0
if vindex[f.v[z].index]!=0:
- nx=f.v[z].co[0]/NOISEDIM
- ny=f.v[z].co[1]/NOISEDIM
- nz=f.v[z].co[2]/NOISEDIM
- nn = ng * heteroTerrain((nx,ny,nz),H,lacunarity,octaves,offset,basis)
+ nx=f.v[z].co[0]/4
+ ny=f.v[z].co[1]/4
+ nz=f.v[z].co[2]/4
+ nn = ng + heteroTerrain((nx,ny,nz),H,lacunarity,octaves,offset,basis)
c=float(f.col[z].r+f.col[z].b+f.col[z].g)/maxcol*nn/vindex[f.v[z].index]
else:
c=0
@@ -179,60 +154,68 @@ def traite_face(f):
def paint():
- global MODEMenu, vindex,ng, mat, ORIName, NEWName
- global ERROR, TextERROR
-
+ global MODEMenu, vindex,ng, mat, ORIName, NEWName
Me=Object.GetSelected()
if Me!=[]:
if Me[0].getType()=='Mesh':
-
+
vindex=[]
ORIName=Me[0].getData().name
- me=NMesh.GetRaw(Me[0].getData().name)
-
- try:
- for m in me.verts:
- vindex.append(0)
-
- for f in me.faces:
- for v in f.v:
- if MODEMenu.val!=2:
- if MODEMenu.val==1:
- vindex[v.index]+=1
- else:
- if v.sel==1:
- vindex[v.index]+=1
- else:
- #print mat
- if f.mat in mat:
- vindex[v.index]+=1
- for f in me.faces:
- if MODEMenu.val==2:
- if f.mat in mat:
- traite_face(f)
- else:
- traite_face(f)
- Me[0].link(me)
- Me[0].makeDisplayList()
+ me1=NMesh.GetRaw(Me[0].getData().name)
+
+ try:
+ o=Object.Get('newMESH')
+ me=o.getData()
+ me=me1
+
except:
- ERROR=2
- TextERROR='No color on this Object.'
-
-def NEWMEcreation(obj):
-
- if obj.getType()=='Mesh':
- nomdelobjet="";
- objnumber=-1; namelist=[]
- OBJ=Object.Get()
-
- for ozero in OBJ:
- if ozero.getType()=='Mesh':
- namelist.append(ozero.getData().name)
-
- ozero=obj
+ o=Object.New('Mesh','newMESH')
+ sc.link(o)
+ me=me1
+
+ name='new.002'
+
+ for m in me.verts:
+ vindex.append(0)
+
+ for f in me.faces:
+ for v in f.v:
+ if MODEMenu.val!=2:
+ if MODEMenu.val==1:
+ vindex[v.index]+=1
+ else:
+ if v.sel==1:
+ vindex[v.index]+=1
+ else:
+ #print mat
+ if f.mat in mat:
+ vindex[v.index]+=1
+ for f in me.faces:
+ if MODEMenu.val==2:
+ if f.mat in mat:
+ traite_face(f)
+ else:
+ traite_face(f)
+
+
+ Me[0].link(me)
+ #o=copy_transform(Me[0],o)
+
+def NEWMEcreation(name):
+ nomdelobjet=""; objnumber=-1; namelist=[]
+ obj=Object.Get()
+
+ for ozero in obj:
+ if ozero.getType()=='Mesh':
+ namelist.append(ozero.getData().name)
+ if ozero.getData().name==name:
+ objnumber=obj.index(ozero)
+
+ if objnumber!=-1:
+ ozero=obj[objnumber]
nomdelobjet=ozero.getName()
Mesh=Blender.NMesh.GetRawFromObject(nomdelobjet)
- name=obj.getData().name
+
n=0; name2=name[:];ok=0
while ok==0:
@@ -241,7 +224,6 @@ def NEWMEcreation(obj):
ok=0;name2=name[0:name.find('.')+1]+'%s'%(n+1)
else: ok=1
n+=1
-
Mesh.name=name2
Obis = Blender.NMesh.PutRaw(Mesh,name2)
copy_transform(ozero,Obis)
@@ -283,26 +265,12 @@ octaves=5.0
offset=1.0
basis=3
-NOISEDIM=4
-NOISEDIMbout=Create(NOISEDIM)
HBout=Create(H)
lacunarityBout=Create(lacunarity)
octavesBout=Create(octaves)
offsetBout=Create(offset)
basisBout=Create(basis)
-
-noiseTYPE={0:'BLENDER',
- 1:'STDPERLIN',
- 2:'STDPERLIN',
- 3:'NEWPERLIN',
- 4:'VORONOI_F1',
- 5:'VORONOI_F2',
- 6:'VORONOI_F3',
- 7:'VORONOI_F2F1',
- 8:'VORONOI_CRACKLE',
- 9:'CELLNOISE'}
-
TMATList= [0,[],[]]
for t in range(16):
@@ -323,9 +291,8 @@ glRct=glRectf
def draw():
global MODEMenu, NSIZE, TDOCMat,TMATList, TAXEList
global mat, ORIName, NEWName, ORIENTMenu
- global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu, NOISEDIMbout,NOISEDIM
+ global NRepeat, ERROR, TextERROR , NOISE, NOISEMenu
global HBout,lacunarityBout,octavesBout,offsetBout,basisBout
- global noiseTYPE
size=Buffer(GL_FLOAT, 4)
glGetFloatv(GL_SCISSOR_BOX, size)
@@ -340,7 +307,7 @@ def draw():
glColor3f(1.0,1.0,1.0)
glRasterPos2f(20, size[3]-15)
- Text("Script Python de displacement paintingt")
+ Text("Script Python de displacement painting")
glRasterPos2f(20, size[3]-28)
Text("Jean-michel Soler, juillet 2004")
@@ -349,20 +316,20 @@ def draw():
n0=70
n1=55
- Button("Create" ,E_CREATE ,5 ,size[3]-n0+16 ,60 ,20)
- Button("Action" ,E_ACTION ,5 ,size[3]-n0-4 ,60 ,20)
- Button("Exit" ,E_EXIT ,5 ,size[3]-n0-24 ,60 ,20)
+ Button("Create" ,17 ,5 ,size[3]-n0+16 ,60 ,20)
+ Button("Action" ,16 ,5 ,size[3]-n0-4 ,60 ,20)
+ Button("Exit" ,1 ,5 ,size[3]-n0-24 ,60 ,20)
- NRepeat=Number("repeat" ,E_REPEAT ,5 ,size[3]-n0-50 ,75 ,20, NRepeat.val,1,10)
+ NRepeat=Number("repeat" ,5 ,5 ,size[3]-n0-50 ,75 ,20, NRepeat.val,1,10)
glColor3f(0.0,0.0,0.0)
glRasterPos2f(80 ,size[3]-n0+24)
Text("MODE")
- MODEMenu= Menu(MOname, E_MODE ,80 ,size[3]-n0 ,100,20, MODEMenu.val, "MODE menu.")
+ MODEMenu= Menu(MOname, 2 ,80 ,size[3]-n0 ,100,20, MODEMenu.val, "MODE menu.")
if MODEMenu.val==2:
- TDOCMat=Toggle("Doc Mat" ,E_DOCMAT ,180 ,size[3]-n0 ,60 ,20,TDOCMat.val)
+ TDOCMat=Toggle("Doc Mat" ,24 ,180 ,size[3]-n0 ,60 ,20,TDOCMat.val)
if TDOCMat.val==1:
#print TMATList
for t in range(TMATList[0]):
@@ -374,10 +341,9 @@ def draw():
80+t*40+40,
size[3]-n0-60+40)
TMATList[2][t]=Toggle("%s"%t , 32+t ,80+t*40+5 ,size[3]-n0-50 ,30 , 20,TMATList[2][t].val)
-
glColor3f(1.0,0.3,0.0)
glRasterPos2f(80+40+5 ,size[3]-n0-80)
- if ERROR>1:
+ if ERROR==1:
Text('Last error : '+TextERROR)
else:
Text('Last error : ')
@@ -385,31 +351,28 @@ def draw():
glColor3f(0.0,0.0,0.0)
glRasterPos2f(240 ,size[3]-n0+24)
Text("ORIENTATION")
- ORIENTMenu= Menu(ORname, E_ORIENT ,240 ,size[3]-n0 ,100,20, ORIENTMenu.val, "ORIENT menu.")
+ ORIENTMenu= Menu(ORname, 3 ,240 ,size[3]-n0 ,100,20, ORIENTMenu.val, "ORIENT menu.")
if ORIENTMenu.val==2 :
for t in range(3):
TAXEList[1][t]=Toggle("%s"%TAXEList[0][t],
- E_AXESEL+t,
+ 40+t,
240+100+t*30 , size[3]-n0 ,30 , 20,
TAXEList[1][t].val)
if ORIENTMenu.val==3 :
- glRasterPos2f(240 ,size[3]-n0-90-4)
+ glRasterPos2f(20 ,size[3]-n0-90+24)
Text("NOISE")
- NOISEMenu= Menu(NOname, E_NOISEME , 240 ,size[3]-n0-118 ,110,20, NOISEMenu.val, "NOISE menu.")
- NOISEDIMbout=Number(" Dim: " ,E_NOISEDIM , 240 ,size[3]-n0-138 ,110,20, NOISEDIMbout.val, 1,100)
-
+ NOISEMenu= Menu(NOname, 45 , 240 ,size[3]-n0-110 ,110,20, NOISEMenu.val, "NOISE menu.")
if NOISEMenu.val==11:
- basisBout=Slider(noiseTYPE[basisBout.val],
- E_NOISEBAS ,40 ,size[3]-n0-118 ,175,20, basisBout.val, 0,9,)
- HBout= Slider("H", E_NOISEH ,40 ,size[3]-n0-138 ,175,20, HBout.val, -2.0,+2.0,0,)
- lacunarityBout=Slider("lacunarity", E_NOISELAC ,40 ,size[3]-n0-158 ,175,20, lacunarityBout.val, -4.0,+4.0,0,)
- octavesBout=Slider("octave", E_NOISEOCT ,40 ,size[3]-n0-178 ,175,20, octavesBout.val, -10.0,+10.0,0,)
- offsetBout=Slider("offset", E_NOISEOFF ,40 ,size[3]-n0-198 ,175,20, offsetBout.val, -5.0,+5.0,0,)
+ HBout= Slider("H", 46 ,110 ,size[3]-n0-190 ,125,20, HBout.val, -2.0,+2.0,0,)
+ lacunarityBout=Slider("lacunarity",47 ,110 ,size[3]-n0-110 ,125,20, lacunarityBout.val, -4.0,+4.0,0,)
+ octavesBout=Slider("octave", 48 ,110 ,size[3]-n0-130 ,125,20, octavesBout.val, -10.0,+10.0,0,)
+ offsetBout=Slider("offset", 49 ,110 ,size[3]-n0-150 ,125,20, offsetBout.val, -5.0,+5.0,0,)
+ basisBout=Slider("noise", 50 ,110 ,size[3]-n0-170 ,125,20, basisBout.val, 0,9,0,)
- NSIZE= Slider("Disp Size", E_NSIZE ,80 ,size[3]-n0-20 ,260,20, NSIZE.val, -4.0,+4.0,0,"SIZE.")
+ NSIZE= Slider("Disp Size", 4 ,80 ,size[3]-n0-20 ,260,20, NSIZE.val, -4.0,+4.0,0,"SIZE.")
@@ -420,22 +383,22 @@ def event(evt, val):
def bevent(evt):
global MODEMenu, NSIZE, ng, TMATList
global mat, ORIENTMenu, NRepeat, TAXEList
- global ERROR,TextERROR, NOISE, NOISEMenu, NOISEDIMbout,NOISEDIM
+ global ERROR,TextERROR, NOISE, NOISEMenu
global HBout,lacunarityBout,octavesBout,offsetBout,basisBout
global H,lacunarity,octaves,offset,basis
- if (evt== E_EXIT):
+ if (evt== 1):
Exit()
- elif (evt== E_ACTION):
+ elif (evt== 16):
for n in range(NRepeat.val):
paint()
- elif (evt== E_NSIZE):
+ elif (evt== 4):
ng=NSIZE.val
- elif (evt== E_DOCMAT) or (evt in E_MATVAL):
+ elif (evt== 24) or (evt in [32,33,34,35,36,37,38,39,40,41,42,43,44]):
Me=Object.GetSelected()
if Me!=[]:
if Me[0].getType()=='Mesh':
@@ -451,29 +414,20 @@ def bevent(evt):
else:
ERROR=1
TextERROR='No Selected Object.'
-
+
+ elif (evt== 17):
+ NEWMEcreation('new.002')
- elif (evt== E_CREATE):
-
- NEWMEcreation(Blender.Object.GetSelected()[0])
- Blender.Draw.Redraw()
-
- ERROR=1
- TextERROR='No Selected Object.'
-
- elif (evt== E_NOISEME):
+ elif (evt== 45):
NOISE=NOISEMenu.val-1
- elif (evt in E_NOISEVAL):
+ elif (evt in [46,47,48,49, 50]):
H=HBout.val
lacunarity=lacunarityBout.val
octaves=octavesBout.val
offset=offsetBout.val
basis=basisBout.val
- elif (evt== E_NOISEDIM):
- NOISEDIM=NOISEDIMbout.val
-
- Blender.Draw.Redraw()
+ Blender.Redraw()
Register(draw, event, bevent)
diff --git a/release/scripts/fixfromarmature.py b/release/scripts/fixfromarmature.py
index 24757f7fa20..5aad231b456 100644
--- a/release/scripts/fixfromarmature.py
+++ b/release/scripts/fixfromarmature.py
@@ -3,9 +3,10 @@
""" Registration info for Blender menus: <- these words are ignored
Name: 'Fix from Armature'
Blender: 232
-Group: 'Generators'
+Group: 'Mesh'
Tip: 'Fix armature deformation.'
"""
+
# $Id$
#
#----------------------------------------------
@@ -35,4 +36,4 @@ try:
scene = Blender.Scene.getCurrent()
scene.link (Obis)
except:
- print "not a mesh or no object selected"
+ Blender.Draw.PupMenu("Error|Not a mesh or no object selected")
diff --git a/release/scripts/hotkeys.py b/release/scripts/hotkeys.py
index b89de987349..94be4e234af 100644
--- a/release/scripts/hotkeys.py
+++ b/release/scripts/hotkeys.py
@@ -417,7 +417,7 @@ def draw():
def event(evt, val):
global hotkeys
- if (evt== QKEY and not val): Exit()
+ if ((evt== QKEY or evt== ESCKEY) and not val): Exit()
def bevent(evt):
global hotkeysmhot, hotL
diff --git a/release/scripts/mod_blender.py b/release/scripts/mod_blender.py
index fd1e376182c..a4e4419381d 100644
--- a/release/scripts/mod_blender.py
+++ b/release/scripts/mod_blender.py
@@ -1,14 +1,12 @@
# $Id$
#
# --------------------------------------------------------------------------
-# mod_blender.py version 0.1 Jun 09, 2004
+# mod_blender.py version 0.2 Jul 26, 2004
# --------------------------------------------------------------------------
# helper functions to be used by other scripts
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
-# Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br
-#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
@@ -26,14 +24,14 @@
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
-# Basic set of modules Blender should have in all supported platforms:
-# XXX still incomplete
+# Basic set of modules Blender should have in all supported platforms.
+# The second and third lines are the contents of the Python23.zip file
+# included with Windows Blender binaries along with zlib.pyd.
+# Other platforms are assumed to have Python installed.
basic_modules = [
'Blender',
-'sys',
-'os',
-'math',
-'chunk',
-'gzip',
-'string'
+'chunk','colorsys','copy','copy_reg','gzip','os','random','repr','stat',
+'string','StringIO','types','UserDict','webbrowser','whrandom',
+'zlib',
+'math'
]
diff --git a/release/scripts/sel_same.py b/release/scripts/sel_same.py
new file mode 100644
index 00000000000..38c216116ba
--- /dev/null
+++ b/release/scripts/sel_same.py
@@ -0,0 +1,368 @@
+#!BPY
+
+"""
+Name: 'Select Same Faces'
+Blender: 232
+Group: 'UV'
+Tooltip: 'Select faces if attributes match the active.'
+"""
+
+# $Id$
+#
+#===============================================#
+# Sel Same script 1.0 by Campbell Barton #
+# email me ideasman@linuxmail.org #
+#===============================================#
+
+
+# --------------------------------------------------------------------------
+# Sel Same Face 1.0 By Campbell Barton (AKA Ideasman)
+# --------------------------------------------------------------------------
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+
+from Blender import *
+from Blender.Mathutils import DotVecs, Vector
+from math import sqrt
+
+
+#====================================#
+# Sanity checks #
+#====================================#
+def error(str):
+ Draw.PupMenu('ERROR%t|'+str)
+af = None
+selection = Object.GetSelected()
+if len(selection) == 0:
+ error('No object selected')
+else:
+ object = Object.GetSelected()[0]
+ if object.getType() != 'Mesh':
+ error('Not a mesh')
+ else:
+ mesh = object.getData()
+
+ # We have a mesh so find AF.
+ for f in mesh.faces:
+ if f.flag & NMesh.FaceFlags['ACTIVE']:
+ af = f
+
+if af == None:
+ error('no active face')
+
+else: # Okay everything seems sane
+
+ #=====================================
+ # Popup menu to select the functions #
+ #====================================#
+ method = Draw.PupMenu(\
+ 'Select Same as Active%t|\
+ Material|\
+ UV Image|\
+ Face Mode|\
+ Vertex Colours|\
+ UV CO-Ords|\
+ Area|\
+ Proportions|\
+ Normal|\
+ Co-Planer|')
+
+ if method != -1:
+ #================================================#
+ # Do we add, seb or set to the existing face sel #
+ #================================================#
+ faceOp = Draw.PupMenu(\
+ 'Active Face Match%t|\
+ Add to Selection|\
+ Subtract From Selection |\
+ Overwrite Selection|\
+ Overwrite Selection Inverse|')
+
+ if faceOp != -1:
+
+ def setFSel(f):
+ if faceOp == 1 or faceOp == 3:
+ f.flag |= NMesh.FaceFlags['SELECT'] # will set selection
+ elif faceOp == 2 or faceOp ==4:
+ f.flag &=~NMesh.FaceFlags['SELECT'] # will unselect, note the '~' to invert bitflags
+
+ def setFUnSel(f):
+ if faceOp == 3:
+ f.flag &=~NMesh.FaceFlags['SELECT'] # will unselect, note the '~' to invert bitflags
+ elif faceOp == 4:
+ f.flag |= NMesh.FaceFlags['SELECT'] # will set selection
+
+
+
+ #================#
+ # Math functions #
+ #================#
+ def compare(f1, f2, limit):
+ if f1 + limit > f2 and f1 - limit < f2:
+ return 1
+ return 0
+
+ def compare2(v1, v2, limit):
+ if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
+ if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
+ return 1
+ return 0
+
+ def compare3(v1, v2, limit):
+ if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
+ if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
+ if v1[2] + limit > v2[2] and v1[2] - limit < v2[2]:
+ return 1
+ return 0
+
+ def colCompare(v1, v2, limit):
+ # Simple test first
+ if v1.r == v2.r:
+ if v1.g == v2.g:
+ if v1.b == v2.b:
+ return 1
+ # Now a test that uses the limit.
+ limit = int(limit * 255)
+ if v1.r + limit >= v2.r and v1.r - limit <= v2.r:
+ if v1.g + limit >= v2.g and v1.g - limit <= v2.g:
+ if v1.b + limit >= v2.b and v1.b - limit <= v2.b:
+ return 1
+ return 0
+
+ # Makes sure face 2 has all the colours of face 1
+ def faceColCompare(f1, f2, limit):
+ avcolIdx = 0
+ while avcolIdx < len(f1.col):
+ match = 0
+
+ vcolIdx = 0
+ while vcolIdx < len(f2.col):
+ if colCompare(f1.col[avcolIdx], f2.col[vcolIdx], limit):
+ match = 1
+ break
+ vcolIdx += 1
+ if match == 0: # premature exit if a motch not found
+ return 0
+ avcolIdx += 1
+ return 1
+
+
+
+ # Makes sure face 2 has matching UVs within the limit.
+ def faceUvCompare(f1, f2, limit):
+ for auv in f1.uv:
+ match = 0
+ for uv in f2.uv:
+ if compare2(auv, uv, limit):
+ match = 1
+ break
+ if match == 0: # premature exit if a motch not found
+ return 0
+ return 1
+
+
+ def measure(v1, v2):
+ return Mathutils.Vector([v1[0]-v2[0], v1[1] - v2[1], v1[2] - v2[2]]).length
+
+ def triArea2D(v1, v2, v3):
+ e1 = measure(v1, v2)
+ e2 = measure(v2, v3)
+ e3 = measure(v3, v1)
+ p = e1+e2+e3
+ return 0.25 * sqrt(p*(p-2*e1)*(p-2*e2)*(p-2*e3))
+ #====================#
+ # End Math Functions #
+ #====================#
+
+
+
+ #=============================#
+ # Blender functions/shortcuts #
+ #=============================#
+ def getLimit(text):
+ return Draw.PupFloatInput(text, 0.1, 0.0, 1.0, 0.1, 3)
+
+ def faceArea(f):
+ if len(f.v) == 4:
+ return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co) + triArea2D(f.v[0].co, f.v[2].co, f.v[3].co)
+ elif len(f.v) == 3:
+ return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co)
+
+ def getEdgeLengths(f):
+ if len(f.v) == 4:
+ return (measure(f.v[0].co, f.v[1].co), measure(f.v[1].co, f.v[2].co), measure(f.v[2].co, f.v[3].co) , measure(f.v[3].co, f.v[0].co) )
+ elif len(f.v) == 3:
+ return (measure(f.v[0].co, f.v[1].co), measure(f.v[1].co, f.v[2].co), measure(f.v[2].co, f.v[0].co) )
+
+
+ def faceCent(f):
+ x = y = z = 0
+ for v in f.v:
+ x += v.co[0]
+ y += v.co[1]
+ z += v.co[2]
+ x = x/len(f.v)
+ y = y/len(f.v)
+ z = z/len(f.v)
+ return Vector([x, y, z])
+
+ #========================================#
+ # Should we bother computing this faces #
+ #========================================#
+ def fShouldCompare(f):
+ # Only calculate for faces that will be affected.
+ if faceOp == 1 and f.flag == 1:
+ return 0
+ elif faceOp == 0 and f.flag == 0:
+ return 0
+ elif f.flag == 64: # Ignore hidden
+ return 0
+ return 1
+
+ #=======================================#
+ # Sel same funcs as called by the menus #
+ #=======================================#
+ def get_same_mat():
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if af.mat == f.mat: setFSel(f)
+ else: setFUnSel(f)
+
+ def get_same_image():
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if af.image == f.image: setFSel(f)
+ else: setFUnSel(f)
+
+ def get_same_mode():
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if af.mode == f.mode: setFSel(f)
+ else: setFUnSel(f)
+
+ def get_same_vcol(limit):
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if faceColCompare(f, af, limit) and faceColCompare(af, f, limit) :
+ setFSel(f)
+ else:
+ setFUnSel(f)
+
+ def get_same_uvco(limit):
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if faceUvCompare(af, f, limit): setFSel(f)
+ else: setFUnSel(f)
+
+ def get_same_area(limit):
+ afArea = faceArea(af)
+ limit = limit * afArea # Make the lomot proportinal to the
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if compare(afArea, faceArea(f), limit): setFSel(f)
+ else: setFUnSel(f)
+
+
+ def get_same_prop(limit):
+
+ # Here we get the perimeter and use it for a proportional limit modifier.
+ afEdgeLens = getEdgeLengths(af)
+ perim = 0
+ for e in afEdgeLens:
+ perim += e
+
+ limit = limit * perim
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ for ae in afEdgeLens:
+ match = 0
+ for e in getEdgeLengths(f):
+ if compare(ae, e, limit):
+ match = 1
+ break
+ if not match:
+ break
+
+ if match: setFSel(f)
+ else: setFUnSel(f)
+
+ def get_same_normal(limit):
+ limit = limit * 2
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ if compare3(af.no, f.no, limit): setFSel(f)
+ else: setFUnSel(f)
+
+ def get_same_coplaner(limit):
+ nlimit = limit * 2 # * 1 # limit for normal test
+ climit = limit * 3 # limit for coplaner test.
+ afCent = faceCent(af)
+ for f in mesh.faces:
+ if fShouldCompare(f):
+ match = 0
+ if compare3(af.no, f.no, nlimit):
+ fCent = faceCent(f)
+ if abs(DotVecs(Vector([af.no[0], af.no[1], af.no[2]]), afCent ) - DotVecs(Vector([af.no[0], af.no[1], af.no[2]]), fCent )) <= climit:
+ match = 1
+ if match:
+ setFSel(f)
+ else:
+ setFUnSel(f)
+ #=====================#
+ # End Sel same funcs #
+ #=====================#
+
+ limit = 1 # some of these dont use the limit so it needs to be set, to somthing.
+ # act on the menu item selected
+ if method == 1: # Material
+ get_same_mat()
+ elif method == 2: # UV Image
+ get_same_image()
+ elif method == 3: # mode
+ get_same_mode()
+ elif method == 4: # vertex colours
+ limit = getLimit('vert col limit: ')
+ if limit != None:
+ get_same_vcol(limit)
+ elif method == 5: # UV-coords
+ limit = getLimit('uv-coord limit: ')
+ if limit != None:
+ get_same_uvco(limit)
+ elif method == 6: # area
+ limit = getLimit('area limit: ')
+ if limit != None:
+ get_same_area(limit)
+ elif method == 7: # proportions
+ limit = getLimit('proportion limit: ')
+ if limit != None:
+ get_same_prop(limit)
+ elif method == 8: # normal
+ limit = getLimit('normal limit: ')
+ if limit != None:
+ get_same_normal(limit)
+ elif method == 9: # coplaner
+ limit = getLimit('coplaner limit: ')
+ if limit != None:
+ get_same_coplaner(limit)
+
+ # If limit is not set then dont bother
+ if limit != None:
+ mesh.update()
diff --git a/release/scripts/sysinfo.py b/release/scripts/sysinfo.py
index 6e05d5b2d35..b40d42eb771 100644
--- a/release/scripts/sysinfo.py
+++ b/release/scripts/sysinfo.py
@@ -1,7 +1,7 @@
#!BPY
"""
Name: 'System Information...'
-Blender: 233
+Blender: 234
Group: 'Help'
Tooltip: 'Information about your Blender environment, useful to diagnose problems.'
"""
@@ -36,6 +36,7 @@ import Blender
from Blender.BGL import *
import sys
+Blender.Window.WaitCursor(1)
# has_textwrap = 1 # see commented code below
output_filename = "system-info.txt"
warnings = 0
@@ -144,5 +145,6 @@ if (warnings):
output.write(", documented in the text above.")
else: output.write("\n==\nNo problems were found.")
-exitmsg = "Done!|Please check the text %s in the Text Editor window." % output.name
+Blender.Window.WaitCursor(0)
+exitmsg = "Done!|Please check the text %s in the Text Editor window" % output.name
Blender.Draw.PupMenu(exitmsg)