diff options
author | Willian Padovani Germano <wpgermano@gmail.com> | 2004-07-27 07:13:45 +0400 |
---|---|---|
committer | Willian Padovani Germano <wpgermano@gmail.com> | 2004-07-27 07:13:45 +0400 |
commit | 7b943f2a6d8ddcb58580896b3526c14c5f776b59 (patch) | |
tree | 6d8c9977ec932f16922f172112bbabf47a476a1f /release | |
parent | d0da285547071aa51291ef3a07ec41d1fd4b0ac1 (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.py | 330 | ||||
-rw-r--r-- | release/scripts/DirectXExporter.py | 2 | ||||
-rw-r--r-- | release/scripts/ac3d_import.py | 24 | ||||
-rw-r--r-- | release/scripts/blender2cal3d.py | 1253 | ||||
-rw-r--r-- | release/scripts/disp_paint.py | 248 | ||||
-rw-r--r-- | release/scripts/fixfromarmature.py | 5 | ||||
-rw-r--r-- | release/scripts/hotkeys.py | 2 | ||||
-rw-r--r-- | release/scripts/mod_blender.py | 20 | ||||
-rw-r--r-- | release/scripts/sel_same.py | 368 | ||||
-rw-r--r-- | release/scripts/sysinfo.py | 6 |
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) |