diff options
author | Jean-Luc Peurière <jlp@nerim.net> | 2008-03-14 02:54:02 +0300 |
---|---|---|
committer | Jean-Luc Peurière <jlp@nerim.net> | 2008-03-14 02:54:02 +0300 |
commit | a68a7f42b02ad0260220d94f36ab61c772db071a (patch) | |
tree | 620b763cd24fbfc1259f00ccb005217914af0c6f /release | |
parent | 526d0bec4722900a86d81ff0f5d71504b2b4ad8c (diff) | |
parent | 15215493bf9d1d08e650109e6eb6189fc76e289e (diff) |
resolved conflict state with HEAD r14096
blenderbuttons still bad
not let this compile
Diffstat (limited to 'release')
66 files changed, 17449 insertions, 4653 deletions
diff --git a/release/Makefile b/release/Makefile index 2791982a6fa..fa288314f03 100644 --- a/release/Makefile +++ b/release/Makefile @@ -31,54 +31,63 @@ include nan_definitions.mk -export VERSION := $(shell cat VERSION) +export VERSION := $(shell ./getversion.py) -BLENDNAME=blender-$(VERSION)-$(CONFIG_GUESS)$(TYPE) +BLENDNAME=blender-$(VERSION)-$(CONFIG_GUESS)-py$(NAN_PYTHON_VERSION)$(TYPE) export DISTDIR=$(NAN_OBJDIR)/$(BLENDNAME) export CONFDIR=$(DISTDIR)/.blender +ifeq ($(OS),beos) + TAR="zip" + TARFLAGS="-ry9" + EXT0="" + EXT1=".zip" + COMPRESS="" + EXT2="" + NOPLUGINS?=true +endif + +ifeq ($(OS),$(findstring $(OS), "freebsd irix linux openbsd solaris")) + TAR="tar" + TARFLAGS="cf" + EXT0="" + EXT1=".tar" + COMPRESS="bzip2" + COMPRESSFLAGS="-f" + EXT2=".bz2" + ifeq ($(OS), solaris) + ifeq ($(CPU), i386) + NOPLUGINS?=true + endif + endif +endif + +ifeq ($(OS),windows) + TAR="zip" + TARFLAGS="-r9" + EXT0=".exe" + EXT1=".zip" + NOPLUGINS?=true + NOSTRIP?=true +endif + +ifeq ($(OS),darwin) + TAR="tar" + TARFLAGS="cf" + EXT0=".app" + EXT1=".tar" + COMPRESS="bzip2" + COMPRESSFLAGS="-f" + EXT2=".bz2" +endif release: all all: - ifeq ($(OS),beos) - @$(MAKE) pkg TYPE="" TAR="zip -ry9" EXT1=".zip" NOPLUGINS="true" - endif - ifeq ($(OS),freebsd) - @$(MAKE) pkg TYPE="" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - endif - ifeq ($(OS),irix) - @$(MAKE) pkg TYPE="" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - endif - ifeq ($(OS),linux) - ifeq ($(CPU),alpha) - @$(MAKE) pkg TYPE="" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - else - @$(MAKE) pkg TYPE="" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - @$(MAKE) pkg TYPE="-static" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - endif - endif - ifeq ($(OS),openbsd) - @$(MAKE) pkg TYPE="-static" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - endif - ifeq ($(OS),solaris) - @$(MAKE) pkg TYPE="" TAR="tar cf" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - endif - ifeq ($(OS),windows) - @$(MAKE) pkg TYPE="" TAR="zip -r9" EXT0=".exe" EXT1=".zip" \ - NOPLUGINS="true" NOSTRIP="true" - endif - ifeq ($(OS),darwin) - @$(MAKE) pkg TYPE="" TAR="tar cf" EXT0"=.app" EXT1=".tar" \ - COMPRESS="bzip2 -f" EXT2=".bz2" - endif + @$(MAKE) pkg TYPE="" +ifeq ($(WITH_BF_STATICOPENGL), true) + @$(MAKE) pkg TYPE="-static" +endif # OS independent targets below: @@ -112,8 +121,9 @@ install: package ifneq ($(OS), darwin) @[ ! -d $(OCGDIR)/bin/.blender ] || \ cp -r $(OCGDIR)/bin/.blender $(DISTDIR) - cp $(NANBLENDERHOME)/bin/.blender/.Blanguages $(CONFDIR) - cp $(NANBLENDERHOME)/bin/.blender/.bfont.ttf $(CONFDIR) + @rm -rf $(DISTDIR)/.svn $(DISTDIR)/*/.svn $(DISTDIR)/*/*/.svn + @cp $(NANBLENDERHOME)/bin/.blender/.Blanguages $(CONFDIR) + @cp $(NANBLENDERHOME)/bin/.blender/.bfont.ttf $(CONFDIR) endif @echo "----> Copy blender$(EXT0) executable" ifeq ($(TYPE),-static) @@ -137,7 +147,7 @@ ifneq ($(NOPLUGINS),true) @cp ../source/blender/blenpluginapi/*.h $(DISTDIR)/plugins/include/ @chmod 755 $(DISTDIR)/plugins/bmake @$(MAKE) -C $(DISTDIR)/plugins all > /dev/null || exit 1; - @rm -fr $(DISTDIR)/plugins/CVS $(DISTDIR)/plugins/*/CVS \ + @rm -fr $(DISTDIR)/plugins/.svn $(DISTDIR)/plugins/*/.svn \ $(DISTDIR)/plugins/*/*.o #on OS X the plugins move to the installation directory @@ -151,7 +161,7 @@ endif @echo "----> Copy python infrastructure" @[ ! -d scripts ] || cp -r scripts $(CONFDIR)/scripts - @[ ! -d $(CONFDIR)/scripts ] || rm -fr $(CONFDIR)/scripts/CVS + @[ ! -d $(CONFDIR)/scripts ] || rm -fr $(CONFDIR)/scripts/.svn $(CONFDIR)/scripts/*/.svn $(CONFDIR)/scripts/*/*/.svn ifeq ($(OS),darwin) @echo "----> Move .blender to .app/Contents/MacOS/" @@ -178,12 +188,11 @@ pkg: install @echo "----> Create distribution file $(BLENDNAME)$(EXT1)" @#enable the next sleep if you get 'tar file changed while reading' @#sleep 10 - @cd $(NAN_OBJDIR) && \ - rm -f $(VERSION)/$(BLENDNAME)$(EXT1)* && \ - $(TAR) $(VERSION)/$(BLENDNAME)$(EXT1) $(BLENDNAME) + rm -f $(NAN_OBJDIR)/$(VERSION)/$(BLENDNAME)$(EXT1)* + @cd $(NAN_OBJDIR) && $(TAR) $(TARFLAGS) $(VERSION)/$(BLENDNAME)$(EXT1) $(BLENDNAME) ifdef COMPRESS @echo "----> Compressing distribution to $(BLENDNAME)$(EXT1)$(EXT2)" - @$(COMPRESS) $(NAN_OBJDIR)/$(VERSION)/$(BLENDNAME)$(EXT1) + @$(COMPRESS) $(COMPRESSFLAGS) $(NAN_OBJDIR)/$(VERSION)/$(BLENDNAME)$(EXT1) endif @#echo "****> Clean up temporary distribution directory" @rm -fr $(DISTDIR) diff --git a/release/datafiles/preview.blend b/release/datafiles/preview.blend Binary files differindex 8d67a88f679..9e09d579b2b 100644 --- a/release/datafiles/preview.blend +++ b/release/datafiles/preview.blend diff --git a/release/datafiles/prvicons b/release/datafiles/prvicons Binary files differnew file mode 100644 index 00000000000..de3980f9676 --- /dev/null +++ b/release/datafiles/prvicons diff --git a/release/datafiles/splash.jpg b/release/datafiles/splash.jpg Binary files differnew file mode 100644 index 00000000000..243e779273d --- /dev/null +++ b/release/datafiles/splash.jpg diff --git a/release/getversion.py b/release/getversion.py new file mode 100755 index 00000000000..a64a6189b6f --- /dev/null +++ b/release/getversion.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# ***** 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. +# +# The Original Code is Copyright (C) 2008 by the Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +import sys, os, re + +nanblenderhome = os.getenv("NANBLENDERHOME"); + +if nanblenderhome == None: + nanblenderhome = os.path.dirname(os.path.abspath(sys.argv[0]))+"/.." + +config = nanblenderhome+"/source/blender/blenkernel/BKE_blender.h" + +infile = open(config) + +major = None +minor = None + +for line in infile.readlines(): + m = re.search("#define BLENDER_VERSION\s+(\d+)", line) + if m: + major = m.group(1) + m = re.search("#define BLENDER_SUBVERSION\s+(\d+)", line) + if m: + minor = m.group(1) + if minor and major: + major = float(major) / 100.0 + break + +infile.close() + +if minor and major: + print str(major)+"."+minor +else: + print "unknownversion" diff --git a/release/scripts/3ds_import.py b/release/scripts/3ds_import.py index 56acc50e38d..07da4df1603 100644 --- a/release/scripts/3ds_import.py +++ b/release/scripts/3ds_import.py @@ -1,7 +1,7 @@ #!BPY """ Name: '3D Studio (.3ds)...' -Blender: 241 +Blender: 244 Group: 'Import' Tooltip: 'Import from 3DS file format (.3ds)' """ @@ -382,7 +382,7 @@ def process_next_chunk(file, previous_chunk, importedObjects, IMAGE_SEARCH): # +1 because of DUMMYVERT face_mapping= bmesh.faces.extend( [ [ bmesh_verts[ myVertMapping[vindex]+1] for vindex in myContextMesh_facels[fIdx]] for fIdx in faces ], indexList=True ) - if contextMeshUV or img: + if bmesh.faces and (contextMeshUV or img): bmesh.faceUV= 1 for ii, i in enumerate(faces): @@ -945,6 +945,7 @@ if __name__=='__main__' and not DEBUG: #load_3ds('/metavr/convert/vehicle/truck_002/TruckTanker1.3DS', False) #load_3ds('/metavr/archive/convert/old/arranged_3ds_to_hpx-2/only-need-engine-trains/Engine2.3DS', False) ''' + else: # DEBUG ONLY TIME= Blender.sys.time() @@ -956,6 +957,11 @@ else: file= open('/tmp/temp3ds_list', 'r') lines= file.readlines() file.close() + # sort by filesize for faster testing + lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] + lines_size.sort() + lines = [f[1] for f in lines_size] + def between(v,a,b): if v <= max(a,b) and v >= min(a,b): @@ -963,8 +969,8 @@ else: return False for i, _3ds in enumerate(lines): - if between(i, 1,200): - _3ds= _3ds[:-1] + if between(i, 650,800): + #_3ds= _3ds[:-1] print 'Importing', _3ds, '\nNUMBER', i, 'of', len(lines) _3ds_file= _3ds.split('/')[-1].split('\\')[-1] newScn= Blender.Scene.New(_3ds_file) @@ -972,4 +978,5 @@ else: load_3ds(_3ds, False) print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) + '''
\ No newline at end of file diff --git a/release/scripts/DirectX8Importer.py b/release/scripts/DirectX8Importer.py index 7e6a8429759..0dda654944d 100644 --- a/release/scripts/DirectX8Importer.py +++ b/release/scripts/DirectX8Importer.py @@ -2,7 +2,7 @@ """ Registration info for Blender menus: Name: 'DirectX(.x)...' -Blender: 240 +Blender: 244 Group: 'Import' Tip: 'Import from DirectX text file format format.' @@ -25,7 +25,7 @@ Tip: 'Import from DirectX text file format format.' # Grab the latest version here :www.omariben.too.it import bpy import Blender -from Blender import NMesh,Object,Material,Texture,Image,Draw +from Blender import Mesh,Object,Material,Texture,Image,Draw class xImport: @@ -41,13 +41,16 @@ class xImport: lines = self.lines print "importing into Blender ..." scene = bpy.data.scenes.active - mesh = NMesh.GetRaw() + + mesh_indicies = {} # the index of each 'Mesh' is used as the key for those meshes indicies + context_indicies = None # will raise an error if used! + + #Get the line of Texture Coords nr_uv_ind = 0 #Get Materials nr_fac_mat = 0 - idx = 0 i = -1 mat_list = [] tex_list = [] @@ -56,31 +59,33 @@ class xImport: l = line.strip() words = line.split() if words[0] == "Material" : - idx += 1 - self.writeMaterials(j, idx, mat_list, tex_list) + #context_indicies["Material"] = j + self.loadMaterials(j, mat_list, tex_list) elif words[0] == "MeshTextureCoords" : - nr_uv_ind = j + context_indicies["MeshTextureCoords"] = j + #nr_uv_ind = j elif words[0] == "MeshMaterialList" : - nr_fac_mat = j + 2 + context_indicies["MeshMaterialList"] = j+2 + #nr_fac_mat = j + 2 elif words[0] == "Mesh": # Avoid a second loop - mesh_line_indicies.append(j) - - #Create The Mesh - for nr_vr_ind in mesh_line_indicies: - self.writeVertices(nr_vr_ind, mesh, nr_uv_ind, nr_fac_mat, tex_list) - - mesh.setMaterials(mat_list) - if nr_fac_mat: - self.writeMeshMaterials(nr_fac_mat, mesh) - NMesh.PutRaw(mesh,"Mesh",1) - + context_indicies = mesh_indicies[j] = {'MeshTextureCoords':0, 'MeshMaterialList':0} + + for mesh_index, value in mesh_indicies.iteritems(): + mesh = Mesh.New() + self.loadVertices(mesh_index, mesh, value['MeshTextureCoords'], value['MeshMaterialList'], tex_list) + + mesh.materials = mat_list[:16] + if value['MeshMaterialList']: + self.loadMeshMaterials(value['MeshMaterialList'], mesh) + scene.objects.new(mesh) + self.file.close() print "... finished" #------------------------------------------------------------------------------ # CREATE THE MESH #------------------------------------------------------------------------------ - def writeVertices(self, nr_vr_ind, mesh, nr_uv, nr_fac_mat, tex_list): + def loadVertices(self, nr_vr_ind, mesh, nr_uv, nr_fac_mat, tex_list): v_ind = nr_vr_ind + 1 lin = self.lines[v_ind] if lin : @@ -92,7 +97,6 @@ class xImport: lin_c = self.CleanLine(lin) nr_vert = int((lin_c.split()[0])) - vx_array = range(v_ind + 1, (v_ind + nr_vert +1)) #-------------------------------------------------- nr_fac_li = v_ind + nr_vert +1 lin_f = self.lines[nr_fac_li] @@ -105,136 +109,80 @@ class xImport: lin_fc = self.CleanLine(lin_f) nr_face = int((lin_fc.split()[0])) - fac_array = range(nr_fac_li + 1, (nr_fac_li + nr_face + 1)) #Get Coordinates - for l in vx_array: + verts_list = [(0,0,0)] # WARNING - DUMMY VERT - solves EEKADOODLE ERROR + for l in xrange(v_ind + 1, (v_ind + nr_vert +1)): line_v = self.lines[l] lin_v = self.CleanLine(line_v) words = lin_v.split() if len(words)==3: - mesh.verts.append(NMesh.Vert(float(words[0]),float(words[1]),float(words[2]))) - + verts_list.append((float(words[0]),float(words[1]),float(words[2]))) + + mesh.verts.extend(verts_list) + del verts_list + + face_list = [] #Make Faces i = 0 mesh_verts = mesh.verts - for f in fac_array: + for f in xrange(nr_fac_li + 1, (nr_fac_li + nr_face + 1)): i += 1 line_f = self.lines[f] lin_f = self.CleanLine(line_f) + + # +1 for dummy vert only! words = lin_f.split() if len(words) == 5: - f= NMesh.Face([\ - mesh_verts[int(words[1])], - mesh_verts[int(words[2])], - mesh_verts[int(words[3])], - mesh_verts[int(words[4])]]) - - mesh.faces.append(f) - if nr_uv : - uv = [] - #--------------------------------- - l1_uv = self.lines[nr_uv + 2 + int(words[1])] - fixed_l1_uv = self.CleanLine(l1_uv) - w1 = fixed_l1_uv.split() - uv_1 = (float(w1[0]), -float(w1[1])) - uv.append(uv_1) - #--------------------------------- - l2_uv = self.lines[nr_uv + 2 + int(words[2])] - fixed_l2_uv = self.CleanLine(l2_uv) - w2 = fixed_l2_uv.split() - uv_2 = (float(w2[0]), -float(w2[1])) - uv.append(uv_2) - #--------------------------------- - l3_uv = self.lines[nr_uv + 2 + int(words[3])] - fixed_l3_uv = self.CleanLine(l3_uv) - w3 = fixed_l3_uv.split() - uv_3 = (float(w3[0]), -float(w3[1])) - uv.append(uv_3) - #--------------------------------- - l4_uv = self.lines[nr_uv + 2 + int(words[4])] - fixed_l4_uv = self.CleanLine(l4_uv) - w4 = fixed_l4_uv.split() - uv_4 = (float(w4[0]), -float(w4[1])) - uv.append(uv_4) - #--------------------------------- - f.uv = uv - if nr_fac_mat : - fac_line = self.lines[nr_fac_mat + i] - fixed_fac = self.CleanLine(fac_line) - w_tex = int(fixed_fac.split()[0]) - name_tex = tex_list[w_tex] - if name_tex : - name_file = Blender.sys.join(my_path,name_tex) - try: - img = Image.Load(name_file) - f.image = img - except: - #Draw.PupMenu("No image to load") - #print "No image " + name_tex + " to load" - pass - + face_list.append((1+int(words[1]), 1+int(words[2]), 1+int(words[3]), 1+int(words[4]))) elif len(words) == 4: - f=NMesh.Face([\ - mesh_verts[int(words[1])],\ - mesh_verts[int(words[2])],\ - mesh_verts[int(words[3])]]) - - mesh.faces.append(f) - if nr_uv : - uv = [] - #--------------------------------- - l1_uv = self.lines[nr_uv + 2 + int(words[1])] - fixed_l1_uv = self.CleanLine(l1_uv) - w1 = fixed_l1_uv.split() - uv_1 = (float(w1[0]), -float(w1[1])) - uv.append(uv_1) - #--------------------------------- - l2_uv = self.lines[nr_uv + 2 + int(words[2])] - fixed_l2_uv = self.CleanLine(l2_uv) - w2 = fixed_l2_uv.split() - uv_2 = (float(w2[0]), -float(w2[1])) - uv.append(uv_2) - #--------------------------------- - l3_uv = self.lines[nr_uv + 2 + int(words[3])] - fixed_l3_uv = self.CleanLine(l3_uv) - w3 = fixed_l3_uv.split() - uv_3 = (float(w3[0]), -float(w3[1])) - uv.append(uv_3) - #--------------------------------- - f.uv = uv - if nr_fac_mat : - fac_line = self.lines[nr_fac_mat + i] - fixed_fac = self.CleanLine(fac_line) - w_tex = int(fixed_fac.split()[0]) - name_tex = tex_list[w_tex] - if name_tex : - name_file = Blender.sys.join(my_path ,name_tex) - try: - img = Image.Load(name_file) - f.image = img - except: - #Draw.PupMenu("No image to load") - #print "No image " + name_tex + " to load" - pass - - - - + face_list.append((1+int(words[1]), 1+int(words[2]), 1+int(words[3]))) + + mesh.faces.extend(face_list) + del face_list + + if nr_uv : + mesh.faceUV = True + for f in mesh.faces: + fuv = f.uv + for ii, v in enumerate(f): + # _u, _v = self.CleanLine(self.lines[nr_uv + 2 + v.index]).split() + + # Use a dummy vert + _u, _v = self.CleanLine(self.lines[nr_uv + 1 + v.index]).split() + + fuv[ii].x = float(_u) + fuv[ii].y = float(_v) + + if nr_fac_mat : + fac_line = self.lines[nr_fac_mat + i] + fixed_fac = self.CleanLine(fac_line) + w_tex = int(fixed_fac.split()[0]) + f.image = tex_list[w_tex] + + # remove dummy vert + mesh.verts.delete([0,]) + def CleanLine(self,line): - fix_line = line.replace(";", " ") - fix_1_line = fix_line.replace('"', ' ') - fix_2_line = fix_1_line.replace("{", " ") - fix_3_line = fix_2_line.replace("}", " ") - fix_4_line = fix_3_line.replace(",", " ") - fix_5_line = fix_4_line.replace("'", " ") - return fix_5_line + return line.replace(\ + ";", " ").replace(\ + '"', ' ').replace(\ + "{", " ").replace(\ + "}", " ").replace(\ + ",", " ").replace(\ + "'", " ") #------------------------------------------------------------------ # CREATE MATERIALS #------------------------------------------------------------------ - def writeMaterials(self, nr_mat, idx, mat_list, tex_list): - name = "Material_" + str(idx) - mat = Material.New(name) + def loadMaterials(self, nr_mat, mat_list, tex_list): + + def load_image(name): + try: + return Image.Load(Blender.sys.join(my_path,name)) + except: + return None + + mat = bpy.data.materials.new() line = self.lines[nr_mat + 1] fixed_line = self.CleanLine(line) words = fixed_line.split() @@ -244,35 +192,33 @@ class xImport: l = self.lines[nr_mat + 5] fix_3_line = self.CleanLine(l) tex_n = fix_3_line.split() - + if tex_n and tex_n[0] == "TextureFilename" : if len(tex_n) > 1: - tex_list.append(tex_n[1]) + tex_list.append(load_image(tex_n[1])) if len(tex_n) <= 1 : l_succ = self.lines[nr_mat + 6] fix_3_succ = self.CleanLine(l_succ) tex_n_succ = fix_3_succ.split() - tex_list.append(tex_n_succ[0]) + tex_list.append(load_image(tex_n_succ[0])) else : - tex_name = None - tex_list.append(tex_name) + tex_list.append(None) # no texture for this index return mat_list, tex_list #------------------------------------------------------------------ # SET MATERIALS #------------------------------------------------------------------ - def writeMeshMaterials(self, nr_fc_mat, mesh): + def loadMeshMaterials(self, nr_fc_mat, mesh): for face in mesh.faces: nr_fc_mat += 1 line = self.lines[nr_fc_mat] fixed_line = self.CleanLine(line) wrd = fixed_line.split() mat_idx = int(wrd[0]) - face.materialIndex = mat_idx - + face.mat = mat_idx #------------------------------------------------------------------ # MAIN @@ -286,4 +232,7 @@ arg = __script__['arg'] if __name__ == '__main__': Blender.Window.FileSelector(my_callback, "Import DirectX", "*.x") -# my_callback('/directxterrain.x') + +#my_callback('/fe/x/directxterrain.x') +#my_callback('/fe/x/Male_Normal_MAX.X') +#my_callback('/fe/x/male_ms3d.x') diff --git a/release/scripts/ac3d_import.py b/release/scripts/ac3d_import.py index d538838fdf3..9a7004e4b4d 100644 --- a/release/scripts/ac3d_import.py +++ b/release/scripts/ac3d_import.py @@ -679,6 +679,8 @@ class AC3DImport: baseimgname = bsys.basename(objtex) if bsys.exists(objtex) == 1: texfname = objtex + elif bsys.exists(bsys.join(self.importdir, objtex)): + texfname = bsys.join(self.importdir, objtex) else: if baseimgname.find('\\') > 0: baseimgname = bsys.basename(objtex.replace('\\','/')) diff --git a/release/scripts/add_mesh_empty.py b/release/scripts/add_mesh_empty.py new file mode 100644 index 00000000000..537bd1e2c3d --- /dev/null +++ b/release/scripts/add_mesh_empty.py @@ -0,0 +1,13 @@ +#!BPY +""" +Name: 'Empty mesh' +Blender: 243 +Group: 'AddMesh' +""" +import BPyAddMesh +import Blender + +def main(): + BPyAddMesh.add_mesh_simple('EmptyMesh', [], [], []) + +main()
\ No newline at end of file diff --git a/release/scripts/add_mesh_torus.py b/release/scripts/add_mesh_torus.py index de2db42d482..4f759256497 100644 --- a/release/scripts/add_mesh_torus.py +++ b/release/scripts/add_mesh_torus.py @@ -51,9 +51,9 @@ def main(): if not Draw.PupBlock('Add Torus', [\ ('Major Radius:', PREF_MAJOR_RAD, 0.01, 100, 'Radius for the main ring of the torus'),\ - ('Minor Radius:', PREF_MINOR_RAD, 0.01, 100, 'Radius for the minor ring of the torus setting the thickness of the ring.'),\ - ('Major Segments:', PREF_MAJOR_SEG, 3, 256, 'Radius for the main ring of the torus'),\ - ('Minor Segments:', PREF_MINOR_SEG, 3, 256, 'Radius for the minor ring of the torus setting the thickness of the ring.'),\ + ('Minor Radius:', PREF_MINOR_RAD, 0.01, 100, 'Radius for the minor ring of the torus setting the thickness of the ring'),\ + ('Major Segments:', PREF_MAJOR_SEG, 3, 256, 'Number of segments for the main ring of the torus'),\ + ('Minor Segments:', PREF_MINOR_SEG, 3, 256, 'Number of segments for the minor ring of the torus'),\ ]): return @@ -61,4 +61,5 @@ def main(): BPyAddMesh.add_mesh_simple('Torus', verts, [], faces) -main()
\ No newline at end of file +main() + diff --git a/release/scripts/bpymodules/BPyMesh.py b/release/scripts/bpymodules/BPyMesh.py index 70fc1a87a52..415c2a12c69 100644 --- a/release/scripts/bpymodules/BPyMesh.py +++ b/release/scripts/bpymodules/BPyMesh.py @@ -56,7 +56,7 @@ def meshWeight2List(me): if not len_groupNames: # no verts? return a vert aligned empty list - return [[] for i in xrange(len(me.verts))] + return [[] for i in xrange(len(me.verts))], [] else: vWeightList= [[0.0]*len_groupNames for i in xrange(len(me.verts))] diff --git a/release/scripts/bpymodules/BPyMessages.py b/release/scripts/bpymodules/BPyMessages.py index 0ff8e178ac1..8ee1aa6c707 100644 --- a/release/scripts/bpymodules/BPyMessages.py +++ b/release/scripts/bpymodules/BPyMessages.py @@ -11,6 +11,8 @@ def Error_NoMeshUvActive(): Draw.PupMenu('Error%t|Active object is not a mesh with texface') def Error_NoMeshMultiresEdit(): Draw.PupMenu('Error%t|Unable to complete action with multires enabled') +def Error_NoMeshFaces(): + Draw.PupMenu('Error%t|Mesh has no faces') # File I/O messages def Error_NoFile(path): diff --git a/release/scripts/bpymodules/BPyObject.py b/release/scripts/bpymodules/BPyObject.py index 7de2c4113f9..54ff949218d 100644 --- a/release/scripts/bpymodules/BPyObject.py +++ b/release/scripts/bpymodules/BPyObject.py @@ -10,7 +10,7 @@ def getObjectArmature(ob): arm = ob.parent if arm and arm.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.ARMATURE: - arm + return arm for m in ob.modifiers: if m.type== Blender.Modifier.Types.ARMATURE: diff --git a/release/scripts/bpymodules/BPyRender.py b/release/scripts/bpymodules/BPyRender.py index bb5ef628356..a83eb6a8de8 100644 --- a/release/scripts/bpymodules/BPyRender.py +++ b/release/scripts/bpymodules/BPyRender.py @@ -91,7 +91,7 @@ def imageFromObjectsOrtho(objects, path, width, height, smooth, alpha= True, cam render_cam_ob= Object.New('Camera') render_cam_ob.link(render_cam_data) render_scn.link(render_cam_ob) - render_scn.setCurrentCamera(render_cam_ob) + render_scn.objects.camera = render_cam_ob render_cam_data.type= 'ortho' @@ -181,7 +181,6 @@ def imageFromObjectsOrtho(objects, path, width, height, smooth, alpha= True, cam Blender.Window.RedrawAll() - render_context.threads= 2 # good for dual core cpu's render_context.render() render_context.saveRenderedImage(path) Render.CloseRenderWindow() @@ -496,3 +495,135 @@ def vcol2image(me_s,\ mat.mode &= ~Blender.Material.Modes.SHADELESS return image + +def bakeToPlane(sce, ob_from, width, height, bakemodes, axis='z', margin=0): + ''' + Bakes terrain onto a plane from one object + sce - scene to bake with + ob_from - mesh object + width/height - image size + bakemodes - list of baking modes to use, Blender.Scene.Render.BakeModes.NORMALS, Blender.Scene.Render.BakeModes.AO ... etc + axis - axis to allign the plane to. + margin - margin setting for baking. + + Example: + import Blender + from Blender import * + import BPyRender + sce = Scene.GetCurrent() + ob = Object.Get('Plane') + BPyRender.bakeToPlane(sce, ob, 512, 512, [Scene.Render.BakeModes.DISPLACEMENT, Scene.Render.BakeModes.NORMALS], 'z', 8 ) + ''' + + # Backup bake settings + rend = sce.render + BACKUP_bakeDist = rend.bakeDist + BACKUP_bakeBias = rend.bakeBias + BACKUP_bakeMode = rend.bakeMode + BACKUP_bakeClear = rend.bakeClear + BACKUP_bakeMargin = rend.bakeMargin + BACKUP_bakeToActive = rend.bakeToActive + + # Backup object selection + BACKUP_obsel = list(sce.objects.selected) + BACKUP_obact = sce.objects.active + + # New bake settings + rend.bakeClear = True + rend.bakeMargin = margin + BACKUP_bakeToActive = True + + # Assume a mesh + me_from = ob_from.getData(mesh=1) + + xmin = ymin = zmin = 10000000000 + xmax = ymax = zmax =-10000000000 + + # Dont trust bounding boxes :/ + #bounds = ob_from.boundingBox + #for v in bounds: + # x,y,z = tuple(v) + mtx = ob_from.matrixWorld + for v in me_from.verts: + x,y,z = tuple(v.co*mtx) + + xmax = max(xmax, x) + ymax = max(ymax, y) + zmax = max(zmax, z) + + xmin = min(xmin, x) + ymin = min(ymin, y) + zmin = min(zmin, z) + + if axis=='x': + xmed = (xmin+xmax)/2.0 + co1 = (xmed, ymin, zmin) + co2 = (xmed, ymin, zmax) + co3 = (xmed, ymax, zmax) + co4 = (xmed, ymax, zmin) + rend.bakeDist = (xmax-xmin)/2.0 + elif axis=='y': + ymed = (ymin+ymax)/2.0 + co1 = (xmin, ymed, zmin) + co2 = (xmin, ymed, zmax) + co3 = (xmax, ymed, zmax) + co4 = (xmax, ymed, zmin) + rend.bakeDist = (ymax-ymin)/2.0 + elif axis=='z': + zmed = (zmin+zmax)/2.0 + co1 = (xmin, ymin, zmed) + co2 = (xmin, ymax, zmed) + co3 = (xmax, ymax, zmed) + co4 = (xmax, ymin, zmed) + rend.bakeDist = (zmax-zmin)/2.0 + else: + raise "invalid axis" + me_plane = Blender.Mesh.New() + ob_plane = Blender.Object.New('Mesh') + ob_plane.link(me_plane) + sce.objects.link(ob_plane) + ob_plane.Layers = ob_from.Layers + + ob_from.sel = 1 # make active + sce.objects.active = ob_plane + ob_plane.sel = 1 + + me_plane.verts.extend([co4, co3, co2, co1]) + me_plane.faces.extend([(0,1,2,3)]) + me_plane.faceUV = True + me_plane_face = me_plane.faces[0] + uvs = me_plane_face.uv + uvs[0].x = 0.0; uvs[0].y = 0.0 + uvs[1].x = 0.0; uvs[1].y = 1.0 + uvs[2].x = 1.0; uvs[2].y = 1.0 + uvs[3].x = 1.0; uvs[3].y = 0.0 + + images_return = [] + + for mode in bakemodes: + img = Blender.Image.New('bake', width, height, 24) + + me_plane_face.image = img + rend.bakeMode = mode + rend.bake() + images_return.append( img ) + + # Restore bake settings + #''' + rend.bakeDist = BACKUP_bakeDist + rend.bakeBias = BACKUP_bakeBias + rend.bakeMode = BACKUP_bakeMode + rend.bakeClear = BACKUP_bakeClear + rend.bakeMargin = BACKUP_bakeMargin + rend.bakeToActive = BACKUP_bakeToActive + + # Restore obsel + sce.objects.selected = BACKUP_obsel + sce.objects.active = BACKUP_obact + + me_plane.verts = None + sce.objects.unlink(ob_plane) + #''' + + return images_return + diff --git a/release/scripts/bpymodules/blend2renderinfo.py b/release/scripts/bpymodules/blend2renderinfo.py new file mode 100644 index 00000000000..1b9dec58d55 --- /dev/null +++ b/release/scripts/bpymodules/blend2renderinfo.py @@ -0,0 +1,95 @@ +#!/usr/bin/python + +# -------------------------------------------------------------------------- +# ***** 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 ***** +# -------------------------------------------------------------------------- + +import struct + +# In Blender, selecting scenes in the databrowser (shift+f4) will tag for rendering. + +# This struct wont change according to ton. +# Note that the size differs on 32/64bit +''' +typedef struct BHead { + int code, len; + void *old; + int SDNAnr, nr; +} BHead; +''' + + +def read_blend_rend_chunk(path): + file = open(path, 'rb') + + if file.read(len('BLENDER')) != 'BLENDER': + return [] + + # + if file.read(1) == '-': + is64bit = True + else: # '_' + is64bit = False + + if file.read(1) == 'V': + isBigEndian = True # ppc + else: # 'V' + isBigEndian = False # x86 + + + # Now read the bhead chunk!!! + file.read(3) # skip the version + + scenes = [] + + while file.read(4) == 'REND': + + if is64bit: sizeof_bhead = sizeof_bhead_left = 24 # 64bit + else: sizeof_bhead = sizeof_bhead_left = 20 # 32bit + + sizeof_bhead_left -= 4 + + if isBigEndian: rend_length = struct.unpack('>i', file.read(4))[0] + else: rend_length = struct.unpack('<i', file.read(4))[0] + + sizeof_bhead_left -= 4 + + # We dont care about the rest of the bhead struct + file.read(sizeof_bhead_left) + + # Now we want the scene name, start and end frame. this is 32bites long + + if isBigEndian: start_frame, end_frame = struct.unpack('>2i', file.read(8)) + else: start_frame, end_frame = struct.unpack('<2i', file.read(8)) + + scene_name = file.read(24) + scene_name = scene_name[ : scene_name.index('\0') ] + + scenes.append( (start_frame, end_frame, scene_name) ) + return scenes + +def main(): + import sys + for arg in sys.argv[1:]: + if arg.lower().endswith('.blend'): + print read_blend_rend_chunk(arg) + +if __name__ == '__main__': + main() + diff --git a/release/scripts/bpymodules/paths_ai2obj.py b/release/scripts/bpymodules/paths_ai2obj.py index dcf56853184..6eb5023a8d4 100644 --- a/release/scripts/bpymodules/paths_ai2obj.py +++ b/release/scripts/bpymodules/paths_ai2obj.py @@ -1,3 +1,4 @@ +# -*- coding: latin-1 -*- """ paths_ai2obj.py # --------------------------------------------------------------- @@ -42,10 +43,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Communiquer les problemes et erreurs sur: # http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender #---------------------------------------------- + +#Changelog #---------------------------------------------- -#Chagelog -#---------------------------------------------- -# 0.1.1 : 2004/08/03, bug in boudingbox reading when Value are negative +# 0.1.1 : 2004/08/03, bug in boundingbox reading when Value are negative # 0.1.2 : 2005/06/12, gmove tranformation properties # 0.1.3 : 2005/06/25, added a __name__ test to use the script alone # 0.1.4 : 2005/06/25, closepath improvements @@ -57,6 +58,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # 0.1.8 : 2006/07/03, two more closepath improvements # 0.1.9 : 2007/05/06, modif on the method that gets the last object on the list data +# 2008/03/12, Added character encoding line so french text +# does not break python interpreters. + """ SHARP_IMPORT=0 SCALE=1 diff --git a/release/scripts/bpymodules/paths_gimp2obj.py b/release/scripts/bpymodules/paths_gimp2obj.py index 8b31c5d7294..c2ce9718c71 100644 --- a/release/scripts/bpymodules/paths_gimp2obj.py +++ b/release/scripts/bpymodules/paths_gimp2obj.py @@ -1,3 +1,4 @@ +# -*- coding: latin-1 -*- """ #---------------------------------------------- # (c) jm soler juillet 2004, @@ -43,6 +44,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_import_gimp.htm # Communiquer les problemes et erreurs sur: # http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender +# Modification History: +# 2008-03-12 Added character encoding line so french text does not break +# python interpreters. #--------------------------------------------- SHARP_IMPORT=0 diff --git a/release/scripts/bvh_import.py b/release/scripts/bvh_import.py index d027956a507..2093ac109f7 100644 --- a/release/scripts/bvh_import.py +++ b/release/scripts/bvh_import.py @@ -110,7 +110,7 @@ def eulerRotate(x,y,z, rot_order): def read_bvh(file_path, GLOBAL_SCALE=1.0): # File loading stuff # Open the file for importing - file = open(file_path, 'r') + file = open(file_path, 'rU') # Seperate into a list of lists, each line a list of words. file_lines = file.readlines() @@ -354,7 +354,7 @@ def bvh_node_dict2objects(bvh_nodes, IMPORT_START_FRAME= 1, IMPORT_LOOP= False): bvh_node.temp.rot= rx*DEG2RAD,ry*DEG2RAD,rz*DEG2RAD - bvh_node.temp.insertIpoKey(Blender.Object.IpoKeys.LOCROT) + bvh_node.temp.insertIpoKey(Blender.Object.IpoKeyTypes.LOCROT) scn.update(1) return objects diff --git a/release/scripts/console.py b/release/scripts/console.py index e253e3c9857..7d9d8be5e9e 100644 --- a/release/scripts/console.py +++ b/release/scripts/console.py @@ -285,6 +285,8 @@ def runUserCode(__USER_CODE_STRING__): # Try and run the user entered line(s) try: # Load all variabls from global dict to local space. + __TMP_VAR_NAME__ = __TMP_VAR__ = '' # so as not to raise an error when del'ing + for __TMP_VAR_NAME__, __TMP_VAR__ in __CONSOLE_VAR_DICT__.items(): exec('%s%s' % (__TMP_VAR_NAME__,'=__TMP_VAR__')) del __TMP_VAR_NAME__ @@ -295,7 +297,8 @@ def runUserCode(__USER_CODE_STRING__): # Flush global dict, allow the user to remove items. __CONSOLE_VAR_DICT__ = {} - + + __TMP_VAR_NAME__ = '' # so as not to raise an error when del'ing # Write local veriables to global __CONSOLE_VAR_DICT__ for __TMP_VAR_NAME__ in dir(): if __TMP_VAR_NAME__ != '__FILE_LIKE_STRING__' and\ @@ -715,6 +718,15 @@ def draw_gui(): else: BGL.glColor3f(1, 1, 0) + if consoleLineIdx == 1: # user input + BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1] * (consoleLineIdx-__CONSOLE_LINE_OFFSET__)) - 8) + Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZES__[__FONT_SIZE__][0]) + else: + BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1] * ((consoleLineIdx-__CONSOLE_LINE_OFFSET__)+wrapLineIndex)) - 8) + Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZES__[__FONT_SIZE__][0]) + + # Wrapping is totally slow, can even hang blender - dont do it! + ''' if consoleLineIdx == 1: # NEVER WRAP THE USER INPUT BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1] * (consoleLineIdx-__CONSOLE_LINE_OFFSET__)) - 8) # BUG, LARGE TEXT DOSENT DISPLAY @@ -751,7 +763,7 @@ def draw_gui(): BGL.glRasterPos2i(margin, (__FONT_SIZES__[__FONT_SIZE__][1] * ((consoleLineIdx-__CONSOLE_LINE_OFFSET__)+wrapLineIndex)) - 8) Draw.Text(cmdBuffer[-consoleLineIdx].cmd, __FONT_SIZES__[__FONT_SIZE__][0]) - + ''' consoleLineIdx += 1 @@ -774,8 +786,9 @@ histIndex = cursor = -1 # How far back from the first letter are we? - in curren # Autoexec, startup code. scriptDir = Get('scriptsdir') +console_autoexec = None if scriptDir: - if scriptDir.endswith(Blender.sys.sep): + if not scriptDir.endswith(Blender.sys.sep): scriptDir += Blender.sys.sep console_autoexec = '%s%s' % (scriptDir, 'console_autoexec.py') @@ -788,7 +801,10 @@ if scriptDir: except: cmdBuffer.append(cmdLine('...console_autoexec.py could not write, this is ok', 1, None)) scriptDir = None # make sure we only use this for console_autoexec.py - + + if not sys.exists(console_autoexec): + console_autoexec = None + else: cmdBuffer.append(cmdLine('...Using existing console_autoexec.py in scripts dir', 1, None)) @@ -802,7 +818,8 @@ def include_console(includeFile): # Execute an external py file as if local exec(include(includeFile)) - + +def standard_imports(): # Write local to global __CONSOLE_VAR_DICT__ for reuse, for ls in (dir(), dir(Blender)): for __TMP_VAR_NAME__ in ls: @@ -810,10 +827,12 @@ def include_console(includeFile): exec('%s%s' % ('__CONSOLE_VAR_DICT__[__TMP_VAR_NAME__]=', __TMP_VAR_NAME__)) exec('%s%s' % ('__CONSOLE_VAR_DICT__["bpy"]=', 'bpy')) - -if scriptDir: + +if scriptDir and console_autoexec: include_console(console_autoexec) # pass the blender module +standard_imports() # import Blender and bpy + #-end autoexec-----------------------------------------------------------------# @@ -827,4 +846,4 @@ def main(): Draw.Register(draw_gui, handle_event, handle_button_event) if __name__ == '__main__': - main()
\ No newline at end of file + main() diff --git a/release/scripts/export_fbx.py b/release/scripts/export_fbx.py index a4a4787d5f3..99d036b38bc 100644 --- a/release/scripts/export_fbx.py +++ b/release/scripts/export_fbx.py @@ -1,7 +1,7 @@ #!BPY """ Name: 'Autodesk FBX (.fbx)...' -Blender: 243 +Blender: 244 Group: 'Export' Tooltip: 'Selection to an ASCII Autodesk FBX ' """ @@ -12,13 +12,8 @@ __version__ = "1.1" __bpydoc__ = """\ This script is an exporter to the FBX file format. -Usage: - -Select the objects you wish to export and run this script from "File->Export" menu. -All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d) -will be exported as mesh data. +http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx """ - # -------------------------------------------------------------------------- # FBX Export v0.1 by Campbell Barton (AKA Ideasman) # -------------------------------------------------------------------------- @@ -41,57 +36,470 @@ will be exported as mesh data. # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- +try: + import time + # import os # only needed for batch export, nbot used yet +except: + time = None # use this to check if they have python modules installed + +# for python 2.3 support +try: + set() +except: + try: + from sets import Set as set + except: + set = None # so it complains you dont have a ! + +# os is only needed for batch 'own dir' option +try: + import os +except: + os = None + import Blender +import bpy +from Blender.Mathutils import Matrix, Vector, RotationMatrix + import BPyObject import BPyMesh import BPySys import BPyMessages -import time -from math import degrees, atan, pi -# Used to add the scene name into the filename without using odd chars +import sys + +## This was used to make V, but faster not to do all that +##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}' +##v = range(255) +##for c in valid: v.remove(ord(c)) +v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,43,47,58,59,60,61,62,63,64,92,94,96,124,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254] +invalid = ''.join([chr(i) for i in v]) +def cleanName(name): + for ch in invalid: name = name.replace(ch, '_') + return name +del v, i + + +def copy_file(source, dest): + file = open(source, 'rb') + data = file.read() + file.close() + + file = open(dest, 'wb') + file.write(data) + file.close() + + +def copy_images(dest_dir, textures): + if not dest_dir.endswith(Blender.sys.sep): + dest_dir += Blender.sys.sep + + image_paths = set() + for img in textures: + image_paths.add(Blender.sys.expandpath(img.filename)) + + # Now copy images + copyCount = 0 + for image_path in image_paths: + if Blender.sys.exists(image_path): + # Make a name for the target path. + dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] + if not Blender.sys.exists(dest_image_path): # Image isnt alredy there + print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) + try: + copy_file(image_path, dest_image_path) + copyCount+=1 + except: + print '\t\tWarning, file failed to copy, skipping.' + + print '\tCopied %d images' % copyCount + +mtx4_identity = Matrix() + +# testing +mtx_x90 = RotationMatrix( 90, 3, 'x') # used +#mtx_x90n = RotationMatrix(-90, 3, 'x') +#mtx_y90 = RotationMatrix( 90, 3, 'y') +#mtx_y90n = RotationMatrix(-90, 3, 'y') +#mtx_z90 = RotationMatrix( 90, 3, 'z') +#mtx_z90n = RotationMatrix(-90, 3, 'z') + +#mtx4_x90 = RotationMatrix( 90, 4, 'x') +mtx4_x90n = RotationMatrix(-90, 4, 'x') # used +#mtx4_y90 = RotationMatrix( 90, 4, 'y') +mtx4_y90n = RotationMatrix(-90, 4, 'y') # used +mtx4_z90 = RotationMatrix( 90, 4, 'z') # used +mtx4_z90n = RotationMatrix(-90, 4, 'z') # used + +def strip_path(p): + return p.split('\\')[-1].split('/')[-1] + +# Used to add the scene name into the filename without using odd chars sane_name_mapping_ob = {} sane_name_mapping_mat = {} sane_name_mapping_tex = {} +sane_name_mapping_take = {} +sane_name_mapping_group = {} -def strip_path(p): - return p.split('\\')[-1].split('/')[-1] +# Make sure reserved names are not used +sane_name_mapping_ob['Scene'] = 'Scene_' +sane_name_mapping_ob['blend_root'] = 'blend_root_' + +def increment_string(t): + name = t + num = '' + while name and name[-1].isdigit(): + num = name[-1] + num + name = name[:-1] + if num: return '%s%d' % (name, int(num)+1) + else: return name + '_0' + + +# todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up. def sane_name(data, dct): - if not data: return None + #if not data: return None name = data.name - try: return dct[name] - except: pass + + # dont cache, only ever call once for each data type now, + # so as to avoid namespace collision between types - like with objects <-> bones + #try: return dct[name] + #except: pass orig_name = name - name = BPySys.cleanName(name) + if not name: + name = 'unnamed' # blank string, ASKING FOR TROUBLE! + else: + #name = BPySys.cleanName(name) + name = cleanName(name) # use our own + + while name in dct.itervalues(): name = increment_string(name) + dct[orig_name] = name return name def sane_obname(data): return sane_name(data, sane_name_mapping_ob) def sane_matname(data): return sane_name(data, sane_name_mapping_mat) def sane_texname(data): return sane_name(data, sane_name_mapping_tex) +def sane_takename(data): return sane_name(data, sane_name_mapping_take) +def sane_groupname(data): return sane_name(data, sane_name_mapping_group) + -# May use this later -""" -# Auto class, use for datastorage only, a like a dictionary but with limited slots -def auto_class(slots): - exec('class container_class(object): __slots__=%s' % slots) - return container_class -""" +def mat4x4str(mat): + return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ]) + +def meshNormalizedWeights(me): + try: # account for old bad BPyMesh + groupNames, vWeightList = BPyMesh.meshWeight2List(me) + except: + return [],[] + + if not groupNames: + return [],[] + + for i, vWeights in enumerate(vWeightList): + tot = 0.0 + for w in vWeights: + tot+=w + + if tot: + for j, w in enumerate(vWeights): + vWeights[j] = w/tot + + return groupNames, vWeightList + header_comment = \ '''; FBX 6.1.0 project file ; Created by Blender FBX Exporter -; for support mail cbarton@metavr.com +; for support mail: ideasman42@gmail.com ; ---------------------------------------------------- ''' -def write_header(file): +# This func can be called with just the filename +def write(filename, batch_objects = None, \ + EXP_OBS_SELECTED = True, + EXP_MESH = True, + EXP_MESH_APPLY_MOD = True, + EXP_MESH_HQ_NORMALS = False, + EXP_ARMATURE = True, + EXP_LAMP = True, + EXP_CAMERA = True, + EXP_EMPTY = True, + EXP_IMAGE_COPY = False, + GLOBAL_MATRIX = Matrix(), + ANIM_ENABLE = True, + ANIM_OPTIMIZE = True, + ANIM_OPTIMIZE_PRECISSION = 6, + ANIM_ACTION_ALL = False, + BATCH_ENABLE = False, + BATCH_GROUP = True, + BATCH_SCENE = False, + BATCH_FILE_PREFIX = '', + BATCH_OWN_DIR = False + ): + + # ----------------- Batch support! + if BATCH_ENABLE: + if os == None: BATCH_OWN_DIR = False + + fbxpath = filename + + # get the path component of filename + tmp_exists = Blender.sys.exists(fbxpath) + + if tmp_exists != 2: # a file, we want a path + while fbxpath and fbxpath[-1] not in ('/', '\\'): + fbxpath = fbxpath[:-1] + if not filename: + Draw.PupMenu('Error%t|Directory does not exist!') + return + + tmp_exists = Blender.sys.exists(fbxpath) + + if tmp_exists != 2: + Draw.PupMenu('Error%t|Directory does not exist!') + return + + if not fbxpath.endswith(Blender.sys.sep): + fbxpath += Blender.sys.sep + del tmp_exists + + + if BATCH_GROUP: + data_seq = bpy.data.groups + else: + data_seq = bpy.data.scenes + + # call this function within a loop with BATCH_ENABLE == False + orig_sce = bpy.data.scenes.active + + + new_fbxpath = fbxpath # own dir option modifies, we need to keep an original + for data in data_seq: # scene or group + newname = BATCH_FILE_PREFIX + BPySys.cleanName(data.name) + + + if BATCH_OWN_DIR: + new_fbxpath = fbxpath + newname + Blender.sys.sep + # path may alredy exist + # TODO - might exist but be a file. unlikely but should probably account for it. + + if Blender.sys.exists(new_fbxpath) == 0: + os.mkdir(new_fbxpath) + + + filename = new_fbxpath + newname + '.fbx' + + print '\nBatch exporting %s as...\n\t"%s"' % (data, filename) + + if BATCH_GROUP: #group + # group, so objects update properly, add a dummy scene. + sce = bpy.data.scenes.new() + sce.Layers = (1<<20) -1 + bpy.data.scenes.active = sce + for ob_base in data.objects: + sce.objects.link(ob_base) + + sce.update(1) + + # TODO - BUMMER! Armatures not in the group wont animate the mesh + + else:# scene + + + data_seq.active = data + + + # Call self with modified args + # Dont pass batch options since we alredy usedt them + write(filename, data.objects, + False, + EXP_MESH, + EXP_MESH_APPLY_MOD, + EXP_MESH_HQ_NORMALS, + EXP_ARMATURE, + EXP_LAMP, + EXP_CAMERA, + EXP_EMPTY, + EXP_IMAGE_COPY, + GLOBAL_MATRIX, + ANIM_ENABLE, + ANIM_OPTIMIZE, + ANIM_OPTIMIZE_PRECISSION, + ANIM_ACTION_ALL + ) + + if BATCH_GROUP: + # remove temp group scene + bpy.data.scenes.unlink(sce) + + bpy.data.scenes.active = orig_sce + + return # so the script wont run after we have batch exported. + + # end batch support + + + # ---------------------------------------------- + # storage classes + class my_bone_class: + __slots__ =(\ + 'blenName',\ + 'blenBone',\ + 'blenMeshes',\ + 'restMatrix',\ + 'parent',\ + 'blenName',\ + 'fbxName',\ + 'fbxArm',\ + '__pose_bone',\ + '__anim_poselist') + + def __init__(self, blenBone, fbxArm): + + # This is so 2 armatures dont have naming conflicts since FBX bones use object namespace + self.fbxName = sane_obname(blenBone) + + self.blenName = blenBone.name + self.blenBone = blenBone + self.blenMeshes = {} # fbxMeshObName : mesh + self.fbxArm = fbxArm + self.restMatrix = blenBone.matrix['ARMATURESPACE'] + + # not used yet + # self.restMatrixInv = self.restMatrix.copy().invert() + # self.restMatrixLocal = None # set later, need parent matrix + + self.parent = None + + # not public + pose = fbxArm.blenObject.getPose() + self.__pose_bone = pose.bones[self.blenName] + + # store a list if matricies here, (poseMatrix, head, tail) + # {frame:posematrix, frame:posematrix, ...} + self.__anim_poselist = {} + + ''' + def calcRestMatrixLocal(self): + if self.parent: + self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.copy().invert() + else: + self.restMatrixLocal = self.restMatrix.copy() + ''' + def setPoseFrame(self, f): + # cache pose info here, frame must be set beforehand + + # Didnt end up needing head or tail, if we do - here it is. + ''' + self.__anim_poselist[f] = (\ + self.__pose_bone.poseMatrix.copy(),\ + self.__pose_bone.head.copy(),\ + self.__pose_bone.tail.copy() ) + ''' + + self.__anim_poselist[f] = self.__pose_bone.poseMatrix.copy() + + # get pose from frame. + def getPoseMatrix(self, f):# ---------------------------------------------- + return self.__anim_poselist[f] + ''' + def getPoseHead(self, f): + #return self.__pose_bone.head.copy() + return self.__anim_poselist[f][1].copy() + def getPoseTail(self, f): + #return self.__pose_bone.tail.copy() + return self.__anim_poselist[f][2].copy() + ''' + # end + + def getAnimParRelMatrix(self, frame): + #arm_mat = self.fbxArm.matrixWorld + #arm_mat = self.fbxArm.parRelMatrix() + if not self.parent: + #return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat) # dont apply arm matrix anymore + return mtx4_z90 * self.getPoseMatrix(frame) + else: + #return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).invert() + return (mtx4_z90 * (self.getPoseMatrix(frame))) * (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert() + + # we need thes because cameras and lights modified rotations + def getAnimParRelMatrixRot(self, frame): + return self.getAnimParRelMatrix(frame) + + def flushAnimData(self): + self.__anim_poselist.clear() + + + class my_object_generic: + # Other settings can be applied for each type - mesh, armature etc. + def __init__(self, ob, matrixWorld = None): + self.fbxName = sane_obname(ob) + self.blenObject = ob + self.fbxGroupNames = [] + self.fbxParent = None # set later on IF the parent is in the selection. + if matrixWorld: self.matrixWorld = matrixWorld * GLOBAL_MATRIX + else: self.matrixWorld = ob.matrixWorld * GLOBAL_MATRIX + self.__anim_poselist = {} # we should only access this + + def parRelMatrix(self): + if self.fbxParent: + return self.matrixWorld * self.fbxParent.matrixWorld.copy().invert() + else: + return self.matrixWorld + + def setPoseFrame(self, f): + self.__anim_poselist[f] = self.blenObject.matrixWorld.copy() + + def getAnimParRelMatrix(self, frame): + if self.fbxParent: + #return (self.__anim_poselist[frame] * self.fbxParent.__anim_poselist[frame].copy().invert() ) * GLOBAL_MATRIX + return (self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert() + else: + return self.__anim_poselist[frame] * GLOBAL_MATRIX + + def getAnimParRelMatrixRot(self, frame): + type = self.blenObject.type + if self.fbxParent: + matrix_rot = (((self.__anim_poselist[frame] * GLOBAL_MATRIX) * (self.fbxParent.__anim_poselist[frame] * GLOBAL_MATRIX).invert())).rotationPart() + else: + matrix_rot = (self.__anim_poselist[frame] * GLOBAL_MATRIX).rotationPart() + + # Lamps need to be rotated + if type =='Lamp': + matrix_rot = mtx_x90 * matrix_rot + elif ob and type =='Camera': + y = Vector(0,1,0) * matrix_rot + matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + + return matrix_rot + + # ---------------------------------------------- + + + + + + print '\nFBX export starting...', filename + start_time = Blender.sys.time() + try: + file = open(filename, 'w') + except: + return False + + sce = bpy.data.scenes.active + world = sce.world + + + # ---------------------------- Write the header first file.write(header_comment) - curtime = time.localtime()[0:6] + if time: + curtime = time.localtime()[0:6] + else: + curtime = [0,0,0,0,0,0] # file.write(\ '''FBXHeaderExtension: { @@ -115,49 +523,78 @@ def write_header(file): file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime) file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version')) - - - - -def write_scene(file, sce, world): - def write_object_tx(ob, loc, matrix): + pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect allong the way + + # --------------- funcs for exporting + def object_tx(ob, loc, matrix, matrix_mod = None): ''' - We have loc to set the location if non blender objects that have a location + Matrix mod is so armature objects can modify their bone matricies ''' - - if ob and not matrix: matrix = ob.matrixWorld - matrix_rot = matrix - #if matrix: - # matrix = matrix_scale * matrix - - if matrix: - loc = tuple(matrix.translationPart()) - scale = tuple(matrix.scalePart()) + if isinstance(ob, Blender.Types.BoneType): + + # we know we have a matrix + # matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod) + matrix = mtx4_z90 * ob.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + + parent = ob.parent + if parent: + #par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod) + par_matrix = mtx4_z90 * parent.matrix['ARMATURESPACE'] # dont apply armature matrix anymore + matrix = matrix * par_matrix.copy().invert() + + matrix_rot = matrix.rotationPart() + + loc = tuple(matrix.translationPart()) + scale = tuple(matrix.scalePart()) + rot = tuple(matrix_rot.toEuler()) - matrix_rot = matrix.rotationPart() - # Lamps need to be rotated - if ob and ob.type =='Lamp': - matrix_rot = Blender.Mathutils.RotationMatrix(90, 4, 'x') * matrix - rot = tuple(matrix_rot.toEuler()) - elif ob and ob.type =='Camera': - y = Blender.Mathutils.Vector(0,1,0) * matrix_rot - matrix_rot = matrix_rot * Blender.Mathutils.RotationMatrix(90, 3, 'r', y) - rot = tuple(matrix_rot.toEuler()) - else: - rot = tuple(matrix_rot.toEuler()) else: - if not loc: - loc = 0,0,0 - scale = 1,1,1 - rot = 0,0,0 + # This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore + #if ob and not matrix: matrix = ob.matrixWorld * GLOBAL_MATRIX + if ob and not matrix: raise "error: this should never happen!" + + matrix_rot = matrix + #if matrix: + # matrix = matrix_scale * matrix + + if matrix: + loc = tuple(matrix.translationPart()) + scale = tuple(matrix.scalePart()) + + matrix_rot = matrix.rotationPart() + # Lamps need to be rotated + if ob and ob.type =='Lamp': + matrix_rot = mtx_x90 * matrix_rot + rot = tuple(matrix_rot.toEuler()) + elif ob and ob.type =='Camera': + y = Vector(0,1,0) * matrix_rot + matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y) + rot = tuple(matrix_rot.toEuler()) + else: + rot = tuple(matrix_rot.toEuler()) + else: + if not loc: + loc = 0,0,0 + scale = 1,1,1 + rot = 0,0,0 + + return loc, rot, scale, matrix, matrix_rot + + def write_object_tx(ob, loc, matrix, matrix_mod= None): + ''' + We have loc to set the location if non blender objects that have a location + + matrix_mod is only used for bones at the moment + ''' + loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod) file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc) file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot) file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale) return loc, rot, scale, matrix, matrix_rot - def write_object_props(ob=None, loc=None, matrix=None): + def write_object_props(ob=None, loc=None, matrix=None, matrix_mod=None): # if the type is 0 its an empty otherwise its a mesh # only difference at the moment is one has a color file.write(''' @@ -165,16 +602,16 @@ def write_scene(file, sce, world): Property: "QuaternionInterpolate", "bool", "",0 Property: "Visibility", "Visibility", "A+",1''') - loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix) + loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix, matrix_mod) - # Rotation order - # eEULER_XYZ + # Rotation order, note, for FBX files Iv loaded normal order is 1 + # setting to zero. + # eEULER_XYZ = 0 # eEULER_XZY # eEULER_YZX # eEULER_YXZ # eEULER_ZXY - # eEULER_ZYX - + # eEULER_ZYX file.write(''' Property: "RotationOffset", "Vector3D", "",0,0,0 @@ -190,7 +627,7 @@ def write_scene(file, sce, world): Property: "TranslationMaxX", "bool", "",0 Property: "TranslationMaxY", "bool", "",0 Property: "TranslationMaxZ", "bool", "",0 - Property: "RotationOrder", "enum", "",1 + Property: "RotationOrder", "enum", "",0 Property: "RotationSpaceForLimitOnly", "bool", "",0 Property: "AxisLen", "double", "",10 Property: "PreRotation", "Vector3D", "",0,0,0 @@ -240,7 +677,7 @@ def write_scene(file, sce, world): Property: "Show", "bool", "",1 Property: "NegativePercentShapeSupport", "bool", "",1 Property: "DefaultAttributeIndex", "int", "",0''') - if ob: + if ob and type(ob) != Blender.Types.BoneType: # Only mesh objects have color file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') file.write('\n\t\t\tProperty: "Size", "double", "",100') @@ -248,6 +685,42 @@ def write_scene(file, sce, world): return loc, rot, scale, matrix, matrix_rot + + # -------------------------------------------- Armatures + #def write_bone(bone, name, matrix_mod): + def write_bone(my_bone): + file.write('\n\tModel: "Model::%s", "Limb" {' % my_bone.fbxName) + file.write('\n\t\tVersion: 232') + + #poseMatrix = write_object_props(my_bone.blenBone, None, None, my_bone.fbxArm.parRelMatrix())[3] + poseMatrix = write_object_props(my_bone.blenBone)[3] # dont apply bone matricies anymore + pose_items.append( (my_bone.fbxName, poseMatrix) ) + + + # file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((my_bone.blenData.head['ARMATURESPACE'] - my_bone.blenData.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length) + file.write('\n\t\t\tProperty: "Size", "double", "",1') + + #((my_bone.blenData.head['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld) - (my_bone.blenData.tail['ARMATURESPACE'] * my_bone.fbxArm.parRelMatrix())).length) + + """ + file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\ + ((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length) + """ + + file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\ + (my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']).length) + + #file.write('\n\t\t\tProperty: "LimbLength", "double", "",1') + file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8') + file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8') + file.write('\n\t\t}') + file.write('\n\t\tMultiLayer: 0') + file.write('\n\t\tMultiTake: 1') + file.write('\n\t\tShading: Y') + file.write('\n\t\tCulling: "CullingOff"') + file.write('\n\t\tTypeFlags: "Skeleton"') + file.write('\n\t}') + def write_camera_switch(): file.write(''' Model: "Model::Camera Switcher", "CameraSwitcher" { @@ -370,7 +843,7 @@ def write_scene(file, sce, world): write_camera_dummy('Producer Right', (4000,0,0), 1, 30000, 1, (0,1,0)) write_camera_dummy('Producer Left', (-4000,0,0), 1, 30000, 1, (0,1,0)) - def write_camera(ob, name): + def write_camera(my_cam): ''' Write a blender camera ''' @@ -379,11 +852,11 @@ def write_scene(file, sce, world): height = render.sizeY aspect = float(width)/height - data = ob.data + data = my_cam.blenObject.data - file.write('\n\tModel: "Model::%s", "Camera" {' % name ) + file.write('\n\tModel: "Model::%s", "Camera" {' % my_cam.fbxName ) file.write('\n\t\tVersion: 232') - loc, rot, scale, matrix, matrix_rot = write_object_props(ob) + loc, rot, scale, matrix, matrix_rot = write_object_props(my_cam.blenObject, None, my_cam.parRelMatrix()) file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0') file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",%.6f' % data.angle) @@ -471,8 +944,8 @@ def write_scene(file, sce, world): file.write('\n\t\tTypeFlags: "Camera"') file.write('\n\t\tGeometryVersion: 124') file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc) - file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Blender.Mathutils.Vector(0,1,0) * matrix_rot) ) - file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Blender.Mathutils.Vector(0,0,-1)*matrix_rot) ) + file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) ) + file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) ) #file.write('\n\t\tUp: 0,0,0' ) #file.write('\n\t\tLookAt: 0,0,0' ) @@ -483,12 +956,12 @@ def write_scene(file, sce, world): file.write('\n\t\tCameraOrthoZoom: 1') file.write('\n\t}') - def write_light(ob, name): - light = ob.data - file.write('\n\tModel: "Model::%s", "Light" {' % name) + def write_light(my_light): + light = my_light.blenObject.data + file.write('\n\tModel: "Model::%s", "Light" {' % my_light.fbxName) file.write('\n\t\tVersion: 232') - write_object_props(ob) + write_object_props(my_light.blenObject, None, my_light.parRelMatrix()) # Why are these values here twice?????? - oh well, follow the holy sdk's output @@ -499,7 +972,20 @@ def write_scene(file, sce, world): #eSPOT light_type = light.type if light_type > 3: light_type = 0 - + + mode = light.mode + if mode & Blender.Lamp.Modes.RayShadow or mode & Blender.Lamp.Modes.Shadows: + do_shadow = 1 + else: + do_shadow = 0 + + if mode & Blender.Lamp.Modes.OnlyShadow or (mode & Blender.Lamp.Modes.NoDiffuse and mode & Blender.Lamp.Modes.NoSpecular): + do_light = 0 + else: + do_light = 1 + + scale = abs(GLOBAL_MATRIX.scalePart()[0]) # scale is always uniform in this case + file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type) file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1') file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1') @@ -507,15 +993,15 @@ def write_scene(file, sce, world): file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0') file.write('\n\t\t\tProperty: "GoboProperty", "object", ""') file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1') - file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (light.energy*100)) - file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % light.spotSize) + file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 + file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col)) - file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (light.energy*100)) - file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % light.spotSize) + file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (min(light.energy*100, 200))) # clamp below 200 + file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % (light.spotSize * scale)) file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50') file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type) - file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1') + file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",%i' % do_light) file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1') file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0') file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1') @@ -528,7 +1014,7 @@ def write_scene(file, sce, world): file.write('\n\t\t\tProperty: "EnableFarAttenuation", "bool", "",0') file.write('\n\t\t\tProperty: "FarAttenuationStart", "double", "",0') file.write('\n\t\t\tProperty: "FarAttenuationEnd", "double", "",0') - file.write('\n\t\t\tProperty: "CastShadows", "bool", "",0') + file.write('\n\t\t\tProperty: "CastShadows", "bool", "",%i' % do_shadow) file.write('\n\t\t\tProperty: "ShadowColor", "ColorRGBA", "",0,0,0,1') file.write('\n\t\t}') file.write('\n\t\tMultiLayer: 0') @@ -539,12 +1025,36 @@ def write_scene(file, sce, world): file.write('\n\t\tGeometryVersion: 124') file.write('\n\t}') - # Material Settings - if world: - world_amb = world.getAmb() - else: - world_amb = (0,0,0) # Default value + # matrixOnly is not used at the moment + def write_null(my_null = None, fbxName = None, matrixOnly = None): + # ob can be null + if not fbxName: fbxName = my_null.fbxName + + file.write('\n\tModel: "Model::%s", "Null" {' % fbxName) + file.write('\n\t\tVersion: 232') + + # only use this for the root matrix at the moment + if matrixOnly: + poseMatrix = write_object_props(None, None, matrixOnly)[3] + + else: # all other Null's + if my_null: poseMatrix = write_object_props(my_null.blenObject, None, my_null.parRelMatrix())[3] + else: poseMatrix = write_object_props()[3] + + pose_items.append((fbxName, poseMatrix)) + + file.write(''' + } + MultiLayer: 0 + MultiTake: 1 + Shading: Y + Culling: "CullingOff" + TypeFlags: "Null" + }''') + # Material Settings + if world: world_amb = world.getAmb() + else: world_amb = (0,0,0) # Default value def write_material(matname, mat): file.write('\n\tMaterial: "Material::%s", "" {' % matname) @@ -561,6 +1071,7 @@ def write_scene(file, sce, world): mat_hard = (float(mat.hard)-1)/5.10 mat_spec = mat.spec/2.0 mat_alpha = mat.alpha + mat_emit = mat.emit mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS if mat_shadeless: mat_shader = 'Lambert' @@ -578,6 +1089,7 @@ def write_scene(file, sce, world): mat_hard = 20.0 mat_spec = 0.2 mat_alpha = 1.0 + mat_emit = 0.0 mat_shadeless = False mat_shader = 'Phong' @@ -588,27 +1100,27 @@ def write_scene(file, sce, world): file.write('\n\t\tProperties60: {') file.write('\n\t\t\tProperty: "ShadingModel", "KString", "", "%s"' % mat_shader) file.write('\n\t\t\tProperty: "MultiLayer", "bool", "",0') - file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",0,0,0') - file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",1') + file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold) # emit and diffuse color are he same in blender + file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",%.4f' % mat_dif) - file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb) - file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.1f' % mat_amb) - file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold) - file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.1f' % mat_dif) + file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_colamb) + file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.4f' % mat_amb) + file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cold) + file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.4f' % mat_emit) file.write('\n\t\t\tProperty: "Bump", "Vector3D", "",0,0,0') file.write('\n\t\t\tProperty: "TransparentColor", "ColorRGB", "",1,1,1') - file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",0') + file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",%.4f' % (1.0 - mat_alpha)) if not mat_shadeless: - file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols) - file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.1f' % mat_spec) + file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.4f,%.4f,%.4f' % mat_cols) + file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.4f' % mat_spec) file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0') file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0') file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1') - file.write('\n\t\t\tProperty: "Emissive", "Vector3D", "",0,0,0') - file.write('\n\t\t\tProperty: "Ambient", "Vector3D", "",%.1f,%.1f,%.1f' % mat_colamb) - file.write('\n\t\t\tProperty: "Diffuse", "Vector3D", "",%.1f,%.1f,%.1f' % mat_cold) + file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0') + file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb) + file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold) if not mat_shadeless: - file.write('\n\t\t\tProperty: "Specular", "Vector3D", "",%.1f,%.1f,%.1f' % mat_cols) + file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols) file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard) file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha) if not mat_shadeless: @@ -669,15 +1181,21 @@ def write_scene(file, sce, world): Property: "Rotation", "Vector", "A+",0,0,0 Property: "Scaling", "Vector", "A+",1,1,1''') file.write('\n\t\t\tProperty: "Texture alpha", "Number", "A+",%i' % num) + + + # WrapModeU/V 0==rep, 1==clamp, TODO add support file.write(''' Property: "TextureTypeUse", "enum", "",0 Property: "CurrentTextureBlendMode", "enum", "",1 Property: "UseMaterial", "bool", "",0 Property: "UseMipMap", "bool", "",0 Property: "CurrentMappingType", "enum", "",0 - Property: "UVSwap", "bool", "",0 - Property: "WrapModeU", "enum", "",0 - Property: "WrapModeV", "enum", "",0 + Property: "UVSwap", "bool", "",0''') + + file.write('\n\t\t\tProperty: "WrapModeU", "enum", "",%i' % tex.clampX) + file.write('\n\t\t\tProperty: "WrapModeV", "enum", "",%i' % tex.clampY) + + file.write(''' Property: "TextureRotationPivot", "Vector3D", "",0,0,0 Property: "TextureScalingPivot", "Vector3D", "",0,0,0 Property: "VideoProperty", "object", "" @@ -698,194 +1216,143 @@ def write_scene(file, sce, world): Texture_Alpha_Source: "None" Cropping: 0,0,0,0 }''') - - ob_meshes = [] - ob_lights = [] - ob_cameras = [] - materials = {} - textures = {} - armatures = [] # We should export standalone armatures also - armatures_totbones = 0 # we need this because each bone is a model - for ob_base in sce.objects.context: - for ob, mtx in BPyObject.getDerivedObjects(ob_base): - #for ob in [ob_base,]: - ob_type = ob.type - if ob_type == 'Camera': - ob_cameras.append((sane_obname(ob), ob)) - elif ob_type == 'Lamp': - ob_lights.append((sane_obname(ob), ob)) - - else: - if ob_type == 'Mesh': me = ob.getData(mesh=1) - else: me = BPyMesh.getMeshFromObject(ob) - - if me: - mats = me.materials - for mat in mats: - # 2.44 use mat.lib too for uniqueness - if mat: materials[mat.name] = mat - - if me.faceUV: - uvlayer_orig = me.activeUVLayer - for uvlayer in me.getUVLayerNames(): - me.activeUVLayer = uvlayer - for f in me.faces: - img = f.image - if img: textures[img.name] = img - - me.activeUVLayer = uvlayer_orig - - arm = BPyObject.getObjectArmature(ob) - - if arm: - armname = sane_obname(arm) - bones = arm.bones.values() - armatures_totbones += len(bones) - armatures.append((arm, armname, bones)) - else: - armname = None - - #### me.transform(ob.matrixWorld) # Export real ob coords. - #### High Quality, not realy needed for now. - #BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines. - ob_meshes.append( (sane_obname(ob), ob, mtx, me, mats, arm, armname) ) - - del ob_type - - materials = [(sane_matname(mat), mat) for mat in materials.itervalues()] - textures = [(sane_texname(img), img) for img in textures.itervalues()] - materials.sort() # sort by name - textures.sort() - - if not materials: - materials = [('null', None)] - - material_mapping = {} # blen name : index - if textures: - texture_mapping_local = {None:0} # ditto - i = 0 - for texname, tex in textures: - texture_mapping_local[tex.name] = i - i+=1 - textures.insert(0, ('_empty_', None)) - - i = 0 - for matname, mat in materials: - if mat: mat = mat.name - material_mapping[mat] = i - i+=1 - - camera_count = 8 - file.write(''' - -; Object definitions -;------------------------------------------------------------------ -Definitions: { - Version: 100 - Count: %i''' % (\ - 1+1+camera_count+\ - len(ob_meshes)+\ - len(ob_lights)+\ - len(ob_cameras)+\ - len(armatures)+\ - armatures_totbones+\ - len(materials)+\ - (len(textures)*2))) # add 1 for the root model 1 for global settings - - file.write(''' - ObjectType: "Model" { - Count: %i - }''' % (\ - 1+camera_count+\ - len(ob_meshes)+\ - len(ob_lights)+\ - len(ob_cameras)+\ - len(armatures)+\ - armatures_totbones)) # add 1 for the root model - - file.write(''' - ObjectType: "Geometry" { - Count: %i - }''' % len(ob_meshes)) - - if materials: - file.write(''' - ObjectType: "Material" { - Count: %i - }''' % len(materials)) - - if textures: - file.write(''' - ObjectType: "Texture" { - Count: %i - }''' % len(textures)) # add 1 for an empty tex + def write_deformer_skin(obname): + ''' + Each mesh has its own deformer + ''' + file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname) file.write(''' - ObjectType: "Video" { - Count: %i - }''' % len(textures)) # add 1 for an empty tex - - file.write(''' - ObjectType: "GlobalSettings" { - Count: 1 - } -} -''') - - file.write(\ -''' -; Object properties -;------------------------------------------------------------------ - -Objects: {''') - - # To comply with other FBX FILES - write_camera_switch() - - # Write the null object - file.write(''' - Model: "Model::blend_root", "Null" { - Version: 232''') - write_object_props() - - file.write(''' - } + Version: 100 MultiLayer: 0 - MultiTake: 1 - Shading: Y - Culling: "CullingOff" - TypeFlags: "Null" + Type: "Skin" + Properties60: { + } + Link_DeformAcuracy: 50 }''') - for obname, ob in ob_cameras: - write_camera(ob, obname) - - for obname, ob in ob_lights: - write_light(ob, obname) + # in the example was 'Bip01 L Thigh_2' + def write_sub_deformer_skin(my_mesh, my_bone, weights): + + ''' + Each subdeformer is spesific to a mesh, but the bone it links to can be used by many sub-deformers + So the SubDeformer needs the mesh-object name as a prefix to make it unique + + Its possible that there is no matching vgroup in this mesh, in that case no verts are in the subdeformer, + a but silly but dosnt really matter + ''' + file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {' % (my_mesh.fbxName, my_bone.fbxName)) + + file.write(''' + Version: 100 + MultiLayer: 0 + Type: "Cluster" + Properties60: { + Property: "SrcModel", "object", "" + Property: "SrcModelReference", "object", "" + } + UserData: "", ""''') + + # Support for bone parents + if my_mesh.fbxBoneParent: + if my_mesh.fbxBoneParent == my_bone: + # TODO - this is a bit lazy, we could have a simple write loop + # for this case because all weights are 1.0 but for now this is ok + # Parent Bones arent used all that much anyway. + vgroup_data = [(j, 1.0) for j in xrange(len(my_mesh.blenData.verts))] + else: + # This bone is not a parent of this mesh object, no weights + vgroup_data = [] + + else: + # Normal weight painted mesh + if my_bone.blenName in weights[0]: + # Before we used normalized wright list + #vgroup_data = me.getVertsFromGroup(bone.name, 1) + group_index = weights[0].index(my_bone.blenName) + vgroup_data = [(j, weight[group_index]) for j, weight in enumerate(weights[1]) if weight[group_index]] + else: + vgroup_data = [] + + file.write('\n\t\tIndexes: ') + + i = -1 + for vg in vgroup_data: + if i == -1: + file.write('%i' % vg[0]) + i=0 + else: + if i==23: + file.write('\n\t\t') + i=0 + file.write(',%i' % vg[0]) + i+=1 + + file.write('\n\t\tWeights: ') + i = -1 + for vg in vgroup_data: + if i == -1: + file.write('%.8f' % vg[1]) + i=0 + else: + if i==38: + file.write('\n\t\t') + i=0 + file.write(',%.8f' % vg[1]) + i+=1 + + if my_mesh.fbxParent: + # TODO FIXME, this case is broken in some cases. skinned meshes just shouldnt have parents where possible! + m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() ) + else: + # Yes! this is it... - but dosnt work when the mesh is a. + m = mtx4_z90 * (my_bone.restMatrix * my_bone.fbxArm.matrixWorld.copy() * my_mesh.matrixWorld.copy().invert() ) + + #m = mtx4_z90 * my_bone.restMatrix + matstr = mat4x4str(m) + matstr_i = mat4x4str(m.invert()) + + file.write('\n\t\tTransform: %s' % matstr_i) # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/ + file.write('\n\t\tTransformLink: %s' % matstr) + file.write('\n\t}') - for obname, ob, mtx, me, mats, arm, armname in ob_meshes: - file.write('\n\tModel: "Model::%s", "Mesh" {' % sane_obname(ob)) + def write_mesh(my_mesh): + + me = my_mesh.blenData + + # if there are non NULL materials on this mesh + if [mat for mat in my_mesh.blenMaterials if mat]: do_materials = True + else: do_materials = False + + if my_mesh.blenTextures: do_textures = True + else: do_textures = False + + + file.write('\n\tModel: "Model::%s", "Mesh" {' % my_mesh.fbxName) file.write('\n\t\tVersion: 232') # newline is added in write_object_props - write_object_props(ob, None, mtx) + + write_object_props(my_mesh.blenObject, None, my_mesh.parRelMatrix()) + file.write('\n\t\t}') file.write('\n\t\tMultiLayer: 0') file.write('\n\t\tMultiTake: 1') file.write('\n\t\tShading: Y') file.write('\n\t\tCulling: "CullingOff"') + # Write the Real Mesh data here file.write('\n\t\tVertices: ') i=-1 + for v in me.verts: if i==-1: - file.write('%.6f,%.6f,%.6f' % tuple(v.co)) - i=0 + file.write('%.6f,%.6f,%.6f' % tuple(v.co)); i=0 else: if i==7: - file.write('\n\t\t') - i=0 + file.write('\n\t\t'); i=0 file.write(',%.6f,%.6f,%.6f'% tuple(v.co)) i+=1 + file.write('\n\t\tPolygonVertexIndex: ') i=-1 for f in me.faces: @@ -932,22 +1399,20 @@ Objects: {''') MappingInformationType: "ByVertice" ReferenceInformationType: "Direct" Normals: ''') - + i=-1 for v in me.verts: if i==-1: - file.write('%.15f,%.15f,%.15f' % tuple(v.no)) - i=0 + file.write('%.15f,%.15f,%.15f' % tuple(v.no)); i=0 else: if i==2: - file.write('\n ') - i=0 + file.write('\n '); i=0 file.write(',%.15f,%.15f,%.15f' % tuple(v.no)) i+=1 file.write('\n\t\t}') - # Write VertexColor Layers + # note, no programs seem to use this info :/ collayers = [] if me.vertexColors: collayers = me.getColorLayerNames() @@ -1043,32 +1508,47 @@ Objects: {''') file.write('\n\t\t}') - if textures: + if do_textures: file.write('\n\t\tLayerElementTexture: %i {' % uvindex) file.write('\n\t\t\tVersion: 101') file.write('\n\t\t\tName: "%s"' % uvlayer) - file.write(''' - MappingInformationType: "ByPolygon" - ReferenceInformationType: "IndexToDirect" - BlendMode: "Translucent" - TextureAlpha: 1 - TextureId: ''') - i=-1 - for f in me.faces: - img_key = f.image - if img_key: img_key = img_key.name + if len(my_mesh.blenTextures) == 1: + file.write('\n\t\t\tMappingInformationType: "AllSame"') + else: + file.write('\n\t\t\tMappingInformationType: "ByPolygon"') + + file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"') + file.write('\n\t\t\tBlendMode: "Translucent"') + file.write('\n\t\t\tTextureAlpha: 1') + file.write('\n\t\t\tTextureId: ') + + if len(my_mesh.blenTextures) == 1: + file.write('0') + else: + #texture_mapping_local = {None:0} + texture_mapping_local = {None:-1} - if i==-1: - i=0 - file.write( '%s' % texture_mapping_local[img_key]) - else: - if i==55: - file.write('\n ') - i=0 + i = 0 # 1 for dummy + for tex in my_mesh.blenTextures: + texture_mapping_local[tex] = i + i+=1 + + i=-1 + for f in me.faces: + img_key = f.image - file.write(',%s' % texture_mapping_local[img_key]) - i+=1 + if i==-1: + i=0 + file.write( '%s' % texture_mapping_local[img_key]) + else: + if i==55: + file.write('\n ') + i=0 + + file.write(',%s' % texture_mapping_local[img_key]) + i+=1 + else: file.write(''' LayerElementTexture: 0 { @@ -1085,44 +1565,50 @@ Objects: {''') # Done with UV/textures. - if materials: - file.write(''' - LayerElementMaterial: 0 { - Version: 101 - Name: "" - MappingInformationType: "ByPolygon" - ReferenceInformationType: "IndexToDirect" - Materials: ''') - - # Build a material mapping for this - material_mapping_local = {} # local-index : global index. - for i, mat in enumerate(mats): - if mat: - material_mapping_local[i] = material_mapping[mat.name] - else: - material_mapping_local[i] = 0 # None material is zero for now. + if do_materials: + file.write('\n\t\tLayerElementMaterial: 0 {') + file.write('\n\t\t\tVersion: 101') + file.write('\n\t\t\tName: ""') - if not material_mapping_local: - material_mapping_local[0] = 0 + if len(my_mesh.blenMaterials) == 1: + file.write('\n\t\t\tMappingInformationType: "AllSame"') + else: + file.write('\n\t\t\tMappingInformationType: "ByPolygon"') - len_material_mapping_local = len(material_mapping_local) + file.write('\n\t\t\tReferenceInformationType: "IndexToDirect"') + file.write('\n\t\t\tMaterials: ') - i=-1 - for f in me.faces: - f_mat = f.mat - if f_mat >= len_material_mapping_local: - f_mat = 0 + if len(my_mesh.blenMaterials) == 1: + file.write('0') + else: + # Build a material mapping for this + #material_mapping_local = [0] * 16 # local-index : global index. + material_mapping_local = [-1] * 16 # local-index : global index. + i= 0 # 1 + for j, mat in enumerate(my_mesh.blenMaterials): + if mat: + material_mapping_local[j] = i + i+=1 + # else leave as -1 - if i==-1: - i=0 - file.write( '%s' % material_mapping_local[f_mat]) - else: - if i==55: - file.write('\n\t\t\t\t') - i=0 + len_material_mapping_local = len(material_mapping_local) + + i=-1 + for f in me.faces: + f_mat = f.mat + if f_mat >= len_material_mapping_local: + f_mat = 0 - file.write(',%s' % material_mapping_local[f_mat]) - i+=1 + if i==-1: + i=0 + file.write( '%s' % (material_mapping_local[f_mat])) + else: + if i==55: + file.write('\n\t\t\t\t') + i=0 + + file.write(',%s' % (material_mapping_local[f_mat])) + i+=1 file.write('\n\t\t}') @@ -1134,7 +1620,7 @@ Objects: {''') TypedIndex: 0 }''') - if materials: + if do_materials: file.write(''' LayerElement: { Type: "LayerElementMaterial" @@ -1142,7 +1628,7 @@ Objects: {''') }''') # Always write this - if textures: + if do_textures: file.write(''' LayerElement: { Type: "LayerElementTexture" @@ -1179,7 +1665,7 @@ Objects: {''') file.write('\n\t\t\t\tTypedIndex: %i' % i) file.write('\n\t\t\t}') - if textures: + if do_textures: file.write(''' LayerElement: { @@ -1206,7 +1692,425 @@ Objects: {''') file.write('\n\t\t\t\tTypedIndex: %i' % i) file.write('\n\t\t\t}') file.write('\n\t\t}') - file.write('\n\t}') + file.write('\n\t}') + + def write_group(name): + file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {' % name) + + file.write(''' + Properties60: { + Property: "MultiLayer", "bool", "",0 + Property: "Pickable", "bool", "",1 + Property: "Transformable", "bool", "",1 + Property: "Show", "bool", "",1 + } + MultiLayer: 0 + }''') + + + # add meshes here to clear because they are not used anywhere. + meshes_to_clear = [] + + ob_meshes = [] + ob_lights = [] + ob_cameras = [] + # in fbx we export bones as children of the mesh + # armatures not a part of a mesh, will be added to ob_arms + ob_bones = [] + ob_arms = [] + ob_null = [] # emptys + + # List of types that have blender objects (not bones) + ob_all_typegroups = [ob_meshes, ob_lights, ob_cameras, ob_arms, ob_null] + + groups = [] # blender groups, only add ones that have objects in the selections + materials = {} + textures = {} + + tmp_ob_type = ob_type = None # incase no objects are exported, so as not to raise an error + + # if EXP_OBS_SELECTED is false, use sceens objects + if not batch_objects: + if EXP_OBS_SELECTED: tmp_objects = sce.objects.context + else: tmp_objects = sce.objects + else: + tmp_objects = batch_objects + + if EXP_ARMATURE: + # This is needed so applying modifiers dosnt apply the armature deformation, its also needed + # ...so mesh objects return their rest worldspace matrix when bone-parents are exported as weighted meshes. + # set every armature to its rest, backup the original values so we done mess up the scene + ob_arms_orig_rest = [arm.restPosition for arm in bpy.data.armatures] + + for arm in bpy.data.armatures: + arm.restPosition = True + + if ob_arms_orig_rest: + for ob_base in bpy.data.objects: + #if ob_base.type == 'Armature': + ob_base.makeDisplayList() + + # This causes the makeDisplayList command to effect the mesh + Blender.Set('curframe', Blender.Get('curframe')) + + + for ob_base in tmp_objects: + for ob, mtx in BPyObject.getDerivedObjects(ob_base): + #for ob in [ob_base,]: + tmp_ob_type = ob.type + if tmp_ob_type == 'Camera': + if EXP_CAMERA: + ob_cameras.append(my_object_generic(ob, mtx)) + elif tmp_ob_type == 'Lamp': + if EXP_LAMP: + ob_lights.append(my_object_generic(ob, mtx)) + elif tmp_ob_type == 'Armature': + if EXP_ARMATURE: + # TODO - armatures dont work in dupligroups! + if ob not in ob_arms: ob_arms.append(ob) + # ob_arms.append(ob) # replace later. was "ob_arms.append(sane_obname(ob), ob)" + elif tmp_ob_type == 'Empty': + if EXP_EMPTY: + ob_null.append(my_object_generic(ob, mtx)) + elif EXP_MESH: + origData = True + if tmp_ob_type != 'Mesh': + me = bpy.data.meshes.new() + try: me.getFromObject(ob) + except: me = None + if me: + meshes_to_clear.append( me ) + mats = me.materials + origData = False + else: + # Mesh Type! + if EXP_MESH_APPLY_MOD: + me = bpy.data.meshes.new() + me.getFromObject(ob) + + # so we keep the vert groups + if EXP_ARMATURE: + orig_mesh = ob.getData(mesh=1) + if orig_mesh.getVertGroupNames(): + ob.copy().link(me) + # If new mesh has no vgroups we can try add if verts are teh same + if not me.getVertGroupNames(): # vgroups were not kept by the modifier + if len(me.verts) == len(orig_mesh.verts): + groupNames, vWeightDict = BPyMesh.meshWeight2Dict(orig_mesh) + BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) + + # print ob, me, me.getVertGroupNames() + meshes_to_clear.append( me ) + origData = False + mats = me.materials + else: + me = ob.getData(mesh=1) + mats = me.materials + + # Support object colors + tmp_colbits = ob.colbits + if tmp_colbits: + tmp_ob_mats = ob.getMaterials(1) # 1 so we get None's too. + for i in xrange(16): + if tmp_colbits & (1<<i): + mats[i] = tmp_ob_mats[i] + del tmp_ob_mats + del tmp_colbits + + + if me: + # This WILL modify meshes in blender if EXP_MESH_APPLY_MOD is disabled. + # so strictly this is bad. but only in rare cases would it have negative results + # say with dupliverts the objects would rotate a bit differently + if EXP_MESH_HQ_NORMALS: + BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines. + + for mat in mats: + # 2.44 use mat.lib too for uniqueness + if mat: materials[mat] = mat + + texture_mapping_local = {} + if me.faceUV: + uvlayer_orig = me.activeUVLayer + for uvlayer in me.getUVLayerNames(): + me.activeUVLayer = uvlayer + for f in me.faces: + img = f.image + textures[img] = texture_mapping_local[img] = img + + me.activeUVLayer = uvlayer_orig + + if EXP_ARMATURE: + armob = BPyObject.getObjectArmature(ob) + blenParentBoneName = None + + # Note - Fixed in BPyObject but for now just copy the function because testers wont have up to date modukes, + # TODO - remove this for 2.45 release since getObjectArmature has been fixed + if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.ARMATURE: + armob = ob.parent + + # parent bone - special case + if (not armob) and ob.parent and ob.parent.type == 'Armature' and ob.parentType == Blender.Object.ParentTypes.BONE: + armob = ob.parent + blenParentBoneName = ob.parentbonename + + + if armob and armob not in ob_arms: + ob_arms.append(armob) + + else: + blenParentBoneName = armob = None + + my_mesh = my_object_generic(ob, mtx) + my_mesh.blenData = me + my_mesh.origData = origData + my_mesh.blenMaterials = mats + my_mesh.blenTextures = texture_mapping_local.values() + + # if only 1 null texture then empty the list + if len(my_mesh.blenTextures) == 1 and my_mesh.blenTextures[0] == None: + my_mesh.blenTextures = [] + + my_mesh.fbxArm = armob # replace with my_object_generic armature instance later + my_mesh.fbxBoneParent = blenParentBoneName # replace with my_bone instance later + + ob_meshes.append( my_mesh ) + + if EXP_ARMATURE: + # now we have the meshes, restore the rest arm position + for i, arm in enumerate(bpy.data.armatures): + arm.restPosition = ob_arms_orig_rest[i] + + if ob_arms_orig_rest: + for ob_base in bpy.data.objects: + if ob_base.type == 'Armature': + ob_base.makeDisplayList() + # This causes the makeDisplayList command to effect the mesh + Blender.Set('curframe', Blender.Get('curframe')) + + del tmp_ob_type, tmp_objects + + # now we have collected all armatures, add bones + for i, ob in enumerate(ob_arms): + + ob_arms[i] = my_arm = my_object_generic(ob) + + my_arm.fbxBones = [] + my_arm.blenData = ob.data + my_arm.blenAction = ob.action + my_arm.blenActionList = [] + + # fbxName, blenderObject, my_bones, blenderActions + #ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, []) + + for bone in my_arm.blenData.bones.values(): + my_bone = my_bone_class(bone, my_arm) + my_arm.fbxBones.append( my_bone ) + ob_bones.append( my_bone ) + + # add the meshes to the bones and replace the meshes armature with own armature class + #for obname, ob, mtx, me, mats, arm, armname in ob_meshes: + for my_mesh in ob_meshes: + # Replace + # ...this could be sped up with dictionary mapping but its unlikely for + # it ever to be a bottleneck - (would need 100+ meshes using armatures) + if my_mesh.fbxArm: + for my_arm in ob_arms: + if my_arm.blenObject == my_mesh.fbxArm: + my_mesh.fbxArm = my_arm + break + + for my_bone in ob_bones: + + # The mesh uses this bones armature! + if my_bone.fbxArm == my_mesh.fbxArm: + my_bone.blenMeshes[my_mesh.fbxName] = me + + + # parent bone: replace bone names with our class instances + # my_mesh.fbxBoneParent is None or a blender bone name initialy, replacing if the names match. + if my_mesh.fbxBoneParent == my_bone.blenName: + my_mesh.fbxBoneParent = my_bone + + bone_deformer_count = 0 # count how many bones deform a mesh + my_bone_blenParent = None + for my_bone in ob_bones: + my_bone_blenParent = my_bone.blenBone.parent + if my_bone_blenParent: + for my_bone_parent in ob_bones: + # Note 2.45rc2 you can compare bones normally + if my_bone_blenParent.name == my_bone_parent.blenName and my_bone.fbxArm == my_bone_parent.fbxArm: + my_bone.parent = my_bone_parent + break + + # Not used at the moment + # my_bone.calcRestMatrixLocal() + bone_deformer_count += len(my_bone.blenMeshes) + + del my_bone_blenParent + + + # Build blenObject -> fbxObject mapping + # this is needed for groups as well as fbxParenting + bpy.data.objects.tag = False + tmp_obmapping = {} + for ob_generic in ob_all_typegroups: + for ob_base in ob_generic: + ob_base.blenObject.tag = True + tmp_obmapping[ob_base.blenObject] = ob_base + + # Build Groups from objects we export + for blenGroup in bpy.data.groups: + fbxGroupName = None + for ob in blenGroup.objects: + if ob.tag: + if fbxGroupName == None: + fbxGroupName = sane_groupname(blenGroup) + groups.append((fbxGroupName, blenGroup)) + + tmp_obmapping[ob].fbxGroupNames.append(fbxGroupName) # also adds to the objects fbxGroupNames + + groups.sort() # not really needed + + # Assign parents using this mapping + for ob_generic in ob_all_typegroups: + for my_ob in ob_generic: + parent = my_ob.blenObject.parent + if parent and parent.tag: # does it exist and is it in the mapping + my_ob.fbxParent = tmp_obmapping[parent] + + + del tmp_obmapping + # Finished finding groups we use + + + materials = [(sane_matname(mat), mat) for mat in materials.itervalues() if mat] + textures = [(sane_texname(img), img) for img in textures.itervalues() if img] + materials.sort() # sort by name + textures.sort() + + camera_count = 8 + file.write(''' + +; Object definitions +;------------------------------------------------------------------ + +Definitions: { + Version: 100 + Count: %i''' % (\ + 1+1+camera_count+\ + len(ob_meshes)+\ + len(ob_lights)+\ + len(ob_cameras)+\ + len(ob_arms)+\ + len(ob_null)+\ + len(ob_bones)+\ + bone_deformer_count+\ + len(materials)+\ + (len(textures)*2))) # add 1 for the root model 1 for global settings + + del bone_deformer_count + + file.write(''' + ObjectType: "Model" { + Count: %i + }''' % (\ + 1+camera_count+\ + len(ob_meshes)+\ + len(ob_lights)+\ + len(ob_cameras)+\ + len(ob_arms)+\ + len(ob_null)+\ + len(ob_bones))) # add 1 for the root model + + file.write(''' + ObjectType: "Geometry" { + Count: %i + }''' % len(ob_meshes)) + + if materials: + file.write(''' + ObjectType: "Material" { + Count: %i + }''' % len(materials)) + + if textures: + file.write(''' + ObjectType: "Texture" { + Count: %i + }''' % len(textures)) # add 1 for an empty tex + file.write(''' + ObjectType: "Video" { + Count: %i + }''' % len(textures)) # add 1 for an empty tex + + tmp = 0 + # Add deformer nodes + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + tmp+=1 + + # Add subdeformers + for my_bone in ob_bones: + tmp += len(my_bone.blenMeshes) + + if tmp: + file.write(''' + ObjectType: "Deformer" { + Count: %i + }''' % tmp) + del tmp + + # we could avoid writing this possibly but for now just write it + + file.write(''' + ObjectType: "Pose" { + Count: 1 + }''') + + if groups: + file.write(''' + ObjectType: "GroupSelection" { + Count: %i + }''' % len(groups)) + + file.write(''' + ObjectType: "GlobalSettings" { + Count: 1 + } +}''') + + file.write(''' + +; Object properties +;------------------------------------------------------------------ + +Objects: {''') + + # To comply with other FBX FILES + write_camera_switch() + + # Write the null object + write_null(None, 'blend_root')# , GLOBAL_MATRIX) + + for my_null in ob_null: + write_null(my_null) + + for my_arm in ob_arms: + write_null(my_arm) + + for my_cam in ob_cameras: + write_camera(my_cam) + + for my_light in ob_lights: + write_light(my_light) + + for my_mesh in ob_meshes: + write_mesh(my_mesh) + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + write_bone(my_bone) write_camera_default() @@ -1221,6 +2125,51 @@ Objects: {''') write_texture(texname, tex, i) i+=1 + for groupname, group in groups: + write_group(groupname) + + # NOTE - c4d and motionbuilder dont need normalized weights, but deep-exploration 5 does and (max?) do. + + # Write armature modifiers + # TODO - add another MODEL? - because of this skin definition. + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + write_deformer_skin(my_mesh.fbxName) + + # Get normalized weights for temorary use + if my_mesh.fbxBoneParent: + weights = None + else: + weights = meshNormalizedWeights(my_mesh.blenData) + + #for bonename, bone, obname, bone_mesh, armob in ob_bones: + for my_bone in ob_bones: + if me in my_bone.blenMeshes.itervalues(): + write_sub_deformer_skin(my_mesh, my_bone, weights) + + # Write pose's really weired, only needed when an armature and mesh are used together + # each by themselves dont need pose data. for now only pose meshes and bones + + file.write(''' + Pose: "Pose::BIND_POSES", "BindPose" { + Type: "BindPose" + Version: 100 + Properties60: { + } + NbPoseNodes: ''') + file.write(str(len(pose_items))) + + + for fbxName, matrix in pose_items: + file.write('\n\t\tPoseNode: {') + file.write('\n\t\t\tNode: "Model::%s"' % fbxName ) + if matrix: file.write('\n\t\t\tMatrix: %s' % mat4x4str(matrix)) + else: file.write('\n\t\t\tMatrix: %s' % mat4x4str(mtx4_identity)) + file.write('\n\t\t}') + + file.write('\n\t}') + + # Finish Writing Objects # Write global settings file.write(''' @@ -1233,7 +2182,7 @@ Objects: {''') Property: "FrontAxisSign", "int", "",1 Property: "CoordAxis", "int", "",0 Property: "CoordAxisSign", "int", "",1 - Property: "UnitScaleFactor", "double", "",1 + Property: "UnitScaleFactor", "double", "",100 } } ''') @@ -1247,14 +2196,26 @@ Objects: {''') Relations: {''') file.write('\n\tModel: "Model::blend_root", "Null" {\n\t}') - for obname, ob in ob_cameras: - file.write('\n\tModel: "Model::%s", "Camera" {\n\t}' % obname) + + for my_null in ob_null: + file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_null.fbxName) + + for my_arm in ob_arms: + file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % my_arm.fbxName) + + for my_mesh in ob_meshes: + file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % my_mesh.fbxName) + + # TODO - limbs can have the same name for multiple armatures, should prefix. + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + file.write('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_bone.fbxName) - for obname, ob in ob_lights: - file.write('\n\tModel: "Model::%s", "Light" {\n\t}' % obname) + for my_cam in ob_cameras: + file.write('\n\tModel: "Model::%s", "Camera" {\n\t}' % my_cam.fbxName) - for obname, ob, mtx, me, mats, arm, armname in ob_meshes: - file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % obname) + for my_light in ob_lights: + file.write('\n\tModel: "Model::%s", "Light" {\n\t}' % my_light.fbxName) file.write(''' Model: "Model::Producer Perspective", "Camera" { @@ -1283,6 +2244,23 @@ Relations: {''') for texname, tex in textures: file.write('\n\tVideo: "Video::%s", "Clip" {\n\t}' % texname) + # deformers - modifiers + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {\n\t}' % my_mesh.fbxName) + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + for fbxMeshObName in my_bone.blenMeshes: # .keys() - fbxMeshObName + # is this bone effecting a mesh? + file.write('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {\n\t}' % (fbxMeshObName, my_bone.fbxName)) + + # This should be at the end + # file.write('\n\tPose: "Pose::BIND_POSES", "BindPose" {\n\t}') + + for groupname, group in groups: + file.write('\n\tGroupSelection: "GroupSelection::%s", "Default" {\n\t}' % groupname) + file.write('\n}') file.write(''' @@ -1290,64 +2268,357 @@ Relations: {''') ;------------------------------------------------------------------ Connections: {''') + + # NOTE - The FBX SDK dosnt care about the order but some importers DO! + # for instance, defining the material->mesh connection + # before the mesh->blend_root crashes cinema4d + # write the fake root node file.write('\n\tConnect: "OO", "Model::blend_root", "Model::Scene"') - for obname, ob in ob_cameras: - file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname) - - for obname, ob in ob_lights: - file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname) - - for obname, ob, mtx, me, mats, arm, armname in ob_meshes: - file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname) + for ob_generic in ob_all_typegroups: # all blender 'Object's we support + for my_ob in ob_generic: + if my_ob.fbxParent: + file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName)) + else: + file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_ob.fbxName) - for obname, ob, mtx, me, mats, arm, armname in ob_meshes: - # Connect all materials to all objects, not good form but ok for now. - for mat in mats: - file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_matname(mat), obname)) + if materials: + for my_mesh in ob_meshes: + # Connect all materials to all objects, not good form but ok for now. + for mat in my_mesh.blenMaterials: + if mat: + file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_name_mapping_mat[mat.name], my_mesh.fbxName)) if textures: - for obname, ob, mtx, me, mats, arm, armname in ob_meshes: - for texname, tex in textures: - file.write('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (texname, obname)) + for my_mesh in ob_meshes: + if my_mesh.blenTextures: + # file.write('\n\tConnect: "OO", "Texture::_empty_", "Model::%s"' % my_mesh.fbxName) + for tex in my_mesh.blenTextures: + if tex: + file.write('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (sane_name_mapping_tex[tex.name], my_mesh.fbxName)) for texname, tex in textures: file.write('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname)) + for my_mesh in ob_meshes: + if my_mesh.fbxArm: + file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName)) + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + for fbxMeshObName in my_bone.blenMeshes: # .keys() + file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName)) + + # limbs -> deformers + # for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + for fbxMeshObName in my_bone.blenMeshes: # .keys() + file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName)) + + + #for bonename, bone, obname, me, armob in ob_bones: + for my_bone in ob_bones: + # Always parent to armature now + if my_bone.parent: + file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.parent.fbxName) ) + else: + # the armature object is written as an empty and all root level bones connect to it + file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.fbxArm.fbxName) ) + + # groups + if groups: + for ob_generic in ob_all_typegroups: + for ob_base in ob_generic: + for fbxGroupName in ob_base.fbxGroupNames: + file.write('\n\tConnect: "OO", "Model::%s", "GroupSelection::%s"' % (ob_base.fbxName, fbxGroupName)) + + for my_arm in ob_arms: + file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % my_arm.fbxName) + file.write('\n}') - # Clear mesh data Only when writing with modifiers applied - #for obname, ob, me, mats, arm, armname in objects: - # me.verts = None + # Needed for scene footer as well as animation + render = sce.render + + # from the FBX sdk + #define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000)) + def fbx_time(t): + # 0.5 + val is the same as rounding. + return int(0.5 + ((t/fps) * 46186158000)) + + fps = float(render.fps) + start = render.sFrame + end = render.eFrame + if end < start: start, end = end, start + if start==end: ANIM_ENABLE = False + + # animations for these object types + ob_anim_lists = ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms + + if ANIM_ENABLE and [tmp for tmp in ob_anim_lists if tmp]: + + frame_orig = Blender.Get('curframe') + + if ANIM_OPTIMIZE: + ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** ANIM_OPTIMIZE_PRECISSION + + # default action, when no actions are avaioable + tmp_actions = [None] # None is the default action + blenActionDefault = None + action_lastcompat = None + + if ANIM_ACTION_ALL: + bpy.data.actions.tag = False + tmp_actions = list(bpy.data.actions) + + + # find which actions are compatible with the armatures + # blenActions is not yet initialized so do it now. + tmp_act_count = 0 + for my_arm in ob_arms: + + # get the default name + if not blenActionDefault: + blenActionDefault = my_arm.blenAction + + arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones]) + + for action in tmp_actions: + + action_chan_names = arm_bone_names.intersection( set(action.getChannelNames()) ) + + if action_chan_names: # at least one channel matches. + my_arm.blenActionList.append(action) + action.tag = True + tmp_act_count += 1 + + # incase there is no actions applied to armatures + action_lastcompat = action + + if tmp_act_count: + # unlikely to ever happen but if no actions applied to armatures, just use the last compatible armature. + if not blenActionDefault: + blenActionDefault = action_lastcompat + + del action_lastcompat + + file.write(''' +;Takes and animation section +;---------------------------------------------------- +Takes: {''') + + if blenActionDefault: + file.write('\n\tCurrent: "%s"' % sane_takename(blenActionDefault)) + else: + file.write('\n\tCurrent: "Default Take"') + + for blenAction in tmp_actions: + # we have tagged all actious that are used be selected armatures + if blenAction: + if blenAction.tag: + print '\taction: "%s" exporting...' % blenAction.name + else: + print '\taction: "%s" has no armature using it, skipping' % blenAction.name + continue + + if blenAction == None: + # Warning, this only accounts for tmp_actions being [None] + file.write('\n\tTake: "Default Take" {') + act_start = start + act_end = end + else: + # use existing name + if blenAction == blenActionDefault: # have we alredy got the name + file.write('\n\tTake: "%s" {' % sane_name_mapping_take[blenAction.name]) + else: + file.write('\n\tTake: "%s" {' % sane_takename(blenAction)) + + tmp = blenAction.getFrameNumbers() + if tmp: + act_start = min(tmp) + act_end = max(tmp) + del tmp + else: + # Fallback on this, theres not much else we can do? :/ + # when an action has no length + act_start = start + act_end = end + + # Set the action active + for my_bone in ob_arms: + if blenAction in my_bone.blenActionList: + ob.action = blenAction + # print '\t\tSetting Action!', blenAction + # sce.update(1) + + file.write('\n\t\tFileName: "Default_Take.tak"') # ??? - not sure why this is needed + file.write('\n\t\tLocalTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed + file.write('\n\t\tReferenceTime: %i,%i' % (fbx_time(act_start-1), fbx_time(act_end-1))) # ??? - not sure why this is needed + + file.write(''' -def write_footer(file, sce, world): + ;Models animation + ;----------------------------------------------------''') + + + # set pose data for all bones + # do this here incase the action changes + ''' + for my_bone in ob_bones: + my_bone.flushAnimData() + ''' + i = act_start + while i <= act_end: + Blender.Set('curframe', i) + for ob_generic in ob_anim_lists: + for my_ob in ob_generic: + #Blender.Window.RedrawAll() + if ob_generic == ob_meshes and my_ob.fbxArm: + # We cant animate armature meshes! + pass + else: + my_ob.setPoseFrame(i) + + i+=1 + + + #for bonename, bone, obname, me, armob in ob_bones: + for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms): + + for my_ob in ob_generic: + + if ob_generic == ob_meshes and my_ob.fbxArm: + # do nothing, + pass + else: + + file.write('\n\t\tModel: "Model::%s" {' % my_ob.fbxName) # ??? - not sure why this is needed + file.write('\n\t\t\tVersion: 1.1') + file.write('\n\t\t\tChannel: "Transform" {') + + context_bone_anim_mats = [ (my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in xrange(act_start, act_end+1) ] + + # ---------------- + # ---------------- + for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale + + if TX_CHAN=='T': context_bone_anim_vecs = [mtx[0].translationPart() for mtx in context_bone_anim_mats] + elif TX_CHAN=='R': context_bone_anim_vecs = [mtx[1].toEuler() for mtx in context_bone_anim_mats] + else: context_bone_anim_vecs = [mtx[0].scalePart() for mtx in context_bone_anim_mats] + + file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation + + for i in xrange(3): + # Loop on each axis of the bone + file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation + file.write('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i] ) + file.write('\n\t\t\t\t\t\tKeyVer: 4005') + + if not ANIM_OPTIMIZE: + # Just write all frames, simple but in-eficient + file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start)) + file.write('\n\t\t\t\t\t\tKey: ') + frame = act_start + while frame <= act_end: + if frame!=act_start: + file.write(',') + + # Curve types are + # C,n is for bezier? - linear is best for now so we can do simple keyframe removal + file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) + #file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame-1), context_bone_anim_vecs[frame-act_start][i] )) + frame+=1 + else: + # remove unneeded keys, j is the frame, needed when some frames are removed. + context_bone_anim_keys = [ (vec[i], j) for j, vec in enumerate(context_bone_anim_vecs) ] + + # last frame to fisrt frame, missing 1 frame on either side. + # removeing in a backwards loop is faster + for j in xrange( (act_end-act_start)-1, 0, -1 ): + # Is this key reduenant? + if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j-1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and\ + abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j+1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT: + del context_bone_anim_keys[j] + + if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]: + # This axis has no moton, its okay to skip KeyCount and Keys in this case + pass + else: + # We only need to write these if there is at least one + file.write('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys)) + file.write('\n\t\t\t\t\t\tKey: ') + for val, frame in context_bone_anim_keys: + if frame != context_bone_anim_keys[0][1]: # not the first + file.write(',') + # frame is alredy one less then blenders frame + file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), val )) + #file.write('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame), val )) + + if i==0: file.write('\n\t\t\t\t\t\tColor: 1,0,0') + elif i==1: file.write('\n\t\t\t\t\t\tColor: 0,1,0') + elif i==2: file.write('\n\t\t\t\t\t\tColor: 0,0,1') + + file.write('\n\t\t\t\t\t}') + file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) ) + file.write('\n\t\t\t\t}') + + # --------------- + + file.write('\n\t\t\t}') + file.write('\n\t\t}') + + # end the take + file.write('\n\t}') + + # end action loop. set original actions + # do this after every loop incase actions effect eachother. + for my_bone in ob_arms: + my_bone.blenObject.action = my_bone.blenAction + + file.write('\n}') + + Blender.Set('curframe', frame_orig) + + else: + # no animation + file.write('\n;Takes and animation section') + file.write('\n;----------------------------------------------------') + file.write('\n') + file.write('\nTakes: {') + file.write('\n\tCurrent: ""') + file.write('\n}') - tuple(world.hor) - tuple(world.amb) - has_mist = world.mode & 1 + # write meshes animation + #for obname, ob, mtx, me, mats, arm, armname in ob_meshes: + - mist_intense, mist_start, mist_end, mist_height = world.mist + # Clear mesh data Only when writing with modifiers applied + for me in meshes_to_clear: + me.verts = None - render = sce.render - file.write('\n;Takes and animation section') - file.write('\n;----------------------------------------------------') - file.write('\n') - file.write('\nTakes: {') - file.write('\n\tCurrent: ""') - file.write('\n}') + + # --------------------------- Footer + if world: + has_mist = world.mode & 1 + mist_intense, mist_start, mist_end, mist_height = world.mist + world_hor = world.hor + else: + has_mist = mist_intense = mist_start = mist_end = mist_height = 0 + world_hor = 0,0,0 + file.write('\n;Version 5 settings') file.write('\n;------------------------------------------------------------------') file.write('\n') file.write('\nVersion5: {') file.write('\n\tAmbientRenderSettings: {') file.write('\n\t\tVersion: 101') - file.write('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world.amb)) + file.write('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world_amb)) file.write('\n\t}') file.write('\n\tFogOptions: {') file.write('\n\t\tFlogEnable: %i' % has_mist) @@ -1355,15 +2626,15 @@ def write_footer(file, sce, world): file.write('\n\t\tFogDensity: %.3f' % mist_intense) file.write('\n\t\tFogStart: %.3f' % mist_start) file.write('\n\t\tFogEnd: %.3f' % mist_end) - file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world.hor)) + file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world_hor)) file.write('\n\t}') file.write('\n\tSettings: {') - file.write('\n\t\tFrameRate: "%i"' % render.fps) + file.write('\n\t\tFrameRate: "%i"' % int(fps)) file.write('\n\t\tTimeFormat: 1') file.write('\n\t\tSnapOnFrames: 0') file.write('\n\t\tReferenceTimeIndex: -1') - file.write('\n\t\tTimeLineStartTime: %i' % render.sFrame) - file.write('\n\t\tTimeLineStopTime: %i' % render.eFrame) + file.write('\n\t\tTimeLineStartTime: %i' % fbx_time(start-1)) + file.write('\n\t\tTimeLineStopTime: %i' % fbx_time(end-1)) file.write('\n\t}') file.write('\n\tRendererSetting: {') file.write('\n\t\tDefaultCamera: "Producer Perspective"') @@ -1377,24 +2648,329 @@ def write_footer(file, sce, world): sane_name_mapping_mat.clear() sane_name_mapping_tex.clear() + ob_arms[:] = [] + ob_bones[:] = [] + ob_cameras[:] = [] + ob_lights[:] = [] + ob_meshes[:] = [] + ob_null[:] = [] + + + # copy images if enabled + if EXP_IMAGE_COPY: + copy_images( Blender.sys.dirname(filename), [ tex[1] for tex in textures if tex[1] != None ]) + + print 'export finished in %.4f sec.' % (Blender.sys.time() - start_time) + return True + -import bpy -def write_ui(filename): - if not filename.lower().endswith('.fbx'): - filename += '.fbx' +# -------------------------------------------- +# UI Function - not a part of the exporter. +# this is to seperate the user interface from the rest of the exporter. +from Blender import Draw, Window +EVENT_NONE = 0 +EVENT_EXIT = 1 +EVENT_REDRAW = 2 +EVENT_FILESEL = 3 + +GLOBALS = {} + +# export opts + +def do_redraw(e,v): GLOBALS['EVENT'] = e + +# toggle between these 2, only allow one on at once +def do_obs_sel(e,v): + GLOBALS['EVENT'] = e + GLOBALS['EXP_OBS_SCENE'].val = 0 + GLOBALS['EXP_OBS_SELECTED'].val = 1 + +def do_obs_sce(e,v): + GLOBALS['EVENT'] = e + GLOBALS['EXP_OBS_SCENE'].val = 1 + GLOBALS['EXP_OBS_SELECTED'].val = 0 + +def do_obs_sce(e,v): + GLOBALS['EVENT'] = e + GLOBALS['EXP_OBS_SCENE'].val = 1 + GLOBALS['EXP_OBS_SELECTED'].val = 0 + +def do_batch_type_grp(e,v): + GLOBALS['EVENT'] = e + GLOBALS['BATCH_GROUP'].val = 1 + GLOBALS['BATCH_SCENE'].val = 0 + +def do_batch_type_sce(e,v): + GLOBALS['EVENT'] = e + GLOBALS['BATCH_GROUP'].val = 0 + GLOBALS['BATCH_SCENE'].val = 1 + +def do_anim_act_all(e,v): + GLOBALS['EVENT'] = e + GLOBALS['ANIM_ACTION_ALL'][0].val = 1 + GLOBALS['ANIM_ACTION_ALL'][1].val = 0 + +def do_anim_act_cur(e,v): + if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val: + Draw.PupMenu('Warning%t|Cant use this with batch export group option') + else: + GLOBALS['EVENT'] = e + GLOBALS['ANIM_ACTION_ALL'][0].val = 0 + GLOBALS['ANIM_ACTION_ALL'][1].val = 1 + +def fbx_ui_exit(e,v): + GLOBALS['EVENT'] = e + +def do_help(e,v): + url = 'http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_fbx' + print 'Trying to open web browser with documentation at this address...' + print '\t' + url - #if not BPyMessages.Warning_SaveOver(filename): - # return - sce = bpy.data.scenes.active - world = sce.world + try: + import webbrowser + webbrowser.open(url) + except: + print '...could not open a browser window.' + + + +# run when export is pressed +#def fbx_ui_write(e,v): +def fbx_ui_write(filename): + + # Dont allow overwriting files when saving normally + if not GLOBALS['BATCH_ENABLE'].val: + if not BPyMessages.Warning_SaveOver(filename): + return + + GLOBALS['EVENT'] = EVENT_EXIT + + # Keep the order the same as above for simplicity + # the [] is a dummy arg used for objects Blender.Window.WaitCursor(1) - file = open(filename, 'w') - write_header(file) - write_scene(file, sce, world) - write_footer(file, sce, world) + + # Make the matrix + GLOBAL_MATRIX = mtx4_identity + GLOBAL_MATRIX[0][0] = GLOBAL_MATRIX[1][1] = GLOBAL_MATRIX[2][2] = GLOBALS['_SCALE'].val + if GLOBALS['_XROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_x90n + if GLOBALS['_YROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_y90n + if GLOBALS['_ZROT90'].val: GLOBAL_MATRIX = GLOBAL_MATRIX * mtx4_z90n + + ret = write(\ + filename, None,\ + GLOBALS['EXP_OBS_SELECTED'].val,\ + GLOBALS['EXP_MESH'].val,\ + GLOBALS['EXP_MESH_APPLY_MOD'].val,\ + GLOBALS['EXP_MESH_HQ_NORMALS'].val,\ + GLOBALS['EXP_ARMATURE'].val,\ + GLOBALS['EXP_LAMP'].val,\ + GLOBALS['EXP_CAMERA'].val,\ + GLOBALS['EXP_EMPTY'].val,\ + GLOBALS['EXP_IMAGE_COPY'].val,\ + GLOBAL_MATRIX,\ + GLOBALS['ANIM_ENABLE'].val,\ + GLOBALS['ANIM_OPTIMIZE'].val,\ + GLOBALS['ANIM_OPTIMIZE_PRECISSION'].val,\ + GLOBALS['ANIM_ACTION_ALL'][0].val,\ + GLOBALS['BATCH_ENABLE'].val,\ + GLOBALS['BATCH_GROUP'].val,\ + GLOBALS['BATCH_SCENE'].val,\ + GLOBALS['BATCH_FILE_PREFIX'].val,\ + GLOBALS['BATCH_OWN_DIR'].val,\ + ) + Blender.Window.WaitCursor(0) + GLOBALS.clear() + + if ret == False: + Draw.PupMenu('Error%t|Path cannot be written to!') + + +def fbx_ui(): + # Only to center the UI + x,y = GLOBALS['MOUSE'] + x-=180; y-=0 # offset... just to get it centered + + Draw.Label('Export Objects...', x+20,y+165, 200, 20) + + if not GLOBALS['BATCH_ENABLE'].val: + Draw.BeginAlign() + GLOBALS['EXP_OBS_SELECTED'] = Draw.Toggle('Selected Objects', EVENT_REDRAW, x+20, y+145, 160, 20, GLOBALS['EXP_OBS_SELECTED'].val, 'Export selected objects on visible layers', do_obs_sel) + GLOBALS['EXP_OBS_SCENE'] = Draw.Toggle('Scene Objects', EVENT_REDRAW, x+180, y+145, 160, 20, GLOBALS['EXP_OBS_SCENE'].val, 'Export all objects in this scene', do_obs_sce) + Draw.EndAlign() + + Draw.BeginAlign() + GLOBALS['_SCALE'] = Draw.Number('Scale:', EVENT_NONE, x+20, y+120, 140, 20, GLOBALS['_SCALE'].val, 0.01, 1000.0, 'Scale all data, (Note! some imports dont support scaled armatures)') + GLOBALS['_XROT90'] = Draw.Toggle('Rot X90', EVENT_NONE, x+160, y+120, 60, 20, GLOBALS['_XROT90'].val, 'Rotate all objects 90 degrese about the X axis') + GLOBALS['_YROT90'] = Draw.Toggle('Rot Y90', EVENT_NONE, x+220, y+120, 60, 20, GLOBALS['_YROT90'].val, 'Rotate all objects 90 degrese about the Y axis') + GLOBALS['_ZROT90'] = Draw.Toggle('Rot Z90', EVENT_NONE, x+280, y+120, 60, 20, GLOBALS['_ZROT90'].val, 'Rotate all objects 90 degrese about the Z axis') + Draw.EndAlign() + + y -= 35 + + Draw.BeginAlign() + GLOBALS['EXP_EMPTY'] = Draw.Toggle('Empty', EVENT_NONE, x+20, y+120, 60, 20, GLOBALS['EXP_EMPTY'].val, 'Export empty objects') + GLOBALS['EXP_CAMERA'] = Draw.Toggle('Camera', EVENT_NONE, x+80, y+120, 60, 20, GLOBALS['EXP_CAMERA'].val, 'Export camera objects') + GLOBALS['EXP_LAMP'] = Draw.Toggle('Lamp', EVENT_NONE, x+140, y+120, 60, 20, GLOBALS['EXP_LAMP'].val, 'Export lamp objects') + GLOBALS['EXP_ARMATURE'] = Draw.Toggle('Armature', EVENT_NONE, x+200, y+120, 60, 20, GLOBALS['EXP_ARMATURE'].val, 'Export armature objects') + GLOBALS['EXP_MESH'] = Draw.Toggle('Mesh', EVENT_REDRAW, x+260, y+120, 80, 20, GLOBALS['EXP_MESH'].val, 'Export mesh objects', do_redraw) #, do_axis_z) + Draw.EndAlign() + + if GLOBALS['EXP_MESH'].val: + # below mesh but + Draw.BeginAlign() + GLOBALS['EXP_MESH_APPLY_MOD'] = Draw.Toggle('Modifiers', EVENT_NONE, x+260, y+100, 80, 20, GLOBALS['EXP_MESH_APPLY_MOD'].val, 'Apply modifiers to mesh objects') #, do_axis_z) + GLOBALS['EXP_MESH_HQ_NORMALS'] = Draw.Toggle('HQ Normals', EVENT_NONE, x+260, y+80, 80, 20, GLOBALS['EXP_MESH_HQ_NORMALS'].val, 'Generate high quality normals') #, do_axis_z) + Draw.EndAlign() + + GLOBALS['EXP_IMAGE_COPY'] = Draw.Toggle('Copy Image Files', EVENT_NONE, x+20, y+80, 160, 20, GLOBALS['EXP_IMAGE_COPY'].val, 'Copy image files to the destination path') #, do_axis_z) + + + Draw.Label('Export Armature Animation...', x+20,y+45, 300, 20) + + GLOBALS['ANIM_ENABLE'] = Draw.Toggle('Enable Animation', EVENT_REDRAW, x+20, y+25, 160, 20, GLOBALS['ANIM_ENABLE'].val, 'Export keyframe animation', do_redraw) + if GLOBALS['ANIM_ENABLE'].val: + Draw.BeginAlign() + GLOBALS['ANIM_OPTIMIZE'] = Draw.Toggle('Optimize Keyframes', EVENT_REDRAW, x+20, y+0, 160, 20, GLOBALS['ANIM_OPTIMIZE'].val, 'Remove double keyframes', do_redraw) + if GLOBALS['ANIM_OPTIMIZE'].val: + GLOBALS['ANIM_OPTIMIZE_PRECISSION'] = Draw.Number('Precission: ', EVENT_NONE, x+180, y+0, 160, 20, GLOBALS['ANIM_OPTIMIZE_PRECISSION'].val, 3, 16, 'Tolerence for comparing double keyframes (higher for greater accuracy)') + Draw.EndAlign() + + Draw.BeginAlign() + GLOBALS['ANIM_ACTION_ALL'][1] = Draw.Toggle('Current Action', EVENT_REDRAW, x+20, y-25, 160, 20, GLOBALS['ANIM_ACTION_ALL'][1].val, 'Use actions currently applied to the armatures (use scene start/end frame)', do_anim_act_cur) + GLOBALS['ANIM_ACTION_ALL'][0] = Draw.Toggle('All Actions', EVENT_REDRAW, x+180,y-25, 160, 20, GLOBALS['ANIM_ACTION_ALL'][0].val, 'Use all actions for armatures', do_anim_act_all) + Draw.EndAlign() + + + Draw.Label('Export Batch...', x+20,y-60, 300, 20) + GLOBALS['BATCH_ENABLE'] = Draw.Toggle('Enable Batch', EVENT_REDRAW, x+20, y-80, 160, 20, GLOBALS['BATCH_ENABLE'].val, 'Automate exporting multiple scenes or groups to files', do_redraw) + + if GLOBALS['BATCH_ENABLE'].val: + Draw.BeginAlign() + GLOBALS['BATCH_GROUP'] = Draw.Toggle('Group > File', EVENT_REDRAW, x+20, y-105, 160, 20, GLOBALS['BATCH_GROUP'].val, 'Export each group as an FBX file', do_batch_type_grp) + GLOBALS['BATCH_SCENE'] = Draw.Toggle('Scene > File', EVENT_REDRAW, x+180, y-105, 160, 20, GLOBALS['BATCH_SCENE'].val, 'Export each scene as an FBX file', do_batch_type_sce) + + # Own dir requires OS module + if os: + GLOBALS['BATCH_OWN_DIR'] = Draw.Toggle('Own Dir', EVENT_NONE, x+20, y-125, 80, 20, GLOBALS['BATCH_OWN_DIR'].val, 'Create a dir for each exported file') + GLOBALS['BATCH_FILE_PREFIX'] = Draw.String('Prefix: ', EVENT_NONE, x+100, y-125, 240, 20, GLOBALS['BATCH_FILE_PREFIX'].val, 64, 'Prefix each file with this name ') + else: + GLOBALS['BATCH_FILE_PREFIX'] = Draw.String('Prefix: ', EVENT_NONE, x+20, y-125, 320, 20, GLOBALS['BATCH_FILE_PREFIX'].val, 64, 'Prefix each file with this name ') + + + Draw.EndAlign() + #y+=80 + + ''' + Draw.BeginAlign() + GLOBALS['FILENAME'] = Draw.String('path: ', EVENT_NONE, x+20, y-170, 300, 20, GLOBALS['FILENAME'].val, 64, 'Prefix each file with this name ') + Draw.PushButton('..', EVENT_FILESEL, x+320, y-170, 20, 20, 'Select the path', do_redraw) + ''' + # Until batch is added + # + + + #Draw.BeginAlign() + Draw.PushButton('Online Help', EVENT_REDRAW, x+20, y-160, 100, 20, 'Open online help in a browser window', do_help) + Draw.PushButton('Cancel', EVENT_EXIT, x+130, y-160, 100, 20, 'Exit the exporter', fbx_ui_exit) + Draw.PushButton('Export', EVENT_FILESEL, x+240, y-160, 100, 20, 'Export the fbx file', do_redraw) + + #Draw.PushButton('Export', EVENT_EXIT, x+180, y-160, 160, 20, 'Export the fbx file', fbx_ui_write) + #Draw.EndAlign() + + # exit when mouse out of the view? + # GLOBALS['EVENT'] = EVENT_EXIT + +#def write_ui(filename): +def write_ui(): + + # globals + GLOBALS['EVENT'] = 2 + #GLOBALS['MOUSE'] = Window.GetMouseCoords() + GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] + GLOBALS['FILENAME'] = '' + ''' + # IF called from the fileselector + if filename == None: + GLOBALS['FILENAME'] = filename # Draw.Create(Blender.sys.makename(ext='.fbx')) + else: + GLOBALS['FILENAME'].val = filename + ''' + GLOBALS['EXP_OBS_SELECTED'] = Draw.Create(1) # dont need 2 variables but just do this for clarity + GLOBALS['EXP_OBS_SCENE'] = Draw.Create(0) + + GLOBALS['EXP_MESH'] = Draw.Create(1) + GLOBALS['EXP_MESH_APPLY_MOD'] = Draw.Create(1) + GLOBALS['EXP_MESH_HQ_NORMALS'] = Draw.Create(0) + GLOBALS['EXP_ARMATURE'] = Draw.Create(1) + GLOBALS['EXP_LAMP'] = Draw.Create(1) + GLOBALS['EXP_CAMERA'] = Draw.Create(1) + GLOBALS['EXP_EMPTY'] = Draw.Create(1) + GLOBALS['EXP_IMAGE_COPY'] = Draw.Create(0) + # animation opts + GLOBALS['ANIM_ENABLE'] = Draw.Create(1) + GLOBALS['ANIM_OPTIMIZE'] = Draw.Create(1) + GLOBALS['ANIM_OPTIMIZE_PRECISSION'] = Draw.Create(6) # decimal places + GLOBALS['ANIM_ACTION_ALL'] = [Draw.Create(0), Draw.Create(1)] # not just the current action + + # batch export options + GLOBALS['BATCH_ENABLE'] = Draw.Create(0) + GLOBALS['BATCH_GROUP'] = Draw.Create(1) # cant have both of these enabled at once. + GLOBALS['BATCH_SCENE'] = Draw.Create(0) # see above + GLOBALS['BATCH_FILE_PREFIX'] = Draw.Create(Blender.sys.makename(ext='_').split('\\')[-1].split('/')[-1]) + GLOBALS['BATCH_OWN_DIR'] = Draw.Create(0) + # done setting globals + + # Used by the user interface + GLOBALS['_SCALE'] = Draw.Create(1.0) + GLOBALS['_XROT90'] = Draw.Create(True) + GLOBALS['_YROT90'] = Draw.Create(False) + GLOBALS['_ZROT90'] = Draw.Create(False) + + # horrible ugly hack so tooltips draw, dosnt always work even + # Fixed in Draw.UIBlock for 2.45rc2, but keep this until 2.45 is released + Window.SetKeyQualifiers(0) + while Window.GetMouseButtons(): Blender.sys.sleep(10) + for i in xrange(100): Window.QHandle(i) + # END HORRID HACK + + # best not do move the cursor + # Window.SetMouseCoords(*[i/2 for i in Window.GetScreenSize()]) + + # hack so the toggle buttons redraw. this is not nice at all + while GLOBALS['EVENT'] != EVENT_EXIT: + + if GLOBALS['BATCH_ENABLE'].val and GLOBALS['BATCH_GROUP'].val and GLOBALS['ANIM_ACTION_ALL'][1].val: + #Draw.PupMenu("Warning%t|Cant batch export groups with 'Current Action' ") + GLOBALS['ANIM_ACTION_ALL'][0].val = 1 + GLOBALS['ANIM_ACTION_ALL'][1].val = 0 + + if GLOBALS['EVENT'] == EVENT_FILESEL: + if GLOBALS['BATCH_ENABLE'].val: + txt = 'Batch FBX Dir' + name = Blender.sys.expandpath('//') + else: + txt = 'Export FBX' + name = Blender.sys.makename(ext='.fbx') + + Blender.Window.FileSelector(fbx_ui_write, txt, name) + #fbx_ui_write('/test.fbx') + break + + Draw.UIBlock(fbx_ui) + + + # GLOBALS.clear() +#test = [write_ui] if __name__ == '__main__': - Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx')) - #write_ui('/test.fbx') + # Cant call the file selector first because of a bug in the interface that crashes it. + # Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx')) + #write('/scratch/test.fbx') + #write_ui('/scratch/test.fbx') + + if not set: + Draw.PupMenu('Error%t|A full install of python2.3 or python 2.4+ is needed to run this script.') + else: + write_ui() diff --git a/release/scripts/export_m3g.py b/release/scripts/export_m3g.py index afb019fcc1e..08215b3d027 100644 --- a/release/scripts/export_m3g.py +++ b/release/scripts/export_m3g.py @@ -234,6 +234,19 @@ Tooltip: 'Export to M3G' # # * Modul shutil is not needed any longer. Exporter has its own copy_file. # (realized and inspired by ideasman_42 and Martin Neumann) +# +# History 0.8 +# +# * Blender works with SpotAngles 1..180 but M3G works only with 0..90 +# M3G use the 'half angle' (cut off angle) (Thanks to Martin Storsjö) +# +# * Error fixed: Texture coordinates was not calculated correct. +# (Thanks to Milan Piskla, Vlad, Max Gilead, Regis Cosnier ...) +# +# * New options in GUI: +# M3G Version 2.0 : Will export M3G files Vers. 2.0 in future +# Game Physics: Adds Game Physics infos for NOPE API +# # --------------------------------------------------------------------------# # TODO: Export only selected mesh # TODO: Optimize Bones <--> Vertex Group mapping @@ -1213,7 +1226,7 @@ class M3GVertexArray(M3GObject3D): # Reverse t coordinate because M3G uses a different 2D coordinate system than Blender. if self.uvmapping: for i in range(0,len(self.components),2): - self.components[i]= int(self.components[i]*(-1)) + self.components[i+1]= int(self.components[i+1]*(-1)) #Error in Version 0.7 for i in range(len(self.components)): if abs(self.components[i])>maxValue:raise Exception( i+". element too great/small!") @@ -1284,7 +1297,7 @@ class M3GVertexArray(M3GObject3D): self.blenderIndexes[key]=index #print"blenderIndexes",self.blenderIndexes else: - # print "VertexArray.append: element=",element + print "VertexArray.append: element=",element self.components.append(element) class M3GVertexBuffer(M3GObject3D): @@ -2001,7 +2014,7 @@ class M3GTranslator: self.scene = Blender.Scene.GetCurrent() self.world = self.translateWorld(self.scene) - for obj in self.scene.objects: + for obj in self.scene.objects : if obj.getType()=='Camera': # older Version: isinstance(obj.getData(),Types.CameraType) self.translateCamera(obj) elif obj.getType()=='Mesh': @@ -2176,7 +2189,7 @@ class M3GTranslator: if mOptions.createAmbientLight & mOptions.lightingEnabled: lLight = M3GLight() lLight.mode = lLight.modes['AMBIENT'] - lLight.color = self.translateRGB(AllWorlds[0].getAmb()) + lLight.color = self.translateRGB(blWorld.getAmb()) self.nodes.append(lLight) #TODO: Set background picture from world @@ -2550,7 +2563,7 @@ class M3GTranslator: mLight.intensity = lamp.energy #SpotAngle, SpotExponent (SPOT) if lampType == Lamp.Types.Spot: - mLight.spotAngle = lamp.spotSize + mLight.spotAngle = lamp.spotSize/2 mLight.spotExponent = lamp.spotBlend self.translateToNode(obj,mLight) @@ -2945,6 +2958,8 @@ class OptionMgr: self.perspectiveCorrection = rdict['perspectiveCorrection'] self.smoothShading = rdict['smoothShading'] self.exportAsJava = rdict['exportAsJava'] + self.exportVersion2 = rdict['exportVersion2'] + self.exportGamePhysics = rdict['exportGamePhysics'] except: self.save() # if data isn't valid, rewrite it @@ -2958,6 +2973,8 @@ class OptionMgr: self.perspectiveCorrection = False self.smoothShading = True self.exportAsJava = False + self.exportVersion2 = False + self.exportGamePhysics = False def save(self): d = {} @@ -2970,7 +2987,9 @@ class OptionMgr: d['perspectiveCorrection'] = self.perspectiveCorrection d['smoothShading'] = self.smoothShading d['exportAsJava'] = self.exportAsJava - + d['exportVersion2'] = self.exportVersion2 + d['exportGamePhysics'] = self.exportGamePhysics + Blender.Registry.SetKey('M3GExport', d, True) @@ -2993,6 +3012,8 @@ def gui(): perspectiveCorrection = Draw.Create(mOptions.perspectiveCorrection) smoothShading = Draw.Create(mOptions.smoothShading) exportAsJava = Draw.Create(mOptions.exportAsJava) + exportVersion2 = Draw.Create(mOptions.exportVersion2) + exportGamePhysics = Draw.Create(mOptions.exportGamePhysics) pupBlock = [\ ('Texturing'),\ @@ -3008,7 +3029,9 @@ def gui(): ('Posing'),\ ('All Armature Actions', exportAllActions, 'Exports all actions for armatures'),\ ('Export'),\ - ('As Java Source', exportAsJava, 'Exports scene as Java source code') + ('As Java Source', exportAsJava, 'Exports scene as Java source code'),\ + ('M3G Version 2.0', exportVersion2, 'Exports M3G Version 2.0 File'),\ + ('Game Physics', exportGamePhysics, 'Includes Game Physics infos for NOPE in export') ] # Only execute if use didn't quit (ESC). @@ -3022,6 +3045,8 @@ def gui(): mOptions.perspectiveCorrection = perspectiveCorrection.val mOptions.smoothShading = smoothShading.val mOptions.exportAsJava = exportAsJava.val + mOptions.exportVersion2 = exportVersion2.val + mOptions.exportGamePhysics = exportGamePhysics.val mOptions.save() if mOptions.exportAsJava: diff --git a/release/scripts/export_mdd.py b/release/scripts/export_mdd.py index 42a85029505..8173059ca5a 100644 --- a/release/scripts/export_mdd.py +++ b/release/scripts/export_mdd.py @@ -58,7 +58,8 @@ def mdd_export(filepath, ob, PREF_STARTFRAME, PREF_ENDFRAME, PREF_FPS): Window.EditMode(0) Blender.Window.WaitCursor(1) - mesh_orig = ob.getData(mesh=1) + mesh_orig = Mesh.New() + mesh_orig.getFromObject(ob.name) #Flip y and z ''' diff --git a/release/scripts/export_obj.py b/release/scripts/export_obj.py index 4c7ced2d200..8e7374f02b5 100644 --- a/release/scripts/export_obj.py +++ b/release/scripts/export_obj.py @@ -68,15 +68,11 @@ def fixName(name): else: return name.replace(' ', '_') -# Used to add the scene name into the filename without using odd chars -global MTL_DICT - # A Dict of Materials # (material.name, image.name):matname_imagename # matname_imagename has gaps removed. MTL_DICT = {} def write_mtl(filename): - global MTL_DICT world = Blender.World.GetCurrent() if world: @@ -88,23 +84,14 @@ def write_mtl(filename): file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) file.write('# Material Count: %i\n' % len(MTL_DICT)) # Write material/image combinations we have used. - for key, mtl_mat_name in MTL_DICT.iteritems(): + for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems(): # Get the Blender data for the material and the image. # Having an image named None will make a bug, dont do it :) file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname - if key[0] == None: - #write a dummy material here? - file.write('Ns 0\n') - file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, - file.write('Kd 0.8 0.8 0.8\n') - file.write('Ks 0.8 0.8 0.8\n') - file.write('d 1\n') # No alpha - file.write('illum 2\n') # light normaly - else: - mat = Blender.Material.Get(key[0]) + if mat: file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse @@ -120,13 +107,20 @@ def write_mtl(filename): else: file.write('illum 2\n') # light normaly + else: + #write a dummy material here? + file.write('Ns 0\n') + file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, + file.write('Kd 0.8 0.8 0.8\n') + file.write('Ks 0.8 0.8 0.8\n') + file.write('d 1\n') # No alpha + file.write('illum 2\n') # light normaly # Write images! - if key[1] != None: # We have an image on the face! - img = Image.Get(key[1]) + if img: # We have an image on the face! file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image - elif key[0] != None: # No face image. if we havea material search for MTex image. + elif not mat: # No face image. if we havea material search for MTex image. for mtex in mat.getTextures(): if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: try: @@ -157,26 +151,26 @@ def copy_images(dest_dir): # Get unique image names uniqueImages = {} - for matname, imagename in MTL_DICT.iterkeys(): # Only use image name + for matname, mat, image in MTL_DICT.itervalues(): # Only use image name # Get Texface images - if imagename != None: - uniqueImages[imagename] = None # Should use sets here. wait until Python 2.4 is default. + if image: + uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. # Get MTex images - if matname != None: - mat= Blender.Material.Get(matname) + if mat: for mtex in mat.getTextures(): if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: - try: - uniqueImages[mtex.tex.image.name] = None - except: - pass + image_tex = mtex.tex.image + if image_tex: + try: + uniqueImages[image_tex] = image_tex + except: + pass # Now copy images copyCount = 0 - for imageName in uniqueImages.iterkeys(): - bImage = Image.Get(imageName) + for bImage in uniqueImages.itervalues(): image_path = sys.expandpath(bImage.filename) if sys.exists(image_path): # Make a name for the target path. @@ -187,12 +181,6 @@ def copy_images(dest_dir): copyCount+=1 print '\tCopied %d images' % copyCount -def veckey3d(v): - return round(v.x, 6), round(v.y, 6), round(v.z, 6) - -def veckey2d(v): - return round(v.x, 6), round(v.y, 6) - def write(filename, objects,\ EXPORT_TRI=False, EXPORT_EDGES=False, EXPORT_NORMALS=False, EXPORT_NORMALS_HQ=False,\ EXPORT_UV=True, EXPORT_MTL=True, EXPORT_COPY_IMAGES=False,\ @@ -204,8 +192,14 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) eg. write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. ''' + + def veckey3d(v): + return round(v.x, 6), round(v.y, 6), round(v.z, 6) + + def veckey2d(v): + return round(v.x, 6), round(v.y, 6) + print 'OBJ Export path: "%s"' % filename - global MTL_DICT temp_mesh_name = '~tmp-mesh' time1 = sys.time() @@ -241,7 +235,7 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) # Initialize totals, these are updated each object totverts = totuvco = totno = 1 - face_vert_index = 1 # used for uvs now + face_vert_index = 1 globalNormals = {} @@ -253,7 +247,11 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scn) if not me: continue - faceuv= me.faceUV + + if EXPORT_UV: + faceuv= me.faceUV + else: + faceuv = False # We have a valid mesh if EXPORT_TRI and me.faces: @@ -293,14 +291,21 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) me.transform(ob_mat) # High Quality Normals - if EXPORT_NORMALS and EXPORT_NORMALS_HQ and faces: - BPyMesh.meshCalcNormals(me) + if EXPORT_NORMALS and faces: + if EXPORT_NORMALS_HQ: + BPyMesh.meshCalcNormals(me) + else: + # transforming normals is incorrect + # when the matrix is scaled, + # better to recalculate them + me.calcNormals() # # Crash Blender #materials = me.getMaterials(1) # 1 == will return None in the list. materials = me.materials materialNames = [] + materialItems = materials[:] if materials: for mat in materials: if mat: # !=None @@ -313,13 +318,13 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) # Possible there null materials, will mess up indicies # but at least it will export, wait until Blender gets fixed. materialNames.extend((16-len(materialNames)) * [None]) - + materialItems.extend((16-len(materialItems)) * [None]) # Sort by Material, then images # so we dont over context switch in the obj file. if EXPORT_MORPH_TARGET: pass - elif faceuv and EXPORT_UV: + elif faceuv: try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) elif len(materials) > 1: @@ -353,10 +358,23 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) file.write('v %.6f %.6f %.6f\n' % tuple(v.co)) # UV - if faceuv and EXPORT_UV: - for f in faces: - for uv in f.uv: - file.write('vt %.6f %.6f 0.0\n' % tuple(uv)) + if faceuv: + uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ + + uv_dict = {} # could use a set() here + for f_index, f in enumerate(faces): + + for uv_index, uv in enumerate(f.uv): + uvkey = veckey2d(uv) + try: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] + except: + uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) + file.write('vt %.6f %.6f\n' % tuple(uv)) + + uv_unique_count = len(uv_dict) + del uv, uvkey, uv_dict, f_index, uv_index + # Only need uv_unique_count and uv_face_mapping # NORMAL, Smooth/Non smoothed. if EXPORT_NORMALS: @@ -376,18 +394,22 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) totno +=1 file.write('vn %.6f %.6f %.6f\n' % noKey) - for f in faces: + if not faceuv: + f_image = None + + for f_index, f in enumerate(faces): f_v= f.v + f_smooth= f.smooth + f_mat = min(f.mat, len(materialNames)-1) if faceuv: + f_image = f.image f_uv= f.uv # MAKE KEY - if EXPORT_UV and faceuv and f.image: # Object is always true. - key = materialNames[min(f.mat,len(materialNames)-1)], f.image.name - #key = materialNames[f.mat], f.image.name + if faceuv and f_image: # Object is always true. + key = materialNames[f_mat], f_image.name else: - key = materialNames[min(f.mat,len(materialNames)-1)], None # No image, use None instead. - #key = materialNames[f.mat], None # No image, use None instead. + key = materialNames[f_mat], None # No image, use None instead. # CHECK FOR CONTEXT SWITCH if key == contextMat: @@ -395,16 +417,13 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) else: if key[0] == None and key[1] == None: # Write a null material, since we know the context has changed. - matstring = '(null)' + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s\n' % (fixName(ob.name), fixName(ob.getData(1))) ) # can be mat_image or (null) file.write('usemtl (null)\n') # mat, image else: - try: # Faster to try then 2x dict lookups. - # We have the material, just need to write the context switch, - matstring = MTL_DICT[key] - - - except KeyError: + mat_data= MTL_DICT.get(key) + if not mat_data: # First add to global dict so we can export to mtl # Then write mtl @@ -413,32 +432,31 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) # If none image dont bother adding it to the name if key[1] == None: - matstring = MTL_DICT[key] ='%s' % fixName(key[0]) + mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image else: - matstring = MTL_DICT[key] = '%s_%s' % (fixName(key[0]), fixName(key[1])) - - if EXPORT_GROUP_BY_MAT: - file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), matstring) ) # can be mat_image or (null) - file.write('usemtl %s\n' % matstring) # can be mat_image or (null) + mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image + + if EXPORT_GROUP_BY_MAT: + file.write('g %s_%s_%s\n' % (fixName(ob.name), fixName(ob.getData(1)), mat_data[0]) ) # can be mat_image or (null) + file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) contextMat = key - - if f.smooth != contextSmooth: - if contextSmooth: # on now off - file.write('s off\n') - contextSmooth = True - else: # was off now on + if f_smooth != contextSmooth: + if f_smooth: # on now off file.write('s 1\n') - contextSmooth = False + contextSmooth = f_smooth + else: # was off now on + file.write('s off\n') + contextSmooth = f_smooth file.write('f') - if faceuv and EXPORT_UV: + if faceuv: if EXPORT_NORMALS: - if f.smooth: # Smoothed, use vertex normals + if f_smooth: # Smoothed, use vertex normals for vi, v in enumerate(f_v): file.write( ' %d/%d/%d' % (\ v.index+totverts,\ - face_vert_index + vi,\ + totuvco + uv_face_mapping[f_index][vi],\ globalNormals[ veckey3d(v.no) ])) # vert, uv, normal else: # No smoothing, face normals @@ -446,20 +464,20 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) for vi, v in enumerate(f_v): file.write( ' %d/%d/%d' % (\ v.index+totverts,\ - face_vert_index + vi,\ + totuvco + uv_face_mapping[f_index][vi],\ no)) # vert, uv, normal else: # No Normals for vi, v in enumerate(f_v): file.write( ' %d/%d' % (\ v.index+totverts,\ - face_vert_index + vi)) # vert, uv + totuvco + uv_face_mapping[f_index][vi])) # vert, uv face_vert_index += len(f_v) else: # No UV's if EXPORT_NORMALS: - if f.smooth: # Smoothed, use vertex normals + if f_smooth: # Smoothed, use vertex normals for v in f_v: file.write( ' %d//%d' % (\ v.index+totverts,\ @@ -486,6 +504,8 @@ EXPORT_GROUP_BY_OB=False, EXPORT_GROUP_BY_MAT=False, EXPORT_MORPH_TARGET=False) # Make the indicies global rather then per mesh totverts += len(me.verts) + if faceuv: + totuvco += uv_unique_count me.verts= None file.close() @@ -512,9 +532,8 @@ def write_ui(filename): if not filename.lower().endswith('.obj'): filename += '.obj' - #if not BPyMessages.Warning_SaveOver(filename): - # return - + if not BPyMessages.Warning_SaveOver(filename): + return EXPORT_APPLY_MODIFIERS = Draw.Create(1) EXPORT_ROTX90 = Draw.Create(1) @@ -535,7 +554,6 @@ def write_ui(filename): # removed too many options are bad! - # Get USER Options pup_block = [\ ('Context...'),\ @@ -631,14 +649,14 @@ def write_ui(filename): full_path= ''.join(context_name) - if BPyMessages.Warning_SaveOver(full_path): - # EXPORT THE FILE. - write(full_path, export_objects,\ - EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\ - EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\ - EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\ - EXPORT_ROTX90, EXPORT_BLEN_OBS,\ - EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_MORPH_TARGET) + # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. + # EXPORT THE FILE. + write(full_path, export_objects,\ + EXPORT_TRI, EXPORT_EDGES, EXPORT_NORMALS,\ + EXPORT_NORMALS_HQ, EXPORT_UV, EXPORT_MTL,\ + EXPORT_COPY_IMAGES, EXPORT_APPLY_MODIFIERS,\ + EXPORT_ROTX90, EXPORT_BLEN_OBS,\ + EXPORT_GROUP_BY_OB, EXPORT_GROUP_BY_MAT, EXPORT_MORPH_TARGET) Blender.Set('curframe', orig_frame) diff --git a/release/scripts/flt_defaultp.py b/release/scripts/flt_defaultp.py new file mode 100644 index 00000000000..5dca8ba63d7 --- /dev/null +++ b/release/scripts/flt_defaultp.py @@ -0,0 +1 @@ +pal = [-256,0,16711680,-16777216,-19529984,-19726592,-19923200,-20119808,-20316416,-20578560,-20840704,-21102848,-21364992,-21692672,-22020352,-22413568,-22806784,-23200000,-23658752,-24117504,-24641792,-25166080,-25755904,-26411264,-27066624,-27787520,-28573952,-29425920,-30343424,-31326464,-32375040,-33489152,-354550016,-371458304,-388366592,-405274880,-422183168,-439156992,-456130816,-473104640,-506855680,-540672256,-574488832,-608305408,-642121984,-676004096,-709886208,-760611072,-811335936,-862060800,-912851200,-980418816,-1048051968,-1115685120,-1183383808,-1267925248,-1352466688,-1453850880,-1555300608,-1656815872,-1775173888,-1893597440,-2028863744,2130771712,-1010376448,-1043996416,-1077681920,-1111367424,-1145052928,-1178738432,-1229201152,-1279663872,-1330126592,-1380654848,-1431183104,-1498488576,-1565794048,-1633165056,-1700536064,-1784684288,-1868832512,-1969823488,-2070814464,2123096320,2005262592,1887428864,1752752384,1601298688,1449779456,1281417472,1096278272,911073536,709026048,490201344,254533888,2023680,-1380857856,-1397700608,-1431320576,-1464940544,-1498560512,-1532180480,-1565865984,-1599551488,-1650014208,-1700476928,-1750939648,-1801402368,-1851865088,-1919170560,-1986476032,-2053781504,-2121086976,2089797632,2005649408,1904723968,1803798528,1686030336,1568262144,1450493952,1315883008,1164494848,1013041152,844810240,659736576,457885696,239192064,3655680,-1767919872,-1784762624,-1801605376,-1818448128,-1852068096,-1885688064,-1919308032,-1952928000,-1986547968,-2020167936,-2070565120,-2120962304,2123542272,2073079552,2022616832,1955376896,1888136960,1820897024,1736879872,1652797184,1568714496,1467854592,1366994688,1249357568,1131654912,997175040,862695168,711372544,560049920,391950080,207007488,5287680,2139657728,2122880512,2106103296,2089326080,2072548864,2055771648,2022217216,1988662784,1955108352,1921553920,1887933952,1854313984,1803916800,1753519616,1703122432,1652725248,1602328064,1535153664,1467979264,1400804864,1316853248,1232901632,1148950016,1048221184,947426816,846632448,729060864,611489280,477140480,326014464,174888448,6919680,1837268224,1820491008,1803713792,1786936576,1770159360,1753382144,1736604928,1719827712,1686273280,1652718848,1619164416,1585609984,1552055552,1518501120,1468169472,1417837824,1367506176,1317174528,1266842880,1199734016,1132625152,1065516288,998407424,914521344,830635264,729971968,629308672,528645376,411204864,293764352,159546624,8551680,-2086957824,-2103735040,-2120512256,-2137289472,2140900608,2107346176,2073791744,2040237056,2006682368,1973127680,1939572992,1906018304,1855686400,1805354496,1755022592,1704690688,1654358784,1587249664,1520140544,1453031424,1369145088,1285258496,1201371904,1100708096,1000044288,882603264,765162240,630943744,496725248,345729536,177956608,10183680,-1699438080,-1716215552,-1732993024,-1766547712,-1800102400,-1833657088,-1867211776,-1900766464,-1934321152,-1967875840,-2018207744,-2068539904,-2118872064,2125763072,2058653696,1991544320,1924434944,1857325568,1773438720,1689551872,1588887808,1488223744,1387559424,1270117888,1152676352,1018457600,884238592,733242368,565468672,397694976,213144064,11815680,-1311918848,-1345473536,-1379028224,-1412582912,-1446137600,-1479692544,-1513247488,-1546802432,-1597134592,-1647466752,-1697798912,-1748131072,-1798463488,-1865573120,-1932682752,-1999792384,-2083679232,2127400960,2043513856,1942849536,1842184960,1724743168,1607301376,1473082112,1338862848,1187866112,1020092160,852317952,667766528,466437632,248331264,13447680,-924399104,-957954048,-991508992,-1025063936,-1058618880,-1092173824,-1142505984,-1192838144,-1243170560,-1293502976,-1343835392,-1410945024,-1478054656,-1545164544,-1629051648,-1712938752,-1796826112,-1897490688,-1998155264,-2098820096,2078705152,1961262848,1827043328,1676046336,1525049344,1357274880,1172722944,971393792,753287168,518403072,283518720,15079680,-570434304,-603989248,-637544192,-671099136,-704654080,-754986496,-805318912,-855651328,-905983744,-973093632,-1040203520,-1107313408,-1174423296,-1258310656,-1342198016,-1442862848,-1543527680,-1644192512,-1761634816,-1879077120,-2013296896,2147450624,1996453376,1828678656,1660903936,1476351744,1275022080,1056914944,822030336,570368256,301928704,16711680,-503325440,-536880384,-570435328,-603990272,-637545216,-671100416,-721432832,-771765248,-822097664,-872430336,-922763008,-989872896,-1056982784,-1124092928,-1191203072,-1275090688,-1358978304,-1459643136,-1560308224,-1660973312,-1778415872,-1895858432,-2030078464,2113891328,1962893824,1795118848,1610566400,1426013696,1224683520,1006575872,771690752,520028160,-452993792,-469771520,-503326464,-536881408,-570436352,-603991552,-637546752,-671101952,-721434368,-771767040,-822099712,-872432384,-922765056,-989875200,-1056985344,-1124095744,-1191206144,-1275093760,-1358981632,-1459646720,-1560312064,-1677754624,-1795197440,-1912640512,-2046860800,2097108736,1946110720,1778335232,1593782272,1392451840,1174343936,939458560,-419439360,-436217088,-452994816,-469772544,-503327488,-536882688,-570437888,-603993088,-637548288,-671103744,-721436416,-771769088,-822101760,-872434688,-922767616,-989878016,-1056988416,-1124098816,-1207986688,-1291874560,-1375762688,-1476428032,-1577093632,-1694536704,-1811979776,-1946200320,-2080421120,2063547904,1912549376,1744773376,1560219904,1358888960,-385884928,-402662656,-419440384,-436218112,-452995840,-469773824,-503329024,-536884224,-570439424,-603994880,-637550336,-671105792,-721438464,-771771392,-822104320,-872437504,-922770688,-989881088,-1056991744,-1124102400,-1191213312,-1275101440,-1358989824,-1459655680,-1560321536,-1677764864,-1795208448,-1912652288,-2046873600,2097094912,1946095872,1778319360,-335553280,-352331008,-369108736,-385886464,-402664192,-419442176,-436220160,-452998144,-486553344,-520108800,-553664256,-587219712,-620775168,-654330880,-687886592,-738219776,-788552960,-838886144,-889219584,-939553024,-1006663936,-1073774848,-1140886016,-1224774656,-1308663296,-1392552192,-1493218560,-1593885184,-1711329280,-1828773632,-1962995456,-2097217536,-285221632,-301999360,-318777088,-335554816,-352332544,-369110528,-385888512,-402666496,-419444480,-436222720,-453000960,-469779200,-503334656,-536890368,-570446080,-604002048,-637558016,-671113984,-721447424,-771780864,-822114560,-872448256,-922782208,-989893632,-1057005056,-1124116736,-1191228672,-1275118080,-1359007744,-1459674880,-1560342272,-1677787136,-234889984,-234890496,-251668224,-268445952,-285223680,-302001664,-318779648,-335557632,-352335616,-369113856,-385892096,-402670336,-419448576,-436227072,-453005568,-469784320,-503340288,-536896256,-570452480,-604008704,-637565184,-671121664,-704678400,-755012608,-805346816,-855681280,-906016000,-973128192,-1040240640,-1107353344,-1174466304,-1258356736,-234889984,-234890496,-234891008,-234891520,-234892032,-234892800,-234893568,-234894336,-234895104,-251673344,-268451584,-285229824,-302008064,-318786560,-335565056,-352343808,-369122560,-385901312,-402680320,-419459328,-436238592,-453017856,-486574592,-520131584,-553688576,-587245824,-620803328,-654361088,-687919104,-738254592,-788590336,-838926336,-234889984,-234890496,-234891008,-234891520,-234892032,-234892800,-234893568,-234894336,-234895104,-234896128,-234897152,-234898176,-234899200,-234900480,-234901760,-234903296,-234904832,-234906368,-234908160,-234909952,-234912000,-251691264,-268470784,-285250560,-302030336,-318810368,-335590656,-352371200,-369152000,-385933056,-402714368,-419495936,-8960,-9472,-9984,-10496,-11008,-11776,-12544,-13312,-14080,-15104,-16128,-17152,-18176,-19456,-20736,-22272,-23808,-25344,-27136,-28928,-30976,-33024,-35328,-37888,-40448,-43264,-46336,-49664,-53248,-57088,-61184,-65536,-926464,-926976,-927488,-928000,-928512,-929280,-930048,-930816,-931584,-932608,-933632,-934656,-935680,-936960,-938240,-939776,-941312,-1008384,-1075712,-1143040,-1210624,-1278208,-1346048,-1414144,-1482240,-1550592,-1619200,-1688064,-1757184,-1826560,-1896192,-2031616,-926464,-926976,-927488,-928000,-928512,-929280,-930048,-996352,-1062656,-1129216,-1195776,-1262336,-1328896,-1395712,-1462528,-1529600,-1596672,-1663744,-1731072,-1798400,-1865984,-1999104,-2132480,-2266112,-2399744,-2533632,-2667776,-2867712,-3067904,-3268352,-3469056,-3670016,-926464,-992512,-1058560,-1124608,-1190656,-1256960,-1323264,-1389568,-1455872,-1522432,-1588992,-1655552,-1722112,-1788928,-1855744,-1988352,-2120960,-2253568,-2386432,-2519296,-2652416,-2785536,-2984448,-3183616,-3382784,-3582208,-3847424,-4112896,-4378624,-4644608,-4976384,-5308416,-1188608,-1254656,-1320704,-1386752,-1452800,-1519104,-1585408,-1651712,-1718016,-1784576,-1851136,-1983232,-2115328,-2247680,-2380032,-2512640,-2645248,-2777856,-2976256,-3174656,-3373312,-3571968,-3836416,-4101120,-4365824,-4630784,-4961536,-5292544,-5689344,-6086400,-6483712,-6946816,-1385216,-1451264,-1517312,-1583360,-1649408,-1715712,-1782016,-1848320,-1980160,-2112256,-2244352,-2376448,-2508544,-2640896,-2838784,-3036928,-3235072,-3433216,-3631616,-3895552,-4159744,-4423936,-4688384,-5018624,-5348864,-5744896,-6141184,-6537728,-7000064,-7462656,-7991040,-8585216,-1581824,-1647872,-1713920,-1779968,-1846016,-1977856,-2109696,-2241536,-2373376,-2505472,-2637568,-2769664,-2967296,-3165184,-3363072,-3561216,-3759360,-4023040,-4286976,-4550912,-4880640,-5210368,-5540352,-5936128,-6331904,-6793472,-7255296,-7782912,-8310784,-8904448,-9563904,-10223616,-1712896,-1778944,-1844992,-1976576,-2108160,-2240000,-2371840,-2503680,-2635520,-2767616,-2965248,-3162880,-3360512,-3558400,-3821824,-4085504,-4349184,-4612864,-4942336,-5271808,-5667072,-6062336,-6457856,-6919168,-7380480,-7907584,-8434944,-9028096,-9687040,-10346240,-11071232,-11862016,-1843968,-1975552,-2107136,-2238720,-2370304,-2502144,-2633984,-2765824,-2963200,-3160832,-3358464,-3556096,-3753728,-4017152,-4280576,-4544256,-4873472,-5202688,-5532160,-5927168,-6322432,-6783232,-7244288,-7771136,-8297984,-8890624,-9549056,-10207744,-10932224,-11722496,-12578560,-13500416,-1975040,-2106624,-2238208,-2369792,-2501376,-2633216,-2830592,-3027968,-3225344,-3422976,-3620608,-3883776,-4146944,-4410368,-4673792,-5003008,-5332224,-5726976,-6121984,-6582528,-7043328,-7504128,-8030720,-8623104,-9215488,-9873664,-10597632,-11387392,-12242944,-13164288,-14085888,-15138816,-2237184,-2368768,-2500352,-2631936,-2763520,-2960896,-3158272,-3355648,-3553024,-3816192,-4079360,-4342528,-4605696,-4934656,-5263616,-5658368,-6053120,-6447872,-6908416,-7368960,-7895296,-8421632,-9013760,-9671680,-10329600,-11053312,-11842816,-12698112,-13619200,-14606080,-15658752,-16777216]
\ No newline at end of file diff --git a/release/scripts/flt_export.py b/release/scripts/flt_export.py index 283c24a3ad0..5e69e203374 100755..100644 --- a/release/scripts/flt_export.py +++ b/release/scripts/flt_export.py @@ -1,45 +1,25 @@ #!BPY """ Registration info for Blender menus: Name: 'OpenFlight (.flt)...' -Blender: 237 +Blender: 245 Group: 'Export' Tip: 'Export to OpenFlight v16.0 (.flt)' """ -__author__ = "Greg MacDonald" -__version__ = "1.2 10/20/05" +__author__ = "Greg MacDonald, Geoffrey Bantle" +__version__ = "2.0 11/21/07" __url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/") __bpydoc__ = """\ This script exports v16.0 OpenFlight files. OpenFlight is a registered trademark of MultiGen-Paradigm, Inc. -Run from "File->Export" menu. - -Options are available from Blender's "Scripts Config Editor," accessible through -the "Scripts->System" menu from the scripts window. - -Features:<br> -* Heirarchy retained.<br> -* Normals retained.<br> -* First texture exported.<br> -* Diffuse material color is exported as the face color, material color, or both -depending on the option settings.<br> -* Double sided faces are exported as two faces.<br> -* Object transforms exported. - -Things To Be Aware Of:<br> -* Object names are exported, not mesh or data names. -* Material indices that don't have a material associated with them will confuse the -exporter. If a warning appears about this, correct it by deleting the offending -material indices in Blender. - -What's Not Handled:<br> -* Animations.<br> -* Vetex colors.<br> +Feature overview and more availible at: +http://wiki.blender.org/index.php/Scripts/Manual/Export/openflight_flt """ # flt_export.py is an OpenFlight exporter for blender. -# Copyright (C) 2005 Greg MacDonald +# +# Copyright (C) 2005 Greg MacDonald, 2007 Blender Foundation. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -56,29 +36,87 @@ What's Not Handled:<br> # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import Blender +from Blender import Modifier +import os.path +import flt_properties +import flt_defaultp as defaultp from flt_filewalker import FltOut +from flt_filewalker import FileFinder +from flt_properties import * +import shutil + +FF = FileFinder() +records = process_recordDefs() class ExporterOptions: def __init__(self): - self.defaults = { 'Diffuse Color To OpenFlight Material': False, - 'Diffuse Color To OpenFlight Face': True} + self.verbose = 1 + self.tolerance = 0.001 + self.writevcol = True - d = Blender.Registry.GetKey('flt_export', True) + #new stuff + self.export_shading = 0 + self.shading_default = 45.0 + self.basepath = os.path.dirname(Blender.Get('filename')) + self.scale = 1.0 - if d == None or d.keys() != self.defaults.keys(): - d = self.defaults - Blender.Registry.SetKey('flt_export', d, True) + #set externals path + if(os.path.exists(os.path.join(self.basepath,'externals'))): + self.externalspath = os.path.join(self.basepath,'externals') + else: + self.externalspath = self.basepath - self.verbose = 1 - self.tolerance = 0.001 - self.use_mat_color = d['Diffuse Color To OpenFlight Material'] - self.use_face_color = d['Diffuse Color To OpenFlight Face'] + self.doxrefs = 1 + + #texture options + if(os.path.exists(os.path.join(self.basepath,'textures'))): + self.texturespath = os.path.join(self.basepath,'textures') + else: + self.texturespath = self.basepath + + #misc + self.write_attrib_files = 0 + self.copy_textures = 0 + self.export_transform = 0 + self.flattenmesh = False + self.xapp = 1 + reg = Blender.Registry.GetKey('flt_export',1) + if(reg and 'xappath' in reg.keys()): + self.xappath = reg['xappath'] + else: + self.xappath = '' options = ExporterOptions() +tex_files = dict() #a list of (possibly) modified texture path names + +tex_layers = ['Layer0', 'Layer1', 'Layer2', 'Layer3', 'Layer4', 'Layer5', 'Layer6', 'Layer7'] +mask = 2147483648 +mtexmasks = [] +for i in xrange(7): + mtexmasks.append(mask) + mask = mask / 2 FLOAT_TOLERANCE = options.tolerance +#need to move all this stuff to flt_properties.py. identity_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] +alltypes = [2,4,14,11,73,63,111] +childtypes = { + 2 : [111,2,73,4,14,63], + 4 : [111], + 73 : [111,2,73,4,14,63], + 63 : [], + 14 : [111,2,73,4,14,63], + 111 : [] +} +recordlen = { + 2: 44, + 4: 28, + 73: 80, + 63: 216, + 14: 384, + 111: 156 +} def is_identity(m): for i in xrange(4): @@ -102,13 +140,47 @@ class MaterialDesc: self.alpha = 1.0 # Range is [0.0, 1.0] class VertexDesc: - def __init__(self, co=None, no=None, uv=None): + def __init__(self, co=None, no=None, uv=None, fltindex=None,cindex=None): if co: self.x, self.y, self.z = tuple(co) else: self.x = self.y = self.z = 0.0 if no: self.nx, self.ny, self.nz = tuple(no) else: self.nx = self.ny = self.nz = 0.0 if uv: self.u, self.v = tuple(uv) else: self.u = self.v = 0.0 + if cindex: self.cindex = cindex + else: self.cindex = 127 + self.fltindex = fltindex + self.accum = 0 + +class shadowVert: + def __init__(self,bvert,object,world,normal): + global options + + self.co = Blender.Mathutils.Vector(bvert.co[0],bvert.co[1],bvert.co[2]) + #if world: + # vec = self.co + # vec = Blender.Mathutils.Vector(vec[0] * options.scale, vec[1] * options.scale, vec[2] * options.scale) #scale + # self.co = Blender.Mathutils.TranslationMatrix(vec) * (self.co * object.getMatrix('worldspace')) + + if normal: + #if world: + # self.no = Blender.Mathutils.Vector(normal * object.getMatrix('worldspace')).normalize() + #else: + self.no = Blender.Mathutils.Vector(normal[0],normal[1],normal[2]) + + else: + #if world: + #self.no = Blender.Mathutils.Vector(bvert.no * object.getMatrix('worldspace')).normalize() + #else: + self.no = Blender.Mathutils.Vector(bvert.no[0],bvert.no[1],bvert.no[2]) + + #do scaling factor + #if options.scale != 1.0: + #self.co[0] = self.co[0] * options.scale + #self.co[1] = self.co[1] * options.scale + #self.co[2] = self.co[2] * options.scale + + self.index = bvert.index class GlobalResourceRepository: def new_face_name(self): @@ -121,44 +193,104 @@ class GlobalResourceRepository: def request_vertex_desc(self, i): return self.vertex_lst[i] - def request_vertex_index(self, desc): - match = None - for i, v in enumerate(self.vertex_lst): - if\ - abs(v.x - desc.x) > FLOAT_TOLERANCE or\ - abs(v.y - desc.y) > FLOAT_TOLERANCE or\ - abs(v.z - desc.z) > FLOAT_TOLERANCE or\ - abs(v.nx - desc.nx) > FLOAT_TOLERANCE or\ - abs(v.ny - desc.ny) > FLOAT_TOLERANCE or\ - abs(v.nz - desc.nz) > FLOAT_TOLERANCE or\ - abs(v.u - desc.u) > FLOAT_TOLERANCE or\ - abs(v.v - desc.v) > FLOAT_TOLERANCE: - pass - else: - match = i - break + def request_vertex_index(self, object, mesh, face, vfindex, uvok,cindex): - if match != None: - return match - else: - self.vertex_lst.append(desc) - return len(self.vertex_lst) - 1 + flatShadeNorm = None + vno = None - def request_texture_index(self, filename): + + if type(face) is list: + vertex = face[vfindex] + elif str(type(face)) == "<type " + "'Blender MVert'>": + vertex = face + vno = Blender.Mathutils.Vector(0.0,0.0,1.0) + elif str(type(face)) == "<type " + "'Blender MEdge'>": + if vfindex == 1: + vertex = face.v1 + elif vfindex == 2: + vertex = face.v2 + elif str(type(face)) == "<type " + "'Blender MFace'>": + if not face.smooth: + flatShadeNorm = face.no + vertex = face.v[vfindex] + else: + return None + + if not self.namehash.has_key(object.name): + self.namehash[object.name] = dict() + indexhash = self.namehash[object.name] + + #export in global space? THIS HAS BEEN MADE REDUNDANT... REMOVE ME + if not options.export_transform: + vertex = shadowVert(vertex,object,True,flatShadeNorm) + else: + vertex = shadowVert(vertex,object,False,flatShadeNorm) + + if vno: + vertex.no = vno + + + #Check to see if this vertex has been visited before. If not, add + if not indexhash.has_key(vertex.index): + if uvok: + newvdesc = VertexDesc(vertex.co, vertex.no, face.uv[vfindex], self.nextvindex,cindex=cindex) + else: + newvdesc = VertexDesc(co=vertex.co, no=vertex.no,fltindex=self.nextvindex,cindex=cindex) + + indexhash[vertex.index] = [newvdesc] + self.vertex_lst.append(newvdesc) + self.nextvindex = self.nextvindex + 1 + return newvdesc.fltindex + + else: + desclist = indexhash[vertex.index] + if uvok: + faceu = face.uv[vfindex][0] + facev = face.uv[vfindex][1] + else: + faceu = 0.0 + facev = 0.0 + for vdesc in desclist: + if\ + abs(vdesc.x - vertex.co[0]) > FLOAT_TOLERANCE or\ + abs(vdesc.y - vertex.co[1]) > FLOAT_TOLERANCE or\ + abs(vdesc.z - vertex.co[2]) > FLOAT_TOLERANCE or\ + abs(vdesc.nx - vertex.no[0]) > FLOAT_TOLERANCE or\ + abs(vdesc.ny - vertex.no[1]) > FLOAT_TOLERANCE or\ + abs(vdesc.nz - vertex.no[2]) > FLOAT_TOLERANCE or\ + vdesc.cindex != cindex or\ + abs(vdesc.u - faceu) > FLOAT_TOLERANCE or\ + abs(vdesc.v - facev) > FLOAT_TOLERANCE: + pass + else: + return vdesc.fltindex + + #if we get this far, we didnt find a match. Add a new one and return + if uvok: + newvdesc = VertexDesc(vertex.co, vertex.no, face.uv[vfindex], self.nextvindex,cindex=cindex) + else: + newvdesc = VertexDesc(co=vertex.co, no=vertex.no,fltindex=self.nextvindex,cindex=cindex) + indexhash[vertex.index].append(newvdesc) + self.vertex_lst.append(newvdesc) + self.nextvindex = self.nextvindex + 1 + return newvdesc.fltindex + + + def request_texture_index(self, image): match = None for i in xrange(len(self.texture_lst)): - if self.texture_lst[i] != filename: + if self.texture_lst[i] != image: continue match = i break if match != None: return match else: - self.texture_lst.append(filename) + self.texture_lst.append(image) return len(self.texture_lst) - 1 def request_texture_filename(self, index): - return self.texture_lst[index] + return Blender.sys.expandpath(self.texture_lst[index].getFilename()) def texture_count(self): return len(self.texture_lst) @@ -239,7 +371,11 @@ class GlobalResourceRepository: return len(self.color_lst) def __init__(self): + #Vertex handling self.vertex_lst = [] + self.nextvindex = 0 + self.namehash = dict() + self.texture_lst = [] self.material_lst = [] self.color_lst = [[255, 255, 255]] @@ -253,7 +389,6 @@ class Node: if self.object: if options.verbose >= 2: print '\t' * level[0], self.name, self.object.type - level[0] += 1 for child in self.children: @@ -288,183 +423,537 @@ class Node: self.header.fw.write_ushort(length+5) # Length of record self.header.fw.write_string(name, length+1) # name + zero terminator + def write_comment(self,comment): + length = len(comment) + if length >= 65535: + comment = comment[:65530] + length = len(comment) + + pad = (length % 4) - 1 + if pad < 0: + pad = None + reclength = length + 5 + else: + reclength = length + 5 + pad + + self.header.fw.write_short(31) # Comment Opcode + self.header.fw.write_ushort(reclength) # Length of record is 4 + comment length + null terminator + pad + self.header.fw.write_string(comment,length+1) # comment + zero terminator + if pad: + self.header.fw.pad(pad) # pad to multiple of 4 bytes + # Initialization sets up basic tree structure. - def __init__(self, parent, header, object, object_lst): + def __init__(self, parent, header, object,props): + global options + self.header = header self.object = object if object: self.name = self.object.name - self.matrix = self.object.getMatrix('localspace') + if not options.export_transform: + oloc = Blender.Mathutils.Vector(object.getLocation('worldspace')) + vec = Blender.Mathutils.Vector(oloc[0] * options.scale, oloc[1] * options.scale, oloc[2] * options.scale) #scale + self.matrix = self.object.getMatrix('worldspace') * Blender.Mathutils.TranslationMatrix(vec - oloc) + else: + self.matrix = self.object.getMatrix('localspace') #do matrix mult here. + self.props = props + self.child_objects = self.header.parenthash[object.name] else: self.name = 'no name' self.matrix = None - + self.props = None + self.child_objects = self.header.child_objects + self.children = [] self.parent = parent if parent: parent.children.append(self) - - left_over = object_lst[:] - self.child_objects = [] - - # Add children to child list and remove from left_over list. - # Pop is faster then remove - i = len(object_lst) - while i: - i-=1 - if object_lst[i].parent == object: - self.child_objects.append(left_over.pop(i)) - # Spawn children. - self.has_object_child = False # For Database class. for child in self.child_objects: - if child.type == 'Mesh': - BlenderMesh(self, header, child, left_over) - self.has_object_child = True - else: # Treat all non meshes as emptys - BlenderEmpty(self, header, child, left_over) - + if(not child.restrictDisplay): + childprops = None + type = None + if not child.properties.has_key('FLT'): + if child.type == 'Empty': + if child.DupGroup: + childprops = FLTXRef.copy() + type = 63 + else: + childprops = FLTGroup.copy() + type = 2 + elif child.type == 'Mesh': + if self.header.childhash[child.name] or not child.parent: + childprops = FLTGroup.copy() + type = 2 + else: + childprops = FLTObject.copy() + type = 4 + + else: + childprops = dict() + for prop in child.properties['FLT']: + childprops[prop] = child.properties['FLT'][prop] + type = child.properties['FLT']['type'] + + if type in self.childtypes and type in alltypes: + Newnode = FLTNode(self,header,child,childprops,type) + if child.type == 'Mesh': + self.header.mnodes.append(Newnode) class FaceDesc: def __init__(self): self.vertex_index_lst = [] + self.mface = None self.texture_index = -1 self.material_index = -1 self.color_index = 127 - -class BlenderMesh(Node): - def blender_export(self): - Node.blender_export(self) - - mesh = self.object.getData() - mesh_hasuv = mesh.hasFaceUV() - # Gather materials and textures. - tex_index_lst = [] - mat_index_lst = [] - color_index_lst = [] - materials = mesh.getMaterials() - - if not materials: - materials = [Blender.Material.New()] - - for mat in materials: - # Gather Color. - if options.use_face_color: - color_index_lst.append(self.header.GRR.request_color_index(mat.getRGBCol())) + self.renderstyle = 0 + self.twoside = 0 + self.name = None #uses next FLT name if not set... fix resolution of conflicts! + self.billboard = 0 + + #Multi-Tex info. Dosn't include first UV Layer! + self.uvlayer = list() #list of list of tuples for UV coordinates. + self.images = list() #list of texture indices for seperate UV layers + self.mtex = list() + self.subface = None #can either be 'Push' or 'Pop' + +def edge_get_othervert(vert, edge): + if edge.v1 == vert: + return edge.v2 + elif edge.v2 == vert: + return edge.v1 + return None + +class FLTNode(Node): + def walkLoop(self, targetvert, startvert, startedge, edgelist, visited, vedges, closeloop): + loop = [targetvert] + + curvert = startvert + curedge = startedge + visited[curedge] = True + found = False + + while not found: + loop.append(curvert) + disk = vedges[curvert.index] + if not closeloop: + if len(disk) == 1: + visited[curedge] = True + break else: - color_index_lst.append(127) # white - # Gather Texture. - mtex_lst = mat.getTextures() - - index = -1 - mtex = mtex_lst[0] # Not doing multi-texturing at the moment. - if mtex != None: - tex = mtex_lst[0].tex - if tex != None: - image = tex.getImage() - if image != None: - filename = image.getFilename() - index = self.header.GRR.request_texture_index(filename) - - tex_index_lst.append(index) - - # Gather Material - mat_desc = MaterialDesc() - mat_desc.name = mat.name - mat_desc.alpha = mat.getAlpha() - mat_desc.shininess = mat.getSpec() * 64.0 # 2.0 => 128.0 - if options.use_mat_color: - mat_desc.diffuse = mat.getRGBCol() + if len(disk) < 2: #what? + visited[curedge] = True + return None + + if disk[0] == curedge: + curedge = disk[1] + else: + curedge = disk[0] + if curedge.v1.index == curvert.index: + curvert = curedge.v2 else: - mat_desc.diffuse = [1.0, 1.0, 1.0] + curvert = curedge.v1 - mat_desc.specular = mat.getSpecCol() - amb = mat.getAmb() - mat_desc.ambient = [amb, amb, amb] - emit = mat.getEmit() - mat_desc.emissive = [emit, emit, emit] + visited[curedge] = True + + if(curvert == targetvert): + found = True + + return loop + + def buildVertFaces(self,vertuse): + for vert in self.exportmesh.verts: + if vertuse[vert.index][0] == False and vertuse[vert.index][1] == 0: + face_desc = FaceDesc() + face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, vert, 0,0,0)) + face_desc.renderstyle = 3 + face_desc.color_index = 227 + self.face_lst.append(face_desc) - mat_index_lst.append(self.header.GRR.request_material_index(mat_desc)) + def buildEdgeFaces(self,vertuse): + for edge in self.exportmesh.edges: + v1 = vertuse[edge.v1.index] + v2 = vertuse[edge.v2.index] + if v1[0] == False and v2[0] == False: + if v1[1] == 1 and v2[1] == 1: + face_desc = FaceDesc() + face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, edge, 1, 0,0)) + face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object, self.exportmesh, edge, 2, 0,0)) + face_desc.renderstyle = 3 + face_desc.color_index = 227 + self.face_lst.append(face_desc) + + + def vertwalk(self, startvert, loop, disk, visited): + visited[startvert] = True + for edge in disk[startvert]: + othervert = edge_get_othervert(startvert, edge) + if not visited[othervert]: + loop.append(othervert) + self.vertwalk(othervert,loop,disk,visited) + + def buildOpenFacesNew(self, vertuse): + wireverts = list() + wiredges = list() + visited = dict() + disk = dict() + loops = list() + + for edge in self.exportmesh.edges: + v1 = vertuse[edge.v1.index] + v2 = vertuse[edge.v2.index] + if v1[0] == False and v2[0] == False: + if v1[1] < 3 and v2[1] < 3: + wireverts.append(edge.v1) + wireverts.append(edge.v2) + wiredges.append(edge) + + #build disk data + for vert in wireverts: + visited[vert] = False + disk[vert] = list() + for edge in wiredges: + disk[edge.v1].append(edge) + disk[edge.v2].append(edge) + + #first pass: do open faces + for vert in wireverts: + if not visited[vert] and vertuse[vert.index][1] == 1: + visited[vert] = True + loop = [vert] + othervert = edge_get_othervert(vert, disk[vert][0]) + self.vertwalk(othervert, loop, disk, visited) + if len(loop) > 2: loops.append( ('Open', loop) ) + + for vert in wireverts: + if not visited[vert]: + visited[vert] = True + loop = [vert] + othervert = edge_get_othervert(vert,disk[vert][0]) + self.vertwalk(othervert, loop, disk, visited) + if len(loop) > 2: loops.append( ('closed', loop) ) + + #now go through the loops and append. + for l in loops: + (type, loop) = l + face_desc = FaceDesc() + for i,vert in enumerate(loop): + face_desc.vertex_index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,loop,i,0,0)) + if type == 'closed': + face_desc.renderstyle = 2 + else: + face_desc.renderstyle = 3 + face_desc.color_index = 227 + self.face_lst.append(face_desc) - # Faces described as lists of indices into the GRR's vertex_lst. - for face in mesh.faces: - - face_v = face.v # Faster access - - # Create vertex description list for each face. - if mesh_hasuv: - vertex_lst = [VertexDesc(v.co, v.no, face.uv[i]) for i, v in enumerate(face_v)] - else: - vertex_lst = [VertexDesc(v.co, v.no) for i, v in enumerate(face_v)] + def sortFLTFaces(self,a,b): + aindex = a.getProperty("FLT_ORIGINDEX") + bindex = b.getProperty("FLT_ORIGINDEX") + + if aindex > bindex: + return 1 + elif aindex < bindex: + return -1 + return 0 + + def buildNormFaces(self): + + global options + meshlayers = self.exportmesh.getUVLayerNames() + oldlayer = self.exportmesh.activeUVLayer + uvok = 0 + subfaceok = 0 + subfacelevel = 0 + + #special case + if self.exportmesh.faceUV and len(meshlayers) == 1: + uvok = 1 + elif self.exportmesh.faceUV and tex_layers[0] in meshlayers: + self.exportmesh.activeUVLayer = tex_layers[0] + uvok = 1 + + #Sort faces according to the subfaces/FLT indices + if "FLT_ORIGINDEX" in self.exportmesh.faces.properties and "FLT_SFLEVEL" in self.exportmesh.faces.properties: + exportfaces = list() + for face in self.exportmesh.faces: + exportfaces.append(face) + exportfaces.sort(self.sortFLTFaces) + subfaceok = 1 + else: + exportfaces = self.exportmesh.faces + # Faces described as lists of indices into the GRR's vertex_lst. + for face in exportfaces: + descs = list() + #first we export the face as normal index_lst = [] - for vert_desc in vertex_lst: - index_lst.append(self.header.GRR.request_vertex_index(vert_desc)) - + face_v = face.verts + for i, v in enumerate(face_v): + index_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,face,i,uvok,0)) face_desc = FaceDesc() face_desc.vertex_index_lst = index_lst + face_desc.mface = face + descs.append(face_desc) - if face.materialIndex < len(materials): - face_desc.color_index = color_index_lst[face.materialIndex] - face_desc.texture_index = tex_index_lst[face.materialIndex] - face_desc.material_index = mat_index_lst[face.materialIndex] - else: - if options.verbose >=1: - print 'Warning: Missing material for material index. Materials will not be imported correctly. Fix by deleting abandoned material indices in Blender.' + #deal with subfaces + if subfaceok: + fsflevel = face.getProperty("FLT_SFLEVEL") + for face_desc in descs: + if fsflevel > subfacelevel: + face_desc.subface = 'Push' + subfacelevel = fsflevel + elif fsflevel < subfacelevel: + face_desc.subface = 'Pop' + subfacelevel = fsflevel + + + if uvok and (face.mode & Blender.Mesh.FaceModes.TWOSIDE): + face_desc.renderstyle = 1 + for face_desc in descs: + if "FLT_COL" in self.exportmesh.faces.properties: + color_index = face.getProperty("FLT_COL") +# if(color_index < 127): +# color_index = 127 #sanity check for face color indices + if(color_index == 0): + color_index = 127 + face_desc.color_index = color_index + else: + face_desc.color_index = 127 + if "FLT_ID" in self.exportmesh.faces.properties: + face_desc.name = face.getProperty("FLT_ID") #need better solution than this. + + if uvok and face.mode & Blender.Mesh.FaceModes["BILLBOARD"]: + face_desc.billboard = 1 + + self.face_lst.append(face_desc) + if uvok: + self.exportmesh.activeUVLayer = oldlayer - self.face_lst.append(face_desc) + def buildTexData(self): + + meshlayers = self.exportmesh.getUVLayerNames() + oldlayer = self.exportmesh.activeUVLayer + uvok = 0 + + if self.exportmesh.faceUV and len(meshlayers) == 1: + uvok = 1 + if self.exportmesh.faceUV and tex_layers[0] in meshlayers: + self.exportmesh.activeUVLayer = tex_layers[0] + uvok = 1 + + if uvok: + #do base layer. UVs have been stored on vertices directly already. + for i, face in enumerate(self.face_lst): + if face.mface: + mface = face.mface + image = mface.image + if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]: + index = self.header.GRR.request_texture_index(image) + else: + index = -1 + face.texture_index = index + + for i, face in enumerate(self.face_lst): + if face.mface: + mface_v = face.mface.v + for v in mface_v: + face.uvlayer.append([]) - # Export double sided face as 2 faces with opposite orientations. - if mesh_hasuv and face.mode & Blender.NMesh.FaceModes['TWOSIDE']: - # Create vertex description list for each face. they have a face mode, so we know they have a UV too. - vertex_lst = [VertexDesc(v.co, -v.no, face.uv[i]) for i, v in enumerate(face_v)] - vertex_lst.reverse() # Reversing flips the face. + for layername in tex_layers[1:]: + if layername in meshlayers: + self.exportmesh.activeUVLayer=layername + for i, face in enumerate(self.face_lst): + if face.mface: + + face.mtex.append(layername) + mface = face.mface + mface_v = mface.v + image = mface.image + + if image != None and mface.mode & Blender.Mesh.FaceModes["TEX"]: + index = self.header.GRR.request_texture_index(image) + face.images.append(index) + else: + face.images.append(-1) + + for j, v in enumerate(mface_v): + face.uvlayer[j].append(tuple(mface.uv[j])) + if uvok: + self.exportmesh.activeUVLayer = oldlayer + def blender_export(self): + global options + Node.blender_export(self) + if self.opcode == 111: + self.exportmesh = Blender.Mesh.New() + self.exportmesh.getFromObject(self.object.name) + + for vert in self.exportmesh.verts: + if not options.export_transform: + vec = vert.co + vec = Blender.Mathutils.Vector(vec[0] * options.scale, vec[1] * options.scale, vec[2] * options.scale) #scale + vert.co = Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace')) - index_lst = [] - for vert_desc in vertex_lst: - index_lst.append(self.header.GRR.request_vertex_index(vert_desc)) + if options.scale != 1.0: + vert.co = vert.co * options.scale + + if("FLT_VCOL") in self.mesh.verts.properties: + for v in self.exportmesh.verts: + self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.exportmesh,v,0,0,v.getProperty("FLT_VCOL"))) + else: + for v in self.mesh.verts: + self.vert_lst.append(self.header.GRR.request_vertex_index(self.object,self.mesh,v,0,0,127)) + + + + elif self.mesh: + orig_mesh = self.object.getData(mesh=True) + self.exportmesh = Blender.Mesh.New() + default = None + + + if options.export_shading: + mods = self.object.modifiers + hasedsplit = False + for mod in mods: + if mod.type == Blender.Modifier.Types.EDGESPLIT: + hasedsplit = True + break + if not hasedsplit: + default = mods.append(Modifier.Types.EDGESPLIT) + default[Modifier.Settings.EDGESPLIT_ANGLE] = options.shading_default + default[Modifier.Settings.EDGESPLIT_FROM_ANGLE] = True + default[Modifier.Settings.EDGESPLIT_FROM_SHARP] = False + self.object.makeDisplayList() + + self.exportmesh.getFromObject(self.object.name) + + #recalculate vertex positions + for vert in self.exportmesh.verts: + if not options.export_transform: + vec = vert.co + vec = Blender.Mathutils.Vector(vec[0] * options.scale, vec[1] * options.scale, vec[2] * options.scale) #scale + vert.co = Blender.Mathutils.TranslationMatrix(vec) * (vert.co * self.object.getMatrix('worldspace')) - face_desc = FaceDesc() - face_desc.vertex_index_lst = index_lst - if face.materialIndex < len(materials): - face_desc.color_index = color_index_lst[face.materialIndex] - face_desc.texture_index = tex_index_lst[face.materialIndex] - face_desc.material_index = mat_index_lst[face.materialIndex] - else: - if options.verbose >=1: - print 'Error: No material for material index. Delete abandoned material indices in Blender.' - - self.face_lst.append(face_desc) + if options.scale != 1.0: + vert.co = vert.co * options.scale + + flipped = self.object.getMatrix('worldspace').determinant() + + if not options.export_transform: + self.exportmesh.calcNormals() + + + if default: + #remove modifier from list + mods.remove(default) + self.object.makeDisplayList() + + #build some adjacency data + vertuse = list() + wiredges = list() + openends = list() + for v in self.exportmesh.verts: + vertuse.append([False,0]) + + #build face incidence data + for face in self.exportmesh.faces: + for i, v in enumerate(face.verts): + vertuse[v.index][0] = True + + for edge in self.exportmesh.edges: #count valance + vertuse[edge.v1.index][1] = vertuse[edge.v1.index][1] + 1 + vertuse[edge.v2.index][1] = vertuse[edge.v2.index][1] + 1 + + #create all face types + self.buildVertFaces(vertuse) + self.buildEdgeFaces(vertuse) + self.buildOpenFacesNew(vertuse) + self.buildNormFaces() + self.buildTexData() + + if not options.export_transform: + if flipped < 0: + for vdesc in self.header.GRR.vertex_lst: + vdesc.accum = 0 + for face in self.face_lst: + face.vertex_index_lst.reverse() + for vert in face.vertex_index_lst: + self.header.GRR.vertex_lst[vert].accum = 1 + + for vdesc in self.header.GRR.vertex_lst: + if vdesc.accum: + vdesc.nx = vdesc.nx * -1 + vdesc.ny = vdesc.ny * -1 + vdesc.nz = vdesc.nz * -1 + def write_faces(self): + sublevel = 0 for face_desc in self.face_lst: - face_name = self.header.GRR.new_face_name() + if face_desc.name: + face_name = face_desc.name + else: + face_name = self.header.GRR.new_face_name() + #grab the alpha value. + alpha = 0 + if face_desc.texture_index > -1: + try: + typestring = os.path.splitext(self.header.GRR.texture_lst[face_desc.texture_index].getFilename())[1] + if typestring == '.inta' or typestring == '.rgba': + alpha = 1 + except: + pass + + if not alpha: + for index in face_desc.images: + try: + typestring = os.path.splitext(self.header.GRR.texture_lst[index].getFilename())[1] + if typestring == '.inta' or typestring == '.rgba': + alpha = 1 + except: + pass + + if face_desc.billboard: + alpha = 2 + + if face_desc.subface: + if face_desc.subface == 'Push': + self.header.fw.write_short(19) + self.header.fw.write_ushort(4) + sublevel += 1 + else: + self.header.fw.write_short(20) + self.header.fw.write_ushort(4) + sublevel -= 1 self.header.fw.write_short(5) # Face opcode self.header.fw.write_ushort(80) # Length of record self.header.fw.write_string(face_name, 8) # ASCII ID self.header.fw.write_int(-1) # IR color code - self.header.fw.write_short(0) # Relative priority - self.header.fw.write_char(0) # Draw type + self.header.fw.write_short(0) # Relative priority + self.header.fw.write_char(face_desc.renderstyle) # Draw type self.header.fw.write_char(0) # Draw textured white. self.header.fw.write_ushort(0) # Color name index self.header.fw.write_ushort(0) # Alt color name index self.header.fw.write_char(0) # Reserved - self.header.fw.write_char(1) # Template + self.header.fw.write_char(alpha) # Template self.header.fw.write_short(-1) # Detail tex pat index self.header.fw.write_short(face_desc.texture_index) # Tex pattern index self.header.fw.write_short(face_desc.material_index) # material index self.header.fw.write_short(0) # SMC code - self.header.fw.write_short(0) # Feature code + self.header.fw.write_short(0) # Feature code self.header.fw.write_int(0) # IR material code self.header.fw.write_ushort(0) # transparency 0 = opaque self.header.fw.write_uchar(0) # LOD generation control self.header.fw.write_uchar(0) # line style index - self.header.fw.write_int(0x00000000) # Flags + self.header.fw.write_int(0) # Flags self.header.fw.write_uchar(2) # Light mode + #self.header.fw.write_uchar(3) # Light mode + self.header.fw.pad(7) # Reserved - self.header.fw.write_uint(-1) # Packed color - self.header.fw.write_uint(-1) # Packed alt color + self.header.fw.write_uint(0) # Packed color + self.header.fw.write_uint(0) # Packed alt color self.header.fw.write_short(-1) # Tex map index self.header.fw.write_short(0) # Reserved self.header.fw.write_uint(face_desc.color_index) # Color index @@ -473,7 +962,24 @@ class BlenderMesh(Node): self.header.fw.write_short(-1) # Shader index self.write_longid(face_name) - + + + #Write Multitexture field if appropriate + mtex = len(face_desc.mtex) + if mtex: + uvmask = 0 + for layername in face_desc.mtex: + mask = mtexmasks[tex_layers.index(layername)-1] + uvmask |= mask + self.header.fw.write_ushort(52) # MultiTexture Opcode + self.header.fw.write_ushort(8 + (mtex * 8)) # Length + self.header.fw.write_uint(uvmask) # UV mask + for i in xrange(mtex): + self.header.fw.write_ushort(face_desc.images[i]) # Tex pattern index + self.header.fw.write_ushort(0) # Tex effect + self.header.fw.write_ushort(0) # Tex Mapping index + self.header.fw.write_ushort(0) # Tex data. User defined + self.write_push() # Vertex list record @@ -484,72 +990,97 @@ class BlenderMesh(Node): for vert_index in face_desc.vertex_index_lst: # Offset into vertex palette self.header.fw.write_int(vert_index*64+8) - + + #UV list record + if mtex: + #length = 8 + (numverts * multitex * 8) + self.header.fw.write_ushort(53) # UV List Ocode + self.header.fw.write_ushort(8 + (num_verts*mtex*8)) # Record Length + self.header.fw.write_uint(uvmask) # UV mask + for i, vert_index in enumerate(face_desc.vertex_index_lst): + for uv in face_desc.uvlayer[i]: + self.header.fw.write_float(uv[0]) #U coordinate + self.header.fw.write_float(uv[1]) #V coordinate self.write_pop() + #clean up faces at the end of meshes.... + if sublevel: + self.header.fw.write_short(20) + self.header.fw.write_ushort(4) + + def write_lps(self): + # Vertex list record + self.write_push() + self.header.fw.write_short(72) # Vertex list opcode + num_verts = len(self.vert_lst) + self.header.fw.write_ushort(4*num_verts+4) # Length of record + for vert_index in self.vert_lst: + # Offset into vertex palette + self.header.fw.write_int(vert_index*64+8) + self.write_pop() def write(self): - if self.open_flight_type == 'Object': - self.header.fw.write_short(4) # Object opcode - self.header.fw.write_ushort(28) # Length of record - self.header.fw.write_string(self.name, 8) # ASCII ID - self.header.fw.pad(16) - - self.write_longid(self.name) + self.header.fw.write_short(self.opcode) + self.header.fw.write_ushort(recordlen[self.opcode]) + exportdict = FLT_Records[self.opcode].copy() + if self.object: + self.props['3t8!id'] = self.object.name[:7] + for key in exportdict.keys(): + if self.props.has_key(key): + exportdict[key] = self.props[key] + + if self.opcode == 63 and options.externalspath: + try: + exportdict['3t200!filename'] = os.path.join(options.externalspath,self.object.DupGroup.name+'.flt') + self.header.xrefnames.append(self.object.DupGroup.name) + except: + pass + + for key in records[self.opcode]: + (type,length,propname) = records[self.opcode][key] + write_prop(self.header.fw,type,exportdict[propname],length) + + if self.props.has_key('comment'): + self.write_comment(self.props['comment']) + self.write_longid(self.name) #fix this! + + if options.export_transform or self.opcode == 63: + #writing transform matrix.... self.write_matrix() - + + if self.opcode == 111: + self.write_lps() + elif self.face_lst != [] or self.children: + self.write_push() if self.face_lst != []: - self.write_push() - + #self.write_push() self.write_faces() + #self.write_pop() - self.write_pop() - else: - self.header.fw.write_short(2) # Group opcode - self.header.fw.write_ushort(44) # Length of record - self.header.fw.write_string(self.name, 8) # ASCII ID - self.header.fw.pad(32) - - self.write_longid(self.name) - - # Because a group can contain faces as well as children. - self.write_push() - - self.write_faces() - - for child in self.children: - child.write() - + if self.children: + #self.write_push() + for child in self.children: + child.write() + #self.write_pop() self.write_pop() - - def __init__(self, parent, header, object, object_lst): - Node.__init__(self, parent, header, object, object_lst) - self.face_lst = [] - - if self.children: - self.open_flight_type= 'Group' - else: # Empty list. - self.open_flight_type = 'Object' - -class BlenderEmpty(Node): - def write(self): - self.header.fw.write_short(2) # Group opcode - self.header.fw.write_ushort(44) # Length of record - self.header.fw.write_string(self.name, 8) # ASCII ID - self.header.fw.pad(32) - - self.write_longid(self.name) - - self.write_matrix() + def __init__(self, parent, header, object,props,type): + self.opcode = type #both these next two lines need to be in the node class.... + self.childtypes = childtypes[self.opcode] + Node.__init__(self, parent, header, object,props) + self.face_lst = [] + self.vert_lst = [] #for light points. + self.mesh = None + self.uvlayer = 0 + self.flipx = False + self.flipy = False + self.flipz = False - if self.children: # != [] - self.write_push() - - for child in self.children: - child.write() - self.write_pop() + if self.object.type == 'Mesh': + self.mesh = self.object.getData(mesh=True) + if(self.mesh.faceUV): + self.uvLayer = len(self.mesh.getUVLayerNames()) class Database(Node): def write_header(self): @@ -568,8 +1099,19 @@ class Database(Node): self.fw.write_int(0) # projection type, 0 = flat earth self.fw.pad(30) self.fw.write_short(1) # double precision - self.fw.pad(140) + self.fw.write_int(100) # database origin type + self.fw.pad(88) + try: + self.fw.write_double(self.header.scene.properties['FLT']['origin lat']) #database origin lattitude + except: + self.fw.write_double(0) + try: + self.fw.write_double(self.header.scene.properties['FLT']['origin lon']) #database origin longitude + except: + self.fw.write_double(0) + self.fw.pad(32) self.fw.write_int(0) # ellipsoid model, 0 = WSG 1984 + self.fw.pad(52) def write_vert_pal(self): @@ -579,14 +1121,13 @@ class Database(Node): self.fw.write_short(67) # Vertex palette opcode. self.fw.write_short(8) # Length of record self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything. - # Write records for individual vertices. for i in xrange(self.GRR.vertex_count()): desc = self.GRR.request_vertex_desc(i) self.fw.write_short(70) # Vertex with color normal and uv opcode. self.fw.write_ushort(64) # Length of record - self.fw.write_ushort(0) # Color name index - self.fw.write_short(0x2000) # Flags set to no color + self.fw.write_ushort(0) # Color name index + self.fw.write_short(0x20000000) # Flags self.fw.write_double(desc.x) self.fw.write_double(desc.y) self.fw.write_double(desc.z) @@ -595,16 +1136,19 @@ class Database(Node): self.fw.write_float(desc.nz) self.fw.write_float(desc.u) self.fw.write_float(desc.v) - self.fw.pad(12) + self.fw.pad(4) + self.fw.write_uint(desc.cindex) + self.fw.pad(4) def write_tex_pal(self): if options.verbose >= 2: print 'Writing texture palette.' # Write record for texture palette - for i in xrange(self.GRR.texture_count()): + for i, img in enumerate(self.GRR.texture_lst): + filename = tex_files[img.name] self.fw.write_short(64) # Texture palette opcode. self.fw.write_short(216) # Length of record - self.fw.write_string(self.GRR.request_texture_filename(i), 200) # Filename + self.fw.write_string(filename, 200) # Filename self.fw.write_int(i) # Texture index self.fw.write_int(0) # X self.fw.write_int(0) # Y @@ -641,13 +1185,17 @@ class Database(Node): self.fw.write_short(32) # Color palette opcode. self.fw.write_short(4228) # Length of record self.fw.pad(128) - count = self.GRR.color_count() + try: + cpalette = self.scene.properties['FLT']['Color Palette'] + except: + cpalette = defaultp.pal + count = len(cpalette) for i in xrange(count): - col = self.GRR.request_max_color(i) - self.fw.write_uchar(255) # alpha - self.fw.write_uchar(col[2]) # b - self.fw.write_uchar(col[1]) # g - self.fw.write_uchar(col[0]) # r + color = struct.unpack('>BBBB',struct.pack('>I',cpalette[i])) + self.fw.write_uchar(color[3]) # alpha + self.fw.write_uchar(color[2]) # b + self.fw.write_uchar(color[1]) # g + self.fw.write_uchar(color[0]) # r self.fw.pad(max(4096-count*4, 0)) def write(self): @@ -657,66 +1205,429 @@ class Database(Node): self.write_mat_pal() self.write_col_pal() - # Wrap everything in a group if it has an object child. - if self.has_object_child: - self.header.fw.write_short(2) # Group opcode - self.header.fw.write_ushort(44) # Length of record - self.header.fw.write_string('g1', 8) # ASCII ID - self.header.fw.pad(32) - self.write_push() - - for child in self.children: - child.write() - + + if options.flattenmesh: + self.mnodes.reverse() + for mnode in self.mnodes: + mnode.write_faces() + else: + for child in self.children: + child.write() self.write_pop() + + def export_textures(self,texturepath): + for i in xrange(self.GRR.texture_count()): + texture = self.GRR.texture_lst[i] + + if options.copy_textures: + filename = os.path.normpath(os.path.join(options.texturespath, os.path.basename(self.GRR.request_texture_filename(i)))) + else: + filename = os.path.normpath(self.GRR.request_texture_filename(i)) + + tex_files[texture.name] = filename + def blender_export(self): + Node.blender_export(self) + self.export_textures(self) + return self.xrefnames def __init__(self, scene, fw): self.fw = fw + self.opcode = 1 + self.childtypes = [73,14,2,63] self.scene = scene - self.all_objects = list(scene.objects) - self.GRR = GlobalResourceRepository() + self.childhash = dict() + self.parenthash = dict() + self.child_objects = list() + self.mnodes = list() + self.xrefnames = list() + for i in self.scene.objects: + self.parenthash[i.name] = list() + self.childhash[i.name] = False + for i in self.scene.objects: + if i.parent: + self.childhash[i.parent.name] = True + self.parenthash[i.parent.name].append(i) + else: + self.child_objects.append(i) - Node.__init__(self, None, self, None, self.all_objects) + self.GRR = GlobalResourceRepository() + Node.__init__(self, None, self, None,None) -def fs_callback(filename): - Blender.Window.WaitCursor(True) - - if Blender.sys.exists(filename): - r = Blender.Draw.PupMenu('Overwrite ' + filename + '?%t|Yes|No') - if r != 1: - if options.verbose >= 1: - print 'Export cancelled.' - return - - time1 = Blender.sys.time() # Start timing +def write_attribute_files(): + for imgname in tex_files: + blentex = Blender.Image.Get(imgname) + exportdict = FLT_Records['Image'].copy() + + if blentex.properties.has_key('FLT'): + for key in exportdict.keys(): + if blentex.properties.has_key(key): + exportdict[key] = blentex.properties['FLT'][key] + + # ClampX/Y override + if blentex.clampX: + exportdict['11i!WrapU'] = 1 + if blentex.clampY: + exportdict['12i!WrapV'] = 1 + + exportdict['16i!Enviorment'] = 0 + + # File type + typecode = 0 + try: + typestring = os.path.splitext(blentex.getFilename())[1] + + if typestring == '.rgba': + typecode = 5 + elif typestring == '.rgb': + typecode = 4 + elif typestring == '.inta': + typecode = 3 + elif typestring == '.int': + typecode = 2 + except: + pass + + exportdict['7i!File Format'] = typecode + + fw = FltOut(tex_files[imgname] + '.attr') + size = blentex.getSize() + fw.write_int(size[0]) + fw.write_int(size[1]) + for key in records['Image']: + (type,length,propname) = records['Image'][key] + write_prop(fw,type,exportdict[propname],length) + fw.close_file() + +#globals used by the scene export function +exportlevel = None +xrefsdone = None + +def dbexport_internal(scene): + global exportlevel + global xrefsdone + global options + + if exportlevel == 0 or not options.externalspath: + fname = os.path.join(options.basepath,scene.name + '.flt') + else: + fname = os.path.join(options.externalspath,scene.name + '.flt') - fw = FltOut(filename) - - db = Database(Blender.Scene.GetCurrent(), fw) + fw = FltOut(fname) + db = Database(scene,fw) if options.verbose >= 1: - print 'Pass 1: Exporting from Blender.\n' - - db.blender_export() + print 'Pass 1: Exporting ', scene.name,'.flt from Blender.\n' + xreflist = db.blender_export() if options.verbose >= 1: - print 'Pass 2: Writing %s\n' % filename - + print 'Pass 2: Writing %s\n' % fname db.write() - fw.close_file() + + if options.doxrefs: + for xname in xreflist: + try: + xrefscene = Blender.Scene.Get(xname) + except: + xrefscene = None + if xrefscene and xname not in xrefsdone: + xrefsdone.append(xname) + exportlevel+=1 + dbexport_internal(xrefscene) + exportlevel-=1 + return fname +#main database export function +def dbexport(): + global exportlevel + global xrefsdone + exportlevel = 0 + xrefsdone = list() + + Blender.Window.WaitCursor(True) + time1 = Blender.sys.time() # Start timing + if options.verbose >= 1: + print '\nOpenFlight Exporter' + print 'Version:', __version__ + print 'Author: Greg MacDonald, Geoffrey Bantle' + print __url__[2] + print + + fname = dbexport_internal(Blender.Scene.GetCurrent()) + if options.verbose >=1: print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1) - Blender.Window.WaitCursor(False) + + #optional: Copy textures + if options.copy_textures: + for imgname in tex_files: + #Check to see if texture exists in target directory + if not os.path.exists(tex_files[imgname]): + #Get original Blender file name + origpath = Blender.sys.expandpath(Blender.Image.Get(imgname).getFilename()) + #copy original to new + if os.path.exists(origpath): + shutil.copyfile(origpath,tex_files[imgname]) + + #optional: Write attribute files + if options.write_attrib_files: + write_attribute_files() + + if options.xapp: + cmd= options.xappath + " " + fname + status = os.system(cmd) + + +#Begin UI code +FLTExport = None +FLTClose = None +FLTLabel = None + +FLTBaseLabel = None +FLTTextureLabel = None +FLTXRefLabel = None + +FLTBaseString = None +FLTTextureString = None +FLTXRefString = None + +FLTBasePath = None +FLTTexturePath = None +FLTXRefPath = None + +FLTShadeExport = None +FLTShadeDefault = None + +FLTCopyTex = None +FLTDoXRef = None +FLTGlobal = None + +FLTScale = None + +FLTXAPP = None +FLTXAPPath = None +FLTXAPPString = None +FLTXAPPLabel = None +FLTXAPPChooser = None + +FLTAttrib = None + +def setshadingangle(ID,val): + global options + options.shading_default = val +def setBpath(fname): + global options + options.basepath = os.path.dirname(fname) + #update xref and textures path too.... + if(os.path.exists(os.path.join(options.basepath,'externals'))): + options.externalspath = os.path.join(options.basepath,'externals') + if(os.path.exists(os.path.join(options.texturespath,'textures'))): + options.texturespath = os.path.join(options.basepath,'textures') +def setexportscale(ID,val): + global options + options.scale = val + +def setTpath(fname): + global options + options.texturespath = os.path.dirname(fname) +def setXpath(fname): + global options + options.externalspath = os.path.dirname(fname) +def setXApath(fname): + global options + options.xappath = fname + d = dict() + d['xappath'] = options.xappath + Blender.Registry.SetKey('flt_export', d, 1) +def event(evt, val): + x = 1 +def but_event(evt): + global options + + global FLTExport + global FLTClose + global FLTLabel + + global FLTBaseLabel + global FLTTextureLabel + global FLTXRefLabel -if options.verbose >= 1: - print '\nOpenFlight Exporter' - print 'Version:', __version__ - print 'Author: Greg MacDonald' - print __url__[2] - print + global FLTBaseString + global FLTTextureString + global FLTXRefString + + global FLTBasePath + global FLTTexturePath + global FLTXRefPath + + global FLTShadeExport + global FLTShadeDefault + + global FLTCopyTex + global FLTDoXRef + global FLTGlobal + + global FLTScale + + + global FLTXAPP + global FLTXAPPath + global FLTXAPPString + global FLTXAPPLabel + global FLTXAPPChooser + + global FLTAttrib + + -fname = Blender.sys.makename(ext=".flt") -Blender.Window.FileSelector(fs_callback, "Export OpenFlight v16.0", fname) + #choose base path for export + if evt == 4: + Blender.Window.FileSelector(setBpath, "DB Root", options.basepath) + + #choose XREF path + if evt == 6: + Blender.Window.FileSelector(setXpath,"DB Externals",options.externalspath) + + #choose texture path + if evt == 8: + Blender.Window.FileSelector(setTpath,"DB Textures",options.texturespath) + + #export shading toggle + if evt == 9: + options.export_shading = FLTShadeExport.val + #export Textures + if evt == 11: + options.copy_textures = FLTCopyTex.val + #export XRefs + if evt == 13: + options.doxrefs = FLTDoXRef.val + #export Transforms + if evt == 12: + options.export_transform = FLTGlobal.val + + if evt == 14: + options.xapp = FLTXAPP.val + if evt == 16: + Blender.Window.FileSelector(setXApath,"External Application",options.xappath) + if evt == 20: + options.write_attrib_files = FLTAttrib.val + + #Export DB + if evt == 1: + dbexport() + + #exit + if evt == 2: + Draw.Exit() + +from Blender.BGL import * +from Blender import Draw +def gui(): + + global options + + global FLTExport + global FLTClose + global FLTLabel + + global FLTBaseLabel + global FLTTextureLabel + global FLTXRefLabel + + global FLTBaseString + global FLTTextureString + global FLTXRefString + + global FLTBasePath + global FLTTexturePath + global FLTXRefPath + + global FLTShadeExport + global FLTShadeDefault + + global FLTCopyTex + global FLTDoXRef + global FLTGlobal + + global FLTScale + + global FLTXAPP + global FLTXAPPath + global FLTXAPPString + global FLTXAPPLabel + global FLTXAPPChooser + + global FLTAttrib + + glClearColor(0.880,0.890,0.730,1.0 ) + glClear(GL_COLOR_BUFFER_BIT) + + areas = Blender.Window.GetScreenInfo() + curarea = Blender.Window.GetAreaID() + curRect = None + + for area in areas: + if area['id'] == curarea: + curRect = area['vertices'] + break + + width = curRect[2] - curRect[0] + height = curRect[3] - curRect[1] + #draw from top to bottom.... + cx = 50 + #Draw Title Bar... + #glRasterPos2d(cx, curRect[3]-100) + #FLTLabel = Draw.Text("FLT Exporter V2.0",'large') + cy = height - 80 + + #base path + FLTBaseLabel = Draw.Label("Base Path:",cx,cy,100,20) + FLTBaseString = Draw.String("",3,cx+100,cy,300,20,options.basepath,255,"Folder to export to") + FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder") + + cy = cy-40 + + #externals path + FLTXRefLabel = Draw.Label("XRefs:",cx,cy,100,20) + FLTXRefString = Draw.String("",5,cx+100,cy,300,20,options.externalspath,255,"Folder for external references") + FLTXRefChooser = Draw.PushButton("...",6,cx+400,cy,20,20,"Choose Folder") + cy = cy-40 + #Textures path + FLTTextureLabel = Draw.Label("Textures:",cx,cy,100,20) + FLTTextureString = Draw.String("",7,cx+100,cy,300,20,options.texturespath,255,"Folder for texture files") + FLTTextureChooser = Draw.PushButton("...",8,cx+400,cy,20,20,"Choose Folder") + cy=cy-40 + #External application path + FLTXAPPLabel = Draw.Label("XApp:",cx,cy,100,20) + FLTXAPPString = Draw.String("",15,cx+100,cy,300,20,options.xappath,255,"External application to launch when done") + FLTXAPPChooser = Draw.PushButton("...",16,cx+400, cy,20,20,"Choose Folder") + + cy = cy-60 + #Shading Options + FLTShadeExport = Draw.Toggle("Default Shading",9,cx,cy,100,20,options.export_shading,"Turn on export of custom shading") + FLTShadDefault = Draw.Number("",10,cx + 120,cy,100,20,options.shading_default,0.0,180.0,"Default shading angle for objects with no custom shading assigned",setshadingangle) + + cy = cy-40 + FLTScale = Draw.Number("Export Scale",14,cx,cy,220,20,options.scale,0.0,100.0,"Export scaling factor",setexportscale) + + cy = cy-40 + #misc Options + FLTCopyTex = Draw.Toggle("Copy Textures",11,cx,cy,220,20,options.copy_textures,"Copy textures to folder indicated above") + cy = cy-40 + FLTGlobal = Draw.Toggle("Export Transforms",12,cx,cy,220,20,options.export_transform,"If unchecked, Global coordinates are used (recommended)") + cy = cy-40 + FLTDoXRef = Draw.Toggle("Export XRefs", 13,cx,cy,220,20,options.doxrefs,"Export External references (only those below current scene!)") + cy = cy-40 + FLTXAPP = Draw.Toggle("Launch External App", 14, cx,cy,220,20,options.xapp,"Launch External Application on export") + cy = cy-40 + FLTAttrib = Draw.Toggle("Write Attribute Files", 20, cx, cy, 220,20,options.write_attrib_files, "Write Texture Attribute files") + #FLTXAPPATH = Draw.String("",15,cx,cy,300,20,options.xappath,255,"External application path") + + + #Draw export/close buttons + FLTExport = Draw.PushButton("Export",1,cx,20,100,20,"Export to FLT") + FLTClose = Draw.PushButton("Close", 2, cx+120,20,100,20,"Close window") + + +Draw.Register(gui,event,but_event)
\ No newline at end of file diff --git a/release/scripts/flt_filewalker.py b/release/scripts/flt_filewalker.py index 442c9728e91..4a9b86c45d2 100644 --- a/release/scripts/flt_filewalker.py +++ b/release/scripts/flt_filewalker.py @@ -17,6 +17,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +__bpydoc__ ="""\ +File read/write module used by OpenFlight I/O and tool scripts. OpenFlight is a +registered trademark of MultiGen-Paradigm, Inc. +""" + import Blender from struct import * import re @@ -199,7 +204,9 @@ class FltOut: self.file.close() def __init__(self, filename): - self.file = open(filename, 'wb') + self.file = open(filename, 'wb') + self.filename = filename + class FileFinder: def add_file_to_search_path(self, filename): diff --git a/release/scripts/flt_import.py b/release/scripts/flt_import.py index ca0db650447..3ba118c0d18 100755..100644 --- a/release/scripts/flt_import.py +++ b/release/scripts/flt_import.py @@ -1,72 +1,26 @@ #!BPY """ Registration info for Blender menus: Name: 'OpenFlight (.flt)...' -Blender: 238 +Blender: 245 Group: 'Import' Tip: 'Import OpenFlight (.flt)' """ -__author__ = "Greg MacDonald, Campbell Barton" -__version__ = "1.2 10/20/05" +__author__ = "Greg MacDonald, Campbell Barton, Geoffrey Bantle" +__version__ = "2.0 11/21/07" __url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/") __bpydoc__ = """\ This script imports OpenFlight files into Blender. OpenFlight is a registered trademark of MultiGen-Paradigm, Inc. -Run from "File->Import" menu. - -Options are available from Blender's "Scripts Config Editor," accessible through -the "Scripts->System" menu from the scripts window. - -All global_prefs are toggle switches that let the user choose what is imported. Most -are straight-forward, but one option could be a source of confusion. The -"Diffuse Color From Face" option when set pulls the diffuse color from the face -colors. Otherwise the diffuse color comes from the material. What may be -confusing is that this global_prefs only works if the "Diffuse Color" option is set. - -New Features:<br> -* Importer is 14 times faster.<br> -* External triangle module is no longer required, but make sure the importer -has a 3d View screen open while its running or triangulation won't work.<br> -* Should be able to import all versions of flight files. - -Features:<br> -* Heirarchy retained.<br> -* First texture imported.<br> -* Colors imported from face or material.<br> -* LOD seperated out into different layers.<br> -* Asks for location of unfound textures or external references.<br> -* Searches Blender's texture directory in the user preferences panel.<br> -* Triangles with more than 4 verts are triangulated if the Triangle python -module is installed.<br> -* Matrix transforms imported.<br> -* External references to whole files are imported. - -Things To Be Aware Of:<br> -* Each new color and face attribute creates a new material and there are only a maximum of 16 -materials per object.<br> -* For triangulated faces, normals must be recomputed outward manually by typing -CTRL+N in edit mode.<br> -* You can change global_prefs only after an initial import.<br> -* External references are imported as geometry and will be exported that way.<br> -* A work around for not using the Triangle python module is to simply to -triangulate in Creator before importing. This is only necessary if your -model contains 5 or more vertices.<br> -* You have to manually blend the material color with the texture color. - -What's Not Handled:<br> -* Special texture repeating modes.<br> -* Replications and instancing.<br> -* Comment and attribute fields.<br> -* Light points.<br> -* Animations.<br> -* External references to a node within a file.<br> -* Multitexturing.<br> -* Vetex colors.<br> +Feature overview and more availible at: +http://wiki.blender.org/index.php/Scripts/Manual/Import/openflight_flt + +Note: This file is a grab-bag of old and new code. It needs some cleanup still. """ # flt_import.py is an OpenFlight importer for blender. -# Copyright (C) 2005 Greg MacDonald +# Copyright (C) 2005 Greg MacDonald, 2007 Blender Foundation # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -87,15 +41,33 @@ import os import BPyMesh import BPyImage import flt_filewalker +import flt_properties +reload(flt_properties) +from flt_properties import * -Vector= Blender.Mathutils.Vector +#Globals. Should Clean these up and minimize their usage. -def col_to_gray(c): - return 0.3*c[0] + 0.59*c[1] + 0.11*c[2] +typecodes = ['c','C','s','S','i','I','f','d','t'] +records = dict() + +FLTBaseLabel = None +FLTBaseString = None +FLTBaseChooser = None +FLTExport = None +FLTClose = None +FLTDoXRef = None +FLTScale = None +FLTShadeImport = None +FLTAttrib = None + +Vector= Blender.Mathutils.Vector +FLOAT_TOLERANCE = 0.01 +FF = flt_filewalker.FileFinder() +current_layer = 0x01 global_prefs = dict() -global_prefs['verbose']= 1 +global_prefs['verbose']= 4 global_prefs['get_texture'] = True global_prefs['get_diffuse'] = True global_prefs['get_specular'] = False @@ -105,8 +77,41 @@ global_prefs['get_ambient'] = False global_prefs['get_shininess'] = True global_prefs['color_from_face'] = True global_prefs['fltfile']= '' +global_prefs['smoothshading'] = 1 +global_prefs['doxrefs'] = 1 +global_prefs['scale'] = 1.0 +global_prefs['attrib'] = 0 msg_once = False +throw_back_opcodes = [2, 73, 4, 11, 96, 14, 91, 98, 63,111] # Opcodes that indicate its time to return control to parent. +do_not_report_opcodes = [76, 78, 79, 80, 81, 82, 94, 83, 33, 112, 100, 101, 102, 97, 31, 103, 104, 117, 118, 120, 121, 124, 125] + +#Process FLT record definitions +for record in FLT_Records: + props = dict() + for prop in FLT_Records[record]: + position = '' + slice = 0 + (format,name) = prop.split('!') + for i in format: + if i not in typecodes: + position = position + i + slice = slice + 1 + else: + break + type = format[slice:] + length = type[1:] + if len(length) == 0: + length = 1 + else: + type = type[0] + length = int(length) + + props[int(position)] = (type,length,prop) + records[record] = props + +def col_to_gray(c): + return 0.3*c[0] + 0.59*c[1] + 0.11*c[2] class MaterialDesc: # Was going to use int(f*1000.0) instead of round(f,3), but for some reason # round produces better results, as in less dups. @@ -185,16 +190,14 @@ class VertexDesc: self.y = 0.0 self.z = 0.0 - ''' # IGNORE_NORMALS + self.nx = 0.0 - self.ny = 1.0 + self.ny = 0.0 self.nz = 0.0 - ''' + self.uv= Vector(0,0) - self.r = 1.0 - self.g = 1.0 - self.b = 1.0 - self.a = 1.0 + self.cindex = 127 #default/lowest + self.cnorm = False class LightPointAppDesc: def make_key(self): @@ -222,7 +225,7 @@ class LightPointAppDesc: self.props.update({'LOD scale': 0.0}) class GlobalResourceRepository: - def request_lightpoint_app(self, desc): + def request_lightpoint_app(self, desc, scene): match = self.light_point_app.get(desc.make_key()) if match: @@ -231,7 +234,7 @@ class GlobalResourceRepository: # Create empty and fill with properties. name = desc.props['type'] + ': ' + desc.props['id'] object = Blender.Object.New('Empty', name) - scene.link(object) + scene.objects.link(object) object.Layers= current_layer object.sel= 1 @@ -306,6 +309,9 @@ class GlobalResourceRepository: return tex def __init__(self): + + #list of scenes xrefs belong to. + self.xrefs = dict() # material self.mat_dict = dict() mat_lst = Blender.Material.Get() @@ -341,108 +347,6 @@ class GlobalResourceRepository: # light point self.light_point_app = dict() -# Globals -GRR = GlobalResourceRepository() -FF = flt_filewalker.FileFinder() -scene = Blender.Scene.GetCurrent() # just hope they dont chenge scenes once the file selector pops up. -current_layer = 0x01 - - -# Opcodes that indicate its time to return control to parent. -throw_back_opcodes = [2, 73, 4, 11, 96, 14, 91, 98, 63] -do_not_report_opcodes = [76, 78, 79, 80, 81, 82, 94, 83, 33, 112, 100, 101, 102, 97, 31, 103, 104, 117, 118, 120, 121, 124, 125] - -opcode_name = { 0: 'db', - 1: 'head', - 2: 'grp', - 4: 'obj', - 5: 'face', - 10: 'push', - 11: 'pop', - 14: 'dof', - 19: 'push sub', - 20: 'pop sub', - 21: 'push ext', - 22: 'pop ext', - 23: 'cont', - 31: 'comment', - 32: 'color pal', - 33: 'long id', - 49: 'matrix', - 50: 'vector', - 52: 'multi-tex', - 53: 'uv lst', - 55: 'bsp', - 60: 'rep', - 61: 'inst ref', - 62: 'inst def', - 63: 'ext ref', - 64: 'tex pal', - 67: 'vert pal', - 68: 'vert w col', - 69: 'vert w col & norm', - 70: 'vert w col, norm & uv', - 71: 'vert w col & uv', - 72: 'vert lst', - 73: 'lod', - 74: 'bndin box', - 76: 'rot edge', - 78: 'trans', - 79: 'scl', - 80: 'rot pnt', - 81: 'rot and/or scale pnt', - 82: 'put', - 83: 'eyepoint & trackplane pal', - 84: 'mesh', - 85: 'local vert pool', - 86: 'mesh prim', - 87: 'road seg', - 88: 'road zone', - 89: 'morph vert lst', - 90: 'link pal', - 91: 'snd', - 92: 'rd path', - 93: 'snd pal', - 94: 'gen matrix', - 95: 'txt', - 96: 'sw', - 97: 'line styl pal', - 98: 'clip reg', - 100: 'ext', - 101: 'light src', - 102: 'light src pal', - 103: 'reserved', - 104: 'reserved', - 105: 'bndin sph', - 106: 'bndin cyl', - 107: 'bndin hull', - 108: 'bndin vol cntr', - 109: 'bndin vol orient', - 110: 'rsrvd', - 111: 'light pnt', - 112: 'tex map pal', - 113: 'mat pal', - 114: 'name tab', - 115: 'cat', - 116: 'cat dat', - 117: 'rsrvd', - 118: 'rsrvd', - 119: 'bounding hist', - 120: 'rsrvd', - 121: 'rsrvd', - 122: 'push attrib', - 123: 'pop attrib', - 124: 'rsrvd', - 125: 'rsrvd', - 126: 'curv', - 127: 'road const', - 128: 'light pnt appear pal', - 129: 'light pnt anim pal', - 130: 'indexed lp', - 131: 'lp sys', - 132: 'indx str', - 133: 'shdr pal'} - class Handler: def in_throw_back_lst(self, opcode): return opcode in self.throw_back_lst @@ -487,11 +391,11 @@ class Node: print '-', self.props['comment'], print - + for child in self.children: child.blender_import() - - # Import comment. + +# Import comment. # if self.props['comment'] != '': # name = 'COMMENT: ' + self.props['id'] # t = Blender.Text.New(name) @@ -568,8 +472,8 @@ class Node: else: if global_prefs['verbose'] >= 3: print p + ' ignored' - elif global_prefs['verbose'] >= 1 and not opcode in do_not_report_opcodes and opcode in opcode_name: - print opcode_name[opcode], 'not handled' + elif global_prefs['verbose'] >= 1 and not opcode in do_not_report_opcodes and opcode in opcode_name: + print 'not handled' def get_level(self): return self.level @@ -581,7 +485,19 @@ class Node: def parse_comment(self): self.props['comment'] = self.header.fw.read_string(self.header.fw.get_length()-4) return True - + + def parse_record(self): + self.props['type'] = self.opcode + props = records[self.opcode] + propkeys = props.keys() + propkeys.sort() + for position in propkeys: + (type,length,name) = props[position] + self.props[name] = read_prop(self.header.fw,type,length) + try: #remove me! + self.props['id'] = self.props['3t8!id'] + except: + pass def __init__(self, parent, header): self.root_handler = Handler() self.child_handler = Handler() @@ -647,20 +563,16 @@ class VertexPalette(Node): return v def parse_vertex_post_common(self, v): - if not v.flags & 0x2000: # 0x2000 = no color - if v.flags & 0x1000: # 0x1000 = packed color - v.a = self.header.fw.read_uchar() - v.b = self.header.fw.read_uchar() - v.g = self.header.fw.read_uchar() - v.r = self.header.fw.read_uchar() - else: - self.header.fw.read_ahead(4) - - color_index = self.header.fw.read_uint() - v.r, v.g, v.b, v.a= self.header.get_color(color_index) - + #if not v.flags & 0x2000: # 0x2000 = no color + #if v.flags & 0x1000: # 0x1000 = packed color + # v.a = self.header.fw.read_uchar() + # v.b = self.header.fw.read_uchar() + # v.g = self.header.fw.read_uchar() + # v.r = self.header.fw.read_uchar() + #else: + self.header.fw.read_ahead(4) #skip packed color + v.cindex = self.header.fw.read_uint() self.vert_desc_lst.append(v) - return True def parse_vertex_c(self): @@ -672,16 +584,10 @@ class VertexPalette(Node): def parse_vertex_cn(self): v = self.parse_vertex_common() - - ''' + v.cnorm = True v.nx = self.header.fw.read_float() v.ny = self.header.fw.read_float() v.nz = self.header.fw.read_float() - ''' - # Just to advance - self.header.fw.read_float() - self.header.fw.read_float() - self.header.fw.read_float() self.parse_vertex_post_common(v) @@ -698,15 +604,10 @@ class VertexPalette(Node): def parse_vertex_cnuv(self): v = self.parse_vertex_common() - ''' + v.cnorm = True v.nx = self.header.fw.read_float() v.ny = self.header.fw.read_float() v.nz = self.header.fw.read_float() - ''' - # Just to advance - self.header.fw.read_float() - self.header.fw.read_float() - self.header.fw.read_float() v.uv[:] = self.header.fw.read_float(), self.header.fw.read_float() @@ -721,89 +622,374 @@ class InterNode(Node): def __init__(self): self.object = None self.mesh = None - self.isMesh = False + self.hasMesh = False self.faceLs= [] self.matrix = None - - def blender_import_my_faces(self): + self.vis = True + self.hasmtex = False + self.uvlayers = dict() + self.blayernames = dict() + self.subfacelevel = 0 + + mask = 2147483648 + for i in xrange(7): + self.uvlayers[mask] = False + mask = mask / 2 + + def blender_import_my_faces(self): + # Add the verts onto the mesh - mesh = self.mesh blender_verts= self.header.vert_pal.blender_verts vert_desc_lst= self.header.vert_pal.vert_desc_lst - vert_list= [ i for flt_face in self.faceLs for i in flt_face.indices] - - mesh.verts.extend([blender_verts[i] for i in vert_list]) - + vert_list= [ i for flt_face in self.faceLs for i in flt_face.indices] #splitting faces apart. Is this a good thing? + face_edges= [] + face_verts= [] + self.mesh.verts.extend([blender_verts[i] for i in vert_list]) new_faces= [] new_faces_props= [] ngon= BPyMesh.ngon vert_index= 1 + + #add vertex color layer for baked face colors. + self.mesh.addColorLayer("FLT_Fcol") + self.mesh.activeColorLayer = "FLT_Fcol" + + FLT_OrigIndex = 0 for flt_face in self.faceLs: - material_index= flt_face.blen_mat_idx - image= flt_face.blen_image - + if flt_face.tex_index != -1: + try: + image= self.header.tex_pal[flt_face.tex_index][1] + except KeyError: + image= None + else: + image= None face_len= len(flt_face.indices) + #create dummy uvert dicts + if len(flt_face.uverts) == 0: + for i in xrange(face_len): + flt_face.uverts.append(dict()) + #May need to patch up MTex info + if self.hasmtex: + #For every layer in mesh, there should be corresponding layer in the face + for mask in self.uvlayers.keys(): + if self.uvlayers[mask]: + if not flt_face.uvlayers.has_key(mask): #Does the face have this layer? + #Create Layer info for this face + flt_face.uvlayers[mask] = dict() + flt_face.uvlayers[mask]['texture index'] = -1 + flt_face.uvlayers[mask]['texture enviorment'] = 3 + flt_face.uvlayers[mask]['texture mapping'] = 0 + flt_face.uvlayers[mask]['texture data'] = 0 + + #now go through and create dummy uvs for this layer + for uvert in flt_face.uverts: + uv = Vector(0.0,0.0) + uvert[mask] = uv + # Get the indicies in reference to the mesh. - uvs= [vert_desc_lst[j].uv for j in flt_face.indices] - if face_len <=4: # tri or quad + if face_len == 1: + pass + elif face_len == 2: + face_edges.append((vert_index, vert_index+1)) + elif flt_face.props['draw type'] == 2 or flt_face.props['draw type'] == 3: + i = 0 + while i < (face_len-1): + face_edges.append((vert_index + i, vert_index + i + 1)) + i = i + 1 + if flt_face.props['draw type'] == 2: + face_edges.append((vert_index + i,vert_index)) + elif face_len == 3 or face_len == 4: # tri or quad + #if face_len == 1: + # pass + #if face_len == 2: + # face_edges.append((vert_index, vert_index+1)) new_faces.append( [i+vert_index for i in xrange(face_len)] ) - new_faces_props.append((material_index, image, uvs)) + new_faces_props.append((None, image, uvs, flt_face.uverts, flt_face.uvlayers, flt_face.color_index, flt_face.props,FLT_OrigIndex,0, flt_face.subfacelevel)) else: # fgon mesh_face_indicies = [i+vert_index for i in xrange(face_len)] - tri_ngons= ngon(mesh, mesh_face_indicies) + tri_ngons= ngon(self.mesh, mesh_face_indicies) new_faces.extend([ [mesh_face_indicies[t] for t in tri] for tri in tri_ngons]) - new_faces_props.extend( [ (material_index, image, (uvs[tri[0]], uvs[tri[1]], uvs[tri[2]]) ) for tri in tri_ngons ] ) + new_faces_props.extend( [ (None, image, (uvs[tri[0]], uvs[tri[1]], uvs[tri[2]]), [flt_face.uverts[tri[0]], flt_face.uverts[tri[1]], flt_face.uverts[tri[2]]], flt_face.uvlayers, flt_face.color_index, flt_face.props,FLT_OrigIndex,1, flt_face.subfacelevel) for tri in tri_ngons ]) vert_index+= face_len - - mesh.faces.extend(new_faces) - - try: mesh.faceUV= True - except: pass - - for i, f in enumerate(mesh.faces): - f.mat, f.image, f.uv= new_faces_props[i] - + FLT_OrigIndex+=1 + + self.mesh.faces.extend(new_faces) + self.mesh.edges.extend(face_edges) + + #add in the FLT_ORIGINDEX layer + if len(self.mesh.faces): + try: self.mesh.faceUV= True + except: pass + + if self.mesh.faceUV == True: + self.mesh.renameUVLayer(self.mesh.activeUVLayer, 'Layer0') + + #create name layer for faces + self.mesh.faces.addPropertyLayer("FLT_ID",Blender.Mesh.PropertyTypes["STRING"]) + #create layer for face color indices + self.mesh.faces.addPropertyLayer("FLT_COL",Blender.Mesh.PropertyTypes["INT"]) + #create index layer for faces. This is needed by both FGONs and subfaces + self.mesh.faces.addPropertyLayer("FLT_ORIGINDEX",Blender.Mesh.PropertyTypes["INT"]) + #create temporary FGON flag layer. Delete after remove doubles + self.mesh.faces.addPropertyLayer("FLT_FGON",Blender.Mesh.PropertyTypes["INT"]) + self.mesh.faces.addPropertyLayer("FLT_SFLEVEL", Blender.Mesh.PropertyTypes["INT"]) + + for i, f in enumerate(self.mesh.faces): + props = new_faces_props[i] + if props[6]['template billboard'] > 0: + f.transp |= Blender.Mesh.FaceTranspModes["ALPHA"] + if props[6]['template billboard'] == 2: + f.mode |= Blender.Mesh.FaceModes["BILLBOARD"] + f.mode |= Blender.Mesh.FaceModes["LIGHT"] + + #f.mat = props[0] + f.image = props[1] + f.uv = props[2] + #set vertex colors + color = self.header.get_color(props[5]) + if not color: + color = [255,255,255,255] + for mcol in f.col: + mcol.a = color[3] + mcol.r = color[0] + mcol.g = color[1] + mcol.b = color[2] + + f.setProperty("FLT_SFLEVEL", props[9]) + f.setProperty("FLT_ORIGINDEX",i) + f.setProperty("FLT_ID",props[6]['id']) + #if props[5] > 13199: + # print "Warning, invalid color index read in! Using default!" + # f.setProperty("FLT_COL",127) + #else: + if(1): #uh oh.... + value = struct.unpack('>i',struct.pack('>I',props[5]))[0] + f.setProperty("FLT_COL",value) + + #if props[8]: + # f.setProperty("FLT_FGON",1) + #else: + # f.setProperty("FLT_FGON",0) + + + #Create multitex layers, if present. + actuvlayer = self.mesh.activeUVLayer + if(self.hasmtex): + #For every multi-tex layer, we have to add a new UV layer to the mesh + for i,mask in enumerate(reversed(sorted(self.uvlayers))): + if self.uvlayers[mask]: + self.blayernames[mask] = "Layer" + str(i+1) + self.mesh.addUVLayer(self.blayernames[mask]) + + #Cycle through availible multi-tex layers and add face UVS + for mask in self.uvlayers: + if self.uvlayers[mask]: + self.mesh.activeUVLayer = self.blayernames[mask] + for j, f in enumerate(self.mesh.faces): + f.transp |= Blender.Mesh.FaceTranspModes["ALPHA"] + f.mode |= Blender.Mesh.FaceModes["LIGHT"] + props = new_faces_props[j] + uvlayers = props[4] + if uvlayers.has_key(mask): #redundant + uverts = props[3] + for k, uv in enumerate(f.uv): + uv[0] = uverts[k][mask][0] + uv[1] = uverts[k][mask][1] + + uvlayer = uvlayers[mask] + tex_index = uvlayer['texture index'] + if tex_index != -1: + try: + f.image = self.header.tex_pal[tex_index][1] + except KeyError: + f.image = None + + if global_prefs['smoothshading'] == True and len(self.mesh.faces): + #We need to store per-face vertex normals in the faces as UV layers and delete them later. + self.mesh.addUVLayer("FLTNorm1") + self.mesh.addUVLayer("FLTNorm2") + self.mesh.activeUVLayer = "FLTNorm1" + for f in self.mesh.faces: + f.smooth = 1 + #grab the X and Y components of normal and store them in UV + for i, uv in enumerate(f.uv): + vert = f.v[i].index + vert_desc = vert_desc_lst[vert_list[vert-1]] + if vert_desc.cnorm: + uv[0] = vert_desc.nx + uv[1] = vert_desc.ny + else: + uv[0] = 0.0 + uv[1] = 0.0 + + #Now go through and populate the second UV Layer with the z component + self.mesh.activeUVLayer = "FLTNorm2" + for f in self.mesh.faces: + for i, uv in enumerate(f.uv): + vert = f.v[i].index + vert_desc = vert_desc_lst[vert_list[vert-1]] + if vert_desc.cnorm: + uv[0] = vert_desc.nz + uv[1] = 0.0 + else: + uv[0] = 0.0 + uv[1] = 0.0 + + + + #Finally, go through, remove dummy vertex, remove doubles and add edgesplit modifier. + Blender.Mesh.Mode(Blender.Mesh.SelectModes['VERTEX']) + self.mesh.verts.delete(0) # remove the dummy vert + self.mesh.sel= 1 + self.header.scene.update(1) #slow! + self.mesh.remDoubles(0.0001) + + edgeHash = dict() + + for edge in self.mesh.edges: + edgeHash[edge.key] = edge.index + + + if global_prefs['smoothshading'] == True and len(self.mesh.faces): + + #rip out the custom vertex normals from the mesh and place them in a face aligned list. Easier to compare this way. + facenorms = [] + self.mesh.activeUVLayer = "FLTNorm1" + for face in self.mesh.faces: + facenorm = [] + for uv in face.uv: + facenorm.append(Vector(uv[0],uv[1],0.0)) + facenorms.append(facenorm) + self.mesh.activeUVLayer = "FLTNorm2" + for i, face in enumerate(self.mesh.faces): + facenorm = facenorms[i] + for j, uv in enumerate(face.uv): + facenorm[j][2] = uv[0] + self.mesh.removeUVLayer("FLTNorm1") + self.mesh.removeUVLayer("FLTNorm2") + + #find hard edges + #store edge data for lookup by faces + #edgeHash = dict() + #for edge in self.mesh.edges: + # edgeHash[edge.key] = edge.index + + edgeNormHash = dict() + #make sure to align the edgenormals to key value! + for i, face in enumerate(self.mesh.faces): + + facenorm = facenorms[i] + faceEdges = [] + faceEdges.append((face.v[0].index,face.v[1].index,facenorm[0],facenorm[1],face.edge_keys[0])) + faceEdges.append((face.v[1].index,face.v[2].index,facenorm[1],facenorm[2],face.edge_keys[1])) + if len(face.v) == 3: + faceEdges.append((face.v[2].index,face.v[0].index,facenorm[2],facenorm[0],face.edge_keys[2])) + elif len(face.v) == 4: + faceEdges.append((face.v[2].index,face.v[3].index,facenorm[2],facenorm[3],face.edge_keys[2])) + faceEdges.append((face.v[3].index,face.v[0].index,facenorm[3],facenorm[0],face.edge_keys[3])) + + #check to see if edgeNormal has been placed in the edgeNormHash yet + #this is a redundant test, and should be optimized to not be called as often as it is. + for j, faceEdge in enumerate(faceEdges): + #the value we are looking for is (faceEdge[2],faceEdge[3]) + hashvalue = (faceEdge[2],faceEdge[3]) + if (faceEdge[0],faceEdge[1]) != faceEdge[4]: + hashvalue = (hashvalue[1],hashvalue[0]) + assert (faceEdge[1],faceEdge[0]) == faceEdge[4] + if edgeNormHash.has_key(faceEdge[4]): + #compare value in the hash, if different, mark as sharp + edgeNorm = edgeNormHash[faceEdge[4]] + if\ + abs(hashvalue[0][0] - edgeNorm[0][0]) > FLOAT_TOLERANCE or\ + abs(hashvalue[0][1] - edgeNorm[0][1]) > FLOAT_TOLERANCE or\ + abs(hashvalue[0][2] - edgeNorm[0][2]) > FLOAT_TOLERANCE or\ + abs(hashvalue[1][0] - edgeNorm[1][0]) > FLOAT_TOLERANCE or\ + abs(hashvalue[1][1] - edgeNorm[1][1]) > FLOAT_TOLERANCE or\ + abs(hashvalue[1][2] - edgeNorm[1][2]) > FLOAT_TOLERANCE: + edge = self.mesh.edges[edgeHash[faceEdge[4]]] + edge.flag |= Blender.Mesh.EdgeFlags.SHARP + + else: + edgeNormHash[faceEdge[4]] = hashvalue + + #add in edgesplit modifier + mod = self.object.modifiers.append(Blender.Modifier.Types.EDGESPLIT) + mod[Blender.Modifier.Settings.EDGESPLIT_FROM_SHARP] = True + mod[Blender.Modifier.Settings.EDGESPLIT_FROM_ANGLE] = False + + if(actuvlayer): + self.mesh.activeUVLayer = actuvlayer + def blender_import(self): -# name = self.props['type'] + ': ' + self.props['id'] + if self.vis and self.parent: + self.vis = self.parent.vis name = self.props['id'] - if self.isMesh: - self.object = Blender.Object.New('Mesh', name) - #self.mesh = self.object.getData() + + if self.hasMesh: self.mesh = Blender.Mesh.New() - self.mesh.verts.extend( Vector() ) # DUMMYVERT - self.object.link(self.mesh) + self.mesh.name = 'FLT_FaceList' + self.mesh.fakeUser = True + self.mesh.verts.extend( Vector()) #DUMMYVERT + self.object = self.header.scene.objects.new(self.mesh) else: - self.object = Blender.Object.New('Empty', name) + self.object = self.header.scene.objects.new('Empty') - if self.parent: - self.parent.object.makeParent([self.object]) + self.object.name = name + self.header.group.objects.link(self.object) - scene.link(self.object) - self.object.Layer = current_layer - self.object.sel = 1 + #id props import + self.object.properties['FLT'] = dict() + for key in self.props: + try: + self.object.properties['FLT'][key] = self.props[key] + except: #horrible... + pass + if self.parent and self.parent.object and (self.header.scene == self.parent.header.scene): + self.parent.object.makeParent([self.object]) + + if self.vis == False: + self.object.restrictDisplay = True + self.object.restrictRender = True + + else: #check for LOD children and set the proper flags + lodlist = list() + for child in self.children: + if child.props.has_key('type') and child.props['type'] == 73: + lodlist.append(child) + + def LODmin(a,b): + if a.props['5d!switch in'] < b.props['5d!switch in']: + return a + return b + + min= None + if len(lodlist) > 1: + for lod in lodlist: + lod.vis = False + min = lodlist[0] + for i in xrange(len(lodlist)): + min= LODmin(min,lodlist[i]) + min.vis = True + + if self.matrix: + self.object.setMatrix(self.matrix) + Node.blender_import(self) # Attach faces to self.faceLs - if self.isMesh: + if self.hasMesh: # Add all my faces into the mesh at once self.blender_import_my_faces() - - if self.matrix: - self.object.setMatrix(self.matrix) - - # Attach properties - #for name, value in self.props.items(): - # self.object.addProperty(name, value) - + def parse_face(self): - child = Face(self) + child = Face(self, self.subfacelevel) child.parse() return True @@ -838,6 +1024,11 @@ class InterNode(Node): child.parse() return True + def parse_dof(self): + child = DOF(self) + child.parse() + return True + def parse_indexed_light_point(self): child = IndexedLightPoint(self) child.parse() @@ -857,32 +1048,42 @@ class InterNode(Node): m[i].append(f) self.matrix = Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3]) -EDGE_FGON= Blender.Mesh.EdgeFlags['FGON'] -FACE_TEX= Blender.Mesh.FaceModes['TEX'] + def parse_subpush(self): + self.parse_push() + self.subfacelevel+= 1 + return True + def parse_subpop(self): + self.parse_pop() + self.subfacelevel -= 1 + return True + + class Face(Node): - def __init__(self, parent): + def __init__(self, parent,subfacelevel): Node.__init__(self, parent, parent.header) self.root_handler.set_handler({31: self.parse_comment, - 10: self.parse_push}) + 10: self.parse_push, + 52: self.parse_multitex}) self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({72: self.parse_vertex_list, 10: self.parse_push, - 11: self.parse_pop}) + 11: self.parse_pop, + 53: self.parse_uvlist}) if parent: - parent.isMesh = True + parent.hasMesh = True - self.indices = list() # face verts here + self.subfacelevel = subfacelevel + self.indices = list() # face verts here + self.uvlayers = dict() # MultiTexture layers keyed to layer bitmask. + self.uverts = list() # Vertex aligned list of dictionaries keyed to layer bitmask. + self.uvmask = 0 # Bitfield read from MTex record self.comment = '' - self.props = dict.fromkeys(['ir color', 'priority', - 'draw type', 'texture white', 'template billboard', - 'smc', 'fid', 'ir material', 'lod generation control', - 'flags', 'light mode']) - - self.header.fw.read_ahead(8) # face id + self.props = dict() + self.props['id'] = self.header.fw.read_string(8) # Load face. self.props['ir color'] = self.header.fw.read_int() self.props['priority'] = self.header.fw.read_short() @@ -919,186 +1120,14 @@ class Face(Node): self.alt_color_index = self.header.fw.read_uint() #self.header.fw.read_ahead(2) #self.shader_index = self.header.fw.read_short() - - - """ - def blender_import_face(self, material_index, image): - - - mesh = self.parent.mesh - face_len= len(self.indices) - - mesh_vert_len_orig= len(mesh.verts) - mesh.verts.extend([ self.header.vert_pal.blender_verts[i] for i in self.indices]) - - # Exception for an edge - if face_len==2: - mesh.edges.extend((mesh.verts[-1], mesh.verts[-2])) - return - - - mesh_face_indicies = range(mesh_vert_len_orig, mesh_vert_len_orig+face_len) - - #print mesh_face_indicies , 'mesh_face_indicies ' - - # First we need to triangulate NGONS - if face_len>4: - tri_indicies = [[i+mesh_vert_len_orig for i in t] for t in BPyMesh.ngon(mesh, mesh_face_indicies) ] # use range because the verts are in order. - else: - tri_indicies= [mesh_face_indicies] # can be a quad but thats ok - - # Extend face or ngon - - mesh.faces.extend(tri_indicies) - #print mesh.faces, 'mesh.faces' - mesh.faceUV= True - - # Now set UVs - for i in xrange(len(mesh.faces)-len(tri_indicies), len(mesh.faces)): - f= mesh.faces[i] - f_v= f.v - for j, uv in enumerate(f.uv): - vertex_index_flt= self.indices[f_v[j].index - mesh_vert_len_orig] - - vert_desc = self.header.vert_pal.vert_desc_lst[vertex_index_flt] - uv.x, uv.y= vert_desc.u, vert_desc.v - - # Only a bug in 2.42, fixed in cvs - for c in f.col: - c.r=c.g=c.b= 255 - - f.mat = material_index - if image: - f.image = image - else: - f.mode &= ~FACE_TEX - - # FGon - - if face_len>4: - # Add edges we know are not fgon - end_index= len(mesh.verts) - start_index= end_index - len(self.indices) - edge_dict= dict([ ((i, i+1), None) for i in xrange(start_index, end_index-1)]) - edge_dict[(start_index, end_index)]= None # wish this was a set - - fgon_edges= {} - for tri in tri_indicies: - for i in (0,1,2): - i1= tri[i] - i2= tri[i-1] - - # Sort - if i1>i2: - i1,i2= i2,i1 - - if not edge_dict.has_key( (i1,i2) ): # if this works its an edge vert - fgon_edges[i1,i2]= None - - - # Now set fgon flags - for ed in mesh.edges: - i1= ed.v1.index - i2= ed.v2.index - if i1>i2: - i1,i2= i2,i1 - - if fgon_edges.has_key( (i1,i2) ): - # This is an edge tagged for fgonning? - fgon_edges[i1, i2] - ed.flag |= EDGE_FGON - del fgon_edges[i1, i2] # make later searches faster? - - if not fgon_edges: - break - """ - + def parse_comment(self): self.comment = self.header.fw.read_string(self.header.fw.get_length()-4) return True - # returns a tuple (material, image) where material is the blender material and - # image is the blender image or None. - def create_blender_material(self): - # Create face material. - mat_desc = MaterialDesc() - - if self.mat_index != -1: - if not self.mat_index in self.header.mat_desc_pal: - if global_prefs['verbose'] >= 1: - #print 'Warning: Material index', self.mat_index, 'not in material palette.' - pass - else: - mat_pal_desc = self.header.mat_desc_pal[self.mat_index] - mat_desc.alpha = mat_pal_desc.alpha * self.alpha # combine face and mat alphas - mat_desc.ambient = mat_pal_desc.ambient - mat_desc.diffuse = mat_pal_desc.diffuse - mat_desc.specular = mat_pal_desc.specular - mat_desc.emissive = mat_pal_desc.emissive - mat_desc.shininess = mat_pal_desc.shininess - else: - # if no material get alpha from just face. - mat_desc.alpha = self.alpha - - # Color. - if global_prefs['color_from_face']: - color = None - if not self.props['flags'] & 0x40000000: - if self.props['flags'] & 0x10000000: # packed color - color = self.packed_color - else: - color = self.header.get_color(self.color_index) - - if color: - r = float(color[0])/255.0 - g = float(color[1])/255.0 - b = float(color[2])/255.0 - mat_desc.diffuse = [r, g, b] - - # Texture - image = None - if self.tex_index != -1 and self.tex_index in self.header.bl_tex_pal: - mat_desc.tex0 = self.header.bl_tex_pal[self.tex_index] - if mat_desc.tex0: - mat_desc.name = FF.strip_path(self.header.tex_pal[self.tex_index]) - image = mat_desc.tex0.image - - # OpenFlight Face Attributes - mat_desc.face_props = self.props - - # Get material. - mat = GRR.request_mat(mat_desc) - - # Add material to mesh. - mesh = self.parent.mesh - - # Return where it is in the mesh for faces. - mesh_materials= mesh.materials - - material_index= -1 - for i,m in enumerate(mesh_materials): - if m.name==mat.name: - material_index= i - break - - if material_index==-1: - material_index= len(mesh_materials) - if material_index==16: - material_index= 15 - if global_prefs['verbose'] >= 1: - print 'Warning: Too many materials per mesh object. Only a maximum of 16 ' + \ - 'allowed. Using 16th material instead.' - - else: - mesh_materials.append(mat) - mesh.materials= mesh_materials - - return (material_index, image) - - def blender_import(self): vert_count = len(self.indices) - if vert_count < 3: + if vert_count < 1: if global_prefs['verbose'] >= 2: print 'Warning: Ignoring face with no vertices.' return @@ -1106,18 +1135,21 @@ class Face(Node): # Assign material and image self.parent.faceLs.append(self) - self.blen_mat_idx, self.blen_image= self.create_blender_material() - - - + #need to store comment in mesh prop layer! # Store comment info in parent. - if self.comment != '': - if self.parent.props['comment'] != '': - self.parent.props['comment'] += '\n\nFrom Face:\n' + self.comment - else: - self.parent.props['comment'] = self.comment - + #if self.comment != '': + # if self.parent.props['comment'] != '': + # self.parent.props['comment'] += '\n\nFrom Face:\n' + self.comment + # else: + # self.parent.props['comment'] = self.comment + + if self.uvlayers: + #Make sure that the mesh knows about the layers that this face uses + self.parent.hasmtex = True + for mask in self.uvlayers.keys(): + self.parent.uvlayers[mask] = True + def parse_vertex_list(self): length = self.header.fw.get_length() fw = self.header.fw @@ -1138,8 +1170,49 @@ class Face(Node): ' to vertex index.' % byte_offset ''' return True - - + + def parse_multitex(self): + #Parse MultiTex Record. + length = self.header.fw.get_length() + fw = self.header.fw + #num layers == (length - 8) / 4 + uvmask = fw.read_uint() + mask = 2147483648 + for i in xrange(7): + if mask & uvmask: + uvlayer = dict() + self.uvlayers[mask] = uvlayer + mask = mask / 2 + + #read in record for each individual layer. + for key in reversed(sorted(self.uvlayers)): + uvlayer = self.uvlayers[key] + uvlayer['texture index'] = fw.read_ushort() + uvlayer['texture enviorment'] = fw.read_ushort() + uvlayer['texture mapping'] = fw.read_ushort() + uvlayer['texture data'] = fw.read_ushort() + + self.uvmask = uvmask + + def parse_uvlist(self): + #for each uvlayer, add uv vertices + length = self.header.fw.get_length() + fw = self.header.fw + uvmask = fw.read_uint() + if uvmask != self.uvmask: #This should never happen! + fw.read_ahead(self.length - 4) #potentially unnessecary? + else: + #need to store in uvverts dictionary for each vertex. + totverts = len(self.indices) + for i in xrange(totverts): + uvert = dict() + for key in reversed(sorted(self.uvlayers)): + uv = Vector(0.0,0.0) + uv[0] = fw.read_float() + uv[1] = fw.read_float() + uvert[key] = uv + self.uverts.append(uvert) + class Object(InterNode): def __init__(self, parent): Node.__init__(self, parent, parent.header) @@ -1152,15 +1225,15 @@ class Object(InterNode): self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({5: self.parse_face, - #130: self.parse_indexed_light_point, - #111: self.parse_inline_light_point, + 19: self.parse_subpush, + 20: self.parse_subpop, + 111: self.parse_inline_light_point, 10: self.parse_push, 11: self.parse_pop}) - self.props['type'] = 'Object' - self.props['id'] = self.header.fw.read_string(8) - - + self.props = dict() + self.props['comment'] = '' + self.parse_record() class Group(InterNode): def __init__(self, parent): @@ -1174,15 +1247,16 @@ class Group(InterNode): self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({5: self.parse_face, - #130: self.parse_indexed_light_point, - #111: self.parse_inline_light_point, + 19: self.parse_subpush, + 20: self.parse_subpop, + 111: self.parse_inline_light_point, 2: self.parse_group, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, - 14: self.parse_unhandled, + 14: self.parse_dof, 91: self.parse_unhandled, 98: self.parse_unhandled, 63: self.parse_xref}) @@ -1190,20 +1264,48 @@ class Group(InterNode): 'special2', 'significance', 'layer code', 'loop count', 'loop duration', 'last frame duration']) - self.props['type'] = 'Group' self.props['comment'] = '' - self.props['id'] = self.header.fw.read_string(8) - self.props['priority'] = self.header.fw.read_short() - self.header.fw.read_ahead(2) - self.props['flags'] = self.header.fw.read_int() - self.props['special1'] = self.header.fw.read_short() - self.props['special2'] = self.header.fw.read_short() - self.props['significance'] = self.header.fw.read_short() - self.props['layer code'] = self.header.fw.read_char() - self.header.fw.read_ahead(5) - self.props['loop count'] = self.header.fw.read_int() - self.props['loop duration'] = self.header.fw.read_float() - self.props['last frame duration'] = self.header.fw.read_float() + self.parse_record() + + #self.props['type'] = str(self.opcode) + ':' + opcode_name[self.opcode] + #props = records[self.opcode] + #propkeys = props.keys() + #propkeys.sort() + #for position in propkeys: + # (type,length,name) = props[position] + # self.props[name] = read_prop(self.header.fw,type,length) + #self.props['id'] = self.props['3t8!id'] + +class DOF(InterNode): + def blender_import(self): + InterNode.blender_import(self) + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({#130: self.parse_indexed_light_point, + 111: self.parse_inline_light_point, + 2: self.parse_group, + 73: self.parse_lod, + 4: self.parse_object, + 10: self.parse_push, + 11: self.parse_pop, + 96: self.parse_unhandled, + 14: self.parse_dof, + 91: self.parse_unhandled, + 98: self.parse_unhandled, + 63: self.parse_xref}) + self.props = dict() + self.props['comment'] = '' + self.parse_record() + class XRef(InterNode): def parse(self): @@ -1214,31 +1316,66 @@ class XRef(InterNode): def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) - + self.root_handler.set_handler({49: self.parse_matrix}) self.root_handler.set_throw_back_lst(throw_back_opcodes) - xref_filename = self.header.fw.read_string(200) - filename = FF.find(xref_filename) + self.props = dict() + self.props['comment'] = '' + self.parse_record() - self.props['type'] = 'XRef' + xref_filename = self.props['3t200!filename'] + self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(xref_filename))[0] #this is really wrong as well.... - if filename != None: - self.xref = Database(filename, self) - self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(filename))[0] + if global_prefs['doxrefs'] and os.path.exists(xref_filename) and not self.header.grr.xrefs.has_key(xref_filename): + self.xref = Database(xref_filename, self.header.grr, self) + self.header.grr.xrefs[xref_filename] = self.xref else: self.xref = None - self.props['id'] = 'X: broken' + + + def blender_import(self): + #name = self.props['type'] + ': ' + self.props['id'] + name = self.props['id'] + self.object = self.header.scene.objects.new('Empty') + self.object.name = name + self.object.enableDupGroup = True + self.header.group.objects.link(self.object) + + #for broken links its ok to leave this empty! they purely for visual purposes anyway..... + try: + self.object.DupGroup = self.header.grr.xrefs[self.props['3t200!filename']].group + except: + pass + + if self.parent and self.parent.object: + self.parent.object.makeParent([self.object]) + + #id props import + self.object.properties['FLT'] = dict() + for key in self.props: + try: + self.object.properties['FLT'][key] = self.props[key] + except: #horrible... + pass + self.object.Layer = current_layer + self.object.sel = 1 + if self.matrix: + self.object.setMatrix(self.matrix) + Node.blender_import(self) + + class LOD(InterNode): def blender_import(self): - self.move_to_next_layer() + #self.move_to_next_layer() InterNode.blender_import(self) - + #self.object.properties['FLT'] = self.props.copy() + def __init__(self, parent): Node.__init__(self, parent, parent.header) InterNode.__init__(self) - + self.root_handler.set_handler({33: self.parse_long_id, 31: self.parse_comment, 10: self.parse_push, @@ -1246,18 +1383,20 @@ class LOD(InterNode): self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({2: self.parse_group, + 111: self.parse_inline_light_point, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, # switch - 14: self.parse_unhandled, # DOF + 14: self.parse_dof, # DOF 91: self.parse_unhandled, # sound 98: self.parse_unhandled, # clip 63: self.parse_xref}) - self.props['type'] = 'LOD' - self.props['id'] = self.header.fw.read_string(8) + self.props = dict() + self.props['comment'] = '' + self.parse_record() class InlineLightPoint(InterNode): def __init__(self, parent): @@ -1274,119 +1413,49 @@ class InlineLightPoint(InterNode): 11: self.parse_pop}) self.indices = list() - - self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance']) - self.app_props = dict() - + self.props = dict() self.props['comment'] = '' - self.props['type'] = 'Light Point' - self.props['id'] = self.header.fw.read_string(8) - - self.app_props.update({'smc': self.header.fw.read_short()}) - self.app_props.update({'fid': self.header.fw.read_short()}) - self.app_props.update({'back color: a': self.header.fw.read_uchar()}) - self.app_props.update({'back color: b': self.header.fw.read_uchar()}) - self.app_props.update({'back color: g': self.header.fw.read_uchar()}) - self.app_props.update({'back color: r': self.header.fw.read_uchar()}) - self.app_props.update({'display mode': self.header.fw.read_int()}) - self.app_props.update({'intensity': self.header.fw.read_float()}) - self.app_props.update({'back intensity': self.header.fw.read_float()}) - self.app_props.update({'minimum defocus': self.header.fw.read_float()}) - self.app_props.update({'maximum defocus': self.header.fw.read_float()}) - self.app_props.update({'fading mode': self.header.fw.read_int()}) - self.app_props.update({'fog punch mode': self.header.fw.read_int()}) - self.app_props.update({'directional mode': self.header.fw.read_int()}) - self.app_props.update({'range mode': self.header.fw.read_int()}) - self.app_props.update({'min pixel size': self.header.fw.read_float()}) - self.app_props.update({'max pixel size': self.header.fw.read_float()}) - self.app_props.update({'actual size': self.header.fw.read_float()}) - self.app_props.update({'trans falloff pixel size': self.header.fw.read_float()}) - self.app_props.update({'trans falloff exponent': self.header.fw.read_float()}) - self.app_props.update({'trans falloff scalar': self.header.fw.read_float()}) - self.app_props.update({'trans falloff clamp': self.header.fw.read_float()}) - self.app_props.update({'fog scalar': self.header.fw.read_float()}) - self.app_props.update({'fog intensity': self.header.fw.read_float()}) - self.app_props.update({'size threshold': self.header.fw.read_float()}) - self.app_props.update({'directionality': self.header.fw.read_int()}) - self.app_props.update({'horizontal lobe angle': self.header.fw.read_float()}) - self.app_props.update({'vertical lobe angle': self.header.fw.read_float()}) - self.app_props.update({'lobe roll angle': self.header.fw.read_float()}) - self.app_props.update({'dir falloff exponent': self.header.fw.read_float()}) - self.app_props.update({'dir ambient intensity': self.header.fw.read_float()}) - self.header.fw.read_ahead(12) # Animation settings. - self.app_props.update({'significance': self.header.fw.read_float()}) - self.props['draw order'] = self.header.fw.read_int() - self.app_props.update({'flags': self.header.fw.read_int()}) - #self.fw.read_ahead(12) # More animation settings. - - # return dictionary: lp_app name => index list - def group_points(self, props): - - name_to_indices = {} - - for i in self.indices: - vert_desc = self.header.vert_pal.vert_desc_lst[i] - app_desc = LightPointAppDesc() - app_desc.props.update(props) - # add vertex normal and color - app_desc.props.update({'nx': vert_desc.nx}) - app_desc.props.update({'ny': vert_desc.ny}) - app_desc.props.update({'nz': vert_desc.nz}) - - app_desc.props.update({'r': vert_desc.r}) - app_desc.props.update({'g': vert_desc.g}) - app_desc.props.update({'b': vert_desc.b}) - app_desc.props.update({'a': vert_desc.a}) - - app_name = GRR.request_lightpoint_app(app_desc) + self.parse_record() - if name_to_indices.get(app_name): - name_to_indices[app_name].append(i) - else: - name_to_indices.update({app_name: [i]}) - - return name_to_indices def blender_import(self): - name = '%s: %s' % (self.props['type'], self.props['id']) + - name_to_indices = self.group_points(self.app_props) + name = self.props['id'] + self.mesh= Blender.Mesh.New() + self.mesh.name = 'FLT_LP' + self.object = self.header.scene.objects.new(self.mesh) + self.object.name = name + #self.mesh.verts.extend(Vector() ) # DUMMYVERT + self.object.Layer = current_layer + self.object.sel= 1 + + self.object.properties['FLT'] = dict() + for key in self.props: + try: + self.object.properties['FLT'][key] = self.props[key] + except: #horrible... + pass + + if self.parent and self.parent.object and self.header.scene == self.parent.header.scene: + self.parent.object.makeParent([self.object]) - for app_name, indices in name_to_indices.iteritems(): - self.object = Blender.Object.New('Mesh', name) - #self.mesh = self.object.getData() - self.mesh= Blender.Mesh.New() - self.mesh.verts.extend( Vector() ) # DUMMYVERT - self.object.link(self.mesh) + if self.matrix: + self.object.setMatrix(self.matrix) - if self.parent: - self.parent.object.makeParent([self.object]) + self.mesh.verts.extend([self.header.vert_pal.blender_verts[i] for i in self.indices]) + + #add color index information. + self.mesh.verts.addPropertyLayer("FLT_VCOL",Blender.Mesh.PropertyTypes["INT"]) + for i, vindex in enumerate(self.indices): + vdesc = self.header.vert_pal.vert_desc_lst[vindex] + v = self.mesh.verts[i] + v.setProperty("FLT_VCOL",vdesc.cindex) + #for i, v in enumerate(self.mesh.verts): + # vdesc = self.header.vert_pal.vert_desc_lst[i] + # v.setProperty("FLT_VCOL",vdesc.cindex) + self.mesh.update() - for i in indices: - vert = self.header.vert_pal.blender_verts[i] - self.mesh.verts.append(vert) - - scene.link(self.object) - self.object.Layer = current_layer - self.object.sel= 1 - - if self.matrix: - self.object.setMatrix(self.matrix) - - # Import comment. - if self.props['comment'] != '': - name = 'COMMENT: ' + self.props['id'] - t = Blender.Text.New(name) - t.write(self.props['comment']) - self.props['comment'] = name - - # Attach properties. - self.props.update({'appearance': app_name}) - for name, value in self.props.iteritems(): - self.object.addProperty(name, value) - - self.mesh.update() - def parse_vertex_list(self): length = self.header.fw.get_length() fw = self.header.fw @@ -1432,7 +1501,7 @@ class IndexedLightPoint(InterNode): app_desc.props.update({'b': vert_desc.b}) app_desc.props.update({'a': vert_desc.a}) - app_name = GRR.request_lightpoint_app(app_desc) + app_name = self.header.grr.request_lightpoint_app(app_desc, self.header.scene) if name_to_indices.get(app_name): name_to_indices[app_name].append(i) @@ -1448,7 +1517,6 @@ class IndexedLightPoint(InterNode): for app_name, indices in name_to_indices.iteritems(): self.object = Blender.Object.New('Mesh', name) - #self.mesh = self.object.getData() self.mesh= Blender.Mesh.New() self.mesh.verts.extend( Vector() ) # DUMMYVERT self.object.link(self.mesh) @@ -1460,7 +1528,7 @@ class IndexedLightPoint(InterNode): vert = self.header.vert_pal.blender_verts[i] self.mesh.verts.append(vert) - scene.link(self.object) + self.header.scene.objects.link(self.object) self.object.Layer = current_layer @@ -1543,7 +1611,7 @@ class Unhandled(InterNode): 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, # switch - 14: self.parse_unhandled, # DOF + 14: self.parse_dof, # DOF 91: self.parse_unhandled, # sound 98: self.parse_unhandled, # clip 63: self.parse_xref}) @@ -1552,30 +1620,31 @@ class Unhandled(InterNode): class Database(InterNode): def blender_import(self): - self.tex_pal = dict(self.tex_pal_lst) - del self.tex_pal_lst - - # Setup Textures - bl_tex_pal_lst = list() - for i in self.tex_pal.iterkeys(): - path_filename = FF.find(self.tex_pal[i]) + for key in self.tex_pal.keys(): + path_filename= FF.find(self.tex_pal[key][0]) if path_filename != None: - img = GRR.request_image(path_filename) + img = self.grr.request_image(path_filename) if img: - tex = GRR.request_texture(img) - tex.setName(FF.strip_path(self.tex_pal[i])) - bl_tex_pal_lst.append( (i, tex) ) - else: - bl_tex_pal_lst.append( (i, None) ) + self.tex_pal[key][1] = img elif global_prefs['verbose'] >= 1: - print 'Warning: Unable to find', self.tex_pal[i] - - self.bl_tex_pal = dict(bl_tex_pal_lst) - - # Setup Materials - self.mat_desc_pal = dict(self.mat_desc_pal_lst) - - InterNode.blender_import(self) + print 'Warning: Unable to find', self.tex_pal[key][0] + + self.scene.properties['FLT'] = dict() + for key in self.props: + try: + self.scene.properties['FLT'][key] = self.props[key] + except: #horrible... + pass + + self.scene.properties['FLT']['Main'] = 0 + self.scene.properties['FLT']['Filename'] = self.bname + + #import color palette + carray = list() + for color in self.col_pal: + carray.append(struct.unpack('>i',struct.pack('>BBBB',color[0],color[1],color[2],color[3]))[0]) + self.scene.properties['FLT']['Color Palette'] = carray + Node.blender_import(self) def parse_appearance_palette(self): props = dict() @@ -1696,9 +1765,10 @@ class Database(InterNode): return True def get_color(self, color_index): + color = None index = color_index / 128 intensity = float(color_index - 128.0 * index) / 127.0 - + if index >= 0 and index <= 1023: brightest = self.col_pal[index] r = int(brightest[0] * intensity) @@ -1707,7 +1777,7 @@ class Database(InterNode): a = int(brightest[3]) color = [r, g, b, a] - + return color def parse_color_palette(self): @@ -1728,15 +1798,56 @@ class Database(InterNode): def parse_texture_palette(self): name = self.fw.read_string(200) index = self.fw.read_int() - self.tex_pal_lst.append( (index, name) ) + self.tex_pal[index]= [name, None] return True - - def __init__(self, filename, parent=None): + + def read_attribute_files(self): + for tex in self.tex_pal.keys(): + [name,image] = self.tex_pal[tex] + basename = os.path.basename(name) + if(image): + basename = basename + ".attr" + dirname = os.path.dirname(Blender.sys.expandpath(image.getFilename())) #can't rely on original info stored in pallette since it might be relative link + newpath = os.path.join(dirname, basename) + if os.path.exists(newpath) and not image.properties.has_key('FLT'): + fw = flt_filewalker.FltIn(newpath) + fw.read_ahead(8) #We dont care what the attribute file says about x/y dimensions + image.properties['FLT']={} + + #need to steal code from parse records.... + props = records['Image'] + propkeys = props.keys() + propkeys.sort() + for position in propkeys: + (type,length,name) = props[position] + image.properties['FLT'][name] = read_prop(fw,type,length) + fw.close_file() + + #copy clamp settings + wrap = image.properties['FLT']['10i!Wrap'] + wrapu = image.properties['FLT']['11i!WrapU'] + wrapv = image.properties['FLT']['12i!WrapV'] + + if wrapu == 3 or wrapv == 3: + wrapuv = (wrap,wrap) + else: + wrapuv = (wrapu, wrapv) + image.clampX = wrapuv[0] + image.clampY = wrapuv[1] + + elif not os.path.exists(newpath): + print "Cannot read attribute file:" + newpath + + def __init__(self, filename, grr, parent=None): if global_prefs['verbose'] >= 1: print 'Parsing:', filename print self.fw = flt_filewalker.FltIn(filename) + self.filename = filename + self.bname = os.path.splitext(os.path.basename(filename))[0] + self.grr = grr + Node.__init__(self, parent, self) InterNode.__init__(self) @@ -1753,23 +1864,26 @@ class Database(InterNode): self.root_handler.set_throw_back_lst(throw_back_opcodes) self.child_handler.set_handler({#130: self.parse_indexed_light_point, - #111: self.parse_inline_light_point, + 111: self.parse_inline_light_point, 2: self.parse_group, 73: self.parse_lod, 4: self.parse_object, 10: self.parse_push, 11: self.parse_pop, 96: self.parse_unhandled, - 14: self.parse_unhandled, + 14: self.parse_dof, 91: self.parse_unhandled, 98: self.parse_unhandled, 63: self.parse_xref}) + self.scene = Blender.Scene.New(self.bname) + self.group = Blender.Group.New(self.bname) + self.vert_pal = None self.lightpoint_appearance_pal = dict() self.tex_pal = dict() - self.tex_pal_lst = list() - self.bl_tex_pal = dict() + #self.tex_pal_lst = list() + #self.bl_tex_pal = dict() self.col_pal = list() self.mat_desc_pal_lst = list() self.mat_desc_pal = dict() @@ -1778,7 +1892,70 @@ class Database(InterNode): 'sw lon', 'ne lat', 'ne lon', 'origin lat', 'origin lon', 'lambert lat1', 'lambert lat2', 'ellipsoid model', 'utm zone', 'radius', 'major axis', 'minor axis']) -def select_file(filename): + +def clearparent(root,childhash): + for child in childhash[root]: + clearparent(child,childhash) + root.clrParent(2,0) + +def fixscale(root,childhash): + for child in childhash[root]: + fixscale(child,childhash) + location = Blender.Mathutils.Vector(root.getLocation('worldspace')) + if location[0] != 0.0 and location[1] != 0.0 and location[2] != 0.0: + #direction = Blender.Mathutils.Vector(0-location[0],0-location[1],0-location[2]) #reverse vector + smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4) + root.setLocation(location * smat) + #if its a mesh, we need to scale all of its vertices too + if root.type == 'Mesh': + smat = Blender.Mathutils.ScaleMatrix(global_prefs['scale'],4) + rmesh = root.getData(mesh=True) + for v in rmesh.verts: + v.co = v.co * smat + + +def reparent(root,childhash,sce): + for child in childhash[root]: + reparent(child,childhash,sce) + + root.makeParent(childhash[root]) + sce.update(1) + +def update_scene(root,sdone): + for object in root.objects: + if object.DupGroup: + try: + child = Blender.Scene.Get(object.DupGroup.name) + except: + child = None + if child and child not in sdone: + update_scene(child,sdone) + root.makeCurrent() + #create a list of children for each object + childhash = dict() + for object in root.objects: + childhash[object] = list() + + for object in root.objects: + if object.parent: + childhash[object.parent].append(object) + + for object in root.objects: + if not object.parent: + #recursivley go through and clear all the children of their transformation, starting at deepest level first. + clearparent(object,childhash) + #now fix the location of everything + fixscale(object,childhash) + #now fix the parenting + reparent(object,childhash,root) + + for object in root.objects: + object.makeDisplayList() + root.update(1) + sdone.append(root) + + +def select_file(filename, grr): if not Blender.sys.exists(filename): msg = 'Error: File ' + filename + ' does not exist.' Blender.Draw.PupMenu(msg) @@ -1801,13 +1978,12 @@ def select_file(filename): global_prefs['get_ambient'] = False global_prefs['get_shininess'] = True global_prefs['color_from_face'] = True + global_prefs['log to blender'] = True + + - # Start loading the file, - # first set the context Blender.Window.WaitCursor(True) Blender.Window.EditMode(0) - for ob in scene.objects: - ob.sel=0 FF.add_file_to_search_path(filename) @@ -1817,7 +1993,7 @@ def select_file(filename): print load_time = Blender.sys.time() - db = Database(filename) + db = Database(filename,grr) db.parse() load_time = Blender.sys.time() - load_time @@ -1828,23 +2004,15 @@ def select_file(filename): import_time = Blender.sys.time() db.blender_import() - import_time = Blender.sys.time() - import_time - - Blender.Window.ViewLayer(range(1,21)) - - # FIX UP AFTER DUMMY VERT AND REMOVE DOUBLES - Blender.Mesh.Mode(Blender.Mesh.SelectModes['VERTEX']) - for ob in scene.objects.context: - if ob.type=='Mesh': - me=ob.getData(mesh=1) - me.verts.delete(0) # remove the dummy vert - me.sel= 1 - me.remDoubles(0.0001) + if global_prefs['attrib']: + print "reading attribute files" + db.read_attribute_files() + Blender.Window.ViewLayer(range(1,21)) - Blender.Window.RedrawAll() - + update_scene(db.scene,[]) + import_time = Blender.sys.time() - import_time if global_prefs['verbose'] >= 1: print 'Done.' print @@ -1854,37 +2022,112 @@ def select_file(filename): Blender.Window.WaitCursor(False) +def setimportscale(ID,val): + global global_prefs + global_prefs['scale'] = val +def setBpath(fname): + global_prefs['fltfile'] = fname -if global_prefs['verbose'] >= 1: - print - print 'OpenFlight Importer' - print 'Version:', __version__ - print 'Author: Greg MacDonald' - print __url__[2] - print +def event(evt,val): + pass +def but_event(evt): + + global FLTBaseLabel + global FLTBaseString + global FLTBaseChooser + global FLTExport + global FLTClose + + global FLTDoXRef + global FLTShadeImport + global FLTAttrib + + #Import DB + if evt == 1: + if global_prefs['verbose'] >= 1: + print + print 'OpenFlight Importer' + print 'Version:', __version__ + print 'Author: Greg MacDonald, Campbell Barton, Geoffrey Bantle' + print __url__[2] + print + + GRR = GlobalResourceRepository() + select_file(global_prefs['fltfile'], GRR) + #choose base path for export + if evt == 4: + Blender.Window.FileSelector(setBpath, "DB Root", global_prefs['fltfile']) + #Import custom shading? + if evt == 9: + global_prefs['smoothshading'] = FLTShadeImport.val + #Import Image attribute files + if evt == 10: + global_prefs['attrib'] = FLTAttrib.val + #export XRefs + if evt == 13: + global_prefs['doxrefs'] = FLTDoXRef.val + + if evt == 2: + Draw.Exit() + -if __name__ == '__main__': - Blender.Window.FileSelector(select_file, "Import OpenFlight", "*.flt") - #select_file('/fe/flt/helnwsflt/helnws.flt') - #select_file('/fe/flt/Container_006.flt') - #select_file('/fe/flt/NaplesORIGINALmesh.flt') - #select_file('/Anti_tank_D30.flt') - #select_file('/metavr/file_examples/flt/cherrypoint/CherryPoint_liter_runway.flt') + +from Blender.BGL import * +from Blender import Draw +def gui(): + + global FLTBaseLabel + global FLTBaseString + global FLTBaseChooser -""" -TIME= Blender.sys.time() -import os -PATH= 'c:\\flt_test' -for FNAME in os.listdir(PATH): - if FNAME.lower().endswith('.flt'): - FPATH= os.path.join(PATH, FNAME) - newScn= Blender.Scene.New(FNAME) - newScn.makeCurrent() - scene= newScn - select_file(FPATH) - -print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) -""" -
\ No newline at end of file + global FLTExport + global FLTClose + + global FLTDoXRef + global FLTShadeImport + + global FLTAttrib + + + glClearColor(0.772,0.832,0.847,1.0) + glClear(GL_COLOR_BUFFER_BIT) + + areas = Blender.Window.GetScreenInfo() + curarea = Blender.Window.GetAreaID() + curRect = None + + for area in areas: + if area['id'] == curarea: + curRect = area['vertices'] + break + + width = curRect[2] - curRect[0] + height = curRect[3] - curRect[1] + cx = 50 + cy = height - 80 + + FLTBaseLabel = Draw.Label("Base file:",cx,cy,100,20) + FLTBaseString = Draw.String("",3,cx+100,cy,300,20,global_prefs['fltfile'],255,"Root DB file") + FLTBaseChooser = Draw.PushButton("...",4,cx+400,cy,20,20,"Choose Folder") + + cy = cy-40 + FLTScale = Draw.Number("Import Scale",14,cx,cy,220,20,global_prefs['scale'],0.0,100.0,"Export scaleing factor",setimportscale) + + cy = cy-40 + FLTDoXRef = Draw.Toggle("Import XRefs", 13,cx,cy,220,20,global_prefs['doxrefs'],"Import External references") + + cy = cy-40 + FLTShadeImport = Draw.Toggle("Import Custom Shading",9,cx,cy,220,20,global_prefs['smoothshading'],"Import custom shading via edgesplit modifiers") + + cy = cy-40 + FLTAttrib = Draw.Toggle("Import Attribute Files", 10,cx,cy,220,20,global_prefs['attrib'],"Import Image Attribute files") + + cy = cy - 40 + FLTExport = Draw.PushButton("Import",1,cx,20,100,20,"Import FLT Database") + FLTClose = Draw.PushButton("Close",2,cx+120,20,100,20,"Close Window") + + + +Draw.Register(gui,event,but_event)
\ No newline at end of file diff --git a/release/scripts/flt_palettemanager.py b/release/scripts/flt_palettemanager.py new file mode 100644 index 00000000000..c641a0a4f08 --- /dev/null +++ b/release/scripts/flt_palettemanager.py @@ -0,0 +1,388 @@ +#!BPY + +""" +Name: 'FLT Palette Manager' +Blender: 240 +Group: 'Misc' +Tooltip: 'Manage FLT colors' +""" + +__author__ = "Geoffrey Bantle" +__version__ = "1.0 11/21/2007" +__email__ = ('scripts', 'Author, ') +__url__ = ('blender', 'elysiun') + +__bpydoc__ ="""\ + +This script manages colors in OpenFlight databases. OpenFlight is a +registered trademark of MultiGen-Paradigm, Inc. + +Todo: +-Figure out whats causing the PC speaker to beep when initializing... + +Feature overview and more availible at: +http://wiki.blender.org/index.php/Scripts/Manual/FLTools +""" + +# -------------------------------------------------------------------------- +# flt_palettemanager.py version 0.1 2005/04/08 +# -------------------------------------------------------------------------- +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Copyright (C) 2007: Blender Foundation +# +# 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 ***** +# -------------------------------------------------------------------------- + +import Blender.Draw as Draw +from Blender.BGL import * +import Blender +import flt_properties +import flt_defaultp as defaultp +from flt_properties import * + + +palette_size = 12 +palette_x = 0 +palette_y = 0 + +colors = list() +curint = 1.0 +curswatch = 0 +#make a default palette, not very useful. +cinc = 1.0 / 1024.0 +cstep = 0.0 +picker = None +ptt = "" +for i in xrange(1024): + colors.append([cstep,cstep,cstep]) + cstep = cstep + cinc +def update_state(): + state = dict() + state["activeScene"] = Blender.Scene.getCurrent() + state["activeObject"] = state["activeScene"].getActiveObject() + state["activeMesh"] = None + if state["activeObject"] and state["activeObject"].type == 'Mesh': + state["activeMesh"] = state["activeObject"].getData(mesh=True) + + state["activeFace"] = None + if state["activeMesh"]: + if state["activeMesh"].faceUV and state["activeMesh"].activeFace != None: + state["activeFace"] = state["activeMesh"].faces[state["activeMesh"].activeFace] + + return state + +def pack_face_index(index, intensity): + return ((127*intensity)+(128*index)) +def unpack_face_index(face_index): + index = face_index / 128 + intensity = float(face_index - 128.0 * index) / 127.0 + return(index,intensity) + +def event(evt,val): + global palette_size + global palette_x + global palette_y + global colors + global curint + global curswatch + + areas = Blender.Window.GetScreenInfo() + curarea = Blender.Window.GetAreaID() + curRect = None + editmode = 0 + + for area in areas: + if area['id'] == curarea: + curRect = area['vertices'] + break + + if evt == Draw.LEFTMOUSE: + mval = Blender.Window.GetMouseCoords() + rastx = mval[0] - curRect[0] + rasty = mval[1] - curRect[1] + + swatchx = (rastx -palette_x) / palette_size #+state["palette_x"] + swatchy = (rasty -palette_y) / palette_size #+state["palette_y"] + if rastx > palette_x and rastx < (palette_x + palette_size * 32) and rasty > palette_y and rasty < (palette_y+ palette_size* 32): + if swatchx < 32 and swatchy < 32: + curswatch = (swatchx * 32) + swatchy + Draw.Redraw(1) + + elif swatchy < 34 and swatchx < 32: + curint = 1.0 - (float(rastx-palette_x)/(palette_size*32.0)) + Draw.Redraw(1) + + #copy current color and intensity to selected faces. + elif evt == Draw.CKEY: + + if Blender.Window.EditMode(): + Blender.Window.EditMode(0) + editmode = 1 + state = update_state() + + #retrieve color from palette + color = struct.unpack('>BBBB',struct.pack('>I',colors[curswatch])) + actmesh = state["activeMesh"] + if actmesh: + if(Blender.Window.GetKeyQualifiers() != Blender.Window.Qual["CTRL"]): + selfaces = list() + for face in actmesh.faces: + if face.sel: + selfaces.append(face) + + if not "FLT_COL" in actmesh.faces.properties: + actmesh.faces.addPropertyLayer("FLT_COL",Blender.Mesh.PropertyTypes["INT"]) + for face in actmesh.faces: + face.setProperty("FLT_COL",127) #default + try: + actmesh.activeColorLayer = "FLT_Fcol" + except: + actmesh.addColorLayer("FLT_Fcol") + actmesh.activeColorLayer = "FLT_Fcol" + + + for face in selfaces: + #First append packed index + color and store in face property + face.setProperty("FLT_COL",int(pack_face_index(curswatch,curint))) + #Save baked color to face vertex colors + for col in face.col: + col.r = int(color[0] * curint) + col.g = int(color[1] * curint) + col.b = int(color[2] * curint) + col.a = int(color[3] * curint) + else: + if Blender.Mesh.Mode() == Blender.Mesh.SelectModes['VERTEX']: + if not 'FLT_VCOL' in actmesh.verts.properties: + actmesh.verts.addPropertyLayer("FLT_VCOL",Blender.Mesh.PropertyTypes["INT"]) + for vert in actmesh.verts: + vert.setProperty("FLT_VCOL",127) + else: + for vert in actmesh.verts: + if vert.sel: + vert.setProperty("FLT_VCOL",int(pack_face_index(curswatch,curint))) + + if editmode: + Blender.Window.EditMode(1) + + Blender.Window.RedrawAll() + + #grab color and intensity from active face + elif evt == Draw.VKEY: + if Blender.Window.EditMode(): + Blender.Window.EditMode(0) + editmode = 1 + state = update_state() + + actmesh = state["activeMesh"] + activeFace = state["activeFace"] + + + if activeFace: + if not "FLT_COL" in actmesh.faces.properties: + actmesh.faces.addPropertyLayer("FLT_COL",Blender.Mesh.PropertyTypes["INT"]) + for face in actmesh.faces: + face.setProperty("FLT_COL",127) #default + try: + actmesh.activeColorLayer = "FLT_Fcol" + except: + actmesh.addColorLayer("FLT_Fcol") + actmesh.activeColorLayer = "FLT_Fcol" + tcol = activeFace.getProperty("FLT_COL") + (index,intensity) = unpack_face_index(tcol) + curswatch = index + curint = intensity + + if editmode: + Blender.Window.EditMode(1) + + Blender.Window.RedrawAll() + + elif evt == Draw.ESCKEY: + Draw.Exit() + + if editmode: + Blender.Window.EditMode(1) + +def update_all(): + global colors + state = update_state() + #update the baked FLT colors for all meshes. + for object in state["activeScene"].objects: + if object.type == "Mesh": + mesh = object.getData(mesh=True) + if 'FLT_COL' in mesh.faces.properties: + mesh.activeColorLayer = "FLT_Fcol" + for face in mesh.faces: + (index,intensity) = unpack_face_index(face.getProperty('FLT_COL')) + color = struct.unpack('>BBBB',struct.pack('>I',colors[index])) + #update the vertex colors for this face + for col in face.col: + col.r = int(color[0] * intensity) + col.g = int(color[1] * intensity) + col.b = int(color[2] * intensity) + col.a = 255 + + +def but_event(evt): + global palette_size + global palette_x + global palette_y + global colors + global curint + global curswatch + global picker + state = update_state() + + if evt == 1: + if picker.val: + rval = (int(picker.val[0]*255),int(picker.val[1]*255),int(picker.val[2]*255),255) + rval = struct.pack('>BBBB',rval[0],rval[1],rval[2],rval[3]) + rval = struct.unpack('>i',rval) + colors[curswatch] = rval[0] + #go cd through all meshes and update their FLT colors + update_all() + + Draw.Redraw(1) +def init_pal(): + global palette_size + global palette_x + global palette_y + global colors + global curint + global curswatch + + state = update_state() + + if not state["activeScene"].properties.has_key('FLT'): + state["activeScene"].properties['FLT'] = dict() + + try: + colors = state["activeScene"].properties['FLT']['Color Palette'] + except: + state["activeScene"].properties['FLT']['Color Palette'] = defaultp.pal + colors = state["activeScene"].properties['FLT']['Color Palette'] + +def draw_palette(): + global palette_size + global palette_x + global palette_y + global colors + global curint + global curswatch + global picker + + state = update_state() + init_pal() + + ssize = palette_size + xpos = palette_x + cid = 0 + + highlight = [(palette_x,palette_y),(palette_x+palette_size,palette_y),(palette_x+palette_size,palette_y+palette_size),(palette_x,palette_y+palette_size)] + for x in xrange(32): + ypos = palette_y + for y in xrange(32): + color = struct.unpack('>BBBB',struct.pack('>I',colors[cid])) + glColor3f(color[0]/255.0,color[1]/255.0,color[2]/255.0) + glBegin(GL_POLYGON) + glVertex2i(xpos,ypos) + glVertex2i(xpos+ssize,ypos) + glVertex2i(xpos+ssize,ypos+ssize) + glVertex2i(xpos,ypos+ssize) + glEnd() + + if curswatch == cid: + highlight[0] = (xpos,ypos) + highlight[1] = (xpos+ssize,ypos) + highlight[2] = (xpos+ssize,ypos+ssize) + highlight[3] = (xpos,ypos+ssize) + + glColor3f(0.0,0.0,0.0) + glBegin(GL_LINE_LOOP) + glVertex2i(xpos,ypos) + glVertex2i(xpos+ssize,ypos) + glVertex2i(xpos+ssize,ypos+ssize) + glVertex2i(xpos,ypos+ssize) + glVertex2i(xpos,ypos) + glEnd() + + + cid = cid + 1 + ypos = ypos + ssize + + xpos = xpos + ssize + + #draw intensity gradient + color = struct.unpack('>BBBB',struct.pack('>I',colors[curswatch])) + color = [color[0]/255.0,color[1]/255.0,color[2]/255.0] + colsteps = [color[0]/255.0,color[1]/255.0,color[2]/255.0] + stripwidth = (palette_size * 32.0) / 256 + strippad = palette_size / 2.0 + + xpos = palette_x + grady = (palette_y + (palette_size * 32.0)) + strippad + for x in xrange(256): + color[0] = color[0] - colsteps[0] + color[1] = color[1] - colsteps[1] + color[2] = color[2] - colsteps[2] + + glColor3f(color[0], color[1] ,color[2]) + glBegin(GL_POLYGON) + glVertex2f(xpos,grady) + glVertex2f(xpos+stripwidth,grady) + glVertex2f(xpos+stripwidth,grady+palette_size) + glVertex2f(xpos,grady+palette_size) + glEnd() + xpos = xpos + stripwidth + + #draw intensity slider bar + #xposition == 512 - ((curint) * 512) + xpos = ((palette_size*32) * (1.0 - curint)) + palette_x + glColor3f(1.0,1.0,1.0) + glBegin(GL_LINE_LOOP) + glVertex2i(xpos-6,grady-1) + glVertex2i(xpos+6,grady-1) + glVertex2i(xpos+6,grady+palette_size+1) + glVertex2i(xpos-6,grady+palette_size+1) + #glVertex2i(xpos-6,grady+7) + glEnd() + + #draw color picker + color = struct.unpack('>BBBB',struct.pack('>I',colors[curswatch])) + pickcol = (color[0]/255.0,color[1]/255.0,color[2]/255.0) + picker = Blender.Draw.ColorPicker(1,highlight[0][0]+1,highlight[0][1]+1,ssize-2,ssize-2,pickcol,ptt) + + #draw highlight swatch + glColor3f(1.0,1.0,1.0) + glBegin(GL_LINE_LOOP) + glVertex2i(highlight[0][0],highlight[0][1]) + glVertex2i(highlight[1][0],highlight[1][1]) + glVertex2i(highlight[2][0],highlight[2][1]) + glVertex2i(highlight[3][0],highlight[3][1]) + glVertex2i(highlight[0][0],highlight[0][1]) + glEnd() + +def gui(): + glClearColor(0.5,0.5,0.5,1.0) + glClear(GL_COLOR_BUFFER_BIT) + draw_palette() + + +init_pal() +Draw.Register(gui,event,but_event) + diff --git a/release/scripts/flt_properties.py b/release/scripts/flt_properties.py new file mode 100644 index 00000000000..2c9e9d7667b --- /dev/null +++ b/release/scripts/flt_properties.py @@ -0,0 +1,619 @@ +#!BPY +# flt_properties.py. For setting default OpenFLight ID property types +# Copyright (C) 2007 Blender Foundation +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +__bpydoc__ ="""\ +Utility functions and data defintions used by OpenFlight I/O and tool scripts. OpenFlight is a +registered trademark of MultiGen-Paradigm, Inc. +""" + + +import struct + +bitsLSB = [2147483648] +for i in xrange(31): + bitsLSB.append(bitsLSB[-1]/2) +bitsRSB = bitsLSB[:] +bitsRSB.reverse() + +def pack_color(col): + return struct.pack('>B',col[3]) + struct.pack('>B',col[2]) + struct.pack('>B',col[1]) + struct.pack('>B',col[0]) + +def unpack_color(col): + string = struct.pack('>I', col) + r = struct.unpack('>B',string[3:4]) + g = struct.unpack('>B',string[2:3]) + b = struct.unpack('>B',string[1:2]) + a = struct.unpack('>B',string[0:1]) + return [r,g,b,a] + +def reverse_bits(len,num): + bitbucket = list() + rval = 0 + + for i in xrange(len): + if num & bitsRSB[i]: + bitbucket.append(1) + else: + bitbucket.append(0) + + bitbucket.reverse() + + for i, bit in enumerate(bitbucket): + if bit: + rval |= bitsLSB[i] + + return rval + + +opcode_name = { 0: 'db', + 1: 'head', + 2: 'grp', + 4: 'obj', + 5: 'face', + 10: 'push', + 11: 'pop', + 14: 'dof', + 19: 'push sub', + 20: 'pop sub', + 21: 'push ext', + 22: 'pop ext', + 23: 'cont', + 31: 'comment', + 32: 'color pal', + 33: 'long id', + 49: 'matrix', + 50: 'vector', + 52: 'multi-tex', + 53: 'uv lst', + 55: 'bsp', + 60: 'rep', + 61: 'inst ref', + 62: 'inst def', + 63: 'ext ref', + 64: 'tex pal', + 67: 'vert pal', + 68: 'vert w col', + 69: 'vert w col & norm', + 70: 'vert w col, norm & uv', + 71: 'vert w col & uv', + 72: 'vert lst', + 73: 'lod', + 74: 'bndin box', + 76: 'rot edge', + 78: 'trans', + 79: 'scl', + 80: 'rot pnt', + 81: 'rot and/or scale pnt', + 82: 'put', + 83: 'eyepoint & trackplane pal', + 84: 'mesh', + 85: 'local vert pool', + 86: 'mesh prim', + 87: 'road seg', + 88: 'road zone', + 89: 'morph vert lst', + 90: 'link pal', + 91: 'snd', + 92: 'rd path', + 93: 'snd pal', + 94: 'gen matrix', + 95: 'txt', + 96: 'sw', + 97: 'line styl pal', + 98: 'clip reg', + 100: 'ext', + 101: 'light src', + 102: 'light src pal', + 103: 'reserved', + 104: 'reserved', + 105: 'bndin sph', + 106: 'bndin cyl', + 107: 'bndin hull', + 108: 'bndin vol cntr', + 109: 'bndin vol orient', + 110: 'rsrvd', + 111: 'light pnt', + 112: 'tex map pal', + 113: 'mat pal', + 114: 'name tab', + 115: 'cat', + 116: 'cat dat', + 117: 'rsrvd', + 118: 'rsrvd', + 119: 'bounding hist', + 120: 'rsrvd', + 121: 'rsrvd', + 122: 'push attrib', + 123: 'pop attrib', + 124: 'rsrvd', + 125: 'rsrvd', + 126: 'curv', + 127: 'road const', + 128: 'light pnt appear pal', + 129: 'light pnt anim pal', + 130: 'indexed lp', + 131: 'lp sys', + 132: 'indx str', + 133: 'shdr pal'} + + +typecodes = ['c','C','s','S','i','I','f','d','t'] + +FLT_GRP = 2 +FLT_OBJ = 4 +FLT_LOD = 73 +FLT_XRF = 63 +FLT_DOF = 14 +FLT_ILP = 111 +FLT_DB = 1 +FLT_FCE = 5 + +#not actual opcodes +FLT_NUL = 0 +FLT_EXP = -1 + +#valid childtypes for each FLT node type +FLT_CHILDTYPES = { + FLT_GRP : [111,2,73,4,14,63], + FLT_OBJ : [111], + FLT_LOD : [111,2,73,4,14,63], + FLT_XRF : [], + FLT_DOF : [111,2,73,4,14,63], + FLT_ILP : [] +} + +#List of nodes that can have faces as children +FLT_FACETYPES = [ + FLT_GRP, + FLT_OBJ, + FLT_LOD, + FLT_DOF +] + +def write_prop(fw,type,value,length): + if type == 'c': + fw.write_char(value) + elif type == 'C': + fw.write_uchar(value) + elif type == 's': + fw.write_short(value) + elif type == 'S': + fw.write_ushort(value) + elif type == 'i': + fw.write_int(value) + elif type == 'I': + fw.write_uint(value) + elif type == 'd': + fw.write_double(value) + elif type == 'f': + fw.write_float(value) + elif type == 't': + fw.write_string(value,length) + +def read_prop(fw,type,length): + rval = None + if type == 'c': + rval = fw.read_char() + elif type == 'C': + rval = fw.read_uchar() + elif type == 's': + rval = fw.read_short() + elif type == 'S': + rval = fw.read_ushort() + elif type == 'i': + rval = fw.read_int() + elif type == 'I': + rval = fw.read_uint() + elif type == 'd': + rval = fw.read_double() + elif type == 'f': + rval = fw.read_float() + elif type == 't': + rval = fw.read_string(length) + return rval + +FLTGroup = { + '3t8!id' : 'G', + '4s!priority' : 0, + '5s!reserved1' : 0, + '6i!flags' : 0, + '7s!special1' : 0, + '8s!special2' : 0, + '9s!significance' : 0, + '10c!layer code' : 0, + '11c!reserved2' : 0, + '12i!reserved3' : 0, + '13i!loop count' : 0, + '14f!loop duration' : 0, + '15f!last frame duration' : 0 +} +FLTGroupDisplay = [5,11,12] + +FLTObject = { + '3t8!id' : 'O', + '4I!flags' : 0, + '5s!priority' : 0, + '6S!transp' : 0, + '7s!SFX1' : 0, + '8s!SFX2' : 0, + '9s!significance' : 0, + '10s!reserved' : 0 +} +FLTObjectDisplay = [10] + +FLTLOD = { + '3t8!id' : 'L', + '4i!reserved' : 0, + '5d!switch in' : 0, + '6d!switch out' : 0, + '7s!sfx ID1' : 0, + '8s!sfx ID2' : 0, + '9I!flags' : 0, + '10d!X co' : 0, + '11d!Y co' : 0, + '12d!Z co' : 0, + '13d!Transition' : 0, + '14d!Sig Size' : 0 +} +FLTLODDisplay = [4] + +FLTInlineLP = { + '3t8!id' : 'Lp', + '4s!smc' : 0, + '5s!fid' : 0, + '6C!back color: a' : 255, + '7C!back color: b' : 255, + '8C!back color: g' : 255, + '9C!back color: r' : 255, + '10i!display mode' : 255, + '11f!intensity' : 1.0, + '12f!back intensity' : 0.0, + '13f!minimum defocus' : 0.0, + '14f!maximum defocus' : 1.0, + '15i!fading mode' : 0, + '16i!fog punch mode' : 0, + '17i!directional mode' : 1, + '18i!range mode' : 0, + '19f!min pixel size' : 1.0, + '20f!max pixel size' : 1024, + '21f!actual size' : 0.25, + '22f!trans falloff pixel size' : 0.25, + '23f!trans falloff exponent' : 1.0, + '24f!trans falloff scalar' : 1.0, + '25f!trans falloff clamp' : 1.0, + '26f!fog scalar' : 0.25, + '27f!fog intensity' : 1.0, + '28f!size threshold' : 0.1, + '29i!directionality' : 0, + '30f!horizontal lobe angle' : 180.0, + '31f!vertical lobe angle' : 180.0, + '32f!lobe roll angle' : 0.0, + '33f!dir falloff exponent' : 1.0, + '34f!dir ambient intensity' : 0.1, + '35f!anim period' : 0, + '36f!anim phase' : 0, + '37f!anim enabled' : 0, + '38f!significance' : 0.0, + '39i!draw order' : 0, + '40I!flags' : 813875616, + '41f!roti' : 0, + '42f!rotj' : 0, + '43f!rotk' : 0 +} + +FLTInlineLPDisplay = [35,36,37,41,42,43] + +FLTXRef = { + '3t200!filename' : '', #we dont actually use this value on export + '4i!reserved' : 0, + '5I!flag' : -478150656, + '6s!bbox' : 0, + '7s!reserved' : 0 +} + +FLTXRefDisplay = [4,7,3] + +FLTDOF = { + '3t8!id' : 'D', + '4i!reserved' : 0, + '5d!ORIGX' : 0.0, + '6d!ORIGY' : 0.0, + '7d!ORIGZ' : 0.0, + '8d!XAXIS-X' : 10.0, + '9d!XAXIS-Y' : 0.0, + '10d!XAXIS-Z' : 0.0, + '11d!XYPLANE-X' : 0.0, + '12d!XYPLANE-Y' : 10.0, + '13d!XZPLANE-Z' : 0.0, + '14d!ZMIN' : 0.0, + '15d!ZMAX' : 0.0, + '16d!ZCUR' : 0.0, + '17d!ZSTEP' : 0.0, + '18d!YMIN' : 0.0, + '19d!YMAX' : 0.0, + '20d!YCUR' : 0.0, + '21d!YSTEP' : 0.0, + '22d!XMIN' : 0.0, + '23d!XMAX' : 0.0, + '24d!XCUR' : 0.0, + '25d!XSTEP' : 0.0, + '26d!PITCH-MIN' : 0.0, + '27d!PITCH-MAX' : 0.0, + '28d!PITCH-CUR' : 0.0, + '29d!PITCH-STEP' : 0.0, + '30d!ROLL-MIN' : 0.0, + '31d!ROLL-MAX' : 0.0, + '32d!ROLL-CUR' : 0.0, + '33d!ROLL-STEP' : 0.0, + '34d!YAW-MIN' : 0.0, + '35d!YAW-MAX' : 0.0, + '36d!YAW-CUR' : 0.0, + '37d!YAW-STEP' : 0.0, + '38d!ZSIZE-MIN' : 0.0, + '39d!ZSIZE-MAX' : 0.0, + '40d!ZSIZE-CUR' : 1.0, + '41d!ZSIZE-STEP' : 0.0, + '42d!YSIZE-MIN' : 0.0, + '43d!YSIZE-MAX' : 0.0, + '44d!YSIZE-CUR' : 1.0, + '45d!YSIZE-STEP' : 0.0, + '46d!XSIZE-MIN' : 0.0, + '47d!XSIZE-MAX' : 0.0, + '48d!XSIZE-CUR' : 1.0, + '49d!XSIZE-STEP' : 0.0, + '50I!FLAG' : 1897582, + '51i!reserved2' : 0 +} + +FLTDOFDisplay = [4] + +FLTImage = { + '3i!RealU Direction' : 0, + '4i!RealV Direction' : 0, + '5i!UpX' : 0, + '6i!UpY' : 0, + '7i!File Format' : 0, + '8i!Min Filter' : 6, + '9i!Mag Filter' : 1, + '10i!Wrap' : 0, + '11i!WrapU' : 0, + '12i!WrapV' : 0, + '13i!Modified' : 0, + '14i!PivotX' : 0, + '15i!PivotY' : 0, + '16i!Enviorment' : 0, + '17i!WhiteAlpha' : 0, + '18i!reserved1' : 0, + '19i!reserved2' : 0, + '20i!reserved3' : 0, + '21i!reserved4' : 0, + '22i!reserved5' : 0, + '23i!reserved6' : 0, + '24i!reserved7' : 0, + '25i!reserved8' : 0, + '26i!reserved9' : 0, + '27d!RealU Direction' : 0, + '28d!RealV Direction' : 0, + '29i!Origin' : 0, + '30i!Kernel no.' : 0, + '31i!Internal Format' : 0, + '32i!External Format' : 0, + '33i!MipMap Filter?' : 0, + '34f!MMF1' : 0.0, + '35f!MMF2' : 0.0, + '36f!MMF3' : 0.0, + '37f!MMF4' : 0.0, + '38f!MMF5' : 0.0, + '39f!MMF6' : 0.0, + '40f!MMF7' : 0.0, + '41f!MMF8' : 0.0, + '42i!Tex CPs?' : 0, + '43f!LOD0 CP' : 0.0, + '44f!Scale0 CP' : 0.0, + '45f!LOD1 CP' : 0.0, + '46f!Scale1 CP' : 0.0, + '47f!LOD2 CP' : 0.0, + '48f!Scale2 CP' : 0.0, + '49f!LOD3 CP' : 0.0, + '50f!Scale3 CP' : 0.0, + '51f!LOD4 CP' : 0.0, + '52f!Scale4 CP' : 0.0, + '53f!LOD5 CP' : 0.0, + '54f!Scale5 CP' : 0.0, + '55f!LOD6 CP' : 0.0, + '56f!Scale6 CP' : 0.0, + '57f!LOD7 CP' : 0.0, + '58f!Scale7 CP' : 0.0, + '59f!Control Clamp' : 0.0, + '60i!Mag Alpha Filter' : 0, + '61i!Mag Color Filter' : 0, + '62f!reserved10' : 0, + '63f!reserved11' : 0, + '64f!reserved12' : 0, + '65f!reserved13' : 0, + '66f!reserved14' : 0, + '67f!reserved15' : 0, + '68f!reserved16' : 0, + '69f!reserved17' : 0, + '70f!reserved18' : 0, + '71d!Lambert Central' : 0.0, + '72d!Lambert Upper' : 0.0, + '73d!Lambert Lower' : 0.0, + '74d!reserved19' : 0, + '75f!reserved20' : 0, + '76f!reserved21' : 0, + '77f!reserved22' : 0, + '78f!reserved23' : 0, + '79f!reserved24' : 0, + '80i!Tex Detail?' : 0, + '81i!Tex J' : 0, + '82i!Tex K' : 0, + '83i!Tex M' : 0, + '84i!Tex N' : 0, + '85i!Tex Scramble' : 0, + '86i!Tex Tile?' : 0, + '87f!Tex Tile LLU' : 0.0, + '88f!Tex Tile LLV' : 0.0, + '89f!Tex Tile URU' : 0.0, + '90f!Tex Tile URV' : 0.0, + '91i!Projection' : 0, + '92i!Earth Model' : 0, + '93i!reserved25' : 0, + '94i!UTM Zone' : 0, + '95i!Image Origin' : 0, + '96i!GPU' : 0, + '97i!reserved26' : 0, + '98i!reserved27' : 0, + '99i!GPU Hemi' : 0, + '100i!reserved41' : 0, + '101i!reserved42' : 0, + '102i!reserved43' : 0, + '103i!Cubemap' : 0, + '104t588!reserved44' : '', + '105t512!Comments' : '', + '106i!reserved28' : 0, + '107i!reserved29' : 0, + '108i!reserved30' : 0, + '109i!reserved31' : 0, + '110i!reserved32' : 0, + '111i!reserved33' : 0, + '112i!reserved34' : 0, + '113i!reserved35' : 0, + '114i!reserved36' : 0, + '115i!reserved37' : 0, + '116i!reserved38' : 0, + '117i!reserved39' : 0, + '118i!reserved40' : 0, + '119i!reserved45' : 0, + '120i!Format Version' : 0, + '121i!GPU num' : 0, +} + +FLTImageDisplay = [18,19,29,21,22,23,24,25,26,62,63,64,65,66,67,68,69,70,74,75,76,77,78,79,93,97,98,102,114] + +FLTHeader = { + '3t8!id' : 'db', + '4i!version' : 1620, + '5i!editversion' : 0, + '6t32!date' : 0, + '7s!NGID' : 0, + '8s!NLID' : 0, + '9s!NOID' : 0, + '10s!NFID' : 0, + '11s!UMULT' : 1, + '12c!units' : 0, + '13c!set white' : 0, + '14I!flags' : 0x80000000, + '15i!reserved1' : 0, + '16i!reserved2' : 0, + '17i!reserved3' : 0, + '18i!reserved4' : 0, + '19i!reserved5' : 0, + '20i!reserved6' : 0, + '21i!projection type' : 0, + '22i!reserved7' : 0, + '23i!reserved8' : 0, + '24i!reserved9' : 0, + '25i!reserved10' : 0, + '26i!reserved11' : 0, + '27i!reserved12' : 0, + '28i!reserved13' : 0, + '29s!NDID' : 0, + '30s!vstore' : 1, + '31i!origin' : 0, + '32d!sw x' : 0, + '33d!sw y' : 0, + '34d!dx' : 0, + '35d!dy' : 0, + '36s!NSID' : 0, + '37s!NPID' : 0, + '38i!reserved14' : 0, + '39i!reserved15' : 0, + '40s!NCID' : 0, + '41s!NTID' : 0, + '42s!NBID' : 0, + '43s!NWID' : 0, + '44i!reserved14' : 0, + '45d!sw lat' : 0, + '46d!sw lon' : 0, + '47d!ne lat' : 0, + '48d!ne lon' : 0, + '49d!origin lat' : 0, + '50d!origin lon' : 0, + '51d!lambert lat1' : 0, + '52d!lambert lat2' : 0, + '53s!NLSID' : 0, + '54s!NLPID' : 0, + '55s!NRID' : 0, + '56s!NCATID' : 0, + '57s!reserved15' : 0, + '58s!reserved16' : 0, + '59s!reserved17' : 0, + '60s!reserved18' : 0, + '61i!ellipsoid model' : 1, + '62s!NAID' : 0, + '63s!NCVID' : 0, + '64s!utm zone' : 0, + '65t6!reserved19' : 0, + '66d!dz' : 0, + '67d!radius' : 0, + '68S!NMID' : 0, + '69S!NLPSID' : 0, + '70i!reserved20' : 0, + '71d!major axis' : 0, + '72d!minor axis' : 0, +} + +FLT_Records = { + 2 : FLTGroup, + 4 : FLTObject, + 73 : FLTLOD, + 63 : FLTXRef, + 14 : FLTDOF, + 1 : FLTHeader, + 111 : FLTInlineLP, + 'Image' : FLTImage +} + +def process_recordDefs(): + records = dict() + for record in FLT_Records: + props = dict() + for prop in FLT_Records[record]: + position = '' + slice = 0 + (format,name) = prop.split('!') + for i in format: + if i not in typecodes: + position = position + i + slice = slice + 1 + else: + break + type = format[slice:] + length = type[1:] + if len(length) == 0: + length = 1 + else: + type = type[0] + length = int(length) + + props[int(position)] = (type,length,prop) + records[record] = props + return records + + diff --git a/release/scripts/flt_toolbar.py b/release/scripts/flt_toolbar.py new file mode 100644 index 00000000000..213fac1bb3c --- /dev/null +++ b/release/scripts/flt_toolbar.py @@ -0,0 +1,654 @@ +#!BPY + +""" +Name: 'FLT Toolbar' +Blender: 240 +Group: 'Misc' +Tooltip: 'Tools for working with FLT databases' +""" + +__author__ = "Geoffrey Bantle" +__version__ = "1.0 11/21/07" +__email__ = ('scripts', 'Author, ') +__url__ = ('blender', 'elysiun') + +__bpydoc__ ="""\ +This script provides tools for working with OpenFlight databases in Blender. OpenFlight is a +registered trademark of MultiGen-Paradigm, Inc. + +Feature overview and more availible at: +http://wiki.blender.org/index.php/Scripts/Manual/FLTools +""" + +# -------------------------------------------------------------------------- +# flt_palettemanager.py version 0.1 2005/04/08 +# -------------------------------------------------------------------------- +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Copyright (C) 2007: Blender Foundation +# +# 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 ***** +# -------------------------------------------------------------------------- + +import Blender.Draw as Draw +from Blender.BGL import * +import Blender +import flt_properties +reload(flt_properties) +from flt_properties import * + +xrefprefix = "" +xrefstack = list() +vofsstack = list() +vquatstack = list() +prop_w = 256 +prop_h = 256 + + +#event codes +evcode = { + "XREF_MAKE" : 100, + "XREF_EDIT" : 101, + "XREF_FILE" : 102, + "XREF_PICK" : 103, + "XREF_SELECT" : 104, + "XREF_POP" : 105, + "XREF_PREFIX" : 106, + "FACE_NAME" : 200, + "FACE_MAKESUB" : 201, + "FACE_KILLSUB" : 202, + "FACE_SELSUB" : 203, + "SCENE_UPDATE" : 303, + "IDPROP_COPY" : 501, + "IDPROP_KILL" : 502, + "CLIGHT_MAKE" : 700, + "DFROMACT" : 701 +} + +XREF_PREFIX = None +XREF_MAKE = None +XREF_EDIT = None +XREF_SELECT = None +XREF_POP = None +FACE_MAKESUB = None +FACE_SELSUB = None +FACE_KILLSUB = None +IDPROP_KILL = None +IDPROP_COPY = None +SCENE_UPDATE = None +CLIGHT_MAKE = None +DFROMACT = None + +def update_state(): + state = dict() + state["activeScene"] = Blender.Scene.getCurrent() + state["activeObject"] = state["activeScene"].getActiveObject() + if state["activeObject"] and not state["activeObject"].sel: + state["activeObject"] = None + state["activeMesh"] = None + if state["activeObject"] and state["activeObject"].type == 'Mesh': + state["activeMesh"] = state["activeObject"].getData(mesh=True) + + state["activeFace"] = None + if state["activeMesh"]: + if state["activeMesh"].faceUV and state["activeMesh"].activeFace != None: + state["activeFace"] = state["activeMesh"].faces[state["activeMesh"].activeFace] + + #update editmode + state["editmode"] = Blender.Window.EditMode() + + return state +def pack_face_index(index, intensity): + return ((127*intensity)+(128*index)) +def unpack_face_index(face_index): + index = face_index / 128 + intensity = float(face_index - 128.0 * index) / 127.0 + return(index,intensity) + +def idprops_append(object, typecode, props): + object.properties["FLT"] = dict() + object.properties["FLT"]['type'] = typecode + for prop in props: + object.properties["FLT"][prop] = props[prop] + object.properties["FLT"]['3t8!id'] = object.name + +def idprops_kill(object): + state = update_state() + if object and object.properties.has_key('FLT'): + object.properties.pop('FLT') + +def idprops_copy(source): + state = update_state() + if source.properties.has_key('FLT'): + for object in state["activeScene"].objects: + if object.sel and object != source and (state["activeScene"].Layers & object.Layers): + idprops_kill(object) + object.properties['FLT'] = dict() + for key in source.properties['FLT']: + object.properties['FLT'][key] = source.properties['FLT'][key] + +def update_all(): + state = update_state() + #update the baked FLT colors for all meshes. + for object in state["activeScene"].objects: + if object.type == "Mesh": + mesh = object.getData(mesh=True) + if 'FLT_COL' in mesh.faces.properties: + mesh.activeColorLayer = "FLT_Fcol" + for face in mesh.faces: + (index,intensity) = unpack_face_index(face.getProperty('FLT_COL')) + color = struct.unpack('>BBBB',struct.pack('>I',state["colors"][index])) + #update the vertex colors for this face + for col in face.col: + col.r = int(color[0] * intensity) + col.g = int(color[1] * intensity) + col.b = int(color[2] * intensity) + col.a = 255 + + +#Change this to find the deep parent +def xref_create(): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + state = update_state() + + def findchildren(object): + children = list() + for candidate in state["activeScene"].objects: + if candidate.parent == object: + children.append(candidate) + retlist = list(children) + for child in children: + retlist = retlist + findchildren(child) + return retlist + + actObject = state["activeObject"] + if actObject and xrefprefix: + scenenames = list() + for scene in Blender.Scene.Get(): + scenenames.append(scene.name) + + if xrefprefix in scenenames: + #build a unique name for the xref... + suffix = 1 + found = False + while not found: + candidate = xrefprefix + str(suffix) + if not candidate in scenenames: + xrefname = candidate + found = True + suffix+=1 + else: + xrefname = xrefprefix + #create our XRef node + xnode = state["activeScene"].objects.new('Empty') + xnode.name = 'X:' + xrefname + xnode.properties['FLT'] = dict() + for prop in FLTXRef: + xnode.properties['FLT'][prop] = FLTXRef[prop] + xnode.properties['FLT']['3t200!filename'] = xrefname + '.flt' + xnode.properties['FLT']['type'] = 63 + xnode.enableDupGroup = True + xnode.DupGroup = Blender.Group.New(xrefname) #this is dangerous... be careful! + + #copy rot and loc of actObject + xnode.setLocation(actObject.getLocation()) + xnode.setEuler(actObject.getEuler()) + + #build the new scene + xrefscene = Blender.Scene.New(xrefname) + xrefscene.properties['FLT'] = dict() + xrefscene.properties['FLT']['Filename'] = xrefname + xrefscene.properties['FLT']['Main'] = 0 + + #find the children of actObject so that we can add them to the group + linkobjects = findchildren(actObject) + linkobjects.append(actObject) + for object in linkobjects: + xrefscene.objects.link(object) + state["activeScene"].objects.unlink(object) + xnode.DupGroup.objects.link(object) + #clear rotation of actObject and location + actObject.setLocation(0.0,0.0,0.0) + actObject.setEuler(0.0,0.0,0.0) + + xrefscene.update(1) + state["activeScene"].update(1) + +def xref_select(): + state = update_state() + candidates = list() + scenelist = [scene.name for scene in Blender.Scene.Get()] + for object in state["activeScene"].objects: + if object.type == 'Empty' and object.enableDupGroup == True and object.DupGroup: + candidates.append(object) + + for object in candidates: + if object.DupGroup.name in scenelist: + object.sel = 1 + +def xref_edit(): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + state = update_state() + + actObject = state["activeObject"] + + if actObject and actObject.type == 'Empty' and actObject.DupGroup: +# if actObject.properties.has_key('FLT') and actObject.properties['FLT']['type'] == 63: + for FLTscene in Blender.Scene.Get(): + if FLTscene.properties.has_key('FLT') and FLTscene.name == actObject.DupGroup.name: + actObject.sel = 0 + xrefstack.append(state["activeScene"]) + vofsstack.append(Blender.Window.GetViewOffset()) + vquatstack.append(Blender.Window.GetViewQuat()) + FLTscene.makeCurrent() + Blender.Window.SetViewOffset(0.0,0.0,0.0) + +def xref_finish(): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + state = update_state() + if xrefstack: + scene = xrefstack.pop() + Blender.Window.SetViewQuat(vquatstack.pop()) + Blender.Window.SetViewOffset(vofsstack.pop()) + scene.makeCurrent() + + +def sortSub(a,b): + aindex = a.getProperty("FLT_ORIGINDEX") + bindex = b.getProperty("FLT_ORIGINDEX") + + if aindex > bindex: + return 1 + elif aindex < bindex: + return -1 + return 0 + +def subface_make(): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + editmode = 0 + if Blender.Window.EditMode(): + Blender.Window.EditMode(0) + editmode = 1 + + state = update_state() + + actmesh = state["activeMesh"] + activeFace = state["activeFace"] + if actmesh: + if not "FLT_ORIGINDEX" in actmesh.faces.properties: + actmesh.faces.addPropertyLayer("FLT_ORIGINDEX",Blender.Mesh.PropertyTypes["INT"]) + for i, face in enumerate(actmesh.faces): + face.setProperty("FLT_ORIGINDEX",i) + if not "FLT_SFLEVEL" in actmesh.faces.properties: + actmesh.faces.addPropertyLayer("FLT_SFLEVEL",Blender.Mesh.PropertyTypes["INT"]) + + #attach the subfaces to the active face. Note, this doesnt really work 100 percent properly yet, just enough for one level! + if activeFace: + #steps: + #remove actface and selected faces from the facelist + #quicksort facelist + #append actface and subfaces to end of facelist. + #generate new indices + facelist = list() + sublist = list() + for face in actmesh.faces: + facelist.append(face) + for face in facelist: + if face == activeFace: + face.setProperty("FLT_SFLEVEL",0) + sublist.insert(0,face) + elif face.sel: + face.setProperty("FLT_SFLEVEL",1) + sublist.append(face) + for face in sublist: + facelist.remove(face) + facelist.sort(sortSub) + for face in sublist: + facelist.append(face) + for i, face in enumerate(facelist): + face.setProperty("FLT_ORIGINDEX",i) + else: + pass + + if editmode: + Blender.Window.EditMode(1) + +def subface_kill(): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + editmode = 0 + if Blender.Window.EditMode(): + Blender.Window.EditMode(0) + editmode = 1 + state = update_state() + + actmesh = state["activeMesh"] + if actmesh: + if "FLT_ORIGINDEX" in actmesh.faces.properties and "FLT_SFLEVEL" in actmesh.faces.properties: + for i,face in enumerate(actmesh.faces): + face.setProperty("FLT_ORIGINDEX",i) + face.setProperty("FLT_SFLEVEL",0) + if editmode: + Blender.Window.EditMode(1) + +def subface_select(): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + editmode = 0 + if Blender.Window.EditMode(): + Blender.Window.EditMode(0) + editmode = 1 + state = update_state() + + actmesh = state["activeMesh"] + activeFace = state["activeFace"] + if actmesh and activeFace: + if "FLT_ORIGINDEX" in actmesh.faces.properties and "FLT_SFLEVEL" in actmesh.faces.properties: + facelist = list() + actIndex = None + sublevel = None + for face in actmesh.faces: + facelist.append(face) + facelist.sort(sortSub) + for i, face in enumerate(facelist): + if face == activeFace: + actIndex = i + sublevel = face.getProperty("FLT_SFLEVEL")+1 + break + leftover = facelist[actIndex+1:] + for face in leftover: + if face.getProperty("FLT_SFLEVEL") == sublevel: + face.sel = 1 + else: + break + if editmode: + Blender.Window.EditMode(1) + +def select_by_typecode(typecode): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + + state = update_state() + + for object in state["activeScene"].objects: + if object.properties.has_key('FLT') and object.properties['FLT']['type'] == typecode and state["activeScene"].Layers & object.Layers: + object.select(1) +def clight_make(): + state = update_state() + actmesh = state["activeMesh"] + actobj = state["activeObject"] + + if actobj and actmesh: + actobj.properties['FLT'] = dict() + actobj.properties['FLT']['type'] = 111 + for prop in FLTInlineLP: + actobj.properties['FLT'][prop] = FLTInlineLP[prop] + + actmesh.verts.addPropertyLayer("FLT_VCOL", Blender.Mesh.PropertyTypes["INT"]) + for v in actmesh.verts: + v.setProperty("FLT_VCOL", 83815) + +def dfromact(): + state = update_state() + actobj = state["activeObject"] + actscene = state["activeScene"] + dof = None + + for object in actscene.objects.context: + if object.sel and (object != actobj): + if not dof: + dof = object + else: + return + + if 'FLT' not in dof.properties: + dof.properties['FLT'] = dict() + + #Warning! assumes 1 BU == 10 meters. + #do origin + dof.properties['FLT']['5d!ORIGX'] = actobj.getLocation('worldspace')[0]*10.0 + dof.properties['FLT']['6d!ORIGY'] = actobj.getLocation('worldspace')[1]*10.0 + dof.properties['FLT']['7d!ORIGZ'] = actobj.getLocation('worldspace')[2]*10.0 + #do X axis + x = Blender.Mathutils.Vector(1.0,0.0,0.0) + x = x * actobj.getMatrix('worldspace') + x = x * 10.0 + dof.properties['FLT']['8d!XAXIS-X'] = x[0] + dof.properties['FLT']['9d!XAXIS-Y'] = x[1] + dof.properties['FLT']['10d!XAXIS-Z'] = x[2] + #do X/Y plane + x = Blender.Mathutils.Vector(1.0,1.0,0.0) + x.normalize() + x = x * actobj.getMatrix('worldspace') + x = x * 10.0 + dof.properties['FLT']['11d!XYPLANE-X'] = x[0] + dof.properties['FLT']['12d!XYPLANE-Y'] = x[1] + dof.properties['FLT']['13d!XZPLANE-Z'] = x[2] + + + + + +def event(evt,val): + if evt == Draw.ESCKEY: + Draw.Exit() + +def but_event(evt): + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + global evcode + + state = update_state() + + #do Xref buttons + if evt == evcode["XREF_PREFIX"]: + xrefprefix = XREF_PREFIX.val + if evt == evcode["XREF_EDIT"]: + xref_edit() + if evt == evcode["XREF_SELECT"]: + xref_select() + if evt == evcode["XREF_MAKE"]: + xref_create() + #do scene buttons + if evt == evcode["SCENE_UPDATE"]: + update_all() + #do face buttons + if evt == evcode["FACE_MAKESUB"]: + subface_make() + if evt== evcode["FACE_KILLSUB"]: + subface_kill() + if evt== evcode["FACE_SELSUB"]: + subface_select() + #common buttons + if evt == evcode["IDPROP_KILL"]: + if state["activeObject"]: + idprops_kill(state["activeObject"]) + if evt == evcode["IDPROP_COPY"]: + if state["activeObject"]: + idprops_copy(state["activeObject"]) + if evt == evcode["XREF_POP"]: + xref_finish() + if evt == evcode["CLIGHT_MAKE"]: + clight_make() + if evt == evcode["DFROMACT"]: + dfromact() + Draw.Redraw(1) + Blender.Window.RedrawAll() + + +def box(x,y,w,h,c,mode): + glColor3f(c[0],c[1],c[2]) + if mode == "outline": + glBegin(GL_LINE_LOOP) + else: + glBegin(GL_POLYGON) + glVertex2i(x,y) + glVertex2i(x+w,y) + glVertex2i(x+w,y+h) + glVertex2i(x,y+h) + glEnd() + +def draw_postcommon(x,y,finaly): + global sheetlabel + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + global evcode + + state = update_state() + + width = prop_w + height = prop_h + + #draw the header + glColor3f(0.15,0.15,0.15) + glBegin(GL_POLYGON) + glVertex2i(x-1,y) + glVertex2i(x+width+1,y) + glVertex2i(x+width+1,y-25) + glVertex2i(x-1,y-25) + glEnd() + glColor3f(1,1,1) + glRasterPos2i(x,y-20) + sheetlabel = Blender.Draw.Text("FLT Tools Panel") + #draw the box outline + glColor3f(0,0,0) + glBegin(GL_LINE_LOOP) + glVertex2i(x-1,y) + glVertex2i(x+1+width,y) + glVertex2i(x+1+width,finaly-1) + glVertex2i(x-1,finaly-1) + glEnd() + return finaly + + +def draw_propsheet(x,y): + global XREF_PREFIX + global XREF_MAKE + global XREF_EDIT + global XREF_SELECT + global XREF_POP + global FACE_MAKESUB + global FACE_SELSUB + global FACE_KILLSUB + global IDPROP_KILL + global IDPROP_COPY + global SCENE_UPDATE + global CLIGHT_MAKE + global xrefprefix + global xrefstack + global vofsstack + global vquatstack + global prop_w + global prop_h + global evcode + + state = update_state() + + width = prop_w + height = prop_h + origx = x + origy = y + + #draw Xref tools + y = y-20 + XREF_PREFIX = Blender.Draw.String("XRef Name:",evcode["XREF_PREFIX"],x,y,width,20,xrefprefix,18,"Xref prefix name, Actual name is generated from this") + y = y-20 + XREF_MAKE = Blender.Draw.PushButton("Make XRef",evcode["XREF_MAKE"],x,y,width,20,"Make External Reference") + y = y-20 + XREF_EDIT = Blender.Draw.PushButton("Edit XRef",evcode["XREF_EDIT"],x,y,width,20,"Edit External Reference") + y = y-20 + XREF_SELECT = Blender.Draw.PushButton("Select XRefs",evcode["XREF_SELECT"],x,y,width,20,"Select External References") + y = y - 20 + XREF_POP = Blender.Draw.PushButton("Return to previous scene",evcode["XREF_POP"],x,y,width,20,"Go up one level in xref hierarchy") + + #Draw facetools + y = y-20 + FACE_MAKESUB = Blender.Draw.PushButton("Make Subfaces",evcode["FACE_MAKESUB"],x,y,width,20,"Make subfaces") + y = y-20 + FACE_SELSUB = Blender.Draw.PushButton("Select Subfaces",evcode["FACE_SELSUB"],x,y,width,20,"Select subfaces") + y = y-20 + FACE_KILLSUB = Blender.Draw.PushButton("Kill Subfaces",evcode["FACE_KILLSUB"],x,y,width,20,"Kill subfaces") + + #Draw ID Property tools + y = y - 20 + IDPROP_KILL = Blender.Draw.PushButton("Delete ID props",evcode["IDPROP_KILL"],x,y,width,20,"Delete ID props") + y = y - 20 + IDPROP_COPY = Blender.Draw.PushButton("Copy to selected",evcode["IDPROP_COPY"],x,y,width,20, "Copy from active to all selected") + + y= y - 20 + CLIGHT_MAKE = Blender.Draw.PushButton("Make Light Point", evcode["CLIGHT_MAKE"],x,y,width,20,"Create inline light points from current mesh") + #General tools + y = y-20 + SCENE_UPDATE = Blender.Draw.PushButton("Update All",evcode["SCENE_UPDATE"],x,y,width,20,"Update all vertex colors") + + y=y-20 + DFROMACT = Blender.Draw.PushButton("Dof from Active", evcode["DFROMACT"],x,y,width,20,"Get Dof origin from active object") + draw_postcommon(origx, origy,y) + +def gui(): + #draw the propsheet/toolbox. + psheety = 280 + #psheetx = psheety + 10 + draw_propsheet(0,psheety) +Draw.Register(gui,event,but_event) +
\ No newline at end of file diff --git a/release/scripts/image_auto_layout.py b/release/scripts/image_auto_layout.py index a4a9e82955f..19ee396c3b1 100644 --- a/release/scripts/image_auto_layout.py +++ b/release/scripts/image_auto_layout.py @@ -42,7 +42,6 @@ This is usefull for game models where 1 image is faster then many, and saves the # Function to find all the images we use import Blender as B -import boxpack2d from Blender.Mathutils import Vector, RotationMatrix from Blender.Scene import Render import BPyMathutils @@ -162,9 +161,10 @@ class faceGroup(object): self.ymin= ymin - (PREF_IMAGE_MARGIN/size[1]) self.box_pack=[\ - image.name,\ + 0.0, 0.0,\ size[0]*(self.xmax - self.xmin),\ - size[1]*(self.ymax - self.ymin)] + size[1]*(self.ymax - self.ymin),\ + image.name] ''' # default. @@ -194,8 +194,8 @@ class faceGroup(object): # X Is flipped :/ #offset_x= (1-(self.box_pack[1]/d)) - (((self.xmax-self.xmin) * self.image.size[0])/d) - offset_x= self.box_pack[1]/width - offset_y= self.box_pack[2]/height + offset_x= self.box_pack[0]/width + offset_y= self.box_pack[1]/height for f in self.faces: for uv in f.uv: @@ -204,7 +204,11 @@ class faceGroup(object): uv.y= offset_y+ (((uv_rot.y-self.ymin) * self.size[1])/height) def consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PREF_KEEP_ASPECT, PREF_IMAGE_MARGIN): #, PREF_SIZE_FROM_UV=True): - '''Main packing function''' + ''' + Main packing function + + All meshes from mesh_list must have faceUV else this function will fail. + ''' face_groups= {} for me in mesh_list: @@ -224,11 +228,11 @@ def consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PR face_groups[image.name]= faceGroup(mesh_list, image, size, PREF_IMAGE_MARGIN) if not face_groups: - B.Draw.PupMenu('No Images found in mesh. aborting.') + B.Draw.PupMenu('No Images found in mesh(es). Aborting!') return if len(face_groups)<2: - B.Draw.PupMenu('Only 1 image found|Select a mesh using 2 or more images.') + B.Draw.PupMenu('Only 1 image found|Select a mesh(es) using 2 or more images.') return ''' @@ -286,7 +290,7 @@ def consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PR render_cam_ob= B.Object.New('Camera') render_cam_ob.link(render_cam_data) render_scn.link(render_cam_ob) - render_scn.setCurrentCamera(render_cam_ob) + render_scn.objects.camera = render_cam_ob render_cam_data.type= 'ortho' render_cam_data.scale= 1.0 @@ -299,8 +303,7 @@ def consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PR # List to send to to boxpack function. boxes2Pack= [ fg.box_pack for fg in face_groups.itervalues()] - - packWidth, packHeight, packedLs = boxpack2d.boxPackIter(boxes2Pack) + packWidth, packHeight = B.Geometry.BoxPack2D(boxes2Pack) if PREF_KEEP_ASPECT: packWidth= packHeight= max(packWidth, packHeight) @@ -308,17 +311,17 @@ def consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PR # packedLs is a list of [(anyUniqueID, left, bottom, width, height)...] # Re assign the face groups boxes to the face_group. - for box in packedLs: - face_groups[ box[0] ].box_pack= box # box[0] is the ID (image name) + for box in boxes2Pack: + face_groups[ box[4] ].box_pack= box # box[4] is the ID (image name) # Add geometry to the mesh for fg in face_groups.itervalues(): # Add verts clockwise from the bottom left. - _x= fg.box_pack[1] / packWidth - _y= fg.box_pack[2] / packHeight - _w= fg.box_pack[3] / packWidth - _h= fg.box_pack[4] / packHeight + _x= fg.box_pack[0] / packWidth + _y= fg.box_pack[1] / packHeight + _w= fg.box_pack[2] / packWidth + _h= fg.box_pack[3] / packHeight render_me.verts.extend([\ Vector(_x, _y, 0),\ @@ -374,10 +377,11 @@ def consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PR for fg in face_groups.itervalues(): fg.move2packed(packWidth, packHeight) - + scn.makeCurrent() - B.Scene.Unlink(render_scn) render_me.verts= None # free a tiny amount of memory. + B.Scene.Unlink(render_scn) + target_image.makeCurrent() def main(): @@ -422,10 +426,23 @@ def main(): if PREF_ALL_SEL_OBS: mesh_list= [ob.getData(mesh=1) for ob in scn_objects.context if ob.type=='Mesh'] # Make sure we have no doubles- dict by name, then get the values back. - mesh_list= dict([(me.name, me) for me in mesh_list]).values() + + for me in mesh_list: me.tag = False + + mesh_list_new = [] + for me in mesh_list: + if me.faceUV and me.tag==False: + me.tag = True + mesh_list_new.append(me) + + # replace list with possible doubles + mesh_list = mesh_list_new else: mesh_list= [ob.getData(mesh=1)] + if not mesh_list[0].faceUV: + B.Draw.PupMenu('Error, active mesh has no images, Aborting!') + return consolidate_mesh_images(mesh_list, scn, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PREF_KEEP_ASPECT, PREF_IMAGE_MARGIN) B.Window.RedrawAll() diff --git a/release/scripts/image_billboard.py b/release/scripts/image_billboard.py index 2ca1d9d46e4..54f0f7c5c55 100644 --- a/release/scripts/image_billboard.py +++ b/release/scripts/image_billboard.py @@ -46,47 +46,37 @@ Usage # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -import Blender as B +import Blender +from Blender import Mesh, Material, Draw import BPyMathutils -# reload(BPyMathutils) +import bpy import BPyRender -reload(BPyRender) -import boxpack2d -# reload(boxpack2d) # for developing. from Blender.Scene import Render +# reload(BPyRender) +# reload(BPyMathutils) + import os -Vector= B.Mathutils.Vector +Vector= Blender.Mathutils.Vector def alpha_mat(image): # returns a material useable for - mtl= B.Material.New() - mtl.mode |= (B.Material.Modes.SHADELESS | B.Material.Modes.ZTRANSP | B.Material.Modes.FULLOSA ) - mtl.alpha= 0.0 # so image sets the alpha - - tex= B.Texture.New() - tex.type= B.Texture.Types.IMAGE - image.antialias = True - tex.setImageFlags('InterPol', 'UseAlpha') - tex.setExtend('Clip') - tex.image= image - - mtl.setTexture(0, tex, B.Texture.TexCo.UV, B.Texture.MapTo.COL | B.Texture.MapTo.ALPHA) - + mtl= bpy.data.materials.new() + mtl.mode |= (Material.Modes.SHADELESS | Material.Modes.ZTRANSP | Material.Modes.FULLOSA | Material.Modes.TEXFACE | Material.Modes.TEXFACE_ALPHA ) return mtl # PupBlock Settings GLOBALS= {} -PREF_RES= B.Draw.Create(512) -PREF_TILE_RES= B.Draw.Create(256) -PREF_AA = B.Draw.Create(1) -PREF_ALPHA= B.Draw.Create(1) -PREF_Z_OFFSET = B.Draw.Create(10.0) -PREF_IMG_PACK= B.Draw.Create(1) +PREF_RES= Draw.Create(512) +PREF_TILE_RES= Draw.Create(256) +PREF_AA = Draw.Create(1) +PREF_ALPHA= Draw.Create(1) +PREF_Z_OFFSET = Draw.Create(10.0) +PREF_IMG_PACK= Draw.Create(1) def save_billboard(PREF_IMAGE_PATH): - B.Window.WaitCursor(1) + Blender.Window.WaitCursor(1) # remove png, add it later PREF_IMAGE_PATH= PREF_IMAGE_PATH.replace('.png', '') @@ -94,7 +84,7 @@ def save_billboard(PREF_IMAGE_PATH): me_ob = GLOBALS['me_ob'] me_data = GLOBALS['me_data'] - time= B.sys.time() + time= Blender.sys.time() me_mat= me_ob.matrixWorld @@ -102,9 +92,10 @@ def save_billboard(PREF_IMAGE_PATH): face_data= [] # Store faces, images etc boxes2Pack= [] me_data.faceUV= True - + for i, f in enumerate(me_data.faces): no= f.no + # Offset the plane by the zoffset on the faces normal plane= [v.co * me_mat for v in f] @@ -117,61 +108,64 @@ def save_billboard(PREF_IMAGE_PATH): else: rot90= False - - #plane.reverse() - no= B.Mathutils.QuadNormal(*plane) + no= Blender.Mathutils.QuadNormal(*plane) plane= [v + no*PREF_Z_OFFSET.val for v in plane] cent= (plane[0]+plane[1]+plane[2]+plane[3] ) /4.0 camera_matrix= BPyMathutils.plane2mat(plane) tmp_path= '%s_%d' % (PREF_IMAGE_PATH, i) img= BPyRender.imageFromObjectsOrtho(ob_sel, tmp_path, PREF_TILE_RES.val, PREF_TILE_RES.val, PREF_AA.val, PREF_ALPHA.val, camera_matrix) - # img.reload() + img.reload() #img.pack() # se we can keep overwriting the path #img.filename= "" + if rot90: + f.uv=Vector(1,1), Vector(0,1), Vector(0,0), Vector(1,0) + else: + f.uv= Vector(0,1), Vector(0,0), Vector(1,0), Vector(1,1) if not PREF_IMG_PACK.val: - f.mode |= B.Mesh.FaceModes.TEX + f.mode |= Mesh.FaceModes.TEX f.image = img - f.uv=Vector(0,1), Vector(0,0), Vector(1,0), Vector(1,1) if PREF_ALPHA.val: - f.transp |= B.Mesh.FaceTranspModes.ALPHA + f.transp |= Mesh.FaceTranspModes.ALPHA else: w= ((plane[0]-plane[1]).length + (plane[2]-plane[3]).length)/2 h= ((plane[1]-plane[2]).length + (plane[3]-plane[0]).length)/2 - face_data.append( (f, img, rot90) ) - boxes2Pack.append( (i, h, w) ) + face_data.append( (f, img) ) + boxes2Pack.append( [0.0,0.0,h, w, i] ) if PREF_IMG_PACK.val: # pack the quads into a square + packWidth, packHeight = Blender.Geometry.BoxPack2D(boxes2Pack) - packWidth, packHeight, packedLs = boxpack2d.boxPackIter(boxes2Pack) render_obs= [] + render_mat= alpha_mat(img) + # Add geometry to the mesh - for box in packedLs: - i= box[0] + for box in boxes2Pack: + i= box[4] - orig_f, img, rot90= face_data[i] + orig_f, img= face_data[i] # New Mesh and Object - render_mat= alpha_mat(img) - render_me= B.Mesh.New() - render_ob= B.Object.New('Mesh') + render_me= bpy.data.meshes.new() + + render_ob= Blender.Object.New('Mesh') render_me.materials= [render_mat] render_ob.link(render_me) render_obs.append(render_ob) # Add verts clockwise from the bottom left. - _x= box[1] / packWidth - _y= box[2] / packHeight - _w= box[3] / packWidth - _h= box[4] / packHeight + _x= box[0] / packWidth + _y= box[1] / packHeight + _w= box[2] / packWidth + _h= box[3] / packHeight render_me.verts.extend([\ @@ -180,65 +174,56 @@ def save_billboard(PREF_IMAGE_PATH): Vector(_x + _w, _y +_h, 0),\ Vector(_x + _w, _y, 0),\ ]) - + render_me.faces.extend(list(render_me.verts)) render_me.faceUV= True - # target_face= render_me.faces[-1] - # TEXFACE isnt used because of the renderign engine cant to alpha's for texdface. - #target_face.image= img - #target_face.mode |= B.Mesh.FaceModes.TEX + render_me.faces[0].uv = [Vector(0,0), Vector(0,1), Vector(1,1), Vector(1,0)] + render_me.faces[0].image = img # Set the UV's, we need to flip them HOZ? - uv1, uv2, uv3, uv4= orig_f.uv - uv3.x= uv4.x= _x+_w - uv1.x= uv2.x= _x - - uv2.y= uv3.y= _y+_h - uv1.y= uv4.y= _y - - if rot90: - orig_f.uv= Vector(uv4), Vector(uv1), Vector(uv2), Vector(uv3) - + for uv in orig_f.uv: + uv.x = _x + (uv.x * _w) + uv.y = _y + (uv.y * _h) + target_image= BPyRender.imageFromObjectsOrtho(render_obs, PREF_IMAGE_PATH, PREF_RES.val, PREF_RES.val, PREF_AA.val, PREF_ALPHA.val, None) + target_image.reload() # incase your overwriting an existing image. # Set to the 1 image. for f in me_data.faces: f.image= target_image if PREF_ALPHA.val: - f.transp |= B.Mesh.FaceTranspModes.ALPHA + f.transp |= Mesh.FaceTranspModes.ALPHA # Free the images data and remove for data in face_data: img= data[1] os.remove(img.filename) img.reload() + # Finish pack me_data.update() me_ob.makeDisplayList() - B.Window.WaitCursor(0) - print '%.2f secs taken' % (B.sys.time()-time) - - + Blender.Window.WaitCursor(0) + print '%.2f secs taken' % (Blender.sys.time()-time) def main(): - scn= B.Scene.GetCurrent() + scn= bpy.data.scenes.active ob_sel= list(scn.objects.context) PREF_KEEP_ASPECT= False - # Error Checking if len(ob_sel) < 2: - B.Draw.PupMenu("Error%t|Select 2 mesh objects") + Draw.PupMenu("Error%t|Select 2 mesh objects") return me_ob= scn.objects.active if not me_ob: - B.Draw.PupMenu("Error%t|No active mesh selected.") + Draw.PupMenu("Error%t|No active mesh selected.") try: ob_sel.remove(me_ob) @@ -246,32 +231,30 @@ def main(): pass if me_ob.type != 'Mesh': - B.Draw.PupMenu("Error%t|Active Object must be a mesh to write billboard images too") + Draw.PupMenu("Error%t|Active Object must be a mesh to write billboard images too") return me_data= me_ob.getData(mesh=1) for f in me_data.faces: if len(f) != 4: - B.Draw.PupMenu("Error%t|Active mesh must have only quads") + Draw.PupMenu("Error%t|Active mesh must have only quads") return - - # Get user input block = [\ 'Image Pixel Size',\ ("Packed Size: ", PREF_RES, 128, 2048, "Pixel width and height to render the billboard to"),\ ("Tile Size: ", PREF_TILE_RES, 64, 1024, "Pixel width and height for each tile to render to"),\ 'Render Settings',\ - ("Pack Final", PREF_IMG_PACK , "Pack all images into 1 image"),\ + ("Pack Final", PREF_IMG_PACK , "Pack the image for each face into images into a single image"),\ ("Oversampling", PREF_AA , "Higher quality woth extra sampling"),\ ("Alpha Clipping", PREF_ALPHA , "Render empty areas as transparent"),\ ("Cam ZOffset: ", PREF_Z_OFFSET, 0.1, 100, "Distance to place the camera away from the quad when rendering")\ ] - if not B.Draw.PupBlock("Billboard Render", block): + if not Draw.PupBlock("Billboard Render", block): return # Set globals @@ -279,7 +262,8 @@ def main(): GLOBALS['me_ob'] = me_ob GLOBALS['me_data'] = me_data - B.Window.FileSelector(save_billboard, 'SAVE BILLBOARD', B.sys.makename(ext='.png')) + Blender.Window.FileSelector(save_billboard, 'SAVE BILLBOARD', Blender.sys.makename(ext='.png')) + # save_billboard('/tmp/test.png') if __name__=='__main__': main() diff --git a/release/scripts/image_find_paths.py b/release/scripts/image_find_paths.py deleted file mode 100644 index 266ecee9435..00000000000 --- a/release/scripts/image_find_paths.py +++ /dev/null @@ -1,167 +0,0 @@ -#!BPY - -""" -Name: 'Fix Broken Paths' -Blender: 242 -Group: 'Image' -Tooltip: 'Search for new image paths to make relative links to' -""" - -__author__ = "Campbell Barton AKA Ideasman" -__url__ = ["blenderartist.org"] - -__bpydoc__ = """\ -Find image target paths - -This script searches for images whos -file paths do not point to an existing image file, -all image paths are made relative where possible. -usefull when moving projects between computers, when absolute paths links are broken. -""" - -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# Script copyright (C) Campbell J Barton -# -# 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 * - -try: - import os -except: - Draw.PupMenu('You need a full python install to use this script') - os= None - - -#==============================================# -# Strips the slashes from the back of a string # -#==============================================# -def stripPath(path): - return path.split('/')[-1].split('\\')[-1] - -# finds the file starting at the root. -def findImage(findRoot, imagePath): - newImageFile = None - - imageFile = imagePath.split('/')[-1].split('\\')[-1] - - # ROOT, DIRS, FILES - pathWalk = os.walk(findRoot) - pathList = [True] - - matchList = [] # Store a list of (match, size), choose the biggest. - while True: - try: - pathList = pathWalk.next() - except: - break - - for file in pathList[2]: - # FOUND A MATCH - if file.lower() == imageFile.lower(): - name = pathList[0] + sys.sep + file - try: - size = os.path.getsize(name) - except: - size = 0 - - if size: - print ' found:', name - matchList.append( (name, size) ) - - if matchList == []: - print 'no match for:', imageFile - return None - else: - # Sort by file size - matchList.sort(lambda A, B: cmp(B[1], A[1]) ) - - print 'using:', matchList[0][0] - # First item is the largest - return matchList[0][0] # 0 - first, 0 - pathname - - -# Makes the pathe relative to the blend file path. -def makeRelative(path, blendBasePath): - if path.startswith(blendBasePath): - path = path.replace(blendBasePath, '//') - path = path.replace('//\\', '//') - return path - -def find_images(findRoot): - print findRoot - - # findRoot = Draw.PupStrInput ('find in: ', '', 100) - - if findRoot == '': - Draw.PupMenu('No Directory Selected') - return - - # Account for // - findRoot = sys.expandpath(findRoot) - - # Strip filename - while findRoot[-1] != '/' and findRoot[-1] != '\\': - findRoot = findRoot[:-1] - - - if not findRoot.endswith(sys.sep): - findRoot += sys.sep - - - if findRoot != '/' and not sys.exists(findRoot[:-1]): - Draw.PupMenu('Directory Dosent Exist') - - blendBasePath = sys.expandpath('//') - - - Window.WaitCursor(1) - # ============ DIR DONE\ - images = Image.Get() - len_images = float(len(images)) - for idx, i in enumerate(images): - - progress = idx / len_images - Window.DrawProgressBar(progress, 'searching for images') - - # If files not there? - if not sys.exists(sys.expandpath(i.filename )): - newImageFile = findImage(findRoot, i.filename) - if newImageFile != None: - newImageFile= makeRelative(newImageFile, blendBasePath) - print 'newpath relink:', newImageFile - i.filename = newImageFile - i.reload() - else: - # Exists - newImageFile= makeRelative(i.filename, blendBasePath) - if newImageFile!=i.filename: - print 'newpath relative:', newImageFile - i.filename = newImageFile - - - Window.RedrawAll() - Window.DrawProgressBar(1.0, '') - Window.WaitCursor(0) - -if __name__ == '__main__' and os: - Window.FileSelector(find_images, 'SEARCH ROOT DIR', sys.expandpath('//')) - - diff --git a/release/scripts/import_dxf.py b/release/scripts/import_dxf.py index a4e4e69b366..4fa7a6472cf 100644 --- a/release/scripts/import_dxf.py +++ b/release/scripts/import_dxf.py @@ -1,54 +1,263 @@ #!BPY -# """ -# Name: 'Drawing eXchange Format (.dxf)' -# Blender: 243 -# Group: 'Import' -# Tooltip: 'Import DXF file.' -# """ -__author__ = 'Kitsu (Ed Blake)' -__version__ = '0.9 1/2007' -__url__ = ["elysiun.com", "BlenderArtists.org"] -__email__ = ["Kitsune_e@yahoo.com"] +""" +Name: 'Autodesk DXF (.dxf)' +Blender: 244 +Group: 'Import' +Tooltip: 'Import for DXF geometry data (Drawing eXchange Format).' +""" +__author__ = 'Kitsu(Ed Blake) & migius(Remigiusz Fiedler)' +__version__ = '1.0.12 - 2008.02.08 by migius' +__url__ = ["http://blenderartists.org/forum/showthread.php?t=84319", + "http://wiki.blender.org/index.php/Scripts/Manual/Import/DXF-3D"] +__email__ = ["Kitsune_e(at)yahoo.com", "migius(at)4d-vectors.de"] __bpydoc__ = """\ -This is a Blender import script for dxf files. - -This script imports the dxf Geometery from dxf versions 2007 and earlier. - -Supported:<br> - At this time only mesh based imports are supported.<br> - Future support for all curve import is planned.<br> - <br> -Currently Supported DXF Ojects:<br> - Lines<br> - LightWeight polylines<br> - True polylines<br> - Text<br> - Mtext<br> - Circles<br> - Arcs<br> - Ellipses<br> - Blocks<br> - 3Dfaces<br> - -Known issues:<br> - Does not convert perfectly between Object Coordinate System (OCS) - and World Coordinate System (WCS). Only rudimentary support for - true polylines have been implimented - splines/fitted curves/ - 3d plines/polymeshes are not supported. - No support for most 3d entities. Doesn't support the new style object - visability. There are problems importing some curves/arcs/circles. - -Notes:<br> - This is primarally a 2d drawing release. Currently only support for - 3d faces has been added. - Blocks are created on layer 19 then referenced at each insert point. The - insert point is designated with a small 3d crosshair. This handle does not render. +This script imports objects from DXF (2d/3d) into Blender. + +This script imports 2d and 3d geometery from DXFr12 format files. +Enhanced features are: +- configurable object filtering and geometry manipulation, +- configurable material pre-processing, +- DXF-data analyze and raporting. + +Supported DXF r12 objects: +LINE, +POINT, +SOLID, +TRACE, +TEXT, +INSERT (=block), +MINSERT (=array of blocks), +CIRCLE, +ARC, +3DFACE, +2d-POLYLINE (=plane, incl. arc, variable-width, curve, spline), +3d-POLYLINE (=non-plane), +3d-POLYMESH, +3d-POLYFACE, +VIEW, VPORT +XREF (External Reference). + +Supported DXF>r12 objects: +ELLIPSE, +(wip v1.0.12 partly supported) LWPOLYLINE (LightWeight), +(wip v1.0.12) MLINE, +(wip v1.0.12) MTEXT + +Unsupported Objects: +DXF r12: DIMENSION. +DXF>r12: SPLINE, GROUP, RAY/XLINE, LEADER, 3DSOLID, BODY, REGION, dynamic BLOCK + +Supported Geometry: 2d and 3d DXF-objects. +Curves imported as curves or meshes optionally. + +Supported layout modes: +"model space" is default, +"paper space" as option (= "layout views") + +Scene definitions produced with AVE_RENDER: +scene: selection of lights assigned to the camera, +lights: DIRECT, OVERHEAD, SH_SPOT, +(wip v1.0.13 import of AVE_RENDER material definitions) + +Hierarchy: +Entire DXF BLOCK hierarchy is preserved after import into Blender +(BLOCKs as groups on layer19, INSERTs as dupliGroups on target layer). + +Supported properties: +visibility status, +frozen status, +thickness, +width, +color, +layer, +(wip v1.0.12: XDATA, grouped status) +It is recommended to use DXF-object properties for assign Blender materials. + +Notes: +- Recommend that you run 'RemoveDoubles' on each imported mesh after using this script +- Blocks are created on layer 19 then referenced at each insert point. +- Big DXF-files (over 1500 objects) decrease import performance. +The problem is not the inefficiency of python-scripting but Blenders performance +in creating new objects in his database - probably a database management problem. + +TODO: +- the new style object visibility +- support for real 3d-solids (ACIS) +- (to see more, search for "--todo--" in script code) + +""" + +""" +History: + v1.0 - 2008.01. by migius + planned tasks: + -- command-line-mode/batch-mode + -- in-place-editing for dupliGroups + -- support for MLINE (is exported to r12 as BLOCK*Unnamed with LINEs) + -- support for MTEXT (is exported to r12 as TEXT???) + -- blender_object.properties['dxf_layer_name'] + -- better support for long dxf-layer-names + -- add configuration file.ini handles multiple material setups + -- added f_layerFilter + -- to-check: obj/mat/group/_mapping-idea from ideasman42: + -- curves: added "fill/non-fill" option for closed curves: CIRCLEs,ELLIPSEs,POLYLINEs + -- bug:? object = Object.Get(obname) -> = SCENE.getChildren(obname) + -- "normalize Z" option to correct non-planar figures + -- LINEs need "width" in 3d-space incl vGroups + -- support width_force for LINEs/ELLIPSEs = "solidify" + -- add better support for color_index BYLAYER=256, BYBLOCK=0 + -- bug: "oneMesh" produces sometimes errors + -- bug: Registry recall from hd_cache ?? only win32 bug?? + + v1.0.12: 2008.02.08 by migius + -- support DXF-definitions of scene, lights and cameras + -- support ortho mode for VIEWs and VPORTs as cameras + a9 bugfix by non-existing tables views, vports, layers (Kai reported) + v1.0.12: 2008.01.17 by migius + a8 lately used INI-dir/filename persistently stored in Registry + a8 lately used DXF-dir/filename persistently stored in Registry + a7 fix missing layersmap{} for dxf-files without "section:layer" + a6 added support for XREF external referenced BLOCKs + a6 check for bug in AutoCAD2002:DXFr12export: ELLIPSE->POLYLINE_ARC fault angles + a6 support VIEWs and VPORTs as cameras: ortho and perspective mode + a6 save resources through ignoring unused BLOCKs (not-inserted or on frozen/blocked layers) + a6 added try_finally: f.close() for all IO-files + a6 added handling for TypeError raise + a5 bugfix f_getOCS for (0,0,z!=1.0) (ellipse in Kai's dxf) + a4 added to analyzeTool: report about VIEWs, VPORTs, unused/xref BLOCKs + a4 bugfix: individual support for 2D/3DPOLYLINE/POLYMESH + a4 added to UI: (*wip)BLOCK-(F): name filtering for BLOCKs + a4 added to UI: BLOCK-(n): filter anoname/hatch BLOCKs *X... + a2 g_scale_as is no more GUI_A-variable + a2 bugfix "material": negative sign color_index + a2 added support for BLOCKs defined with origin !=(0,0,0) + a1 added 'global.reLocation-vector' option + + v1.0.11: 2007.11.24 by migius + c8 added 'curve_resolution_U' option + c8 added context_sensitivity for some UI-buttons + c8 bugfix ELLIPSE rotation, added closed_variant and caps + c7 rebuild UI: new layout, grouping and meta-buttons + c6 rewritten support for ELLIPSE mesh & curve representation + c6 restore selector-buttons for DXF-drawTypes: LINE & Co + c6 change header of INI/INF-files: # at begin + c6 apply scale(1,1,1) after glob.Scale for all mesh objects, not for curve objects. + c5 fixing 'material_on' option + c4 added "analyze DXF-file" UI-option: print LAYER/BLOCK-dependences into a textfile + c3 human-formating of data in INI-Files + c2 added "caps" for closed Bezier-curves + c2 added "set elevation" UI-option + c1 rewrite POLYLINE2d-arc-segments Bezier-interpreter + b9 many bugs fixed + b9 rewrite POLYLINE2d-arc-segments trimming (clean-trim) + b8 added "import from frozen layers" UI-option + b8 added "import from paper space" UI-option + b8 support Bezier curves for LINEs incl.thickness(0.0-10.0) + b8 added meshSmooth_on for circle/arc/polyline + b8 added vertexGroups for circle/arc + b7 added width_force for ARCs/CIRCLEs = "thin_box" option + b3 cleanup code, rename f_drawArc/Bulg->f_calcArc/Bulg + b2 fixing material assignment by LAYER+COLOR + b1 fixing Bezier curves representation of POLYLINEs-arc-segments + b0 added global_scale_presets: "yard/feet/inch to meter" + + v1.0.10: 2007.10.18 by migius + a6 bugfix CircleDrawCaps for OSX + a5 added two "curve_res" UI-buttons for Bezier curves representation + a5 improved Bezier curves representation of circles/arcs: correct handlers + a4 try to fix malformed endpoints of Blender curves of ARC/POLYLINE-arc segments. + a3 bugfix: open-POLYLINEs with end_point.loc==start_point.loc + a2 bugfix: f_transform for OCS=(0,0,-1) oriented objects + a1 added "fill_on=caps" option to draw top and bottom sides of CIRCLEs and ELLIPSEs + a1 rewrite f_CIRCLE.Draw: from Mesh.Primitive to Mesh + a1 bugfix "newScene"-mode: all Cylinders/Arcs were drawn at <0,0,0>location + + v1.0.beta09: 2007.09.02 by migius + g5 redesign UI: grouping of buttons + g3 update multi-import-mode: <*.*> button + g- added multi-import-mode: (path/*) for importing many dxf-files at once + g- added import into newScene + g- redesign UI: user presets, into newScene-import + f- cleanup code + f- bugfix: thickness for Bezier/Bsplines into Blender-curves + f- BlenderWiki documentation, on-line Manual + f- added import POLYLINE-Bsplines into Blender-NURBSCurves + f- added import POLYLINE-arc-segments into Blender-BezierCurves + f- added import POLYLINE-Bezier-curves into Blender-Curves + d5 rewrite: Optimization Levels, added 'directDrawing' + d4 added: f_set_thick(controlled by ini-parameters) + d4 bugfix: face-normals in objects with minus thickness + d4 added: placeholder'Empty'-size in f_Insert.draw + d3 rewrite f_Text.Draw: added support for all Text's parameters + d2 redesign: progressbar + e- tuning by ideasman42: better use of the Py API. + c- tuning by ideasman42 + b- rewrite f_Text.Draw rotation/transform + b- bugfix: POLYLINE-segment-intersection more reliable now + b- bugfix: circle:_thic, 'Empties':no material_assignment + b- added material assignment (from layer and/or color) + a- added empty, cylinder and UVsphere for POINTs + a- added support for 2d-POLYLINE: splines, fitted curves, fitted surfaces + a- redesign f_Drawer for block_definitions + a- rewrite import into Blender-Curve-Object + + v1.0.beta08 - 2007.07.27 by migius: "full 3d"-release + l- bugfix: solid_vgroups, clean:scene.objects.new() + l- redesign UI to standard Draw.Register+FileSelector, advanced_config_option + k- bugfix UI:fileSelect() for MacOSX os.listdir() + k- added reset/save/load for config-data + k- redesign keywords/drawTypes/Draw.Create_Buttons + j- new UI using UIBlock() with own FileSelector, cause problem Window.FileSelector() + i- rewritten Class:Settings for better config-parameter management + h- bugfix: face-normals in objects with minus thickness + h- added Vertex-Groups in POLYLINE and SOLID meshes, for easy material assignment + h- beautify code, whitespace->tabs + h- added settings.thic_force switch for forcing thickness + h- added "one Mesh" option for all entities from the same Layer, sorted in<br> + Vertex-Groups(color_name) (fewer objects = better import performance) + g- rewrote: insert-point-handle-object is a small tetrahedron + e- bugfix: closed-polymesh3d + - rewrote: UI, type_map.keys, f_drawer, all class_f_draw(added "settings" as attribut) + - added 2d/3d-support for Polyline_Width incl. angle intersection + beta07: 2007.06.19 by migius + - added 3d-support for LWPolylines + - added 2d/3d-support for Points + beta06: 2007.06.15 by migius + - cleanup code + - added 2d/3d-support for MINSERT=BlockArray in f_drawer, added f_rotXY_Vec + beta05: 2007.06.14 by migius + - added 2d/3d-support for 3d-PolyLine, PolyMesh and PolyFace + - added Global-Scale for size control of imported scenes + beta04: 2007.06.12 by migius + - rewrote the f_drawBulge for correct import the arc-segments of Polylines + beta03: 2007.06.10 by migius + - rewrote interface + beta02: 2007.06.09 by migius + - added 3d-support for Arcs and Circles + - added support for Object_Thickness(=height) + beta01: 2007.06.08 by migius + - added 3d-support for Blocks/Inserts within nested-structures + - rewrote f_transform for correct 3d-location/3d-rotation + - added 3d-support Lines, 3dFaces + - added 2d+3d-support for Solids and Traces + + v0.9 - 2007.01 by kitsu: (for 2.43) + - first draft of true POLYLINE import + - + + v0.8 - 2006.12 by kitsu: + - first draft of object space coordinates OCS import + - + + v0.5b - 2006.10 by kitsu: (for 2.42a) + - dxfReader.py + - color_map.py """ # -------------------------------------------------------------------------- -# DXF Import v0.9 by Ed Blake (AKA Kitsu) +# DXF Import v1.0 by Ed Blake (AKA kitsu) and Remigiusz Fiedler (AKA migius) # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # @@ -70,2195 +279,5667 @@ Notes:<br> # -------------------------------------------------------------------------- import Blender +#import bpy from Blender import * -Sys = sys +#from Blender.Mathutils import Vector, Matrix +#import BPyMessages -from dxfReader import readDXF, get_name, get_layer +from dxfReader import readDXF +#from dxfReader import get_name, get_layer from dxfReader import Object as dxfObject from dxfColorMap import color_map + from math import * try: - import os - if os.name:# != 'mac': - import psyco - psyco.log() - psyco.full(memory=100) - psyco.profile(0.05, memory=100) - psyco.profile(0.2) + import os + if os.name:# != 'mac': + import psyco + psyco.log(Blender.Get('tempdir')+"/blender.log-psyco") + #psyco.log() + psyco.full(memory=100) + psyco.profile(0.05, memory=100) + psyco.profile(0.2) + #print 'psyco imported' except ImportError: - pass + #print 'psyco not imported' + pass + +print '\n\n\n\n' +print 'DXF-Importer *** start ***' #--------------------- -SCENE = Scene.GetCurrent() +SCENE = None WORLDX = Mathutils.Vector((1,0,0)) +WORLDY = Mathutils.Vector((1,1,0)) +WORLDZ = Mathutils.Vector((0,0,1)) + +G_SCALE = 1.0 #(0.0001-1000) global scaling factor for all dxf data +G_ORIGIN_X = 0.0 #global translation-vector (x,y,z) in DXF units +G_ORIGIN_Y = 0.0 +G_ORIGIN_Z = 0.0 +MIN_DIST = 0.001 #cut-off value for sort out short-distance polyline-"duoble_vertex" +ARC_RESOLUTION = 64 #(4-500) arc/circle resolution - number of segments +ARC_RADIUS = 1.0 #(0.01-100) arc/circle radius for number of segments algorithm +CURV_RESOLUTION = 12 #(1-128) Bezier curves U-resolution +CURVARC_RESOLUTION = 4 #(3-32) resolution of circle represented as Bezier curve +THIN_RESOLUTION = 8 #(4-64) thin_cylinder arc_resolution - number of segments +MIN_THICK = MIN_DIST * 10.0 #minimal thickness by forced thickness +MIN_WIDTH = MIN_DIST * 10.0 #minimal width by forced width +TRIM_LIMIT = 3.0 #limit for triming of polylines-wide-segments (values:0.0 - 5.0) +ELEVATION = 0.0 #standard elevation = coordinate Z + +TARGET_LAYER = 3 #target blender_layer +GROUP_BYLAYER = 0 #(0/1) all entities from same layer import into one blender-group + +FILENAME_MAX = 180 #max length of path+file_name string (FILE_MAXDIR + FILE_MAXFILE) +MAX_NAMELENGTH = 17 #max_effective_obnamelength in blender =21=17+(.001) +INIFILE_DEFAULT_NAME = 'importDXF' +INIFILE_EXTENSION = '.ini' +INIFILE_HEADER = '#ImportDXF.py ver.1.0 config data' +INFFILE_HEADER = '#ImportDXF.py ver.1.0 analyze of DXF-data' + AUTO = BezTriple.HandleTypes.AUTO -BYLAYER=256 +FREE = BezTriple.HandleTypes.FREE +VECT = BezTriple.HandleTypes.VECT +ALIGN = BezTriple.HandleTypes.ALIGN +cur_COUNTER = 0 #counter for progress_bar + + """This module provides wrapper objects for dxf entities. - - The wrappers expect a "dxf object" as input. The dxf object is - an object with a type and a data attribute. Type is a lowercase - string matching the 0 code of a dxf entity. Data is a list containing - dxf objects or lists of [code, data] pairs. - - This module is not general, and is only for dxf import. + + The wrappers expect a "dxf object" as input. The dxf object is + an object with a type and a data attribute. Type is a lowercase + string matching the 0 code of a dxf entity. Data is a list containing + dxf objects or lists of [code, data] pairs. + + This module is not general, and is only for dxf import. """ # from Stani's dxf writer v1.1 (c)www.stani.be (GPL) #---color values -BYBLOCK=0 -BYLAYER=256 +BYBLOCK = 0 +BYLAYER = 256 -#---block-type flags (bit coded values, may be combined): -ANONYMOUS =1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application +#---block-type flags (bit coded values, may be combined): +ANONYMOUS =1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application NON_CONSTANT_ATTRIBUTES =2 # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all) -XREF =4 # This block is an external reference (xref) -XREF_OVERLAY =8 # This block is an xref overlay -EXTERNAL =16 # This block is externally dependent -RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input) -REFERENCED =64 # This definition is a referenced external reference (ignored on input) +XREF =4 # This block is an external reference (xref) +XREF_OVERLAY =8 # This block is an xref overlay +EXTERNAL =16 # This block is externally dependent +RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input) +REFERENCED =64 # This definition is a referenced external reference (ignored on input) + +#---polyline flags +CLOSED =1 # This is a closed polyline (or a polygon mesh closed in the M direction) +CURVE_FIT =2 # Curve-fit vertices have been added +SPLINE_FIT =4 # Spline-fit vertices have been added +POLYLINE_3D =8 # This is a 3D polyline +POLYGON_MESH =16 # This is a 3D polygon mesh +CLOSED_N =32 # The polygon mesh is closed in the N direction +POLYFACE_MESH =64 # The polyline is a polyface mesh +CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline + +#---text flags +#horizontal +LEFT = 0 +CENTER = 1 +RIGHT = 2 +ALIGNED = 3 #if vertical alignment = 0 +MIDDLE = 4 #if vertical alignment = 0 +FIT = 5 #if vertical alignment = 0 +#vertical +BASELINE = 0 +BOTTOM = 1 +MIDDLE = 2 +TOP = 3 #---mtext flags #attachment point -TOP_LEFT = 1 -TOP_CENTER = 2 -TOP_RIGHT = 3 -MIDDLE_LEFT = 4 +TOP_LEFT = 1 +TOP_CENTER = 2 +TOP_RIGHT = 3 +MIDDLE_LEFT = 4 MIDDLE_CENTER = 5 -MIDDLE_RIGHT = 6 -BOTTOM_LEFT = 7 +MIDDLE_RIGHT = 6 +BOTTOM_LEFT = 7 BOTTOM_CENTER = 8 -BOTTOM_RIGHT = 9 +BOTTOM_RIGHT = 9 #drawing direction -LEFT_RIGHT = 1 -TOP_BOTTOM = 3 -BY_STYLE = 5 #the flow direction is inherited from the associated text style -#line spacing style (optional): -AT_LEAST = 1 #taller characters will override -EXACT = 2 #taller characters will not override +LEFT_RIGHT = 1 +TOP_BOTTOM = 3 +BY_STYLE = 5 #the flow direction is inherited from the associated text style +#line spacing style (optional): +AT_LEAST = 1 #taller characters will override +EXACT = 2 #taller characters will not override -#---polyline flags -CLOSED =1 # This is a closed polyline (or a polygon mesh closed in the M direction) -CURVE_FIT =2 # Curve-fit vertices have been added -SPLINE_FIT =4 # Spline-fit vertices have been added -POLYLINE_3D =8 # This is a 3D polyline -POLYGON_MESH =16 # This is a 3D polygon mesh -CLOSED_N =32 # The polygon mesh is closed in the N direction -POLYFACE_MESH =64 # The polyline is a polyface mesh -CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline -#---text flags -#horizontal -LEFT = 0 -CENTER = 1 -RIGHT = 2 -ALIGNED = 3 #if vertical alignment = 0 -MIDDLE = 4 #if vertical alignment = 0 -FIT = 5 #if vertical alignment = 0 -#vertical -BASELINE = 0 -BOTTOM = 1 -MIDDLE = 2 -TOP = 3 -class Layer: - """Class for objects representing dxf layers.""" - - def __init__(self, obj, name=None, color=None, frozen=None): - """Expects an entity object of type line as input.""" - self.type = obj.type - self.data = obj.data[:] - - if name: - self.name = name - else: - self.name = obj.get_type(2)[0] - if color: - self.color = color - else: - self.color = obj.get_type(62)[0] - if frozen: - self.frozen = frozen - else: - self.flags = obj.get_type(70)[0] - self.frozen = self.flags&1 - - def __repr__(self): - return "%s: name - %s, color - %s" %(self.__class__.__name__, self.name, self.color) - - - -class Line: - """Class for objects representing dxf lines.""" - - def __init__(self, obj): - """Expects an entity object of type line as input.""" - if not obj.type == 'line': - raise TypeError, "Wrong type %s for line object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.points = self.get_points(obj.data) - - - - - def get_points(self, data): - """Gets start and end points for a line type object. - - Lines have a fixed number of points (two) and fixed codes for each value. - """ - - # start x, y, z and end x, y, z = 0 - sx, sy, sz, ex, ey, ez = 0, 0, 0, 0, 0, 0 - for item in data: - if item[0] == 10: # 10 = x - sx = item[1] - elif item[0] == 20: # 20 = y - sy = item[1] - elif item[0] == 30: # 30 = z - sz = item[1] - elif item[0] == 11: # 11 = x - ex = item[1] - elif item[0] == 21: # 21 = y - ey = item[1] - elif item[0] == 31: # 31 = z - ez = item[1] - return [[sx, sy, sz], [ex, ey, ez]] - - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - def draw(self, curves=False): - """Do all the specific things needed to import lines into Blender.""" - # Generate the geometery - points = self.points - edges = [[0, 1]] - - me = Mesh.New('line') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.edges.extend(edges) # add edges to the mesh - - # Now Create an object - ob = Object.New('Mesh', 'line') # link mesh to an object - ob.link(me) - - return ob - - - - -class LWpolyline: - """Class for objects representing dxf LWpolylines.""" - - def __init__(self, obj): - """Expects an entity object of type lwpolyline as input.""" - if not obj.type == 'lwpolyline': - raise TypeError, "Wrong type %s for polyline object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.num_points = obj.get_type(90)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.elevation = obj.get_type(38) - if self.elevation: - self.elevation = self.elevation[0] - else: - self.elevation = 0 - - self.flags = obj.get_type(70) - if self.flags: - self.flags = self.flags[0] - else: - self.flags = 0 - - self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.points = self.get_points(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - - def get_points(self, data): - """Gets points for a polyline type object. - - Polylines have no fixed number of verts, and - each vert can have a number of properties. - Verts should be coded as - 10:xvalue - 20:yvalue - 40:startwidth or 0 - 41:endwidth or 0 - 42:bulge or 0 - for each vert - """ - num = self.num_points - point = None - points = [] - for item in data: - if item[0] == 10: # 10 = x - if point: - points.append(point) - point = Vertex() - point.x = item[1] - elif item[0] == 20: # 20 = y - point.y = item[1] - elif item[0] == 40: # 40 = start width - point.swidth = item[1] - elif item[0] == 41: # 41 = end width - point.ewidth = item[1] - elif item[0] == 42: # 42 = bulge - point.bulge = item[1] - points.append(point) - return points - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - def draw(self, curves=False): - """Do all the specific things needed to import plines into Blender.""" - # Generate the geometery - points = [] - for i in range(len(self.points)): - point = self.points[i] - if not point.bulge: - points.append(point.loc) - elif point.bulge and i < len(self.points)-1:# > 0: - center, radius, start, end = solveBulge(point, self.points[i+1]) - #print center, radius, start, end - verts, nosense = drawArc(center, radius, start, end) - verts.pop(0) # remove first - verts.pop() #remove last - if point.bulge >= 0: - verts.reverse() - points.extend(verts) - edges = [[num, num+1] for num in range(len(points)-1)] - if self.closed: - edges.append([len(self.points)-1, 0]) - - me = Mesh.New('lwpline') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.edges.extend(edges) # add edges to the mesh - - # Now Create an object - ob = Object.New('Mesh', 'lwpline') # link mesh to an object - ob.link(me) - transform(self.extrusion, ob) - ob.LocZ = self.elevation - - return ob - - - -class Polyline: - """Class for objects representing dxf LWpolylines.""" - - def __init__(self, obj): - """Expects an entity object of type polyline as input.""" - if not obj.type == 'polyline': - raise TypeError, "Wrong type %s for polyline object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - self.points = [] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.elevation = obj.get_type(30) - if self.elevation: - self.elevation = self.elevation[0] - else: - self.elevation = 0 - - self.flags = obj.get_type(70) - if self.flags: - self.flags = self.flags[0] - else: - self.flags = 0 - - self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - def draw(self, curves=False): - """Do all the specific things needed to import plines into Blender.""" - # Generate the geometery - points = [] - for i in range(len(self.points)): - point = self.points[i] - if not point.bulge: - points.append(point.loc) - elif point.bulge and i < len(self.points)-1:# > 0: - center, radius, start, end = solveBulge(point, self.points[i+1]) - #print center, radius, start, end - verts, nosense = drawArc(center, radius, start, end) - verts.pop(0) # remove first - verts.pop() #remove last - if point.bulge >= 0: - verts.reverse() - points.extend(verts) - edges = [[num, num+1] for num in range(len(points)-1)] - if self.closed: - edges.append([len(self.points)-1, 0]) - - me = Mesh.New('pline') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.edges.extend(edges) # add edges to the mesh - - # Now Create an object - ob = Object.New('Mesh', 'pline') # link mesh to an object - ob.link(me) - transform(self.extrusion, ob) - ob.LocZ = self.elevation - - return ob - - - - -class Vertex(object): - """Generic vertex object used by polylines (and maybe others).""" - - def __init__(self, obj=None): - """Initializes vertex data. - - The optional obj arg is an entity object of type vertex. - """ - self.loc = [0,0,0] - self.bulge = 0 - self.swidth = 0 - self.ewidth = 0 - self.flags = 0 - - if obj is not None: - if not obj.type == 'vertex': - raise TypeError, "Wrong type %s for vertex object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - self.get_props(obj.data) - - - def get_props(self, data): - """Gets coords for a vertex type object. - - Each vert can have a number of properties. - Verts should be coded as - 10:xvalue - 20:yvalue - 40:startwidth or 0 - 41:endwidth or 0 - 42:bulge or 0 - """ - for item in data: - if item[0] == 10: # 10 = x - self.x = item[1] - elif item[0] == 20: # 20 = y - self.y = item[1] - elif item[0] == 30: # 30 = z - self.z = item[1] - elif item[0] == 40: # 40 = start width - self.swidth = item[1] - elif item[0] == 41: # 41 = end width - self.ewidth = item[1] - elif item[0] == 42: # 42 = bulge - self.bulge = item[1] - elif item[0] == 70: # 70 = vert flags - self.flags = item[1] - - - def __len__(self): - return 3 - - - def __getitem__(self, key): - return self.loc[key] - - - def __setitem__(self, key, value): - if key in [0,1,2]: - self.loc[key] - - - def __iter__(self): - return self.loc.__iter__() - - - def __str__(self): - return str(self.loc) - - - def __repr__(self): - return "Vertex %s, swidth=%s, ewidth=%s, bulge=%s" %(self.loc, self.swidth, self.ewidth, self.bulge) - - - def getx(self): - return self.loc[0] - - def setx(self, value): - self.loc[0] = value - - x = property(getx, setx) - - - def gety(self): - return self.loc[1] - - def sety(self, value): - self.loc[1] = value - - y = property(gety, sety) - - - def getz(self): - return self.loc[2] - - def setz(self, value): - self.loc[2] = value - - z = property(getz, setz) - - - -class Text: - """Class for objects representing dxf Text.""" - - def __init__(self, obj): - """Expects an entity object of type text as input.""" - if not obj.type == 'text': - raise TypeError, "Wrong type %s for text object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.height = obj.get_type(40)[0] - self.value = obj.get_type(1)[0] # The text string value - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.rotation = obj.get_type(50) # radians? - if not self.rotation: - self.rotation = 0 - else: - self.rotation = self.rotation[0] - - self.width_factor = obj.get_type(41) # Scaling factor along local x axis - if not self.width_factor: - self.width_factor = 1 - else: - self.width_factor = self.width_factor[0] - - self.oblique = obj.get_type(51) # skew in degrees -90 <= oblique <= 90 - if not self.oblique: - self.oblique = 0 - else: - self.oblique = self.oblique[0] - - self.halignment = obj.get_type(72) # horiz. alignment - if not self.halignment: # 0=left, 1=center, 2=right, 3=aligned, 4=middle, 5=fit - self.halignment = 0 - else: - self.halignment = self.halignment[0] - - self.valignment = obj.get_type(73) # vert. alignment - if not self.valignment: # 0=baseline, 1=bottom, 2=middle, 3=top - self.valignment = 0 - else: - self.valignment = self.valignment[0] - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data, self.halignment, self.valignment) - self.extrusion = self.get_extrusion(obj.data) - - - - - def get_loc(self, data, halign, valign): - """Gets adjusted location for text type objects. - - If group 72 and/or 73 values are nonzero then the first alignment point values - are ignored and AutoCAD calculates new values based on the second alignment - point and the length and height of the text string itself (after applying the - text style). If the 72 and 73 values are zero or missing, then the second - alignment point is meaningless. - - I don't know how to calc text size... - """ - # bottom left x, y, z and justification x, y, z = 0 - x, y, z, jx, jy, jz = 0, 0, 0, 0, 0, 0 - for item in data: - if item[0] == 10: # 10 = x - x = item[1] - elif item[0] == 20: # 20 = y - y = item[1] - elif item[0] == 30: # 30 = z - z = item[1] - elif item[0] == 11: # 11 = x - jx = item[1] - elif item[0] == 21: # 21 = y - jy = item[1] - elif item[0] == 31: # 31 = z - jz = item[1] - - if halign or valign: - x, y, z = jx, jy, jz - return [x, y, z] - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) - - - def draw(self, curves=False): - """Do all the specific things needed to import texts into Blender.""" - # Generate the geometery - txt = Text3d.New("text") - txt.setSize(1) - txt.setShear(self.oblique/90) - txt.setExtrudeDepth(0.5) - if self.halignment == 0: - align = Text3d.LEFT - elif self.halignment == 1: - align = Text3d.MIDDLE - elif self.halignment == 2: - align = Text3d.RIGHT - elif self.halignment == 3: - align = Text3d.FLUSH - else: - align = Text3d.MIDDLE - txt.setAlignment(align) - txt.setText(self.value) - - # Now Create an object - ob = Object.New('Text', 'text') # link mesh to an object - ob.link(txt) - - transform(self.extrusion, ob) - - # move the object center to the text location - ob.loc = tuple(self.loc) - # scale it to the text size - ob.SizeX = self.height*self.width_factor - ob.SizeY = self.height - ob.SizeZ = self.height - # and rotate it around z - ob.RotZ = radians(self.rotation) - - return ob - - - - -class Mtext: - """Class for objects representing dxf Mtext.""" - - def __init__(self, obj): - """Expects an entity object of type mtext as input.""" - if not obj.type == 'mtext': - raise TypeError, "Wrong type %s for mtext object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.height = obj.get_type(40)[0] - self.width = obj.get_type(41)[0] - self.alignment = obj.get_type(71)[0] # alignment 1=TL, 2=TC, 3=TR, 4=ML, 5=MC, 6=MR, 7=BL, 8=BC, 9=BR - self.value = self.get_text(obj.data) # The text string value - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - self.rotation = obj.get_type(50) # radians - if not self.rotation: - self.rotation = 0 - else: - self.rotation = self.rotation[0] - - self.width_factor = obj.get_type(42) # Scaling factor along local x axis - if not self.width_factor: - self.width_factor = 1 - else: - self.width_factor = self.width_factor[0] - - self.line_space = obj.get_type(44) # percentage of default - if not self.line_space: - self.line_space = 1 - else: - self.line_space = self.line_space[0] - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_text(self, data): - """Reconstructs mtext data from dxf codes.""" - primary = '' - secondary = [] - for item in data: - if item[0] == 1: # There should be only one primary... - primary = item[1] - elif item[0] == 3: # There may be any number of extra strings (in order) - secondary.append(item[1]) - if not primary: - #raise ValueError, "Empty Mtext Object!" - string = "Empty Mtext Object!" - if not secondary: - string = primary.replace(r'\P', '\n') - else: - string = ''.join(secondary)+primary - string = string.replace(r'\P', '\n') - return string - def get_loc(self, data): - """Gets location for a mtext type objects. - - Mtext objects have only one point indicating location. - """ - loc = [0,0,0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) - - - def draw(self, curves=False): - """Do all the specific things needed to import mtexts into Blender.""" - # Generate the geometery - txt = Text3d.New("mtext") - txt.setSize(1) - # Blender doesn't give access to its text object width currently - # only to the text3d's curve width... - #txt.setWidth(text.width/10) - txt.setLineSeparation(self.line_space) - txt.setExtrudeDepth(0.5) - txt.setText(self.value) - - # Now Create an object - ob = Object.New('Text', 'mtext') # link mesh to an object - ob.link(txt) - - transform(self.extrusion, ob) - - # move the object center to the text location - ob.loc = tuple(self.loc) - # scale it to the text size - ob.SizeX = self.height*self.width_factor - ob.SizeY = self.height - ob.SizeZ = self.height - # and rotate it around z - ob.RotZ = radians(self.rotation) - - return ob - - - - - - -class Circle: - """Class for objects representing dxf Circles.""" - - def __init__(self, obj): - """Expects an entity object of type circle as input.""" - if not obj.type == 'circle': - raise TypeError, "Wrong type %s for circle object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.radius = obj.get_type(40)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_loc(self, data): - """Gets the center location for circle type objects. - - Circles have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) - - - def draw(self, curves=False): - """Do all the specific things needed to import circles into Blender.""" - # Generate the geometery - # Now Create an object - if curves: - ob = drawCurveCircle(self) - else: - center = self.loc - radius = self.radius - - circ = 2 * pi * radius - if circ < 65: # if circumfrance is too small - verts = 32 # set a fixed number of 32 verts - else: - verts = circ/.5 # figure out how many verts we need - if verts > 100: # Blender only accepts values - verts = 100 # [3:100] - - c = Mesh.Primitives.Circle(int(verts), radius*2) - - ob = Object.New('Mesh', 'circle') - ob.link(c) # link curve data with this object - - ob.loc = tuple(center) - transform(self.extrusion, ob) - - return ob - - - - -class Arc: - """Class for objects representing dxf arcs.""" - - def __init__(self, obj): - """Expects an entity object of type arc as input.""" - if not obj.type == 'arc': - raise TypeError, "Wrong type %s for arc object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.radius = obj.get_type(40)[0] - self.start_angle = obj.get_type(50)[0] - self.end_angle = obj.get_type(51)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_loc(self, data): - """Gets the center location for arc type objects. - - Arcs have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) - - - def draw(self, curves=False): - """Do all the specific things needed to import arcs into Blender.""" - # Generate the geometery - # Now Create an object - if curves: - ob = drawCurveArc(self) - else: - center = self.loc - radius = self.radius - start = self.start_angle - end = self.end_angle - verts, edges = drawArc(None, radius, start, end) - - a = Mesh.New('arc') - - a.verts.extend(verts) # add vertices to mesh - a.edges.extend(edges) # add edges to the mesh - - ob = Object.New('Mesh', 'arc') - ob.link(a) # link curve data with this object - ob.loc = tuple(center) - ob.RotX = radians(180) - - transform(self.extrusion, ob) - ob.size = (1,1,1) - - return ob - - -class BlockRecord: - """Class for objects representing dxf block_records.""" - - def __init__(self, obj): - """Expects an entity object of type block_record as input.""" - if not obj.type == 'block_record': - raise TypeError, "Wrong type %s for block_record object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.name = obj.get_type(2)[0] - - # optional data (with defaults) - self.insertion_units = obj.get_type(70) - if not self.insertion_units: - self.insertion_units = None - else: - self.insertion_units = self.insertion_units[0] - - self.insert_units = obj.get_type(1070) - if not self.insert_units: - self.insert_units = None - else: - self.insert_units = self.insert_units[0] - - - - - - - def __repr__(self): - return "%s: name - %s, insert units - %s" %(self.__class__.__name__, self.name, self.insertion_units) - - - - -class Block: - """Class for objects representing dxf blocks.""" - - def __init__(self, obj): - """Expects an entity object of type block as input.""" - if not obj.type == 'block': - raise TypeError, "Wrong type %s for block object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - self.name = obj.name - - # required data - self.flags = obj.get_type(70)[0] - self.entities = dxfObject('block_contents') - self.entities.data = objectify([ent for ent in obj.data if type(ent) != list]) - - # optional data (with defaults) - self.path = obj.get_type(1) - if self.path: - self.path = self.path[0] - else: - self.path = '' - - self.discription = obj.get_type(4) - if self.discription: - self.discription = self.discription[0] - else: - self.discription = '' - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data) - - - - - def get_loc(self, data): - """Gets the insert point of the block.""" - loc = [0, 0, 0] - for item in data: - if type(item) != list: - continue - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def __repr__(self): - return "%s: name - %s, description - %s, xref-path - %s" %(self.__class__.__name__, self.name, self.discription, self.path) - - - - -class Insert: - """Class for objects representing dxf inserts.""" - - def __init__(self, obj): - """Expects an entity object of type insert as input.""" - if not obj.type == 'insert': - raise TypeError, "Wrong type %s for insert object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.block = obj.get_type(2)[0] - - # optional data (with defaults) - self.rotation = obj.get_type(50) - if self.rotation: - self.rotation = self.rotation[0] - else: - self.rotation = 0 - - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data) - self.scale = self.get_scale(obj.data) - self.rows, self.columns = self.get_array(obj.data) - self.extrusion = self.get_extrusion(obj.data) - - - - - - def get_loc(self, data): - """Gets the center location for circle type objects. - - Circles have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_scale(self, data): - """Gets the x/y/z scale factor for the block. - """ - scale = [1, 1, 1] - for item in data: - if item[0] == 41: # 41 = x scale - scale[0] = item[1] - elif item[0] == 42: # 42 = y scale - scale[1] = item[1] - elif item[0] == 43: # 43 = z scale - scale[2] = item[1] - return scale - - - - def get_array(self, data): - """Returns the pair (row number, row spacing), (column number, column spacing).""" - columns = 1 - rows = 1 - cspace = 0 - rspace = 0 - for item in data: - if item[0] == 70: # 70 = columns - columns = item[1] - elif item[0] == 71: # 71 = rows - rows = item[1] - if item[0] == 44: # 44 = columns - cspace = item[1] - elif item[0] == 45: # 45 = rows - rspace = item[1] - return (rows, rspace), (columns, cspace) - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, block - %s" %(self.__class__.__name__, self.layer, self.block) - - - def draw(self, handle, settings): - """Do all the specific things needed to import blocks into Blender. - - Blocks are made of three objects: - the block_record in the tables section - the block in the blocks section - the insert object in the entities section - - block_records give the insert units, blocks provide the objects drawn in the - block, and the insert object gives the location/scale/rotation of the block - instances. To draw a block you must first get a group with all the - blocks entities drawn in it, then scale the entities to match the world - units, then dupligroup that data to an object matching each insert object.""" - - # Create an object - ob = Object.New('Mesh', self.block) - ob.link(handle) # Give the object a handle - - if settings.drawTypes['blocks']: - # get our block group - block = settings.blocks(self.block) - - ob.DupGroup = block - ob.enableDupGroup = True - - ob.loc = tuple(self.loc) - transform(self.extrusion, ob) - ob.RotZ += radians(self.rotation) - ob.size = tuple(self.scale) - - return ob - - - - -class Ellipse: - """Class for objects representing dxf ellipses.""" - - def __init__(self, obj): - """Expects an entity object of type ellipse as input.""" - if not obj.type == 'ellipse': - raise TypeError, "Wrong type %s for ellipse object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # required data - self.ratio = obj.get_type(40)[0] - self.start_angle = obj.get_type(41)[0] - self.end_angle = obj.get_type(42)[0] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.loc = self.get_loc(obj.data) - self.major = self.get_major(obj.data) - self.extrusion = self.get_extrusion(obj.data) - self.radius = sqrt(self.major[0]**2 + self.major[0]**2 + self.major[0]**2) - - - - - def get_loc(self, data): - """Gets the center location for arc type objects. - - Arcs have a single coord location. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 10: # 10 = x - loc[0] = item[1] - elif item[0] == 20: # 20 = y - loc[1] = item[1] - elif item[0] == 30: # 30 = z - loc[2] = item[1] - return loc - - - - def get_major(self, data): - """Gets the major axis for ellipse type objects. - - The ellipse major axis defines the rotation of the ellipse and its radius. - """ - loc = [0, 0, 0] - for item in data: - if item[0] == 11: # 11 = x - loc[0] = item[1] - elif item[0] == 21: # 21 = y - loc[1] = item[1] - elif item[0] == 31: # 31 = z - loc[2] = item[1] - return loc - - - - def get_extrusion(self, data): - """Find the axis of extrusion. - - Used to get the objects Object Coordinate System (ocs). - """ - vec = [0,0,1] - for item in data: - if item[0] == 210: # 210 = x - vec[0] = item[1] - elif item[0] == 220: # 220 = y - vec[1] = item[1] - elif item[0] == 230: # 230 = z - vec[2] = item[1] - return vec - - - def __repr__(self): - return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) - - - def draw(self, curves=False): - """Do all the specific things needed to import ellipses into Blender.""" - # Generate the geometery - # Now Create an object - if curves: - ob = drawCurveArc(self) - else: - major = Mathutils.Vector(self.major) - delta = Mathutils.AngleBetweenVecs(major, WORLDX) - center = self.loc - radius = major.length - start = degrees(self.start_angle) - end = degrees(self.end_angle) - verts, edges = drawArc(None, radius, start, end) - - e = Mesh.New('ellipse') - - e.verts.extend(verts) # add vertices to mesh - e.edges.extend(edges) # add edges to the mesh - - - ob = Object.New('Mesh', 'arc') - ob.link(e) # link curve data with this object - ob.loc = tuple(center) - ob.SizeY = self.ratio - #ob.RotZ = radians(delta) - ob.RotX = radians(180) - - - transform(self.extrusion, ob) - ob.RotZ = radians(delta) - - return ob - - - -class Face: - """Class for objects representing dxf 3d faces.""" - - def __init__(self, obj): - """Expects an entity object of type 3dfaceplot as input.""" - if not obj.type == '3dface': - raise TypeError, "Wrong type %s for 3dface object!" %obj.type - self.type = obj.type - self.data = obj.data[:] - - # optional data (with defaults) - self.space = obj.get_type(67) - if self.space: - self.space = self.space[0] - else: - self.space = 0 - - self.color_index = obj.get_type(62) - if self.color_index: - self.color_index = self.color_index[0] - else: - self.color_index = BYLAYER - - discard, self.layer = get_layer(obj.data) - obj.data.remove(discard) - self.points = self.get_points(obj.data) - - - - - def get_points(self, data): - """Gets 3-4 points for a 3d face type object. - - Faces have three or optionally four verts. - """ - - a = [0, 0, 0] - b = [0, 0, 0] - c = [0, 0, 0] - d = False - for item in data: - # ----------- a ------------- - if item[0] == 10: # 10 = x - a[0] = item[1] - elif item[0] == 20: # 20 = y - a[1] = item[1] - elif item[0] == 30: # 30 = z - a[2] = item[1] - # ----------- b ------------- - elif item[0] == 11: # 11 = x - b[0] = item[1] - elif item[0] == 21: # 21 = y - b[1] = item[1] - elif item[0] == 31: # 31 = z - b[2] = item[1] - # ----------- c ------------- - elif item[0] == 12: # 12 = x - c[0] = item[1] - elif item[0] == 22: # 22 = y - c[1] = item[1] - elif item[0] == 32: # 32 = z - c[2] = item[1] - # ----------- d ------------- - elif item[0] == 13: # 13 = x - d = [0, 0, 0] - d[0] = item[1] - elif item[0] == 23: # 23 = y - d[1] = item[1] - elif item[0] == 33: # 33 = z - d[2] = item[1] - out = [a,b,c] - if d: - out.append(d) - return out - - - def __repr__(self): - return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) - - - def draw(self, curves=False): - """Do all the specific things needed to import 3d faces into Blender.""" - # Generate the geometery - points = self.points - if len(self.points) > 3: - faces = [[0, 1, 2, 3]] - else: - faces = [[0, 1, 2]] - - me = Mesh.New('3dface') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.faces.extend(faces) # add faces to the mesh - - # Now Create an object - ob = Object.New('Mesh', '3dface') # link mesh to an object - ob.link(me) - - return ob - - - -# type to object map +class View: #----------------------------------------------------------------- + """Class for objects representing dxf VIEWs. + """ + def __init__(self, obj, active=None): + """Expects an object of type VIEW as input. + """ + if not obj.type == 'view': + raise TypeError, "Wrong type %s for VIEW object!" %obj.type + + self.type = obj.type + self.name = obj.get_type(2)[0] + self.data = obj.data[:] + + + self.centerX = getit(obj.data, 10, 0.0) #view center pointX (in DCS) + self.centerY = getit(obj.data, 20, 0.0) #view center pointY (in DCS) + self.height = obj.get_type(40)[0] #view height (in DCS) + self.width = obj.get_type(41)[0] #view width (in DCS) + + self.dir = [0,0,0] + self.dir[0] = getit(obj.data, 11, 0.0) #view directionX from target (in WCS) + self.dir[1] = getit(obj.data, 21, 0.0) # + self.dir[2] = getit(obj.data, 31, 0.0) # + + self.target = [0,0,0] + self.target[0] = getit(obj.data, 12, 0.0) #target pointX(in WCS) + self.target[1] = getit(obj.data, 22, 0.0) # + self.target[2] = getit(obj.data, 32, 0.0) # + + self.length = obj.get_type(42)[0] #Lens length + self.clip_front = getit(obj.data, 43) #Front clipping plane (offset from target point) + self.clip_back = getit(obj.data, 44) #Back clipping plane (offset from target point) + self.twist = obj.get_type(50)[0] #view twist angle in degrees + + self.flags = getit(obj, 70, 0) + self.paperspace = self.flags & 1 # + + self.mode = obj.get_type(71)[0] #view mode (VIEWMODE system variable) + + def __repr__(self): + return "%s: name - %s, focus length - %s" %(self.__class__.__name__, self.name, self.length) + + + def draw(self, settings): + """for VIEW: generate Blender_camera. + """ + obname = 'vw_%s' %self.name # create camera object name + #obname = 'ca_%s' %self.name # create camera object name + obname = obname[:MAX_NAMELENGTH] + + if self.target == [0,0,0] and Mathutils.Vector(self.dir).length == 1.0: + cam= Camera.New('ortho', obname) + ob= SCENE.objects.new(cam) + cam.type = 'ortho' + cam.scale = 1.0 # for ortho cameras + else: + cam= Camera.New('persp', obname) + ob= SCENE.objects.new(cam) + cam.type = 'persp' + cam.angle = 60.0 # for persp cameras + if self.length: + #cam.angle = 2 * atan(17.5/self.length) * 180/pi + cam.lens = self.length #for persp cameras + # hack to update Camera>Lens setting (inaccurate as a focal length) + #curLens = cam.lens; cam.lens = curLens + # AutoCAD gets clip distance from target: + dist = Mathutils.Vector(self.dir).length + cam.clipEnd = dist - self.clip_back + cam.clipStart = dist - self.clip_front + + cam.drawLimits = 1 + cam.drawSize = 10 + + v = Mathutils.Vector(self.dir) +# print 'deb:view cam:', cam #------------ +# print 'deb:view self.target:', self.target #------------ +# print 'deb:view self.dir:', self.dir #------------ +# print 'deb:view self.twist:', self.twist #------------ +# print 'deb:view self.clip_front=%s, self.clip_back=%s, dist=%s' %(self.clip_front, self.clip_back, dist) #------------ + transform(v.normalize(), -self.twist, ob) + ob.loc = Mathutils.Vector(self.target) + Mathutils.Vector(self.dir) + return ob + + +class Vport: #----------------------------------------------------------------- + """Class for objects representing dxf VPORTs. + """ + def __init__(self, obj, active=None): + """Expects an object of type VPORT as input. + """ + if not obj.type == 'vport': + raise TypeError, "Wrong type %s for VPORT object!" %obj.type + + self.type = obj.type + self.name = obj.get_type(2)[0] + self.data = obj.data[:] + #print 'deb:vport name, data:', self.name #------- + #print 'deb:vport data:', self.data #------- + + self.height = obj.get_type(40)[0] #vport height (in DCS) + self.centerX = getit(obj.data, 12, 0.0) #vport center pointX (in DCS) + self.centerY = getit(obj.data, 22, 0.0) #vport center pointY (in DCS) + self.width = self.height * obj.get_type(41)[0] #vport aspect ratio - width (in DCS) + + self.dir = [0,0,0] + self.dir[0] = getit(obj.data, 16, 0.0) #vport directionX from target (in WCS) + self.dir[1] = getit(obj.data, 26, 0.0) # + self.dir[2] = getit(obj.data, 36, 0.0) # + + self.target = [0,0,0] + self.target[0] = getit(obj.data, 17, 0.0) #target pointX(in WCS) + self.target[1] = getit(obj.data, 27, 0.0) # + self.target[2] = getit(obj.data, 37, 0.0) # + + self.length = obj.get_type(42)[0] #Lens length + self.clip_front = getit(obj.data, 43) #Front clipping plane (offset from target point) + self.clip_back = getit(obj.data, 44) #Back clipping plane (offset from target point) + self.twist = obj.get_type(51)[0] #view twist angle + + self.flags = getit(obj, 70, 0) + self.paperspace = self.flags & 1 # + + self.mode = obj.get_type(71)[0] #view mode (VIEWMODE system variable) + + def __repr__(self): + return "%s: name - %s, focus length - %s" %(self.__class__.__name__, self.name, self.length) + + def draw(self, settings): + """for VPORT: generate Blender_camera. + """ + obname = 'vp_%s' %self.name # create camera object name + #obname = 'ca_%s' %self.name # create camera object name + obname = obname[:MAX_NAMELENGTH] + + if self.target == [0,0,0] and Mathutils.Vector(self.dir).length == 1.0: + cam= Camera.New('ortho', obname) + ob= SCENE.objects.new(cam) + cam.type = 'ortho' + cam.scale = 1.0 # for ortho cameras + else: + cam= Camera.New('persp', obname) + ob= SCENE.objects.new(cam) + cam.type = 'persp' + cam.angle = 60.0 # for persp cameras + if self.length: + #cam.angle = 2 * atan(17.5/self.length) * 180/pi + cam.lens = self.length #for persp cameras + # hack to update Camera>Lens setting (inaccurate as a focal length) + #curLens = cam.lens; cam.lens = curLens + # AutoCAD gets clip distance from target: + dist = Mathutils.Vector(self.dir).length + cam.clipEnd = dist - self.clip_back + cam.clipStart = dist - self.clip_front + + cam.drawLimits = 1 + cam.drawSize = 10 + + v = Mathutils.Vector(self.dir) +# print 'deb:view cam:', cam #------------ +# print 'deb:view self.target:', self.target #------------ +# print 'deb:view self.dir:', self.dir #------------ +# print 'deb:view self.twist:', self.twist #------------ +# print 'deb:view self.clip_front=%s, self.clip_back=%s, dist=%s' %(self.clip_front, self.clip_back, dist) #------------ + transform(v.normalize(), -self.twist, ob) + ob.loc = Mathutils.Vector(self.target) + Mathutils.Vector(self.dir) + return ob + + + +class Layer: #----------------------------------------------------------------- + """Class for objects representing dxf LAYERs. + """ + def __init__(self, obj, name=None, color=None, frozen=None): + """Expects an object of type layer as input. + """ + self.type = obj.type + self.data = obj.data[:] + + if name: + self.name = name + #self.bfname = name #--todo---see layernamesmap in f_getLayersmap --- + else: + self.name = obj.get_type(2)[0] #layer name of object + + if color: + self.color = color + else: + self.color = obj.get_type(62)[0] #color of object + + if frozen: + self.frozen = frozen + else: + self.flags = obj.get_type(70)[0] + self.frozen = self.flags & 1 + + + def __repr__(self): + return "%s: name - %s, color - %s" %(self.__class__.__name__, self.name, self.color) + + + +def getit(obj, typ, default=None): #------------------------------------------ + """Universal procedure for geting data from list/objects. + """ + it = default + if type(obj) == list: #if obj is a list, then searching in a list + for item in obj: + #print 'deb:getit item, type(item)', item, type(item) + try: + if item[0] == typ: + it = item[1] + break #as soon as the first found + except: + # --todo-- I found one case where item was a text instance + # that failed with no __getitem__ + pass + else: #else searching in Object with get_type-Methode + item = obj.get_type(typ) + if item: + it = item[0] + #print 'deb:getit:typ, it', typ, it #---------- + return it + + + +def get_extrusion(data): #------------------------------------------------- + """Find the axis of extrusion. + + Used to get from object_data the objects Object_Coordinate_System (ocs). + """ + #print 'deb:get_extrusion: data: \n', data #--------------- + vec = [0,0,1] + vec[0] = getit(data, 210, 0) # 210 = x + vec[1] = getit(data, 220, 0) # 220 = y + vec[2] = getit(data, 230, 1) # 230 = z + #print 'deb:get_extrusion: vec: ', vec #--------------- + return vec + + + +class Solid: #----------------------------------------------------------------- + """Class for objects representing dxf SOLID or TRACE. + """ + def __init__(self, obj): + """Expects an entity object of type solid or trace as input. + """ + if obj.type == 'trace': + obj.type = 'solid' + if not obj.type == 'solid': + raise TypeError, "Wrong type \'%s\' for solid/trace object!" %obj.type + + self.type = obj.type + self.data = obj.data[:] + + self.space = getit(obj, 67, 0) + self.thic = getit(obj, 39, 0) + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.extrusion = get_extrusion(obj.data) + self.points = self.get_points(obj.data) + + + + def get_points(self, data): + """Gets start and end points for a solid type object. + + Solids have 3 or 4 points and fixed codes for each value. + """ + + # start x, y, z and end x, y, z = 0 + a = [0, 0, 0] + b = [0, 0, 0] + c = [0, 0, 0] + d = [0, 0, 0] + a[0] = getit(data, 10, None) # 10 = x + a[1] = getit(data, 20, None) # 20 = y + a[2] = getit(data, 30, 0) # 30 = z + b[0] = getit(data, 11, None) + b[1] = getit(data, 21, None) + b[2] = getit(data, 31, 0) + c[0] = getit(data, 12, None) + c[1] = getit(data, 22, None) + c[2] = getit(data, 32, 0) + out = [a,b,c] + + d[0] = getit(data, 13, None) + if d[0] != None: + d[1] = getit(data, 23, None) + d[2] = getit(data, 33, 0) + out.append(d) + #print 'deb:solid.vertices:---------\n', out #----------------------- + return out + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, settings): + """for SOLID: generate Blender_geometry. + """ + points = self.points + if not points: return + edges, faces = [], [] + l = len(self.points) + + obname = 'so_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + + vg_left, vg_right, vg_top, vg_bottom, vg_start, vg_end = [], [], [], [], [], [] + thic = set_thick(self.thic, settings) + if thic != 0: + thic_points = [[v[0], v[1], v[2] + thic] for v in points[:]] + if thic < 0.0: + thic_points.extend(points) + points = thic_points + else: + points.extend(thic_points) + + if l == 4: + faces = [[0,1,3,2], [4,6,7,5], [0,4,5,1], + [1,5,7,3], [3,7,6,2], [2,6,4,0]] + vg_left = [2,6,4,0] + vg_right = [1,5,7,3] + vg_top = [4,6,7,5] + vg_bottom = [0,1,3,2] + vg_start = [0,4,5,1] + vg_end = [3,7,6,2] + elif l == 3: + faces = [[0,1,2], [3,5,4], [0,3,4,1], [1,4,5,2], [2,5,3,0]] + vg_top = [3,4,5] + vg_bottom = [0,1,2] + vg_left = [2,5,3,0] + vg_right = [1,4,5,2] + vg_start = [0,3,4,1] + elif l == 2: faces = [[0,1,3,2]] + else: + if l == 4: faces = [[0,1,3,2]] + elif l == 3: faces = [[0,1,2]] + elif l == 2: edges = [[0,1]] + + + + me = Mesh.New(obname) # create a new mesh + me.verts.extend(points) # add vertices to mesh + if faces: me.faces.extend(faces) # add faces to the mesh + if edges: me.edges.extend(edges) # add faces to the mesh + + ob = SCENE.objects.new(me) # create a new mesh_object + if settings.var['vGroup_on']: + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + replace = Blender.Mesh.AssignModes.ADD #or .AssignModes.ADD/REPLACE + if vg_left: me.addVertGroup('side.left') ; me.assignVertsToGroup('side.left', vg_left, 1.0, replace) + if vg_right:me.addVertGroup('side.right') ; me.assignVertsToGroup('side.right', vg_right, 1.0, replace) + if vg_top: me.addVertGroup('side.top') ; me.assignVertsToGroup('side.top', vg_top, 1.0, replace) + if vg_bottom:me.addVertGroup('side.bottom'); me.assignVertsToGroup('side.bottom',vg_bottom, 1.0, replace) + if vg_start:me.addVertGroup('side.start') ; me.assignVertsToGroup('side.start', vg_start, 1.0, replace) + if vg_end: me.addVertGroup('side.end') ; me.assignVertsToGroup('side.end', vg_end, 1.0, replace) + + transform(self.extrusion, 0, ob) + + return ob + + + + +class Line: #----------------------------------------------------------------- + """Class for objects representing dxf LINEs. + """ + def __init__(self, obj): + """Expects an entity object of type line as input. + """ + if not obj.type == 'line': + raise TypeError, "Wrong type \'%s\' for line object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + self.space = getit(obj, 67, 0) + self.thic = getit(obj, 39, 0) + #print 'deb:self.thic: ', self.thic #--------------------- + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.extrusion = get_extrusion(obj.data) + self.points = self.get_points(obj.data) + + + def get_points(self, data): + """Gets start and end points for a line type object. + + Lines have a fixed number of points (two) and fixed codes for each value. + """ + # start x,y,z and end x,y,z = 0 + a = [0, 0, 0] + b = [0, 0, 0] + a[0] = getit(data, 10, None) # 10 = x + a[1] = getit(data, 20, None) # 20 = y + a[2] = getit(data, 30, 0) # 30 = z + b[0] = getit(data, 11, None) + b[1] = getit(data, 21, None) + b[2] = getit(data, 31, 0) + out = [a,b] + return out + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, settings): + """for LINE: generate Blender_geometry. + """ + # Generate the geometery + #settings.var['curves_on']=False + + points = self.points + thic = set_thick(self.thic, settings) + width = 0.0 + if settings.var['lines_as'] == 4: # as thin_box + thic = settings.var['thick_min'] + width = settings.var['width_min'] + elif settings.var['lines_as'] == 3: # as thin cylinder + cyl_rad = 0.5 * settings.var['width_min'] + + if settings.var['lines_as'] == 5: # LINE curve representation------------------------- + obname = 'li_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + + c = Curve.New(obname) # create new curve data + curve = c.appendNurb(BezTriple.New(points[0])) + curve.append(BezTriple.New(points[1])) + for point in curve: + point.handleTypes = [VECT, VECT] + curve.flagU = 0 # 0 sets the curve not cyclic=open + c.setResolu(settings.var['curve_res']) + c.update() #important for handles calculation + + ob = SCENE.objects.new(c) # create a new curve_object + + #if False: # --todo-- better support for 210-group + if thic != 0.0: #hack: Blender2.45 curve-extrusion + t = thic * 0.5 + if abs(t) > 5.0: t = 5.0 * cmp(t,0) # Blender2.45 accepts only (0.0 - 5.0) + e = self.extrusion + c.setExt1(abs(t)) # curve-extrusion + ob.LocX += t * e[0] + ob.LocY += t * e[1] + ob.LocZ += t * e[2] + #c.setExt1(1.0) # curve-extrusion: Blender2.45 accepts only (0.0 - 5.0) + #ob.LocZ = t + self.loc[2] + #ob.SizeZ *= abs(t) + return ob + + else: # LINE mesh representation ------------------------------ + global activObjectLayer + global activObjectName + #print 'deb:draw:line.ob IN activObjectName: ', activObjectName #--------------------- + + if activObjectLayer == self.layer and settings.var['one_mesh_on']: + obname = activObjectName + #print 'deb:line.draw obname from activObjectName: ', obname #--------------------- + ob = Object.Get(obname) # open an existing mesh_object + #ob = SCENE.getChildren(obname) # open an existing mesh_object + me = Mesh.Get(ob.name) # open objects mesh data + else: + obname = 'li_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + activObjectName = ob.name + activObjectLayer = self.layer + #print ('deb:line.draw new line.ob+mesh:"%s" created!' %ob.name) #--------------------- + + faces, edges = [], [] + n = len(me.verts) + + #if settings.var['width_force']: #--todo----------- + + if thic != 0: + t, e = thic, self.extrusion + #print 'deb:thic, extr: ', t, e #--------------------- + points.extend([[v[0]+t*e[0], v[1]+t*e[1], v[2]+t*e[2]] for v in points[:]]) + faces = [[0+n, 1+n, 3+n, 2+n]] + else: + edges = [[0+n, 1+n]] + + me.verts.extend(points) # adds vertices to global mesh + if faces: me.faces.extend(faces) # add faces to the mesh + if edges: me.edges.extend(edges) # add faces to the mesh + + if settings.var['vGroup_on']: + # entities with the same color build one vertexGroup for easier material assignment ---- + ob.link(me) # link mesh to that object + vG_name = 'color_%s' %self.color_index + if edges: faces = edges + replace = Blender.Mesh.AssignModes.ADD #or .AssignModes.REPLACE or ADD + try: + me.assignVertsToGroup(vG_name, faces[0], 1.0, replace) + #print 'deb: existed vGroup:', vG_name #--------------------- + except: + me.addVertGroup(vG_name) + me.assignVertsToGroup(vG_name, faces[0], 1.0, replace) + #print 'deb: create new vGroup:', vG_name #--------------------- + + + #print 'deb:draw:line.ob OUT activObjectName: ', activObjectName #--------------------- + return ob + + + +class Point: #----------------------------------------------------------------- + """Class for objects representing dxf POINTs. + """ + def __init__(self, obj): + """Expects an entity object of type point as input. + """ + if not obj.type == 'point': + raise TypeError, "Wrong type %s for point object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + self.space = getit(obj, 67, 0) + self.thic = getit(obj, 39, 0) + #print 'deb:self.thic: ', self.thic #--------------------- + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.extrusion = get_extrusion(obj.data) + self.points = self.get_points(obj.data) + + + def get_points(self, data): + """Gets coordinates for a point type object. + + Points have fixed codes for each value. + """ + a = [0, 0, 0] + a[0] = getit(data, 10, None) # 10 = x + a[1] = getit(data, 20, None) # 20 = y + a[2] = getit(data, 30, 0) # 30 = z + out = [a] + return out + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, settings): + """for POINT: generate Blender_geometry. + """ + points = self.points + obname = 'po_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + points_as = settings.var['points_as'] + thic = settings.var['thick_min'] + if thic < settings.var['dist_min']: thic = settings.var['dist_min'] + + if True: #--todo-- points_as in [1,3,4,5]: + if True: # points_as in [1,5]: # as 'empty' + c = 'Empty' + elif points_as == 3: # as 'thin sphere' + res = settings.var['thin_res'] + c = Mesh.Primitives.UVsphere(res,res,thic) + elif points_as == 4: # as 'thin box' + c = Mesh.Primitives.Cube(thic) + ob = SCENE.objects.new(c, obname) # create a new object + transform(self.extrusion, 0, ob) + ob.loc = tuple(points[0]) + + elif points_as == 2: # as 'vertex' + global activObjectLayer + global activObjectName + #print 'deb:draw:point.ob IN activObjectName: ', activObjectName #--------------------- + if activObjectLayer == self.layer and settings.var['one_mesh_on']: + obname = activObjectName + #print 'deb:draw:point.ob obname from activObjectName: ', obname #--------------------- + ob = Object.Get(obname) # open an existing mesh_object + #ob = SCENE.getChildren(obname) # open an existing mesh_object + me = Mesh.Get(ob.name) # open objects mesh data + else: + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + activObjectName = ob.name + activObjectLayer = self.layer + #print ('deb:draw:point new point.ob+mesh:"%s" created!' %ob.name) #--------------------- + me.verts.extend(points) # add vertices to mesh + + return ob + + + +class LWpolyline: #----------------------------------------------------------------- + """Class for objects representing dxf LWPOLYLINEs. + """ + def __init__(self, obj): + """Expects an entity object of type lwpolyline as input. + """ + #print 'deb:LWpolyline.START:----------------' #------------------------ + if not obj.type == 'lwpolyline': + raise TypeError, "Wrong type %s for polyline object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.num_points = obj.get_type(90)[0] + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + + self.color_index = getit(obj, 62, BYLAYER) + + self.elevation = getit(obj, 30, 0) + self.thic = getit(obj, 39, 0) + self.flags = getit(obj, 70, 0) + + self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen + + self.layer = getit(obj.data, 8, None) + self.points = self.get_points(obj.data) + self.extrusion = get_extrusion(obj.data) + + #print 'deb:LWpolyline.obj.data:\n', obj.data #------------------------ + #print 'deb:LWpolyline.ENDinit:----------------' #------------------------ + + + def get_points(self, data): + """Gets points for a polyline type object. + + LW-Polylines have no fixed number of verts, and + each vert can have a number of properties. + Verts should be coded as + 10:xvalue + 20:yvalue + 40:startwidth or 0 + 41:endwidth or 0 + 42:bulge or 0 + for each vert + """ + num = self.num_points + point = None + points = [] + for item in data: + if item[0] == 10: # 10 = x + if point: + points.append(point) + point = Vertex() + point.x = item[1] + elif item[0] == 20: # 20 = y + point.y = item[1] + elif item[0] == 40: # 40 = start width + point.swidth = item[1] + elif item[0] == 41: # 41 = end width + point.ewidth = item[1] + elif item[0] == 42: # 42 = bulge + point.bulge = item[1] + points.append(point) + return points + + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, settings): + """for LWPOLYLINE: generate Blender_geometry. + """ + #print 'deb:LWpolyline.draw.START:----------------' #------------------------ + points = [] + obname = 'lw_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + #settings.var['curves_on'] == True + #print 'deb:index_len: ', len(self.points) #------------------ + for i, point in enumerate(self.points): + #print 'deb:index: ', i #------------------ + if not point.bulge: + points.append(point.loc) + elif point.bulge and not self.closed and i == len(self.points)-1: + points.append(point.loc) + elif point.bulge: # + if i == len(self.points)-1: + point2 = self.points[0] + else: + point2 = self.points[i+1] + arc_res = settings.var['arc_res']/sqrt(settings.var['arc_rad']) + verts, center = calcBulge(point, point2, arc_res) +# if i == len(self.points)-1: +# if self.closed: +# verts.pop() #remove last(=first) vertex +# else: +# verts.pop() #remove last vertex, because this point will be writen as the next vertex + points.extend(verts) + + thic = self.thic + if settings.var['thick_force'] and thic == 0: thic = settings.var['thick_min'] + if settings.var['thick_on'] and thic != 0: + len1 = len(points) + points.extend([[point[0], point[1], point[2]+thic] for point in points]) + faces = [] + #print 'deb:len1:', len1 #----------------------- + faces = [[num, num+1, num+len1+1, num+len1] for num in xrange(len1 - 1)] + if self.closed: + faces.append([len1-1, 0, len1, 2*len1-1]) + #print 'deb:faces_list:\n', faces #----------------------- + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(points) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + else: + edges = [[num, num+1] for num in xrange(len(points)-1)] + if self.closed: + edges.append([len(points)-1, 0]) + #print 'deb:edges_list:\n', edges #----------------------- + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(points) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + ob.LocZ = self.elevation + transform(self.extrusion, 0, ob) + + #print 'deb:LWpolyline.draw.END:----------------' #------------------------ + return ob + + + +class Polyline: #----------------------------------------------------------------- + """Class for objects representing dxf POLYLINEs. + """ + def __init__(self, obj): + """Expects an entity object of type polyline as input. + """ + #print 'deb:polyline.init.START:----------------' #------------------------ + if not obj.type == 'polyline': + raise TypeError, "Wrong type %s for polyline object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + #print 'deb:polyline.obj.data[:]:\n', obj.data[:] #------------------------ + self.points = [] + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.elevation = getit(obj, 30, 0) + #print 'deb:elevation: ', self.elevation #--------------- + self.thic = getit(obj, 39, 0) + self.color_index = getit(obj, 62, BYLAYER) + + self.flags = getit(obj, 70, 0) + self.closed = self.flags & 1 # closed in the M direction + self.curved = self.flags & 2 # Bezier-curve-fit vertices have been added + self.spline = self.flags & 4 # Bspline-fit vertices have been added + self.poly3d = self.flags & 8 # 3D-polyline + self.plmesh = self.flags & 16 # 3D-polygon mesh + self.closeN = self.flags & 32 # closed in the N direction + self.plface = self.flags & 64 # 3D-polyface mesh + self.contin = self.flags & 128 # the linetype pattern is generated continuously + + if self.poly3d or self.plface or self.plmesh: + self.poly2d = False # its not a 2D-polyline + else: + self.poly2d = True # it is a 2D-polyline + + self.swidth = getit(obj, 40, 0) # default start width + self.ewidth = getit(obj, 41, 0) # default end width + #self.bulge = getit(obj, 42, None) # bulge of the segment + self.vectorsM = getit(obj, 71, None) # PolyMesh: expansion in M-direction / PolyFace: number of the vertices + self.vectorsN = getit(obj, 72, None) # PolyMesh: expansion in M-direction / PolyFace: number of faces + #self.resolM = getit(obj, 73, None) # resolution of surface in M direction + #self.resolN = getit(obj, 74, None) # resolution of surface in N direction + self.curvetyp = getit(obj, 75, 0) # type of curve/surface: 0=None/5=Quadric/6=Cubic/8=Bezier + self.curvNormal = False + self.curvQBspline = False + self.curvCBspline = False + self.curvBezier = False + if self.curvetyp == 0: self.curvNormal = True + elif self.curvetyp == 5: self.curvQBspline = True + elif self.curvetyp == 6: self.curvCBspline = True + elif self.curvetyp == 8: self.curvBezier = True + + self.layer = getit(obj.data, 8, None) + self.extrusion = get_extrusion(obj.data) + + self.points = [] #list with vertices coordinats + self.faces = [] #list with vertices assigment to faces + #print 'deb:polyline.init.ENDinit:----------------' #------------ + + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + + def draw(self, settings): #-------------%%%% DRAW POLYLINE %%%--------------- + """for POLYLINE: generate Blender_geometry. + """ + ob = [] + #---- 3dPolyFace - mesh with free topology + if self.plface and settings.drawTypes['plmesh']: + ob = self.drawPlFace(settings) + #---- 3dPolyMesh - mesh with ortogonal topology + elif self.plmesh and settings.drawTypes['plmesh']: + ob = self.drawPlMesh(settings) + #---- 2dPolyline - plane polyline with arc/wide/thic segments + elif (self.poly2d and settings.drawTypes['polyline']): + if settings.var['plines_as'] == 5: # and self.spline: + ob = self.drawPolyCurve(settings) + else: + ob = self.drawPoly2d(settings) + #---- 3dPolyline - non-plane polyline (thin segments = without arc/wide/thic) + elif (self.poly3d and settings.drawTypes['pline3']): + if settings.var['plines3_as'] == 5: # and self.spline: + ob = self.drawPolyCurve(settings) + else: + ob = self.drawPoly2d(settings) + return ob + + + + def drawPlFace(self, settings): #---- 3dPolyFace - mesh with free topology + """Generate the geometery of polyface. + """ + #print 'deb:drawPlFace.START:----------------' #------------------------ + points = [] + faces = [] + #print 'deb:len of pointsList ====== ', len(self.points) #------------------------ + for point in self.points: + if point.face: + faces.append(point.face) + else: + points.append(point.loc) + + + #print 'deb:drawPlFace: len of points_list:\n', len(points) #----------------------- + #print 'deb:drawPlFace: len of faces_list:\n', len(faces) #----------------------- + #print 'deb:drawPlFace: points_list:\n', points #----------------------- + #print 'deb:drawPlFace: faces_list:\n', faces #----------------------- + obname = 'pf_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(points) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + #print 'deb:drawPlFace: len of me.faces:\n', len(me.faces) #----------------------- + + if settings.var['meshSmooth_on']: # ---------------------- + for i in xrange(len(me.faces)): + me.faces[i].smooth = True + #me.Mode(AUTOSMOOTH) + transform(self.extrusion, 0, ob) + #print 'deb:drawPlFace.END:----------------' #------------------------ + return ob + + + + def drawPlMesh(self, settings): #---- 3dPolyMesh - mesh with orthogonal topology + """Generate the geometery of polymesh. + """ + #print 'deb:polymesh.draw.START:----------------' #------------------------ + #points = [] + #print 'deb:len of pointsList ====== ', len(self.points) #------------------------ + faces = [] + m = self.vectorsM + n = self.vectorsN + for j in xrange(m - 1): + for i in xrange(n - 1): + nn = j * n + faces.append([nn+i, nn+i+1, nn+n+i+1, nn+n+i]) + + if self.closed: #mesh closed in N-direction + nn = (m-1)*n + for i in xrange(n - 1): + faces.append([nn+i, nn+i+1, i+1, i]) + + if self.closeN: #mesh closed in M-direction + for j in xrange(m-1): + nn = j * n + faces.append([nn+n-1, nn, nn+n, nn+n-1+n]) + + if self.closed and self.closeN: #mesh closed in M/N-direction + faces.append([ (n*m)-1, (m-1)*n, 0, n-1]) + + #print 'deb:len of points_list:\n', len(points) #----------------------- + #print 'deb:faces_list:\n', faces #----------------------- + obname = 'pm_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend([point.loc for point in self.points]) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + if settings.var['meshSmooth_on']: # ---------------------- + for i in xrange(len(faces)): + me.faces[i].smooth = True + #me.Mode(AUTOSMOOTH) + + transform(self.extrusion, 0, ob) + #print 'deb:polymesh.draw.END:----------------' #------------------------ + return ob + + + def drawPolyCurve(self, settings): #---- Polyline - draw as Blender-curve + """Generate the geometery of polyline as Blender-curve. + """ + #print 'deb:polyline2dCurve.draw.START:----------------' #--- + if len(self.points) < 2: + #print 'deb:drawPoly2d exit, cause POLYLINE has less than 2 vertices' #--------- + return + + if self.spline: pline_typ = 'ps' # Polyline-nurbSpline + elif self.curved: pline_typ = 'pc' # Polyline-bezierCurve + else: pline_typ = 'pl' # Polyline + obname = '%s_%s' %(pline_typ, self.layer) # create object_name from layer name + obname = obname[:MAX_NAMELENGTH] + d_points = [] + + if settings.var['Z_force_on']: + self.elevation = settings.var['Z_elev'] + for point in self.points: + point.loc[2] = self.elevation + d_points.append(point) + else: #for DXFr10-format: update all points[].loc[2] == None -> 0.0 + for point in self.points: + if point.loc[2] == None: + point.loc[2] = self.elevation + d_points.append(point) + + thic = set_thick(self.thic, settings) + if thic != 0.0: #hack: Blender<2.45 curve-extrusion + LocZ = d_points[0].loc[2] + temp_points = [] + for point in d_points: + point.loc[2] = 0.0 + temp_points.append(point) + d_points = temp_points + + #print 'deb:polyline2dCurve.draw d_points=', d_points #--------------- + pline = Curve.New(obname) # create new curve data + #pline.setResolu(24) #--todo----- + + if False: #self.spline: # NURBSplines-----FAKE(with Bezier)----- + #print 'deb:polyline2dCurve.draw self.spline!' #--------------- + curve = pline.appendNurb(BezTriple.New(d_points[0])) + for p in d_points[1:]: + curve.append(BezTriple.New(p)) + for point in curve: + point.handleTypes = [AUTO, AUTO] + if self.closed: + curve.flagU = 1 # Set curve cyclic=close + else: + curve.flagU = 0 # Set curve not cyclic=open + curve[0].handleTypes = [FREE, ALIGN] #remi--todo----- + curve[-1].handleTypes = [ALIGN, FREE] #remi--todo----- + + elif self.spline: # NURBSplines-----OK----- + #print 'deb:polyline2dCurve.draw self.spline!' #--------------- + weight1 = 0.5 + weight2 = 1.0 + # generate middlepoints except start/end-segments --- + if self.curvQBspline: + temp_points = [] + point = d_points[0].loc + point.append(weight1) + temp_points.append(point) + for i in xrange(1,len(d_points)-2): + point1 = d_points[i].loc + point2 = d_points[i+1].loc + mpoint = list((Mathutils.Vector(point1) + Mathutils.Vector(point2)) * 0.5) + mpoint.append(weight2) + point1.append(weight1) + temp_points.append(point1) + temp_points.append(mpoint) + point2.append(weight1) + temp_points.append(point2) + point = d_points[-1].loc + point.append(weight1) + temp_points.append(point) + d_points = temp_points + else: + temp_points = [] + for d in d_points: + d = d.loc + d.append(weight1) + temp_points.append(d) + d_points = temp_points + + if not self.closed: + # generate extended startpoint and endpoint------ + point1 = Mathutils.Vector(d_points[0][:3]) + point2 = Mathutils.Vector(d_points[1][:3]) + startpoint = list(point1 - point2 + point1) + startpoint.append(weight1) + point1 = Mathutils.Vector(d_points[-1][:3]) + point2 = Mathutils.Vector(d_points[-2][:3]) + endpoint = list(point1 - point2 + point1) + endpoint.append(weight1) + temp_points = [] + temp_points.append(startpoint) + temp_points.extend(d_points) + d_points = temp_points + d_points.append(endpoint) + + point = d_points[0] + curve = pline.appendNurb(point) + curve.setType(4) #NURBS curve + for point in d_points[1:]: + curve.append(point) + if self.closed: + curve.flagU = 1 # Set curve cyclic=close + else: + curve.flagU = 0 # Set curve not cyclic=open + + elif self.curved: #--Bezier-curves---OK------- + #print 'deb:polyline2dCurve.draw self.curved!' #--------------- + curve = pline.appendNurb(BezTriple.New(d_points[0])) + for p in d_points[1:]: + curve.append(BezTriple.New(p)) + for point in curve: + point.handleTypes = [AUTO, AUTO] + if self.closed: + curve.flagU = 1 # Set curve cyclic=close + else: + curve.flagU = 0 # Set curve not cyclic=open + curve[0].handleTypes = [FREE, ALIGN] #remi--todo----- + curve[-1].handleTypes = [ALIGN, FREE] #remi--todo----- + + else: #--straight line- and arc-segments----OK------ + #print 'deb:polyline2dCurve.draw curve:', curve #----- + points = [] + arc_res = settings.var['curve_arc'] + prevHandleType = VECT + #d_points.append(d_points[0]) #------ first vertex added at the end of list -------- + #curve.setType(0) #polygon_type of Blender_curve + for i in xrange(len(d_points)): + point1 = d_points[i] + #point2 = d_points[i+1] + if False: #-----outdated!- standard calculation ---------------------------------- + if point1.bulge and (i < len(d_points)-2 or self.closed): + verts, center = calcBulge(point1, point2, arc_res, triples=False) + if i == 0: curve = pline.appendNurb(BezTriple.New(verts[0])) + else: curve.append(BezTriple.New(verts[0])) + curve[-1].handleTypes = [VECT, VECT] #--todo--calculation of bezier-tangents + for p in verts[1:]: + curve.append(BezTriple.New(p)) + curve[-1].handleTypes = [AUTO, AUTO] + else: + if i == 0: curve = pline.appendNurb(BezTriple.New(point1.loc)) + else: curve.append(BezTriple.New(point1.loc)) + curve[-1].handleTypes = [VECT, VECT] #--todo--calculation of bezier-tangents + + elif True: #----- optimised Bezier-Handles calculation -------------------------------- + #print 'deb:drawPlineCurve: i:', i #--------- + if point1.bulge and not (i == len(d_points)-1 and point1.bulge and not self.closed): + if i == len(d_points)-1: point2 = d_points[0] + else: point2 = d_points[i+1] + + + # calculate additional points for bulge + VectorTriples = calcBulge(point1, point2, arc_res, triples=True) + + if prevHandleType == FREE: + #print 'deb:drawPlineCurve: VectorTriples[0]:', VectorTriples[0] #--------- + VectorTriples[0][:3] = prevHandleVect + #print 'deb:drawPlineCurve: VectorTriples[0]:', VectorTriples[0] #--------- + + if i == 0: curve = pline.appendNurb(BezTriple.New(VectorTriples[0])) + else: curve.append(BezTriple.New(VectorTriples[0])) + curve[-1].handleTypes = [prevHandleType, FREE] + + for p in VectorTriples[1:-1]: + curve.append(BezTriple.New(p)) + curve[-1].handleTypes = [FREE, FREE] + + prevHandleVect = VectorTriples[-1][:3] + prevHandleType = FREE + #print 'deb:drawPlineCurve: prevHandleVect:', prevHandleVect #--------- + else: + #print 'deb:drawPlineCurve: else' #---------- + if prevHandleType == FREE: + VectorTriples = prevHandleVect + list(point1) + list(point1) + #print 'deb:drawPlineCurve: VectorTriples:', VectorTriples #--------- + curve.append(BezTriple.New(VectorTriples)) + curve[-1].handleTypes = [FREE, VECT] + prevHandleType = VECT + else: + if i == 0: curve = pline.appendNurb(BezTriple.New(point1.loc)) + else: curve.append(BezTriple.New(point1.loc)) + curve[-1].handleTypes = [VECT, VECT] + + + + #print 'deb:drawPlineCurve: curve[-1].vec[0]', curve[-1].vec[0] #---------- + + if self.closed: + curve.flagU = 1 # Set curve cyclic=close + if prevHandleType == FREE: + #print 'deb:drawPlineCurve:closed curve[0].vec:', curve[0].vec #---------- + #print 'deb:drawPlineCurve:closed curve[0].handleTypes:', curve[0].handleTypes #---------- + prevHandleType2 = curve[0].handleTypes[1] + p0h1,p0,p0h2 = curve[0].vec + #print 'deb:drawPlineCurve:closed p0h1:', p0h1 #---------- + p0h1 = prevHandleVect + #p0h1 = [0,0,0] + #print 'deb:drawPlineCurve:closed p0h1:', p0h1 #---------- + #curve[0].vec = [p0h1,p0,p0h2] + curve.__setitem__(0,BezTriple.New(p0h1+p0+p0h2)) + + curve[0].handleTypes = [FREE,prevHandleType2] + #print 'deb:drawPlineCurve:closed curve[0].vec:', curve[0].vec #---------- + #print 'deb:drawPlineCurve:closed curve[0].handleTypes:', curve[0].handleTypes #---------- + else: + curve[0].handleTypes[0] = VECT + else: + curve.flagU = 0 # Set curve not cyclic=open + + if settings.var['fill_on']: + pline.setFlag(6) # 2+4 set top and button caps + else: + pline.setFlag(pline.getFlag() & ~6) # dont set top and button caps + + pline.setResolu(settings.var['curve_res']) + pline.update() + ob = SCENE.objects.new(pline) # create a new curve_object + + if thic != 0.0: #hack: Blender<2.45 curve-extrusion + thic = thic * 0.5 + pline.setExt1(1.0) # curve-extrusion accepts only (0.0 - 2.0) + ob.LocZ = thic + LocZ + + transform(self.extrusion, 0, ob) + if thic != 0.0: + ob.SizeZ *= abs(thic) + + #print 'deb:polyline2dCurve.draw.END:----------------' #----- + return ob + + + def drawPoly2d(self, settings): #---- 2dPolyline - plane lines/arcs with wide/thic + """Generate the geometery of regular polyline. + """ + #print 'deb:polyline2d.draw.START:----------------' #------------------------ + points = [] + d_points = [] + swidths = [] + ewidths = [] + swidth_default = self.swidth #default start width of POLYLINEs segments + ewidth_default = self.ewidth #default end width of POLYLINEs segments + thic = set_thick(self.thic, settings) + if self.spline: pline_typ = 'ps' + elif self.curved: pline_typ = 'pc' + else: pline_typ = 'pl' + obname = '%s_%s' %(pline_typ, self.layer) # create object_name from layer name + obname = obname[:MAX_NAMELENGTH] + + if len(self.points) < 2: + #print 'deb:drawPoly2d exit, cause POLYLINE has less than 2 vertices' #--------- + return + + if settings.var['Z_force_on']: + self.elevation = settings.var['Z_elev'] + for point in self.points: + point.loc[2] = self.elevation + d_points.append(point) + else: #for DXFr10-format: update all non-existing LocZ points[].loc[2] == None -> 0.0 elevation + for point in self.points: + if point.loc[2] == None: + point.loc[2] = self.elevation + d_points.append(point) + #print 'deb:drawPoly2d len of d_pointsList ====== ', len(d_points) #------------------------ + #print 'deb:drawPoly2d d_pointsList ======:\n ', d_points #------------------------ + + + #if closed polyline, add duplic of the first vertex at the end of pointslist + if self.closed: #new_b8 + if d_points[-1].loc != d_points[0].loc: # if not equal, then set the first at the end of pointslist + d_points.append(d_points[0]) + else: + if d_points[-1].loc == d_points[0].loc: # if equal, then set to closed, and modify the last point + d_points[-1] = d_points[0] + self.closed = True + #print 'deb:drawPoly2d len of d_pointsList ====== ', len(d_points) #------------------------ + #print 'deb:drawPoly2d d_pointsList ======:\n ', d_points #------------------------ + + + # routine to sort out of "double.vertices" ------------------------------------ + minimal_dist = settings.var['dist_min'] * 0.1 + temp_points = [] + for i in xrange(len(d_points)-1): + point = d_points[i] + point2 = d_points[i+1] + #print 'deb:double.vertex p1,p2', point, point2 #------------------------ + delta = Mathutils.Vector(point2.loc) - Mathutils.Vector(point.loc) + if delta.length > minimal_dist: + temp_points.append(point) + #else: print 'deb:drawPoly2d double.vertex sort out!' #------------------------ + temp_points.append(d_points[-1]) #------ incl. last vertex ------------- + #if self.closed: temp_points.append(d_points[1]) #------ loop start vertex ------------- + d_points = temp_points #-----vertex.list without "double.vertices" + #print 'deb:drawPoly2d d_pointsList =after DV-outsorting=====:\n ', d_points #------------------------ + + #print 'deb:drawPoly2d len of d_pointsList ====== ', len(d_points) #------------------------ + if len(d_points) < 2: #if too few vertex, then return + #print 'deb:drawPoly2d corrupted Vertices' #--------- + return + + # analyze of straight- and bulge-segments + # generation of additional points for bulge segments + arc_res = settings.var['arc_res']/sqrt(settings.var['arc_rad']) + wide_segment_exist = False + bulg_points = [] # for each point set None (or center for arc-subPoints) + for i in xrange(len(d_points)-1): + point1 = d_points[i] + point2 = d_points[i+1] + #print 'deb:drawPoly2d_bulg tocalc.point1:', point1 #------------------------ + #print 'deb:drawPoly2d_bulg tocalc.point2:', point2 #------------------------ + + swidth = point1.swidth + ewidth = point1.ewidth + if swidth == None: swidth = swidth_default + if ewidth == None: ewidth = ewidth_default + if swidth != 0.0 or ewidth != 0.0: wide_segment_exist = True + + if settings.var['width_force']: # force minimal width for thin segments + width_min = settings.var['width_min'] + if swidth < width_min: swidth = width_min + if ewidth < width_min: ewidth = width_min + if not settings.var['width_on']: # then force minimal width for all segments + swidth = width_min + ewidth = width_min + + #if point1.bulge and (i < (len(d_points)-1) or self.closed): + if point1.bulge and i < (len(d_points)-1): #10_b8 + verts, center = calcBulge(point1, point2, arc_res) #calculate additional points for bulge + points.extend(verts) + delta_width = (ewidth - swidth) / len(verts) + width_list = [swidth + (delta_width * ii) for ii in xrange(len(verts)+1)] + swidths.extend(width_list[:-1]) + ewidths.extend(width_list[1:]) + bulg_list = [center for ii in xrange(len(verts))] + #the last point in bulge has index False for better indexing of bulg_end! + bulg_list[-1] = None + bulg_points.extend(bulg_list) + + else: + points.append(point1.loc) + swidths.append(swidth) + ewidths.append(ewidth) + bulg_points.append(None) + points.append(d_points[-1].loc) + + + #--calculate width_vectors: left-side- and right-side-points ---------------- + # 1.level:IF width --------------------------------------- + if (settings.var['width_on'] and wide_segment_exist) or settings.var['width_force']: + #new_b8 points.append(d_points[0].loc) #temporarly add first vertex at the end (for better loop) + dist_min05 = 0.5 * settings.var['dist_min'] #minimal width for zero_witdh + + pointsLs = [] # list of left-start-points + pointsLe = [] # list of left-end-points + pointsRs = [] # list of right-start-points + pointsRe = [] # list of right-end-points + pointsW = [] # list of all border-points + #rotMatr90 = Mathutils.Matrix(rotate 90 degree around Z-axis) = normalvectorXY + rotMatr90 = Mathutils.Matrix([0, -1, 0], [1, 0, 0], [0, 0, 1]) + bulg_in = False + last_bulg_point = False + for i in xrange(len(points)-1): + point1 = points[i] + point2 = points[i+1] + point1vec = Mathutils.Vector(point1) + point2vec = Mathutils.Vector(point2) + swidth05 = swidths[i] * 0.5 + ewidth05 = ewidths[i] * 0.5 + if swidth05 == 0: swidth05 = dist_min05 + if ewidth05 == 0: ewidth05 = dist_min05 + normal_vector = rotMatr90 * (point2vec-point1vec).normalize() + if last_bulg_point: + last_bulg_point = False + bulg_in = True + elif bulg_points[i] != None: + centerVec = Mathutils.Vector(bulg_points[i]) + if bulg_points[i+1] == None: last_bulg_point = True + bulg_in = True + else: bulg_in = False + + if bulg_in: + #makes clean intersections for arc-segments + radius1vec = point1vec - centerVec + radius2vec = point2vec - centerVec + angle = Mathutils.AngleBetweenVecs(normal_vector, radius1vec) + if angle < 90.0: + normal_vector1 = radius1vec.normalize() + normal_vector2 = radius2vec.normalize() + else: + normal_vector1 = - radius1vec.normalize() + normal_vector2 = - radius2vec.normalize() + + swidth05vec = swidth05 * normal_vector1 + ewidth05vec = ewidth05 * normal_vector2 + pointsLs.append(point1vec + swidth05vec) #vertex left start + pointsRs.append(point1vec - swidth05vec) #vertex right start + pointsLe.append(point2vec + ewidth05vec) #vertex left end + pointsRe.append(point2vec - ewidth05vec) #vertex right end + + else: + swidth05vec = swidth05 * normal_vector + ewidth05vec = ewidth05 * normal_vector + pointsLs.append(point1vec + swidth05vec) #vertex left start + pointsRs.append(point1vec - swidth05vec) #vertex right start + pointsLe.append(point2vec + ewidth05vec) #vertex left end + pointsRe.append(point2vec - ewidth05vec) #vertex right end + + # additional last point is also calculated + #pointsLs.append(pointsLs[0]) + #pointsRs.append(pointsRs[0]) + #pointsLe.append(pointsLe[0]) + #pointsRe.append(pointsRe[0]) + + pointsLc, pointsRc = [], [] # lists Left/Right corners = intersection points + + # 2.level:IF width and corner-trim + if settings.var['pl_trim_on']: #optional clean corner-intersections + # loop preset + # set STARTpoints of the first point points[0] + if not self.closed: + pointsLc.append(pointsLs[0]) + pointsRc.append(pointsRs[0]) + else: + pointsLs.append(pointsLs[0]) + pointsRs.append(pointsRs[0]) + pointsLe.append(pointsLe[0]) + pointsRe.append(pointsRe[0]) + points.append(points[0]) + vecL3, vecL4 = pointsLs[0], pointsLe[0] + vecR3, vecR4 = pointsRs[0], pointsRe[0] + lenL = len(pointsLs)-1 + #print 'deb:drawPoly2d pointsLs():\n', pointsLs #---------------- + #print 'deb:drawPoly2d lenL, len.pointsLs():', lenL,',', len(pointsLs) #---------------- + bulg_in = False + last_bulg_point = False + + # LOOP: makes (ENDpoints[i],STARTpoints[i+1]) + for i in xrange(lenL): + if bulg_points[i] != None: + if bulg_points[i+1] == None: #makes clean intersections for arc-segments + last_bulg_point = True + if not bulg_in: + bulg_in = True + #pointsLc.extend((points[i], pointsLs[i])) + #pointsRc.extend((points[i], pointsRs[i])) + vecL1, vecL2 = vecL3, vecL4 + vecR1, vecR2 = vecR3, vecR4 + vecL3, vecL4 = pointsLs[i+1], pointsLe[i+1] + vecR3, vecR4 = pointsRs[i+1], pointsRe[i+1] + #compute left- and right-cornerpoints + #cornerpointL = Geometry.LineIntersect2D(vec1, vec2, vec3, vec4) + cornerpointL = Mathutils.LineIntersect(vecL1, vecL2, vecL3, vecL4) + cornerpointR = Mathutils.LineIntersect(vecR1, vecR2, vecR3, vecR4) + #print 'deb:drawPoly2d cornerpointL: ', cornerpointL #------------- + #print 'deb:drawPoly2d cornerpointR: ', cornerpointR #------------- + + # IF not cornerpoint THEN check if identic start-endpoints (=collinear segments) + if cornerpointL == None or cornerpointR == None: + if vecL2 == vecL3 and vecR2 == vecR3: + #print 'deb:drawPoly2d pointVec: ####### identic ##########' #---------------- + pointsLc.append(pointsLe[i]) + pointsRc.append(pointsRe[i]) + else: + pointsLc.extend((pointsLe[i],points[i+1],pointsLs[i+1])) + pointsRc.extend((pointsRe[i],points[i+1],pointsRs[i+1])) + else: + cornerpointL = cornerpointL[0] # because Mathutils.LineIntersect() -> (pkt1,pkt2) + cornerpointR = cornerpointR[0] + #print 'deb:drawPoly2d cornerpointL: ', cornerpointL #------------- + #print 'deb:drawPoly2d cornerpointR: ', cornerpointR #------------- + pointVec0 = Mathutils.Vector(points[i]) + pointVec = Mathutils.Vector(points[i+1]) + pointVec2 = Mathutils.Vector(points[i+2]) + #print 'deb:drawPoly2d pointVec0: ', pointVec0 #------------- + #print 'deb:drawPoly2d pointVec: ', pointVec #------------- + #print 'deb:drawPoly2d pointVec2: ', pointVec2 #------------- + # if diststance(cornerL-center-cornerR) < limiter * (seg1_endWidth + seg2_startWidth) + max_cornerDist = (vecL2 - vecR2).length + (vecL3 - vecR3).length + is_cornerDist = (cornerpointL - pointVec).length + (cornerpointR - pointVec).length + #corner_angle = Mathutils.AngleBetweenVecs((pointVec0 - pointVec),(pointVec - pointVec2)) + #print 'deb:drawPoly2d corner_angle: ', corner_angle #------------- + #print 'deb:drawPoly2d max_cornerDist, is_cornerDist: ', max_cornerDist, is_cornerDist #------------- + #if abs(corner_angle) < 90.0: + # intersection --------- limited by TRIM_LIMIT (1.0 - 5.0) + if is_cornerDist < max_cornerDist * settings.var['pl_trim_max']: + # clean corner intersection + pointsLc.append(cornerpointL) + pointsRc.append(cornerpointR) + elif False: # the standard no-intersection + # --todo-- not optimal, because produces X-face + pointsLc.extend((pointsLe[i],pointsLs[i+1])) + pointsRc.extend((pointsRe[i],pointsRs[i+1])) + elif False: # --todo-- the optimised non-intersection + if (cornerpointL - vecL1).length < (cornerpointR - vecR1).length: + left_angle = True + else: + left_angle = False + limit_dist = settings.var['dist_min'] + if left_angle: # if left turning angle + print 'deb:drawPoly2d it is left turning angle' #------------- + # to avoid triangelface/doubleVertex + delta1 = (cornerpointL - vecL1).normalize() * limit_dist + delta4 = (cornerpointL - vecL4).normalize() * limit_dist + pointsLc.extend((cornerpointL - delta1, cornerpointL - delta4)) + pointsRc.extend((pointsRe[i],pointsRs[i+1])) + else: # if right turning angle + print 'deb:drawPoly2d right turning angle' #------------- + delta1 = (cornerpointR - vecR1).normalize() * limit_dist + delta4 = (cornerpointR - vecR4).normalize() * limit_dist + pointsRc.extend((cornerpointR - delta1, cornerpointR - delta4)) + pointsLc.extend((pointsLe[i],pointsLs[i+1])) + else: + pointsLc.extend((pointsLe[i],points[i+1],pointsLs[i+1])) + pointsRc.extend((pointsRe[i],points[i+1],pointsRs[i+1])) + if not self.closed: + pointsLc.append(pointsLe[-1]) + pointsRc.append(pointsRe[-1]) + + # 2.level:IF width but no-trim + else: + # loop preset + # set STARTpoints of the first point points[0] + if not self.closed: + pointsLc.append(pointsLs[0]) + pointsRc.append(pointsRs[0]) + else: + pointsLs.append(pointsLs[0]) + pointsRs.append(pointsRs[0]) + pointsLe.append(pointsLe[0]) + pointsRe.append(pointsRe[0]) + points.append(points[0]) + vecL3, vecL4 = pointsLs[0], pointsLe[0] + vecR3, vecR4 = pointsRs[0], pointsRe[0] + lenL = len(pointsLs)-1 + #print 'deb:drawPoly2d pointsLs():\n', pointsLs #---------------- + #print 'deb:drawPoly2d lenL, len.pointsLs():', lenL,',', len(pointsLs) #---------------- + bulg_in = False + last_bulg_point = False + + # LOOP: makes (ENDpoints[i],STARTpoints[i+1]) + for i in xrange(lenL): + vecL1, vecL2 = vecL3, vecL4 + vecR1, vecR2 = vecR3, vecR4 + vecL3, vecL4 = pointsLs[i+1], pointsLe[i+1] + vecR3, vecR4 = pointsRs[i+1], pointsRe[i+1] + if bulg_points[i] != None: + #compute left- and right-cornerpoints + if True: + cornerpointL = Mathutils.LineIntersect(vecL1, vecL2, vecL3, vecL4) + cornerpointR = Mathutils.LineIntersect(vecR1, vecR2, vecR3, vecR4) + pointsLc.append(cornerpointL[0]) + pointsRc.append(cornerpointR[0]) + else: + pointVec = Mathutils.Vector(point[i]) + + else: # IF non-bulg + pointsLc.extend((pointsLe[i],points[i+1],pointsLs[i+1])) + pointsRc.extend((pointsRe[i],points[i+1],pointsRs[i+1])) + if not self.closed: + pointsLc.append(pointsLe[-1]) + pointsRc.append(pointsRe[-1]) + + len1 = len(pointsLc) + #print 'deb:drawPoly2d len1:', len1 #----------------------- + #print 'deb:drawPoly2d len1 len(pointsLc),len(pointsRc):', len(pointsLc),len(pointsRc) #----------------------- + pointsW = pointsLc + pointsRc # all_points_List = left_side + right_side + #print 'deb:drawPoly2d pointsW():\n', pointsW #---------------- + + # 2.level:IF width and thickness --------------------- + if thic != 0: + thic_pointsW = [] + thic_pointsW.extend([[point[0], point[1], point[2]+thic] for point in pointsW]) + if thic < 0.0: + thic_pointsW.extend(pointsW) + pointsW = thic_pointsW + else: + pointsW.extend(thic_pointsW) + faces = [] + f_start, f_end = [], [] + f_bottom = [[num, num+1, len1+num+1, len1+num] for num in xrange(len1-1)] + f_top = [[num, len1+num, len1+num+1, num+1] for num in xrange(len1+len1, len1+len1+len1-1)] + f_left = [[num, len1+len1+num, len1+len1+num+1, num+1] for num in xrange(len1-1)] + f_right = [[num, num+1, len1+len1+num+1, len1+len1+num] for num in xrange(len1, len1+len1-1)] + + if self.closed: + f_bottom.append([len1-1, 0, len1, len1+len1-1]) #bottom face + f_top.append( [len1+len1+len1-1, len1+len1+len1+len1-1, len1+len1+len1, len1+len1+0]) #top face + f_left.append( [0, len1-1, len1+len1+len1-1, len1+len1]) #left face + f_right.append( [len1, len1+len1+len1, len1+len1+len1+len1-1, len1+len1-1]) #right face + else: + f_start = [[0, len1, len1+len1+len1, len1+len1]] + f_end = [[len1+len1-1, 0+len1-1, len1+len1+len1-1, len1+len1+len1+len1-1]] + + faces = f_left + f_right + f_bottom + f_top + f_start + f_end + #faces = f_bottom + f_top + #faces = f_left + f_right + f_start + f_end + #print 'deb:faces_list:\n', faces #----------------------- + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(pointsW) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + # The mesh must first be linked to an object so the method knows which object to update. + # This is because vertex groups in Blender are stored in the object -- not in the mesh, + # which may be linked to more than one object. + if settings.var['vGroup_on']: + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + replace = Blender.Mesh.AssignModes.REPLACE #or .AssignModes.ADD + vg_left, vg_right, vg_top, vg_bottom = [], [], [], [] + for v in f_left: vg_left.extend(v) + for v in f_right: vg_right.extend(v) + for v in f_top: vg_top.extend(v) + for v in f_bottom: vg_bottom.extend(v) + me.addVertGroup('side.left') ; me.assignVertsToGroup('side.left', list(set(vg_left)), 1.0, replace) + me.addVertGroup('side.right') ; me.assignVertsToGroup('side.right', list(set(vg_right)), 1.0, replace) + me.addVertGroup('side.top') ; me.assignVertsToGroup('side.top', list(set(vg_top)), 1.0, replace) + me.addVertGroup('side.bottom'); me.assignVertsToGroup('side.bottom',list(set(vg_bottom)), 1.0, replace) + if not self.closed: + me.addVertGroup('side.start'); me.assignVertsToGroup('side.start', f_start[0], 1.0, replace) + me.addVertGroup('side.end') ; me.assignVertsToGroup('side.end', f_end[0], 1.0, replace) + + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + #if self.spline or self.curved: + if True: + smooth_len = len(f_left) + len(f_right) + for i in xrange(smooth_len): + me.faces[i].smooth = True + #me.Modes(AUTOSMOOTH) + + # 2.level:IF width, but no-thickness --------------------- + else: + faces = [] + faces = [[num, len1+num, len1+num+1, num+1] for num in xrange(len1 - 1)] + if self.closed: + faces.append([len1, 0, len1-1, len1+len1-1]) + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(pointsW) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + + # 1.level:IF no-width, but thickness --------------------- + elif thic != 0: + len1 = len(points) + thic_points = [] + thic_points.extend([[point[0], point[1], point[2]+thic] for point in points]) + if thic < 0.0: + thic_points.extend(points) + points = thic_points + else: + points.extend(thic_points) + faces = [] + faces = [[num, num+1, num+len1+1, num+len1] for num in xrange(len1 - 1)] + if self.closed: + faces.append([len1-1, 0, len1, 2*len1-1]) + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(points) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + #if self.spline or self.curved: + if True: + for i in xrange(len(faces)): + me.faces[i].smooth = True + #me.Modes(AUTOSMOOTH) + + # 1.level:IF no-width and no-thickness --------------------- + else: + edges = [[num, num+1] for num in xrange(len(points)-1)] + if self.closed: + edges.append([len(points)-1, 0]) + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + me.verts.extend(points) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + transform(self.extrusion, 0, ob) + #print 'deb:polyline.draw.END:----------------' #----------------------- + return ob + + + + +class Vertex(object): #----------------------------------------------------------------- + """Generic vertex object used by POLYLINEs (and maybe others). + """ + + def __init__(self, obj=None): + """Initializes vertex data. + + The optional obj arg is an entity object of type vertex. + """ + #print 'deb:Vertex.init.START:----------------' #----------------------- + self.loc = [0,0,0] + self.face = [] + self.swidth = 0 + self.ewidth = 0 + self.bulge = 0 + if obj is not None: + if not obj.type == 'vertex': + raise TypeError, "Wrong type %s for vertex object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + self.get_props(obj.data) + #print 'deb:Vertex.init.END:----------------' #------------------------ + + + def get_props(self, data): + """Gets coords for a VERTEX type object. + + Each vert can have a number of properties. + Verts should be coded as + 10:xvalue + 20:yvalue + 40:startwidth or 0 + 41:endwidth or 0 + 42:bulge or 0 + """ + self.x = getit(data, 10, None) + self.y = getit(data, 20, None) + self.z = getit(data, 30, None) + + self.flags = getit(data, 70, 0) # flags + self.curved = self.flags&1 # Bezier-curve-fit:additional-vertex + self.curv_t = self.flags&2 # Bezier-curve-fit:tangent exists + self.spline = self.flags&8 # Bspline-fit:additional-vertex + self.splin2 = self.flags&16 # Bspline-fit:control-vertex + self.poly3d = self.flags&32 # polyline3d:control-vertex + self.plmesh = self.flags&64 # polymesh3d:control-vertex + self.plface = self.flags&128 # polyface + + # if PolyFace.Vertex with Face_definition + if self.curv_t: + self.curv_tangent = getit(data, 50, None) # curve_tangent + + if self.plface and not self.plmesh: + v1 = getit(data, 71, 0) # polyface:Face.vertex 1. + v2 = getit(data, 72, 0) # polyface:Face.vertex 2. + v3 = getit(data, 73, 0) # polyface:Face.vertex 3. + v4 = getit(data, 74, None) # polyface:Face.vertex 4. + self.face = [abs(v1)-1,abs(v2)-1,abs(v3)-1] + if v4 != None: + if abs(v4) != abs(v1): + self.face.append(abs(v4)-1) + else: #--parameter for polyline2d + self.swidth = getit(data, 40, None) # start width + self.ewidth = getit(data, 41, None) # end width + self.bulge = getit(data, 42, 0) # bulge of segment + + + def __len__(self): + return 3 + + + def __getitem__(self, key): + return self.loc[key] + + + def __setitem__(self, key, value): + if key in [0,1,2]: + self.loc[key] + + + def __iter__(self): + return self.loc.__iter__() + + + def __str__(self): + return str(self.loc) + + + def __repr__(self): + return "Vertex %s, swidth=%s, ewidth=%s, bulge=%s, face=%s" %(self.loc, self.swidth, self.ewidth, self.bulge, self.face) + + + def getx(self): + return self.loc[0] + def setx(self, value): + self.loc[0] = value + x = property(getx, setx) + + + def gety(self): + return self.loc[1] + def sety(self, value): + self.loc[1] = value + y = property(gety, sety) + + + def getz(self): + return self.loc[2] + def setz(self, value): + self.loc[2] = value + z = property(getz, setz) + + + +class Text: #----------------------------------------------------------------- + """Class for objects representing dxf TEXT. + """ + def __init__(self, obj): + """Expects an entity object of type text as input. + """ + if not obj.type == 'text': + raise TypeError, "Wrong type %s for text object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.height = 1.7 * obj.get_type(40)[0] #text.height + self.value = obj.get_type(1)[0] #The text string value + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.color_index = getit(obj, 62, BYLAYER) + self.thic = getit(obj, 39, 0) + + self.rotation = getit(obj, 50, 0) # radians + self.width_factor = getit(obj, 41, 1) # Scaling factor along local x axis + self.oblique = getit(obj, 51, 0) # oblique angle: skew in degrees -90 <= oblique <= 90 + + #self.style = getit(obj, 7, 'STANDARD') # --todo---- Text style name (optional, default = STANDARD) + + #Text generation flags (optional, default = 0): + #2 = backward (mirrored in X), + #4 = upside down (mirrored in Y) + self.flags = getit(obj, 71, 0) + self.mirrorX, self.mirrorY = 1.0, 1.0 + if self.flags&2: self.mirrorX = - 1.0 + if self.flags&4: self.mirrorY = - 1.0 + + # vertical.alignment: 0=baseline, 1=bottom, 2=middle, 3=top + self.valignment = getit(obj, 73, 0) + #Horizontal text justification type (optional, default = 0) integer codes (not bit-coded) + #0=left, 1=center, 2=right + #3=aligned, 4=middle, 5=fit + self.halignment = getit(obj, 72, 0) + + self.layer = getit(obj.data, 8, None) + self.loc1, self.loc2 = self.get_loc(obj.data) + if self.loc2[0] != None and self.halignment != 5: + self.loc = self.loc2 + else: + self.loc = self.loc1 + self.extrusion = get_extrusion(obj.data) + + + + def get_loc(self, data): + """Gets adjusted location for text type objects. + + If group 72 and/or 73 values are nonzero then the first alignment point values + are ignored and AutoCAD calculates new values based on the second alignment + point and the length and height of the text string itself (after applying the + text style). If the 72 and 73 values are zero or missing, then the second + alignment point is meaningless. + I don't know how to calc text size... + """ + # bottom left x, y, z and justification x, y, z = 0 + #x, y, z, jx, jy, jz = 0, 0, 0, 0, 0, 0 + x = getit(data, 10, None) #First alignment point (in OCS). + y = getit(data, 20, None) + z = getit(data, 30, 0.0) + jx = getit(data, 11, None) #Second alignment point (in OCS). + jy = getit(data, 21, None) + jz = getit(data, 31, 0.0) + return [x, y, z],[jx, jy, jz] + + + def __repr__(self): + return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) + + + def draw(self, settings): + """for TEXTs: generate Blender_geometry. + """ + obname = 'tx_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + txt = Text3d.New(obname) + ob = SCENE.objects.new(txt) # create a new text_object + + txt.setText(self.value) + txt.setSize(1.0) #Blender<2.45 accepts only (0.0 - 5.0) + #txt.setSize(self.height) + #txt.setWidth(self.bold) + #setLineSeparation(sep) + txt.setShear(self.oblique/90) + + thic = set_thick(self.thic, settings) + if thic != 0.0: + thic = self.thic * 0.5 + self.loc[2] += thic + txt.setExtrudeDepth(1.0) #Blender<2.45 accepts only (0.1 - 10.0) + if self.halignment == 0: + align = Text3d.LEFT + elif self.halignment == 1: + align = Text3d.MIDDLE + elif self.halignment == 2: + align = Text3d.RIGHT + else: + align = Text3d.LEFT + txt.setAlignment(align) + + if self.valignment == 1: + txt.setYoffset(0.0) + elif self.valignment == 2: + txt.setYoffset(- self.height * 0.5) + elif self.valignment == 3: + txt.setYoffset(- self.height) + + # move the object center to the text location + ob.loc = tuple(self.loc) + transform(self.extrusion, self.rotation, ob) + + # flip it and scale it to the text width + ob.SizeX *= self.height * self.width_factor * self.mirrorX + ob.SizeY *= self.height * self.mirrorY + if thic != 0.0: ob.SizeZ *= abs(thic) + return ob + + + +def set_thick(thickness, settings): + """Set thickness relative to settings variables. + + Set thickness relative to settings variables: + 'thick_on','thick_force','thick_min'. + Accepted also minus values of thickness + python trick: sign(x)=cmp(x,0) + """ + if settings.var['thick_force']: + if settings.var['thick_on']: + if abs(thickness) < settings.var['thick_min']: + thic = settings.var['thick_min'] * cmp(thickness,0) + else: thic = thickness + else: thic = settings.var['thick_min'] + else: + if settings.var['thick_on']: thic = thickness + else: thic = 0.0 + return thic + + + + +class Mtext: #----------------------------------------------------------------- + """Class for objects representing dxf MTEXT. + """ + + def __init__(self, obj): + """Expects an entity object of type mtext as input. + """ + if not obj.type == 'mtext': + raise TypeError, "Wrong type %s for mtext object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.height = obj.get_type(40)[0] + self.width = obj.get_type(41)[0] + self.alignment = obj.get_type(71)[0] # alignment 1=TL, 2=TC, 3=TR, 4=ML, 5=MC, 6=MR, 7=BL, 8=BC, 9=BR + self.value = self.get_text(obj.data) # The text string value + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.color_index = getit(obj, 62, BYLAYER) + self.rotation = getit(obj, 50, 0) # radians + + self.width_factor = getit(obj, 42, 1) # Scaling factor along local x axis + self.line_space = getit(obj, 44, 1) # percentage of default + + self.layer = getit(obj.data, 8, None) + self.loc = self.get_loc(obj.data) + self.extrusion = get_extrusion(obj.data) + + + def get_text(self, data): + """Reconstructs mtext data from dxf codes. + """ + primary = '' + secondary = [] + for item in data: + if item[0] == 1: # There should be only one primary... + primary = item[1] + elif item[0] == 3: # There may be any number of extra strings (in order) + secondary.append(item[1]) + if not primary: + #raise ValueError, "Empty Mtext Object!" + string = "Empty Mtext Object!" + if not secondary: + string = primary.replace(r'\P', '\n') + else: + string = ''.join(secondary)+primary + string = string.replace(r'\P', '\n') + return string + + + def get_loc(self, data): + """Gets location for a mtext type objects. + + Mtext objects have only one point indicating + """ + loc = [0, 0, 0] + loc[0] = getit(data, 10, None) + loc[1] = getit(data, 20, None) + loc[2] = getit(data, 30, 0.0) + return loc + + + def __repr__(self): + return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) + + + def draw(self, settings): + """for MTEXTs: generate Blender_geometry. + """ + # Now Create an object + obname = 'tm_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + txt = Text3d.New(obname) + ob = SCENE.objects.new(txt) # create a new text_object + + txt.setSize(1) + # Blender doesn't give access to its text object width currently + # only to the text3d's curve width... + #txt.setWidth(text.width/10) + txt.setLineSeparation(self.line_space) + txt.setExtrudeDepth(0.5) + txt.setText(self.value) + + # scale it to the text size + ob.SizeX = self.height * self.width_factor + ob.SizeY = self.height + ob.SizeZ = self.height + + # move the object center to the text location + ob.loc = tuple(self.loc) + transform(self.extrusion, self.rotation, ob) + + return ob + + + + +class Circle: #----------------------------------------------------------------- + """Class for objects representing dxf CIRCLEs. + """ + + def __init__(self, obj): + """Expects an entity object of type circle as input. + """ + if not obj.type == 'circle': + raise TypeError, "Wrong type %s for circle object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.radius = obj.get_type(40)[0] + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.thic = getit(obj, 39, 0) + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.loc = self.get_loc(obj.data) + self.extrusion = get_extrusion(obj.data) + + + + def get_loc(self, data): + """Gets the center location for circle type objects. + + Circles have a single coord location. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 10, None) + loc[1] = getit(data, 20, None) + loc[2] = getit(data, 30, 0.0) + return loc + + + + def __repr__(self): + return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) + + + def draw(self, settings): + """for CIRCLE: generate Blender_geometry. + """ + obname = 'ci_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + radius = self.radius + + thic = set_thick(self.thic, settings) + width = 0.0 + if settings.var['lines_as'] == 4: # as thin_box + thic = settings.var['thick_min'] + width = settings.var['width_min'] + if settings.var['lines_as'] == 3: # as thin cylinder + cyl_rad = 0.5 * settings.var['width_min'] + + if settings.var['lines_as'] == 5: # draw CIRCLE as curve ------------- + if True: # universal version + arc_res = settings.var['curve_arc'] + #arc_res = 3 + start, end = 0.0, 360.0 + VectorTriples = calcArc(None, radius, start, end, arc_res, True) + c = Curve.New(obname) # create new curve data + curve = c.appendNurb(BezTriple.New(VectorTriples[0])) + for p in VectorTriples[1:-1]: + curve.append(BezTriple.New(p)) + for point in curve: + point.handleTypes = [FREE, FREE] + else: # standard version + c = Curve.New(obname) # create new curve data + p1 = (0, -radius, 0) + p2 = (radius, 0, 0) + p3 = (0, radius, 0) + p4 = (-radius, 0, 0) + + p1 = BezTriple.New(p1) + p2 = BezTriple.New(p2) + p3 = BezTriple.New(p3) + p4 = BezTriple.New(p4) + + curve = c.appendNurb(p1) + curve.append(p2) + curve.append(p3) + curve.append(p4) + for point in curve: + point.handleTypes = [AUTO, AUTO] + + curve.flagU = 1 # 1 sets the curve cyclic=closed + if settings.var['fill_on']: + c.setFlag(6) # 2+4 set top and button caps + else: + c.setFlag(c.getFlag() & ~6) # dont set top and button caps + + c.setResolu(settings.var['curve_res']) + c.update() + + #--todo-----to check--------------------------- + ob = SCENE.objects.new(c) # create a new curve_object + ob.loc = tuple(self.loc) + if thic != 0.0: #hack: Blender<2.45 curve-extrusion + thic = thic * 0.5 + c.setExt1(1.0) # curve-extrusion accepts only (0.0 - 2.0) + ob.LocZ = thic + self.loc[2] + transform(self.extrusion, 0, ob) + if thic != 0.0: + ob.SizeZ *= abs(thic) + return ob + + elif False: # create a new mesh_object with buildin_circle_primitive + verts_num = settings.var['arc_res'] * sqrt(radius / settings.var['arc_rad']) + if verts_num > 100: verts_num = 100 # Blender accepts only values [3:500] + if verts_num < 4: verts_num = 4 # Blender accepts only values [3:500] + if thic != 0: + loc2 = thic * 0.5 #-----blenderAPI draw Cylinder with 2*thickness + self.loc[2] += loc2 #---new location for the basis of cylinder + #print 'deb:circleDraw:self.loc2:', self.loc #----------------------- + c = Mesh.Primitives.Cylinder(int(verts_num), radius*2, abs(thic)) + else: + c = Mesh.Primitives.Circle(int(verts_num), radius*2) + + #c.update() + ob = SCENE.objects.new(c, obname) # create a new circle_mesh_object + ob.loc = tuple(self.loc) + transform(self.extrusion, 0, ob) + return ob + + else: # draw CIRCLE as mesh ----------------------------------------------- + cir = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(cir) # create a new circle_object + # set a number of segments in entire circle + arc_res = settings.var['arc_res'] * sqrt(radius) / sqrt(settings.var['arc_rad']) + start, end = 0.0 , 360.0 + verts = calcArc(None, radius, start, end, arc_res, False) + verts = verts[:-1] #list without last point/edge (cause by circle it is equal to the first point) + #print 'deb:circleDraw: verts:', verts #--------------- + + if thic != 0: + len1 = len(verts) + thic_verts = [] + thic_verts.extend([[point[0], point[1], point[2]+thic] for point in verts]) + if thic < 0.0: + thic_verts.extend(verts) + verts = thic_verts + else: + verts.extend(thic_verts) + faces = [] + f_band = [[num, num+1, num+len1+1, num+len1] for num in xrange(len1 - 1)] + #f_band = [[num, num+1, num+len1+1, num+len1] for num in xrange(len1)] + f_band.append([len1 - 1, 0, len1, len1 + len1 -1]) + faces = f_band + smooth_len = len(f_band) + if settings.var['fill_on']: + if thic < 0.0: + verts.append([0,0,thic]) #center of top side + verts.append([0,0,0]) #center of bottom side + else: + verts.append([0,0,0]) #center of bottom side + verts.append([0,0,thic]) #center of top side + center1 = len(verts)-2 + center2 = len(verts)-1 + f_bottom = [[num+1, num, center1] for num in xrange(len1 - 1)] + f_bottom.append([0, len1 - 1, center1]) + f_top = [[num+len1, num+1+len1, center2] for num in xrange(len1 - 1)] + f_top.append([len1-1+len1, 0+len1, center2]) + #print 'deb:circleDraw:verts:', verts #--------------- + faces = f_band + f_bottom + f_top + #print 'deb:circleDraw:faces:', faces #--------------- + cir.verts.extend(verts) # add vertices to mesh + cir.faces.extend(faces) # add faces to the mesh + + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + for i in xrange(smooth_len): + cir.faces[i].smooth = True + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + if settings.var['vGroup_on']: + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + replace = Blender.Mesh.AssignModes.REPLACE #or .AssignModes.ADD + vg_band, vg_top, vg_bottom = [], [], [] + for v in f_band: vg_band.extend(v) + cir.addVertGroup('side.band') ; cir.assignVertsToGroup('side.band', list(set(vg_band)), 1.0, replace) + if settings.var['fill_on']: + for v in f_top: vg_top.extend(v) + for v in f_bottom: vg_bottom.extend(v) + cir.addVertGroup('side.top') ; cir.assignVertsToGroup('side.top', list(set(vg_top)), 1.0, replace) + cir.addVertGroup('side.bottom'); cir.assignVertsToGroup('side.bottom',list(set(vg_bottom)), 1.0, replace) + + else: # if thic == 0 + if settings.var['fill_on']: + len1 = len(verts) + verts.append([0,0,0]) #center of circle + center1 = len1 + faces = [] + faces.extend([[num, num+1, center1] for num in xrange(len1)]) + faces.append([len1-1, 0, center1]) + #print 'deb:circleDraw:verts:', verts #--------------- + #print 'deb:circleDraw:faces:', faces #--------------- + cir.verts.extend(verts) # add vertices to mesh + cir.faces.extend(faces) # add faces to the mesh + else: + cir.verts.extend(verts) # add vertices to mesh + edges = [[num, num+1] for num in xrange(len(verts))] + edges[-1][1] = 0 # it points the "new" last edge to the first vertex + cir.edges.extend(edges) # add edges to the mesh + + ob.loc = tuple(self.loc) + transform(self.extrusion, 0, ob) + return ob + + + + +class Arc: #----------------------------------------------------------------- + """Class for objects representing dxf ARCs. + """ + + def __init__(self, obj): + """Expects an entity object of type arc as input. + """ + if not obj.type == 'arc': + raise TypeError, "Wrong type %s for arc object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.radius = obj.get_type(40)[0] + self.start_angle = obj.get_type(50)[0] + self.end_angle = obj.get_type(51)[0] + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.thic = getit(obj, 39, 0) + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.loc = self.get_loc(obj.data) + self.extrusion = get_extrusion(obj.data) + #print 'deb:Arc__init__: center, radius, start, end:\n', self.loc, self.radius, self.start_angle, self.end_angle #--------- + + + + def get_loc(self, data): + """Gets the center location for arc type objects. + + Arcs have a single coord location. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 10, None) + loc[1] = getit(data, 20, None) + loc[2] = getit(data, 30, 0.0) + return loc + + + + def __repr__(self): + return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) + + + def draw(self, settings): + """for ARC: generate Blender_geometry. + """ + obname = 'ar_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + + center = self.loc + radius = self.radius + start = self.start_angle + end = self.end_angle + #print 'deb:calcArcPoints:\n center, radius, start, end:\n', center, radius, start, end #--------- + thic = set_thick(self.thic, settings) + width = 0.0 + if settings.var['lines_as'] == 4: # as thin_box + thic = settings.var['thick_min'] + width = settings.var['width_min'] + if settings.var['lines_as'] == 3: # as thin cylinder + cyl_rad = 0.5 * settings.var['width_min'] + + if settings.var['lines_as'] == 5: # draw ARC as curve ------------- + arc_res = settings.var['curve_arc'] + triples = True + VectorTriples = calcArc(None, radius, start, end, arc_res, triples) + arc = Curve.New(obname) # create new curve data + curve = arc.appendNurb(BezTriple.New(VectorTriples[0])) + for p in VectorTriples[1:]: + curve.append(BezTriple.New(p)) + for point in curve: + point.handleTypes = [FREE, FREE] + curve.flagU = 0 # 0 sets the curve not cyclic=open + arc.setResolu(settings.var['curve_res']) + + arc.update() #important for handles calculation + + ob = SCENE.objects.new(arc) # create a new curve_object + ob.loc = tuple(self.loc) + if thic != 0.0: #hack: Blender<2.45 curve-extrusion + thic = thic * 0.5 + arc.setExt1(1.0) # curve-extrusion: Blender2.45 accepts only (0.0 - 5.0) + ob.LocZ = thic + self.loc[2] + transform(self.extrusion, 0, ob) + if thic != 0.0: + ob.SizeZ *= abs(thic) + return ob + + else: # draw ARC as mesh -------------------- + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + # set a number of segments in entire circle + arc_res = settings.var['arc_res'] * sqrt(radius) / sqrt(settings.var['arc_rad']) + + verts = calcArc(None, radius, start, end, arc_res, False) + #verts = [list(point) for point in verts] + len1 = len(verts) + #print 'deb:len1:', len1 #----------------------- + if width != 0: + radius_out = radius + (0.5 * width) + radius_in = radius - (0.5 * width) + if radius_in <= 0.0: + radius_in = settings.var['dist_min'] + #radius_in = 0.0 + verts_in = [] + verts_out = [] + for point in verts: + pointVec = Mathutils.Vector(point) + pointVec = pointVec.normalize() + verts_in.append(list(radius_in * pointVec)) #vertex inside + verts_out.append(list(radius_out * pointVec)) #vertex outside + verts = verts_in + verts_out + + #print 'deb:verts:', verts #--------------------- + if thic != 0: + thic_verts = [] + thic_verts.extend([[point[0], point[1], point[2]+thic] for point in verts]) + if thic < 0.0: + thic_verts.extend(verts) + verts = thic_verts + else: + verts.extend(thic_verts) + f_bottom = [[num, num+1, len1+num+1, len1+num] for num in xrange(len1-1)] + f_top = [[num, len1+num, len1+num+1, num+1] for num in xrange(len1+len1, len1+len1+len1-1)] + f_left = [[num, len1+len1+num, len1+len1+num+1, num+1] for num in xrange(len1-1)] + f_right = [[num, num+1, len1+len1+num+1, len1+len1+num] for num in xrange(len1, len1+len1-1)] + f_start = [[0, len1, len1+len1+len1, len1+len1]] + f_end = [[len1+len1-1, 0+len1-1, len1+len1+len1-1, len1+len1+len1+len1-1]] + faces = f_left + f_right + f_bottom + f_top + f_start + f_end + + me.verts.extend(verts) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + smooth_len = len(f_left) + len(f_right) + for i in xrange(smooth_len): + me.faces[i].smooth = True + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + if settings.var['vGroup_on']: + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + replace = Blender.Mesh.AssignModes.REPLACE #or .AssignModes.ADD + vg_left, vg_right, vg_top, vg_bottom = [], [], [], [] + for v in f_left: vg_left.extend(v) + for v in f_right: vg_right.extend(v) + for v in f_top: vg_top.extend(v) + for v in f_bottom: vg_bottom.extend(v) + me.addVertGroup('side.left') ; me.assignVertsToGroup('side.left', list(set(vg_left)), 1.0, replace) + me.addVertGroup('side.right') ; me.assignVertsToGroup('side.right', list(set(vg_right)), 1.0, replace) + me.addVertGroup('side.top') ; me.assignVertsToGroup('side.top', list(set(vg_top)), 1.0, replace) + me.addVertGroup('side.bottom'); me.assignVertsToGroup('side.bottom',list(set(vg_bottom)), 1.0, replace) + me.addVertGroup('side.start'); me.assignVertsToGroup('side.start', f_start[0], 1.0, replace) + me.addVertGroup('side.end') ; me.assignVertsToGroup('side.end', f_end[0], 1.0, replace) + + + else: # if thick=0 - draw only flat ring + faces = [[num, len1+num, len1+num+1, num+1] for num in xrange(len1 - 1)] + me.verts.extend(verts) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + elif thic != 0: + thic_verts = [] + thic_verts.extend([[point[0], point[1], point[2]+thic] for point in verts]) + if thic < 0.0: + thic_verts.extend(verts) + verts = thic_verts + else: + verts.extend(thic_verts) + faces = [] + #print 'deb:len1:', len1 #----------------------- + #print 'deb:verts:', verts #--------------------- + faces = [[num, num+1, num+len1+1, num+len1] for num in xrange(len1 - 1)] + + me.verts.extend(verts) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + for i in xrange(len(faces)): + me.faces[i].smooth = True + + else: + edges = [[num, num+1] for num in xrange(len(verts)-1)] + me.verts.extend(verts) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + #me.update() + #ob = SCENE.objects.new(me) # create a new arc_object + #ob.link(me) + ob.loc = tuple(center) + #ob.loc = Mathutils.Vector(ob.loc) + transform(self.extrusion, 0, ob) + #ob.size = (1,1,1) + return ob + + +class BlockRecord: #----------------------------------------------------------------- + """Class for objects representing dxf block_records. + """ + + def __init__(self, obj): + """Expects an entity object of type block_record as input. + """ + if not obj.type == 'block_record': + raise TypeError, "Wrong type %s for block_record object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.name = getit(obj, 2, None) + + # optional data (with defaults) + self.insertion_units = getit(obj, 70, None) + self.insert_units = getit(obj, 1070, None) + """code 1070 Einfügeeinheiten: + 0 = Keine Einheiten; 1 = Zoll; 2 = Fuß; 3 = Meilen; 4 = Millimeter; + 5 = Zentimeter; 6 = Meter; 7 = Kilometer; 8 = Mikrozoll; + 9 = Mils; 10 = Yard; 11 = Angstrom; 12 = Nanometer; + 13 = Mikrons; 14 = Dezimeter; 15 = Dekameter; + 16 = Hektometer; 17 = Gigameter; 18 = Astronomische Einheiten; + 19 = Lichtjahre; 20 = Parsecs + """ + + + def __repr__(self): + return "%s: name - %s, insert units - %s" %(self.__class__.__name__, self.name, self.insertion_units) + + + + +class Block: #----------------------------------------------------------------- + """Class for objects representing dxf BLOCKs. + """ + + def __init__(self, obj): + """Expects an entity object of type block as input. + """ + if not obj.type == 'block': + raise TypeError, "Wrong type %s for block object!" %obj.type + + self.type = obj.type + self.name = obj.name + self.data = obj.data[:] + + # required data + self.flags = getit(obj, 70, 0) + self.anonim = self.flags & 1 #anonymous block generated by hatching, associative dimensioning, other + self.atrib = self.flags & 2 # has attribute definitions + self.xref = self.flags & 4 # is an external reference (xref) + self.xref_lay = self.flags & 8 # is an xref overlay + self.dep_ext = self.flags & 16 # is externally dependent + self.dep_res = self.flags & 32 # resolved external reference + self.xref_ext = self.flags & 64 # is a referenced external reference xref + #--todo--- if self.flag > 4: self.xref = True + + # optional data (with defaults) + self.path = getit(obj, 1, '') # Xref path name + self.discription = getit(obj, 4, '') + + self.entities = dxfObject('block_contents') #creates empty entities_container for this block + self.entities.data = objectify([ent for ent in obj.data if type(ent) != list]) + + self.layer = getit(obj.data, 8, None) + self.loc = self.get_loc(obj.data) + + #print 'deb:Block %s data:\n%s' %(self.name, self.data) #------------ + #print 'deb:Block %s self.entities.data:\n%s' %(self.name, self.entities.data) #------------ + + + + def get_loc(self, data): + """Gets the insert point of the block. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 10, 0.0) # 10 = x + loc[1] = getit(data, 20, 0.0) # 20 = y + loc[2] = getit(data, 30, 0.0) # 30 = z + return loc + + + def __repr__(self): + return "%s: name - %s, description - %s, xref-path - %s" %(self.__class__.__name__, self.name, self.discription, self.path) + + + + +class Insert: #----------------------------------------------------------------- + """Class for objects representing dxf INSERTs. + """ + + def __init__(self, obj): + """Expects an entity object of type insert as input. + """ + if not obj.type == 'insert': + raise TypeError, "Wrong type %s for insert object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + #print 'deb:Insert_init_ self.data:\n', self.data #----------- + + # required data + self.name = obj.get_type(2)[0] + + # optional data (with defaults) + self.rotation = getit(obj, 50, 0) + self.space = getit(obj, 67, 0) + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.loc = self.get_loc(obj.data) + self.scale = self.get_scale(obj.data) + self.rows, self.columns = self.get_array(obj.data) + self.extrusion = get_extrusion(obj.data) + + #self.flags = getit(obj.data, 66, 0) # + #self.attrib = self.flags & 1 + + + def get_loc(self, data): + """Gets the origin location of the insert. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 10, 0.0) + loc[1] = getit(data, 20, 0.0) + loc[2] = getit(data, 30, 0.0) + return loc + + + def get_scale(self, data): + """Gets the x/y/z scale factors of the insert. + """ + scale = [1, 1, 1] + scale[0] = getit(data, 41, 1.0) + scale[1] = getit(data, 42, 1.0) + scale[2] = getit(data, 43, 1.0) + return scale + + + def get_array(self, data): + """Returns the pair (row number, row spacing), (column number, column spacing). + """ + columns = getit(data, 70, 1) + rows = getit(data, 71, 1) + cspace = getit(data, 44, 0.0) + rspace = getit(data, 45, 0.0) + return (rows, rspace), (columns, cspace) + + + def get_target(self, data): + """Gets the origin location of the insert. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 1011, 0.0) + loc[1] = getit(data, 1021, 0.0) + loc[2] = getit(data, 1031, 0.0) + return loc + + + def get_color(self, data): + """Gets the origin location of the insert. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 1010, 0.0) + loc[1] = getit(data, 1020, 0.0) + loc[2] = getit(data, 1030, 0.0) + return loc + + + def get_ave_render(self, data): + """Gets the origin location of the insert. + """ + loc = [0, 0, 0] + loc[0] = getit(data, 1010, 0.0) + loc[1] = getit(data, 1020, 0.0) + loc[2] = getit(data, 1030, 0.0) + return loc + + + def __repr__(self): + return "%s: layer - %s, name - %s" %(self.__class__.__name__, self.layer, self.name) + + + def draw(self, settings, deltaloc): + """for INSERT(block): draw empty-marker for duplicated Blender_Group. + + Blocks are made of three objects: + the block_record in the tables section + the block in the blocks section + the insert object (one or more) in the entities section + block_record gives the insert units, + block provides the objects drawn in the block, + insert object gives the location/scale/rotation of the block instances. + """ + + name = self.name.lower() + if name == 'ave_render': + if settings.var['lights_on']: #if lights support activated + a_data = get_ave_data(self.data) + # AVE_RENDER objects: + # 7:'Pref', 0:'Full Opt', 0:'Quick Opt', 1:'Scanl Opt', 2:'Raytr Opt', 0:'RFile Opt' + # 0:'Fog Opt', 0:'BG Opt', 0:'SCENE1','','','','','','','','','', + # '','','','','','','','','','','','', + + if a_data.key == 'SCENE': # define set of lights as blender group + scene_lights = 1 + elif False: # define set of lights as blender group + scene_lights = 1 + return + elif name == 'ave_global': + if settings.var['lights_on']: #if lights support activated + return + elif name == 'sh_spot': + if settings.var['lights_on']: #if lights support activated + obname = settings.blocknamesmap[self.name] + obname = 'sp_%s' %obname # create object name from block name + #obname = obname[:MAX_NAMELENGTH] + # blender: 'Lamp', 'Sun', 'Spot', 'Hemi', 'Area', or 'Photon' + li = Lamp.New('Spot', obname) + ob = SCENE.objects.new(li) + intensity = 2.0 #--todo-- ----------- + li.setEnergy(intensity) + target = self.get_target(self.data) + color = self.get_color(self.data) + li.R = color[0] + li.G = color[1] + li.B = color[2] + + ob.loc = tuple(self.loc) + transform(self.extrusion, 0, ob) + return ob + + elif name == 'overhead': + if settings.var['lights_on']: #if lights support activated + obname = settings.blocknamesmap[self.name] + obname = 'la_%s' %obname # create object name from block name + #obname = obname[:MAX_NAMELENGTH] + # blender: 'Lamp', 'Sun', 'Spot', 'Hemi', 'Area', or 'Photon' + li = Lamp.New('Lamp', obname) + ob = SCENE.objects.new(li) + intensity = 2.0 #--todo-- ----------- + li.setEnergy(intensity) + target = self.get_target(self.data) + color = self.get_color(self.data) + li.R = color[0] + li.G = color[1] + li.B = color[2] + + ob.loc = tuple(self.loc) + transform(self.extrusion, 0, ob) + return ob + + elif name == 'direct': + if settings.var['lights_on']: #if lights support activated + obname = settings.blocknamesmap[self.name] + obname = 'su_%s' %obname # create object name from block name + #obname = obname[:MAX_NAMELENGTH] + # blender: 'Lamp', 'Sun', 'Spot', 'Hemi', 'Area', or 'Photon' + li = Lamp.New('Sun', obname) + ob = SCENE.objects.new(li) + intensity = 2.0 #--todo-- ----------- + li.setEnergy(intensity) + color = self.get_color(self.data) + li.R = color[0] + li.G = color[1] + li.B = color[2] + + ob.loc = tuple(self.loc) + transform(self.extrusion, 0, ob) + return ob + + elif settings.drawTypes['insert']: #if insert_drawType activated + print 'deb:draw. settings.blocknamesmap:', settings.blocknamesmap #-------------------- + obname = settings.blocknamesmap[self.name] + obname = 'in_%s' %obname # create object name from block name + #obname = obname[:MAX_NAMELENGTH] + + # if material BYBLOCK def needed: use as placeholder a mesh-vertex instead of empty + ob = SCENE.objects.new('Empty', obname) # create a new empty_object + empty_size = 1.0 * settings.var['g_scale'] + if empty_size < 0.01: empty_size = 0.01 #Blender limits (0.01-10.0) + elif empty_size > 10.0: empty_size = 10.0 + ob.drawSize = empty_size + + # get our block_def-group + block = settings.blocks(self.name) + ob.DupGroup = block + ob.enableDupGroup = True + + if block.name.startswith('xr_'): + ob.name = 'xb_' + ob.name[3:] + + #print 'deb:draw.block.deltaloc:', deltaloc #-------------------- + ob.loc = tuple(self.loc) + if deltaloc: + deltaloc = rotXY_Vec(self.rotation, deltaloc) + #print 'deb:draw.block.loc:', deltaloc #-------------------- + ob.loc = [ob.loc[0]+deltaloc[0], ob.loc[1]+deltaloc[1], ob.loc[2]+deltaloc[2]] + transform(self.extrusion, self.rotation, ob) + ob.size = tuple(self.scale) + return ob + + + + +class Ellipse: #----------------------------------------------------------------- + """Class for objects representing dxf ELLIPSEs. + """ + + def __init__(self, obj): + """Expects an entity object of type ellipse as input. + """ + if not obj.type == 'ellipse': + raise TypeError, "Wrong type %s for ellipse object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.ratio = obj.get_type(40)[0] # Ratio of minor axis to major axis + self.start_angle = obj.get_type(41)[0] # in radians + self.end_angle = obj.get_type(42)[0] + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.thic = getit(obj, 39, 0.0) + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.loc = self.get_loc(obj.data) + self.major = self.get_major(obj.data) + self.extrusion = get_extrusion(obj.data) + + + def get_loc(self, data): + """Gets the center location for arc type objects. + + Arcs have a single coord location. + """ + loc = [0.0, 0.0, 0.0] + loc[0] = getit(data, 10, 0.0) + loc[1] = getit(data, 20, 0.0) + loc[2] = getit(data, 30, 0.0) + return loc + + + def get_major(self, data): + """Gets the major axis for ellipse type objects. + + The ellipse major axis defines the rotation of the ellipse and its radius. + """ + loc = [0.0, 0.0, 0.0] + loc[0] = getit(data, 11, 0.0) + loc[1] = getit(data, 21, 0.0) + loc[2] = getit(data, 31, 0.0) + return loc + + + def __repr__(self): + return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) + + + def draw(self, settings): + """for ELLIPSE: generate Blender_geometry. + """ + obname = 'el_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + + center = self.loc + if True: + start = degrees(self.start_angle) + end = degrees(self.end_angle) + if abs(end - 360.0) < 0.00001: end = 360.0 + ellipse_closed = False + if end - start == 360.0: ellipse_closed = True + + else: # bug in AutoCAD_2002 dxf-exporter into r12 for ELLIPSE->POLYLINE_ARC + #print 'deb:calcEllipse---------:\n start=%s\n end=%s' %(self.start_angle, self.end_angle) #--------- + if self.start_angle > pi+pi: self.start_angle %= pi+pi + if self.end_angle > pi+pi: self.end_angle %= pi+pi + if abs(self.end_angle - pi - pi) < 0.00001: self.end_angle = pi + pi + ellipse_closed = False + if abs(self.end_angle - self.start_angle) == pi + pi: ellipse_closed = True + test = self.start_angle % pi + if test < 0.001 or pi - test < 0.001: start = self.start_angle + else: + start = atan(tan(self.start_angle) * self.ratio) + if start < 0.0: start += pi + if self.start_angle > pi: start += pi + test = self.end_angle % pi + if test < 0.001 or pi - test < 0.001: end = self.end_angle + else: + end = atan(tan(self.end_angle) * self.ratio) + if end < 0.0: end += pi + if self.end_angle > pi: end += pi + start = degrees(start) + end = degrees(end) + + # rotation = Angle between major and WORLDX + # doesnt work, couse produces always positive value: rotation = Mathutils.AngleBetweenVecs(major, WORLDX) + if self.major[0] == 0: + rotation = 90.0 + if self.major[1] < 0: rotation += 180 + else: + rotation = degrees(atan(self.major[1] / self.major[0])) + if self.major[0] < 0: + rotation += 180.0 + + major = Mathutils.Vector(self.major) + #radius = sqrt(self.major[0]**2 + self.major[1]**2 + self.major[2]**2) + radius = major.length + #print 'deb:calcEllipse:\n center, radius, start, end:\n', center, radius, start, end #--------- + + thic = set_thick(self.thic, settings) + width = 0.0 + if settings.var['lines_as'] == 4: # as thin_box + thic = settings.var['thick_min'] + width = settings.var['width_min'] + if settings.var['lines_as'] == 3: # as thin cylinder + cyl_rad = 0.5 * settings.var['width_min'] + + if settings.var['lines_as'] == 5: # draw ELLIPSE as curve ------------- + arc_res = settings.var['curve_arc'] + triples = True + VectorTriples = calcArc(None, radius, start, end, arc_res, triples) + arc = Curve.New(obname) # create new curve data + curve = arc.appendNurb(BezTriple.New(VectorTriples[0])) + if ellipse_closed: + for p in VectorTriples[1:-1]: + curve.append(BezTriple.New(p)) + for point in curve: + point.handleTypes = [FREE, FREE] + curve.flagU = 1 # 0 sets the curve not cyclic=open + if settings.var['fill_on']: + arc.setFlag(6) # 2+4 set top and button caps + else: + arc.setFlag(arc.getFlag() & ~6) # dont set top and button caps + else: + for p in VectorTriples[1:]: + curve.append(BezTriple.New(p)) + for point in curve: + point.handleTypes = [FREE, FREE] + curve.flagU = 0 # 0 sets the curve not cyclic=open + + arc.setResolu(settings.var['curve_res']) + arc.update() #important for handles calculation + + ob = SCENE.objects.new(arc) # create a new curve_object + ob.loc = tuple(self.loc) + if thic != 0.0: #hack: Blender<2.45 curve-extrusion + thic = thic * 0.5 + arc.setExt1(1.0) # curve-extrusion: Blender2.45 accepts only (0.0 - 5.0) + ob.LocZ = thic + self.loc[2] + transform(self.extrusion, rotation, ob) + ob.SizeY *= self.ratio + if thic != 0.0: + ob.SizeZ *= abs(thic) + return ob + + + else: # draw ELLIPSE as mesh -------------------------------------- + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + # set a number of segments in entire circle + arc_res = settings.var['arc_res'] * sqrt(radius) / sqrt(settings.var['arc_rad']) + + + verts = calcArc(None, radius, start, end, arc_res, False) + #verts = [list(point) for point in verts] + if False: #--todo--: if ellipse_closed: + verts = verts[:-1] #list without last point/edge (cause closed curve) + len1 = len(verts) + #print 'deb:len1:', len1 #----------------------- + if width != 0: + radius_out = radius + (0.5 * width) + radius_in = radius - (0.5 * width) + if radius_in <= 0.0: + radius_in = settings.var['dist_min'] + #radius_in = 0.0 + verts_in = [] + verts_out = [] + for point in verts: + pointVec = Mathutils.Vector(point) + pointVec = pointVec.normalize() + verts_in.append(list(radius_in * pointVec)) #vertex inside + verts_out.append(list(radius_out * pointVec)) #vertex outside + verts = verts_in + verts_out + + #print 'deb:verts:', verts #--------------------- + if thic != 0: + thic_verts = [] + thic_verts.extend([[point[0], point[1], point[2]+thic] for point in verts]) + if thic < 0.0: + thic_verts.extend(verts) + verts = thic_verts + else: + verts.extend(thic_verts) + f_bottom = [[num, num+1, len1+num+1, len1+num] for num in xrange(len1-1)] + f_top = [[num, len1+num, len1+num+1, num+1] for num in xrange(len1+len1, len1+len1+len1-1)] + f_left = [[num, len1+len1+num, len1+len1+num+1, num+1] for num in xrange(len1-1)] + f_right = [[num, num+1, len1+len1+num+1, len1+len1+num] for num in xrange(len1, len1+len1-1)] + f_start = [[0, len1, len1+len1+len1, len1+len1]] + f_end = [[len1+len1-1, 0+len1-1, len1+len1+len1-1, len1+len1+len1+len1-1]] + faces = f_left + f_right + f_bottom + f_top + f_start + f_end + + me.verts.extend(verts) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + smooth_len = len(f_left) + len(f_right) + for i in xrange(smooth_len): + me.faces[i].smooth = True + + if settings.var['vGroup_on']: + # each MeshSide becomes vertexGroup for easier material assignment --------------------- + replace = Blender.Mesh.AssignModes.REPLACE #or .AssignModes.ADD + vg_left, vg_right, vg_top, vg_bottom = [], [], [], [] + for v in f_left: vg_left.extend(v) + for v in f_right: vg_right.extend(v) + for v in f_top: vg_top.extend(v) + for v in f_bottom: vg_bottom.extend(v) + me.addVertGroup('side.left') ; me.assignVertsToGroup('side.left', list(set(vg_left)), 1.0, replace) + me.addVertGroup('side.right') ; me.assignVertsToGroup('side.right', list(set(vg_right)), 1.0, replace) + me.addVertGroup('side.top') ; me.assignVertsToGroup('side.top', list(set(vg_top)), 1.0, replace) + me.addVertGroup('side.bottom'); me.assignVertsToGroup('side.bottom',list(set(vg_bottom)), 1.0, replace) + me.addVertGroup('side.start'); me.assignVertsToGroup('side.start', f_start[0], 1.0, replace) + me.addVertGroup('side.end') ; me.assignVertsToGroup('side.end', f_end[0], 1.0, replace) + + + else: # if thick=0 - draw only flat ring + faces = [[num, len1+num, len1+num+1, num+1] for num in xrange(len1 - 1)] + me.verts.extend(verts) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + elif thic != 0: + thic_verts = [] + thic_verts.extend([[point[0], point[1], point[2]+thic] for point in verts]) + if thic < 0.0: + thic_verts.extend(verts) + verts = thic_verts + else: + verts.extend(thic_verts) + faces = [] + #print 'deb:len1:', len1 #----------------------- + #print 'deb:verts:', verts #--------------------- + faces = [[num, num+1, num+len1+1, num+len1] for num in xrange(len1 - 1)] + + me.verts.extend(verts) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + if settings.var['meshSmooth_on']: # left and right side become smooth ---------------------- + for i in xrange(len(faces)): + me.faces[i].smooth = True + + else: + edges = [[num, num+1] for num in xrange(len(verts)-1)] + me.verts.extend(verts) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + #print 'deb:calcEllipse transform rotation: ', rotation #--------- + ob.loc = tuple(center) + #old ob.SizeY = self.ratio + transform(self.extrusion, rotation, ob) + #old transform(self.extrusion, 0, ob) + ob.SizeY *= self.ratio + + return ob + + + +class Face: #----------------------------------------------------------------- + """Class for objects representing dxf 3DFACEs. + """ + + def __init__(self, obj): + """Expects an entity object of type 3dfaceplot as input. + """ + if not obj.type == '3dface': + raise TypeError, "Wrong type %s for 3dface object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # optional data (with defaults) + self.space = getit(obj, 67, 0) + self.color_index = getit(obj, 62, BYLAYER) + + self.layer = getit(obj.data, 8, None) + self.points = self.get_points(obj.data) + + + def get_points(self, data): + """Gets 3-4 points for a 3d face type object. + + Faces have three or optionally four verts. + """ + a = [0, 0, 0] + b = [0, 0, 0] + c = [0, 0, 0] + d = [0, 0, 0] + a[0] = getit(data, 10, None) # 10 = x + a[1] = getit(data, 20, None) # 20 = y + a[2] = getit(data, 30, 0.0) # 30 = z + b[0] = getit(data, 11, None) + b[1] = getit(data, 21, None) + b[2] = getit(data, 31, 0.0) + c[0] = getit(data, 12, None) + c[1] = getit(data, 22, None) + c[2] = getit(data, 32, 0.0) + out = [a,b,c] + + d[0] = getit(data, 13, None) + if d[0] != None: + d[1] = getit(data, 23, None) + d[2] = getit(data, 33, 0.0) + out.append(d) + + #if len(out) < 4: print '3dface with only 3 vertices:\n',a,b,c,d #----------------- + return out + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, settings): + """for 3DFACE: generate Blender_geometry. + """ + # Generate the geometery + points = self.points + + global activObjectLayer + global activObjectName + #print 'deb:draw:face.ob IN activObjectName: ', activObjectName #--------------------- + + if activObjectLayer == self.layer and settings.var['one_mesh_on']: + obname = activObjectName + #print 'deb:face.draw obname from activObjectName: ', obname #--------------------- + ob = Object.Get(obname) # open an existing mesh_object + #ob = SCENE.getChildren(obname) # open an existing mesh_object + else: + obname = 'fa_%s' %self.layer # create object name from layer name + obname = obname[:MAX_NAMELENGTH] + me = Mesh.New(obname) # create a new mesh + ob = SCENE.objects.new(me) # create a new mesh_object + activObjectName = ob.name + activObjectLayer = self.layer + #print ('deb:except. new face.ob+mesh:"%s" created!' %ob.name) #--------------------- + + me = Mesh.Get(ob.name) # open objects mesh data + faces, edges = [], [] + n = len(me.verts) + if len(self.points) == 4: + faces = [[0+n,1+n,2+n,3+n]] + elif len(self.points) == 3: + faces = [[0+n,1+n,2+n]] + elif len(self.points) == 2: + edges = [[0+n,1+n]] + + me.verts.extend(points) # add vertices to mesh + if faces: me.faces.extend(faces) # add faces to the mesh + if edges: me.edges.extend(edges) # add faces to the mesh + if settings.var['vGroup_on']: + # entities with the same color build one vertexGroup for easier material assignment --------------------- + ob.link(me) # link mesh to that object + vG_name = 'color_%s' %self.color_index + if edges: faces = edges + replace = Blender.Mesh.AssignModes.ADD #or .AssignModes.REPLACE or ADD + try: + me.assignVertsToGroup(vG_name, faces[0], 1.0, replace) + #print 'deb: existed vGroup:', vG_name #--------------------- + except: + me.addVertGroup(vG_name) + me.assignVertsToGroup(vG_name, faces[0], 1.0, replace) + #print 'deb: create new vGroup:', vG_name #-------------------- + + #print 'deb:draw:face.ob OUT activObjectName: ', activObjectName #--------------------- + return ob + + +#--------------------------------------------------------------------------------------- +# type to object maping (sorted-dictionary for f_obiectify ONLY!, format={'key':Class} ) type_map = { - 'line':Line, - 'lwpolyline':LWpolyline, - 'text':Text, - 'mtext':Mtext, - 'circle':Circle, - 'arc':Arc, - 'layer':Layer, - 'block_record':BlockRecord, - 'block':Block, - 'insert':Insert, - 'ellipse':Ellipse, - '3dface':Face + 'vport':Vport, + 'view':View, + 'layer':Layer, + 'block_record':BlockRecord, + 'block':Block, + 'insert':Insert, + 'point':Point, + '3dface':Face, + 'line':Line, +# 'mline':MLine, + 'polyline':Polyline, + 'lwpolyline':LWpolyline, +# 'region':Region, + 'trace':Solid, + 'solid':Solid, + 'text':Text, + 'mtext':Mtext, + 'circle':Circle, + 'ellipse':Ellipse, + 'arc':Arc } -def objectify(data): - """Expects a section type object's data as input. - - Maps object data to the correct object type. - """ - objects = [] # colector for finished objects - known_types = type_map.keys() # so we don't have to call foo.keys() every iteration - index = 0 - while index < len(data): - item = data[index] - if type(item) != list and item.type in known_types: - # proccess the object and append the resulting object - objects.append(type_map[item.type](item)) - elif type(item) != list and item.type == 'table': - item.data = objectify(item.data) # tables have sub-objects - objects.append(item) - elif type(item) != list and item.type == 'polyline': - pline = Polyline(item) - while 1: - index += 1 - item = data[index] - if item.type == 'vertex': - v = Vertex(item) - pline.points.append(v) - elif item.type == 'seqend': - break - else: - print "Error: non-vertex found before seqend!" - index -= 1 - break - objects.append(pline) - else: - # we will just let the data pass un-harrased - objects.append(item) - index += 1 - return objects - -class MatColors: - """A smart container for color based materials. - - This class is a wrapper around a dictionary mapping color indicies to materials. - When called with a color index it returns a material corrisponding to that index. - Behind the scenes it checks if that index is in its keys, and if not it creates - a new material. It then adds the new index:material pair to its dict and returns - the material. - """ - - def __init__(self, map): - """Expects a dictionary mapping layer names to color idices.""" - self.map = map - self.colors = {} - - - def __call__(self, color=None): - """Return the material associated with color. - - If a layer name is provided the color of that layer is used. - """ - if not color: - color = 0 - if type(color) == str: # Layer name - try: - color = self.map[color].color # color = layer_map[name].color - except KeyError: - layer = Layer(name=color, color=0, frozen=False) - self.map[color] = layer - color = 0 - color = abs(color) - if color not in self.colors.keys(): - self.add(color) - return self.colors[color] - - - - - def add(self, color): - """Create a new material using the provided color index.""" - global color_map - mat = Material.New('ColorIndex-%s' %color) - mat.setRGBCol(color_map[color]) - mat.setMode("Shadeless", "Wire") - self.colors[color] = mat - - - - -class Blocks: - """A smart container for blocks. - - This class is a wrapper around a dictionary mapping block names to Blender data blocks. - When called with a name string it returns a block corrisponding to that name. - Behind the scenes it checks if that name is in its keys, and if not it creates - a new data block. It then adds the new name:block pair to its dict and returns - the block. - """ - - def __init__(self, map, settings): - """Expects a dictionary mapping block names to block objects.""" - self.map = map - self.settings = settings - self.blocks = {} - - - def __call__(self, name=None): - """Return the data block associated with name. - - If no name is provided return self.blocks. - """ - if not name: - return self.blocks - if name not in self.blocks.keys(): - self.add(name) - return self.blocks[name] - - - - def add(self, name): - """Create a new block group for the block with name.""" - write = self.settings.write - group = Group.New(name) - block = self.map[name] - write("\nDrawing %s block entities..." %name) - drawEntities(block.entities, self.settings, group) - write("Done!") - self.blocks[name] = group - - - - - -class Settings: - """A container for all the import settings and objects used by the draw functions. - - This is like a collection of globally accessable persistant properties and functions. - """ - # Optimization constants - MIN = 0 - MID = 1 - PRO = 2 - MAX = 3 - - def __init__(self, drawing, curves, optimization, **kwds): - """Given the drawing initialize all the important settings used by the draw functions.""" - self.curves = curves - self.optimization = optimization - print "Setting optimization level %s!" %optimization - self.drawTypes = kwds - self.layers = True - self.blocks = True - - # First sort out all the sections - sections = dict([(item.name, item) for item in drawing.data]) - - # The header section may be omited - if 'header' in sections.keys(): - self.write("Found header!") - else: - self.write("File contains no header!") - - # The tables section may be partialy or completely missing. - if 'tables' in sections.keys(): - self.write("Found tables!") - tables = dict([(item.name, item) for item in sections["tables"].data]) - if 'layer' in tables.keys(): - self.write("Found layers!") - # Read the layers table and get the layer colors - self.colors = getLayers(drawing) - else: - self.write("File contains no layers table!") - self.layers = False - self.colors = MatColors({}) - else: - self.write("File contains no tables!") - self.write("File contains no layers table!") - self.layers = False - self.colors = MatColors({}) - - # The blocks section may be omited - if 'blocks' in sections.keys(): - self.write("Found blocks!") - # Read the block definitions and build our block object - if self.drawTypes['blocks']: - self.blocks = getBlocks(drawing, self) - else: - self.write("File contains no blocks!") - self.drawTypes['blocks'] = False - def write(self, text, newline=True): - """Wraps the built-in print command in a optimization check.""" - if self.optimization <= self.MID: - if newline: - print text - else: - print text, - - def redraw(self): - """Update Blender if optimization level is low enough.""" - if self.optimization <= self.MIN: - Blender.Redraw() - - - def progress(self, done, text): - """Wrapper for Blender.Window.DrawProgressBar.""" - if self.optimization <= self.PRO: - Window.DrawProgressBar(done, text) - - - def isOff(self, name): - """Given a layer name look up the layer object and return its visable status.""" - # colors are negative if layer is off - try: - layer = self.colors.map[name] - except KeyError: - return False - - if layer.frozen or layer.color < 0: - return True - else: - return False - - - - -def main(filename=None): - editmode = Window.EditMode() # are we in edit mode? If so ... - if editmode: Window.EditMode(0) # leave edit mode before - Window.WaitCursor(True) # Let the user know we are thinking - - try: - if not filename: - print "DXF import: error, no file selected. Attempting to load default file." - try: - filename = Sys.expandpath(r".\examples\big-test.dxf") - except IOError: - print "DXF import: error finding default test file, exiting..." - return None - drawing = readDXF(filename, objectify) - drawDrawing(drawing) - finally: - # restore state even if things didn't work - Window.WaitCursor(False) - if editmode: Window.EditMode(1) # and put things back how we fond them - -def getOCS(az): - """An implimentation of the Arbitrary Axis Algorithm.""" - # world x, y, and z axis - wx = WORLDX - wy = Mathutils.Vector((0,1,0)) - wz = Mathutils.Vector((0,0,1)) - - #decide if we need to transform our coords - if az[0] == 0 and az[1] == 0: - return False - # elif abs(az[0]) < 0.0001 or abs(az[1]) < 0.0001: - # return False - az = Mathutils.Vector(az) - - cap = 0.015625 # square polar cap value (1/64.0) - if abs(az.x) < cap and abs(az.y) < cap: - ax = Mathutils.CrossVecs(wy, az) - else: - ax = Mathutils.CrossVecs(wz, az) - ax = ax.normalize() - ay = Mathutils.CrossVecs(az, ax) - ay = ay.normalize() - return ax, ay, az - -def transform(normal, obj): - """Use the calculated ocs to determine the objects location/orientation in space. - - Quote from dxf docs: - The elevation value stored with an entity and output in DXF files is a sum - of the Z-coordinate difference between the UCS XY plane and the OCS XY - plane, and the elevation value that the user specified at the time the entity - was drawn. - """ - ocs = getOCS(normal) - if ocs: - #print ocs - x, y, z = ocs - x = x.resize4D() - y = y.resize4D() - z = -z.resize4D() - x.w = 0 - y.w = 0 - z.w = 0 - o = Mathutils.Vector(obj.loc) - o = o.resize4D() - mat = Mathutils.Matrix(x, y, z, o) - obj.setMatrix(mat) - -def getLayers(drawing): - """Build a dictionary of name:color pairs for the given drawing.""" - tables = drawing.tables - for table in tables.data: - if table.name == 'layer': - layers = table - break - map = {} - for item in layers.data: - if type(item) != list and item.type == 'layer': - map[item.name] = item - colors = MatColors(map) - return colors -def getBlocks(drawing, settings): - """Build a dictionary of name:block pairs for the given drawing.""" - map = {} - for item in drawing.blocks.data: - if type(item) != list and item.type == 'block': - try: - map[item.name] = item - except KeyError: - # annon block - print "Cannot map %s - %s!" %(item.name, item) - blocks = Blocks(map, settings) - return blocks -def drawDrawing(drawing): - """Given a drawing object recreate the drawing in Blender.""" - print "Getting settings..." - Window.WaitCursor(False) - #width, height = Window.GetScreenSize() - Window.SetMouseCoords() - - # Create a PupBlock to get user preferences - lines = Draw.Create(1) - arcs = Draw.Create(1) - circles = Draw.Create(1) - polylines = Draw.Create(1) - text = Draw.Create(1) - blocks = Draw.Create(1) - faces = Draw.Create(1) - optim = Draw.Create(1) - - block = [] - block.append("Draw Options:") - block.append(("Lines", lines, "Toggle drawing of lines")) - block.append(("Arcs", arcs, "Toggle drawing of arcs")) - block.append(("Circles", circles, "Toggle drawing of circles")) - block.append(("Polylines", polylines, "Toggle drawing of polylines")) - block.append(("Text", text, "Toggle drawing of text")) - block.append(("Blocks", blocks, "Toggle drawing of blocks")) - block.append(("Faces", faces, "Toggle drawing of faces")) - block.append("Update Optimization:") - block.append(("Level: ", optim, 0, 3)) - - retval = Draw.PupBlock("DXF Import", block) - - Window.WaitCursor(True) # Let the user know we are thinking - # The settings object controls how dxf entities are drawn - settings = Settings(drawing, curves=False, - optimization=int(optim.val), - lines=bool(lines.val), - arcs=bool(arcs.val), - circles=bool(circles.val), - polylines=bool(polylines.val), - text=bool(text.val), - blocks=bool(blocks.val), - faces=bool(faces.val) - ) - - settings.write("Drawings entities...") - # Draw all the know entity types in the current scene - drawEntities(drawing.entities, settings) - - # Set the visable layers - SCENE.setLayers([i+1 for i in range(18)]) - Blender.Redraw(-1) - settings.write("Done!") - - -def drawEntities(entities, settings, group=None): - """Draw every kind of thing in the entity list. - - If provided 'group' is the Blender group new entities are to be added to. - """ - for _type in type_map.keys(): - # for each known type get a list of that type and call the associated draw function - drawer(_type, entities.get_type(_type), settings, group) - - -def drawer(_type, entities, settings, group): - """Call with a list of entities and a settings object to generate Blender geometry.""" - if entities: - # Break out early if settings says we aren't drawing the current type - block = False - skip = False - if _type == 'block_record': - skip = True - if _type == 'line' and not settings.drawTypes['lines']: - skip = True - elif _type == 'arc' and not settings.drawTypes['arcs']: - skip = True - elif _type == 'circle' and not settings.drawTypes['circles']: - skip = True - elif _type in ['lwpolyline', 'polyline'] and not settings.drawTypes['polylines']: - skip = True - elif _type in ['text', 'mtext'] and not settings.drawTypes['text']: - skip = True - elif _type == 'insert': - if not settings.drawTypes['blocks']: - skip = True - block = True - elif _type == '3dface' and not settings.drawTypes['faces']: - skip = True - if skip: - settings.write("Skipping %s type entities!" %_type) - return - - message = "Drawing %ss..." %_type - settings.write(message, False) - settings.progress(0, message) - - if block: - # create one 'handle' data block to use with all blocks - handle = Mesh.New('insert') - handle.verts.extend( - [(-0.01,0,0), - (0.01,0,0), - (0,-0.01,0), - (0,0.01,0), - (0,0,-0.01), - (0,0,0.01)] - ) - handle.edges.extend([(0,1),(2,3),(4,5)]) - - # For now we only want model-space objects - entities = [entity for entity in entities if entity.space == 0] - - if group: - block_def = True - else: - block_def = False - - tot = len(entities) - cur = 1.0 - for entity in entities: - settings.write('\b.', False) - settings.progress(cur/tot, message) - cur += 1 - - # First get the layer group - if not block_def: - group = getGroup('layer %s' %entity.layer) # add overhead just to make things a little cleaner - - if block: - ob = entity.draw(handle, settings) - else: - ob = entity.draw(settings.curves) - - setColor(entity, ob, settings) - # Link it to the scene and add it to the correct group - SCENE.link(ob) - setGroup(group, ob) - - # Set the visability - if settings.isOff(entity.layer): - ob.layers = [20] - elif block_def: - ob.layers = [19] - else: - ob.layers = [i+1 for i in range(20)] - - # # Set the visability - # if settings.isOff(entity.layer) or block_def: - # ob.restrictDisplay = True - # ob.restrictRender = True - - settings.redraw() - message = "\nFinished drawing %ss!" %entities[0].type - settings.write(message) - settings.progress(1, message) - - -def getGroup(name): - """Returns a Blender group object.""" - try: - group = Group.Get(name) - except: # What is the exception? - group = Group.New(name) - return group -def setColor(entity, ob, settings): - # Set the color - if entity.color_index == BYLAYER: - mat = settings.colors(entity.layer) - else: - mat = settings.colors(entity.color_index) - try: - ob.setMaterials([mat]) - except ValueError: - settings.write("material error - %s!" %mat) - ob.colbits = 0x01 # Set OB materials. -def setGroup(group, it): - try: - group.objects.link(it) - except: - group.objects.append(it) - -def solveBulge(p1, p2): - """return the center, radius, start angle, and end angle given two points. - - Needs to take into account bulge sign. - negative = clockwise - positive = counter-clockwise - - to find center given two points, and arc angle - calculate radius - Cord = sqrt(start^2 + end^2) - S = (bulge*Cord)/2 - radius = ((Cord/2)^2+S^2)/2*S - angle of arc = 4*atan( bulge ) - angle from p1 to center is (180-angle)/2 - get vector pointing from p1 to p2 (p2 - p1) - normalize it and multiply by radius - rotate around p1 by angle to center point to center. - - start angle = angle between (center - p1) and worldX - end angle = start angle + angle of arc - """ - bulge = p1.bulge - p2 = Mathutils.Vector(p2.loc) - p1 = Mathutils.Vector(p1.loc) - cord = p2 - p1 # vector from p1 to p2 - clength = cord.length - s = (bulge * clength)/2 # sagitta (height) - radius = abs(((clength/2)**2 + s**2)/(2*s)) # magic formula - angle = abs(degrees(4*atan(bulge))) # theta (included angle) - delta = (180 - angle)/2 # the angle from cord to center - if bulge > 0: - delta = -delta - radial = cord.normalize() * radius # a radius length vector aligned with cord - rmat = Mathutils.RotationMatrix(delta, 3, 'Z') - center = p1 + (rmat * radial) # rotate radial by delta degrees, then add to p1 to find center - if bulge < 0: - sv = (p1 - center) # start from point 2 - else: - sv = (p2 - center) # start from point 1 - start = Mathutils.AngleBetweenVecs(sv, WORLDX) # start angle is the angle between the first leg of the section and the x axis - # The next bit is my cludge to figure out if start should be negative - rmat = Mathutils.RotationMatrix(start, 3, 'Z') - rstart = rmat * sv - if Mathutils.AngleBetweenVecs(rstart, WORLDX) < start: - start = -start - # the end angle is just 'angle' more than start angle - end = start + angle - return list(center), radius, start, end -def drawArc(center, radius, start, end, step=0.5): - """Draw a mesh arc with the given parameters.""" - # center is currently set by object - - # if start > end: - # start = start - 360 - if end > 360: - end = end%360 - startmatrix = Mathutils.RotationMatrix(start, 3, "Z") - startpoint = startmatrix * Mathutils.Vector((radius, 0, 0)) - endmatrix = Mathutils.RotationMatrix(end, 3, "Z") - endpoint = endmatrix * Mathutils.Vector((radius, 0, 0)) - points = [startpoint] - - if end < start: - end +=360 - - delta = end - start - length = radians(delta) * radius - if radius < step*10: # if circumfrance is too small - pieces = int(delta/10) # set a fixed step of 10 degrees - else: - pieces = int(length/step) # figure out how many pieces we need for our arc - if pieces == 0: # stupid way to avoid a div by zero error - pieces = 1 # what would be a smarter way to fix this? - step = delta/pieces # set step so pieces * step = degrees in arc - - stepmatrix = Mathutils.RotationMatrix(step, 3, "Z") - point = Mathutils.Vector(startpoint) - for i in range(int(pieces)): - point = stepmatrix * point - points.append(point) - points.append(endpoint) - - if center: - points = [[point[0]+center[0], point[1]+center[1], point[2]+center[2]] for point in points] - edges = [[num, num+1] for num in range(len(points)-1)] - - return points, edges -# Here are some alternate drawing functions for creating curve geometery. - -def drawCurveCircle(circle): - """Given a dxf circle object return a blender circle object using curves.""" - c = Curve.New('circle') # create new curve data - - center = circle.loc - radius = circle.radius - - p1 = (0, -radius, 0) - p2 = (radius, 0, 0) - p3 = (0, radius, 0) - p4 = (-radius, 0, 0) - - p1 = BezTriple.New(p1) - p2 = BezTriple.New(p2) - p3 = BezTriple.New(p3) - p4 = BezTriple.New(p4) - - curve = c.appendNurb(p1) - curve.append(p2) - curve.append(p3) - curve.append(p4) - for point in curve: - point.handleTypes = [AUTO, AUTO] - curve.flagU = 1 # Set curve cyclic - c.update() - - ob = Object.New('Curve', 'circle') # make curve object - return ob - -def drawCurveArc(arc): - """Given a dxf circle object return a blender circle object using curves.""" - if start > end: - start = start - 360 - startmatrix = Mathutils.RotationMatrix(start, 3, "Z") - startpoint = startmatrix * Mathutils.Vector((radius, 0, 0)) - endmatrix = Mathutils.RotationMatrix(end, 3, "Z") - endpoint = endmatrix * Mathutils.Vector((radius, 0, 0)) - # Note: handles must be tangent to arc and of correct length... - - a = Curve.New('arc') # create new curve data - - center = circle.loc - radius = circle.radius - - p1 = (0, -radius, 0) - p2 = (radius, 0, 0) - p3 = (0, radius, 0) - p4 = (-radius, 0, 0) - - p1 = BezTriple.New(p1) - p2 = BezTriple.New(p2) - p3 = BezTriple.New(p3) - p4 = BezTriple.New(p4) - - curve = a.appendNurb(p1) - curve.append(p2) - curve.append(p3) - curve.append(p4) - for point in curve: - point.handleTypes = [AUTO, AUTO] - curve.flagU = 1 # Set curve cyclic - a.update() - - ob = Object.New('Curve', 'arc') # make curve object - return ob + + +def objectify(data): #----------------------------------------------------------------- + """Expects a section type object's data as input. + + Maps object data to the correct object type. + """ + #print 'deb:objectify start %%%%%%%%%%%' #--------------- + objects = [] # colector for finished objects + known_types = type_map.keys() # so we don't have to call foo.keys() every iteration + curves_on = GUI_A['curves_on'].val + index = 0 + while index < len(data): + item = data[index] + #print 'deb:objectify item: \n', item #------------ + if type(item) != list and item.type == 'table': + item.data = objectify(item.data) # tables have sub-objects + objects.append(item) + elif type(item) != list and item.type == 'polyline': #remi --todo----------- + #print 'deb:gosub Polyline\n' #------------- + pline = Polyline(item) + while 1: + index += 1 + item = data[index] + if item.type == 'vertex': + #print 'deb:objectify gosub Vertex--------' #------------- + v = Vertex(item) + if pline.spline: # if Bspline-curve + # then for Blender-mesh filter only additional_vertices + # OR + # then for Blender-curve filter only spline_control_vertices + if (v.spline and not curves_on) or (curves_on and v.splin2): #correct for real NURBS-import + #if (v.spline and not curves_on) or (curves_on and not v.splin2): #fake for Bezier-emulation of NURBS-import + pline.points.append(v) + elif pline.curved: # if Bezier-curve + # then for Blender-mesh filter only curve_additional_vertices + # OR + # then for Blender-curve filter curve_control_vertices + if not curves_on or (curves_on and not v.curved): + pline.points.append(v) + else: + pline.points.append(v) + elif item.type == 'seqend': + #print 'deb:objectify it is seqEND ---------\n' #------------- + break + else: + print "Error: non-vertex found before seqend!" + index -= 1 #so go back one step + break + objects.append(pline) + elif type(item) != list and item.type in ['block', 'insert']: + if not settings.var['block_nn'] and item.name.startswith('*X'): + #print 'deb:objectify item.type:"%s", item.name:"%s"' %(item.type, item.name) #------------ + pass + elif settings.var['blockFilter_on'] and not settings.accepted_block(item.name): + pass + else: + try: + objects.append(type_map[item.type](item)) + except TypeError: + pass + elif type(item) != list and item.type in known_types: + # proccess the object and append the resulting object + try: + objects.append(type_map[item.type](item)) + except TypeError: + pass + else: + #we will just let the data pass un-harrased + #objects.append(item) + pass + index += 1 + #print 'deb:objectify objects:\n', objects #------------ + #print 'deb:objectify END %%%%%%%%' #------------ + return objects + + + +class MatColors: #----------------------------------------------------------------- + """A smart container for dxf-color based materials. + + This class is a wrapper around a dictionary mapping dxf-color indicies to materials. + When called with a color_index + it returns a material corresponding to that index. + Behind the scenes it checks if that index is in its keys, and if not it creates + a new material. It then adds the new index:material pair to its dict and returns + the material. + """ + + def __init__(self, layersmap): + """Expects a map - a dictionary mapping layer names to layers. + """ + #self.layersmap = layersmap # a dictionary of layername:layerobject + self.colMaterials = {} # a dictionary of color_index:blender_material + #print 'deb:init_MatColors argument.map: ', map #------------------ + + + def __call__(self, color=None): + """Return the material associated with color. + + If a layer name is provided, the color of that layer is used. + """ + if color == None: color = 256 # color 256=BYLAYER + if type(color) == str: # looking for color of LAYER named "color" + #--todo---bug with ARC from ARC-T0.DXF layer="T-3DARC-1"----- + #print 'deb:color is string:--------: ', color + #try: + #color = layersmap[color].color + #print 'deb:color=self.map[color].color:', color #------------------ + #except KeyError: + #layer = Layer(name=color, color=256, frozen=False) + #layersmap[color] = layer + #color = 0 + if layersmap: color = layersmap[color].color + if color == 256: # color 0 = BYLAYER + #--todo-- should looking for color of LAYER + #if layersmap: color = layersmap[color].color + color = 3 + if color == 0: # color 0 = BYBLOCK + #--todo-- should looking for color of paret-BLOCK + #if layersmap: color = layersmap[color].color + color = 3 + color = abs(color) # cause the value could be nagative = means the layer is turned off + + if color not in self.colMaterials.keys(): + self.add(color) + return self.colMaterials[color] + + + def add(self, color): + """Create a new material 'ColorNr-N' using the provided color index-N. + """ + #global color_map #--todo-- has not to be global? + mat = Material.New('ColorNr-%s' %color) + mat.setRGBCol(color_map[color]) + #mat.mode |= Material.Modes.SHADELESS #--todo-- + #mat.mode |= Material.Modes.WIRE +# try: mat.setMode('Shadeless', 'Wire') #work-around for 2.45rc-bug +# except: pass + self.colMaterials[color] = mat + + + +class MatLayers: #----------------------------------------------------------------- + """A smart container for dxf-layer based materials. + + This class is a wrapper around a dictionary mapping dxf-layer names to materials. + When called with a layer name it returns a material corrisponding to that. + Behind the scenes it checks if that layername is in its keys, and if not it creates + a new material. It then adds the new layername:material pair to its dict and returns + the material. + """ + + def __init__(self, layersmap): + """Expects a map - a dictionary mapping layer names to layers. + """ + #self.layersmap = layersmap # a dictionary of layername:layer + self.layMaterials = {} # a dictionary of layer_name:blender_material + #print 'deb:init_MatLayers argument.map: ', map #------------------ + + + def __call__(self, layername=None, color=None): + """Return the material associated with dxf-layer. + + If a dxf-layername is not provided, create a new material + """ + #global layernamesmap + layername_short = layername + if layername in layernamesmap.keys(): + layername_short = layernamesmap[layername] + colorlayername = layername_short + if color: colorlayername = str(color) + colorlayername + if colorlayername not in self.layMaterials.keys(): + self.add(layername, color, colorlayername) + return self.layMaterials[colorlayername] + + + def add(self, layername, color, colorlayername): + """Create a new material 'layername'. + """ + try: mat = Material.Get('L-%s' %colorlayername) + except: mat = Material.New('L-%s' %colorlayername) + #print 'deb:MatLayers material: ', mat #---------- + #global settings + #print 'deb:MatLayers material_from: ', settings.var['material_from'] #---------- + if settings.var['material_from'] == 3 and color: + if color == 0 or color == 256: mat_color = 3 + else: mat_color = color + elif layersmap and layername: + mat_color = layersmap[layername].color + else: mat_color = 3 + #print 'deb:MatLayers color: ', color #----------- + #print 'deb:MatLayers mat_color: ', mat_color #----------- + mat.setRGBCol(color_map[abs(mat_color)]) + #mat.mode |= Material.Modes.SHADELESS + #mat.mode |= Material.Modes.WIRE +# try: mat.setMode('Shadeless', 'Wire') #work-around for 2.45rc-bug +# except: pass + self.layMaterials[colorlayername] = mat + + + + +class Blocks: #----------------------------------------------------------------- + """A smart container for blocks. + + This class is a wrapper around a dictionary mapping block names to Blender data blocks. + When called with a name string it returns a block corresponding to that name. + Behind the scenes it checks if that name is in its keys, and if not it creates + a new data block. It then adds the new name:block_data pair to its dict and returns + the block. + """ + + def __init__(self, blocksmap, settings): + """Expects a dictionary mapping block_name:block_data. + """ + self.blocksmap = blocksmap #a dictionary mapping block_name:block_data + self.settings = settings + self.blocks = {} #container for blender groups representing blocks + + + def __call__(self, name=None): + """Return the data block associated with that block_name. + + If that name is not in its keys, it creates a new data block. + If no name is provided return entire self.blocks container. + """ + if name == None: + return self.blocks + if name not in self.blocks.keys(): + self.addBlock(name) + return self.blocks[name] + + + def addBlock(self, name): + """Create a new 'block group' for the block name. + """ + block = self.blocksmap[name] + prefix = 'bl' + if block.xref: prefix = 'xr' + blender_group = Group.New('%s_%s' %(prefix,name)) # Blender groupObject contains definition of BLOCK + block_def = [blender_group, block.loc] + self.settings.write("\nDrawing block:\'%s\' ..." % name) + + if block.xref: + obname = 'xr_%s' %name # create object name from xref block name + #obname = obname[:MAX_NAMELENGTH] + # if material BYBLOCK def needed: use as placeholder a mesh-vertex instead of empty + ob = SCENE.objects.new('Empty', obname) # create a new empty_object + empty_size = 1.0 * settings.var['g_scale'] + if empty_size < 0.01: empty_size = 0.01 #Blender limits (0.01-10.0) + elif empty_size > 10.0: empty_size = 10.0 + ob.drawSize = empty_size + ob.loc = tuple(block.loc) + ob.properties['xref_path'] = block.path + ob.layers = [19] + insertFlag=True; blockFlag=True + global oblist + oblist.append((ob, insertFlag, blockFlag)) + else: + drawEntities(block.entities, self.settings, block_def) + self.settings.write("Drawing block:\'%s\' done!" %name) + self.blocks[name] = blender_group + + + + + +class Settings: #----------------------------------------------------------------- + """A container for all the import settings and objects used by the draw functions. + + This is like a collection of globally accessable persistant properties and functions. + """ + # Optimization constants + MIN = 0 + MID = 1 + PRO = 2 + MAX = 3 + + def __init__(self, keywords, drawTypes): + """initialize all the important settings used by the draw functions. + """ + self.obj_number = 1 #global object_number for progress_bar + + self.var = dict(keywords) #a dictionary of (key_variable:Value) control parameter + self.drawTypes = dict(drawTypes) #a dictionary of (entity_type:True/False) = import on/off for this entity_type + + self.var['colorFilter_on'] = False #deb:remi------------ + self.acceptedColors = [0,2,3,4,5,6,7,8,9, + 10 ] + + self.var['layerFilter_on'] = False #deb:remi------------ + self.acceptedLayers = ['3', + '0' + ] + + #self.var['blockFilter_on'] = 0 #deb:remi------------ + self.acceptedBlocks = ['WALL_1871', + 'BOX02' + ] + self.unwantedBlocks = ['BOX05', + 'BOX04' + ] + + + def update(self, keywords, drawTypes): + """update all the important settings used by the draw functions. + mostly used after loading parameters from INI-file + """ + + for k, v in keywords.iteritems(): + self.var[k] = v + #print 'deb:settings_update var %s= %s' %(k, self.var[k]) #-------------- + for t, v in drawTypes.iteritems(): + self.drawTypes[t] = v + #print 'deb:settings_update drawType %s= %s' %(t, self.drawTypes[t]) #-------------- + + self.drawTypes['arc'] = self.drawTypes['line'] + self.drawTypes['circle'] = self.drawTypes['line'] + self.drawTypes['ellipse'] = self.drawTypes['line'] + self.drawTypes['trace'] = self.drawTypes['solid'] + self.drawTypes['insert'] = self.drawTypes['block'] + #self.drawTypes['vport'] = self.drawTypes['view'] + + #print 'deb:self.drawTypes', self.drawTypes #--------------- + + + def validate(self, drawing): + """Given the drawing, build dictionaries of Layers, Colors and Blocks. + """ + + #de: paßt die distance parameter an globalScale + if self.var['g_scale'] != 1.0: + self.var['dist_min'] = self.var['dist_min'] / self.var['g_scale'] + self.var['thick_min'] = self.var['thick_min'] / self.var['g_scale'] + self.var['width_min'] = self.var['width_min'] / self.var['g_scale'] + self.var['arc_rad'] = self.var['arc_rad'] / self.var['g_scale'] + + self.g_origin = Mathutils.Vector(self.var['g_originX'], self.var['g_originY'], self.var['g_originZ']) + + # First sort out all the section_items + sections = dict([(item.name, item) for item in drawing.data]) + + # The section:header may be omited + if 'header' in sections.keys(): + self.write("found section:header") + else: + self.write("File contains no section:header!") + + if self.var['optimization'] == 0: self.var['one_mesh_on'] = 0 + # The section:tables may be partialy or completely missing. + self.layersTable = False + self.colMaterials = MatColors({}) #A container for dxf-color based materials + self.layMaterials = MatLayers({}) #A container for dxf-layer based materials + #self.collayMaterials = MatColLayers({}) #A container for dxf-color+layer based materials + global layersmap, layernamesmap + layersmap, layernamesmap = {}, {} + if 'tables' in sections.keys(): + self.write("found section:tables") + views, vports, layers = False, False, False + for table in drawing.tables.data: + if table.name == 'layer': + self.write("found table:layers") + layers = table + elif table.name == 'view': + print "found table:view" + views = table + elif table.name == 'vport': + print "found table:vport" + vports = table + if layers: #---------------------------------- + # Read the layers table and get the layer colors + layersmap, layernamesmap = getLayersmap(layers) + self.colMaterials = MatColors(layersmap) + self.layMaterials = MatLayers(layersmap) + else: + self.write("File contains no table:layers!") + + + if views: #---------------------------------- + if self.var['views_on']: + global oblist + for item in views.data: + if type(item) != list and item.type == 'view': + #print 'deb:settings_valid views dir(item)=', dir(item) #------------- + #print 'deb:settings_valid views item=', item #------------- + ob = item.draw(self) + #viewsmap[item.name] = [item.length] + #--todo-- add to obj_list for global.Scaling + insertFlag, blockFlag = False, False + oblist.append((ob, insertFlag, blockFlag)) + + else: + self.write("File contains no table:views!") + + + if vports: #---------------------------------- + if self.var['views_on']: + global oblist + for item in vports.data: + if type(item) != list and item.type == 'vport': + #print 'deb:settings_valid views dir(item)=', dir(item) #------------- + #print 'deb:settings_valid views item=', item #------------- + ob = item.draw(self) + #viewsmap[item.name] = [item.length] + #--todo-- add to obj_list for global.Scaling + insertFlag, blockFlag = False, False + oblist.append((ob, insertFlag, blockFlag)) + else: + self.write("File contains no table:vports!") + + + else: + self.write("File contains no section:tables!") + self.write("File contains no table:layers!") + + + # The section:blocks may be omited + if 'blocks' in sections.keys(): + self.write("found section:blocks") + # Read the block definitions and build our block object + if self.drawTypes['insert']: #if support for entity type 'Insert' is activated + #Build a dictionary of blockname:block_data pairs + blocksmap, self.obj_number = getBlocksmap(drawing, layersmap, self.var['layFrozen_on']) + self.blocknamesmap = getBlocknamesmap(blocksmap) + self.blocks = Blocks(blocksmap, self) # initiates container for blocks_data + self.usedBlocks = blocksmap.keys() + #print 'deb:settings_valid self.usedBlocks', self.usedBlocks #---------- + else: + self.write("ignored, because support for BLOCKs is turn off!") + #print 'deb:settings_valid self.obj_number', self.obj_number #---------- + else: + self.write("File contains no section:blocks!") + self.drawTypes['insert'] = False + + # The section:entities + if 'entities' in sections.keys(): + self.write("found section:entities") + + self.obj_number += len(drawing.entities.data) + #print 'deb:settings_valid self.obj_number', self.obj_number #---------- + self.obj_number = 1.0 / self.obj_number + + + def accepted_block(self, name): + if name not in self.usedBlocks: return False + if name in self.unwantedBlocks: return False + elif name in self.acceptedBlocks: return True + #elif (name.find('*X')+1): return False + #elif name.startswith('3'): return True + #elif name.endswith('H'): return False + return True + + + def write(self, text, newline=True): + """Wraps the built-in print command in a optimization check. + """ + if self.var['optimization'] <= self.MID: + if newline: + print text + else: + print text, + + + def redraw(self): + """Update Blender if optimization level is low enough. + """ + if self.var['optimization'] <= self.MIN: + Blender.Redraw() + + + def progress(self, done, text): + """Wrapper for Blender.Window.DrawProgressBar. + """ + if self.var['optimization'] <= self.PRO: + progressbar = done * self.obj_number + Window.DrawProgressBar(progressbar, text) + #print 'deb:drawer done, progressbar: ', done, progressbar #----------------------- + + def layer_isOff(self, layername): # no more used ------- + """Given a layer name, and return its visible status. + """ + # if layer is off then color_index is negative + if layersmap and layersmap[layername].color < 0: return True + #print 'deb:layer_isOff: layer is ON' #--------------- + return False + + + def layer_isFrozen(self, layername): # no more used ------- + """Given a layer name, and return its frozen status. + """ + if layersmap and layersmap[layername].frozen: return True + #print 'deb:layer_isFrozen: layer is not FROZEN' #--------------- + return False + + + +def analyzeDXF(dxfFile): #--------------------------------------- + """list statistics about LAYER and BLOCK dependences into textfile.INF + + """ + Window.WaitCursor(True) # Let the user know we are thinking + print 'reading DXF file: %s.' % dxfFile + time1 = Blender.sys.time() #time marker1 + drawing = readDXF(dxfFile, objectify) + print 'finish reading in %.4f sec.' % (Blender.sys.time()-time1) + + # First sort out all the section_items + sections = dict([(item.name, item) for item in drawing.data]) + + # The section:header may be omited + if 'header' in sections.keys(): print "found section:header" + else: print "File contains no section:header!" + + # The section:tables may be partialy or completely missing. + layersTable = False + global layersmap + layersmap = {} + viewsmap = {} + vportsmap = {} + layersmap_str = '#File contains no table:layers!' + viewsmap_str = '#File contains no table:views!' + vportsmap_str = '#File contains no table:vports!' + if 'tables' in sections.keys(): + print "found section:tables" + views, vports, layers = False, False, False + for table in drawing.tables.data: + if table.name == 'layer': + print "found table:layers" + layers = table + elif table.name == 'view': + print "found table:view" + views = table + elif table.name == 'vport': + print "found table:vport" + vports = table + if layers: #---------------------------------- + for item in layers.data: + if type(item) != list and item.type == 'layer': + #print dir(item) + layersmap[item.name] = [item.color, item.frozen] + #print 'deb:analyzeDXF: layersmap=' , layersmap #------------- + layersmap_str = '#list of LAYERs: name, color, frozen_status ---------------------------\n' + key_list = layersmap.keys() + key_list.sort() + for key in key_list: + #for layer_name, layer_data in layersmap.iteritems(): + layer_name, layer_data = key, layersmap[key] + layer_str = '\'%s\': col=%s' %(layer_name,layer_data[0])#------------- + if layer_data[1]: layer_str += ', frozen' + layersmap_str += layer_str + '\n' + #print 'deb:analyzeDXF: layersmap_str=\n' , layersmap_str #------------- + else: + print "File contains no table:layers!" + + if views: #---------------------------------- + for item in views.data: + if type(item) != list and item.type == 'view': + #print dir(item) + viewsmap[item.name] = [item.length] + #print 'deb:analyzeDXF: viewsmap=' , viewsmap #------------- + viewsmap_str = '#list of VIEWs: name, focus_length ------------------------------------\n' + key_list = viewsmap.keys() + key_list.sort() + for key in key_list: + #for view_name, view_data in viewsmap.iteritems(): + view_name, view_data = key, viewsmap[key] + view_str = '\'%s\': length=%s' %(view_name,view_data[0])#------------- + #if view_data[1]: view_str += ', something' + viewsmap_str += view_str + '\n' + #print 'deb:analyzeDXF: layersmap_str=\n' , layersmap_str #------------- + else: + print "File contains no table:views!" + + if vports: #---------------------------------- + for item in vports.data: + if type(item) != list and item.type == 'vport': + #print dir(item) + vportsmap[item.name] = [item.length] + #print 'deb:analyzeDXF: vportsmap=' , vportsmap #------------- + vportsmap_str = '#list of VPORTs: name, focus_length -----------------------------------\n' + key_list = vportsmap.keys() + key_list.sort() + for key in key_list: + #for vport_name, vport_data in vportsmap.iteritems(): + vport_name, vport_data = key, vportsmap[key] + vport_str = '\'%s\': length=%s' %(vport_name,vport_data[0])#------------- + #if vport_data[1]: vport_str += ', something' + vportsmap_str += vport_str + '\n' + #print 'deb:analyzeDXF: vportsmap_str=\n' , vportsmap_str #------------- + else: + print "File contains no table:vports!" + + else: + print "File contains no section:tables!" + print "File contains no tables:layers,views,vports!" + + # The section:blocks may be omited + if 'blocks' in sections.keys(): + print "found section:blocks" + blocksmap = {} + for item in drawing.blocks.data: + #print 'deb:getBlocksmap item=' ,item #-------- + #print 'deb:getBlocksmap item.entities=' ,item.entities #-------- + #print 'deb:getBlocksmap item.entities.data=' ,item.entities.data #-------- + if type(item) != list and item.type == 'block': + xref = False + if item.xref: xref = True + childList = [] + used = False + for item2 in item.entities.data: + if type(item2) != list and item2.type == 'insert': + #print 'deb:getBlocksmap dir(item2)=', dir(item2) #---------- + item2str = [item2.name, item2.layer, item2.color_index, item2.scale, item2.space] + childList.append(item2str) + try: blocksmap[item.name] = [used, childList, xref] + except KeyError: print 'Cannot map "%s" - "%s" as Block!' %(item.name, item) + #print 'deb:analyzeDXF: blocksmap=' , blocksmap #------------- + + for item2 in drawing.entities.data: + if type(item2) != list and item2.type == 'insert': + if not layersmap or (layersmap and not layersmap[item2.layer][1]): #if insert_layer is not frozen + blocksmap[item2.name][0] = True # marked as world used BLOCK + + key_list = blocksmap.keys() + key_list.reverse() + for key in key_list: + if blocksmap[key][0]: #if used + for child in blocksmap[key][1]: + if not layersmap or (layersmap and not layersmap[child[1]][1]): #if insert_layer is not frozen + blocksmap[child[0]][0] = True # marked as used BLOCK + + blocksmap_str = '#list of BLOCKs: name:(unused)(xref) -[child_name, layer, color, scale, space]-------\n' + key_list = blocksmap.keys() + key_list.sort() + for key in key_list: + #for block_name, block_data in blocksmap.iteritems(): + block_name, block_data = key, blocksmap[key] + block_str = '\'%s\': ' %(block_name) #------------- + used = '(unused)' + if block_data[0]: used = '' +# else: used = '(unused)' + xref = '' + if block_data[2]: xref = '(xref)' + blocksmap_str += block_str + used + xref +'\n' + if block_data: + for block_item in block_data[1]: + block_data_str = ' - %s\n' %block_item + blocksmap_str += block_data_str + #print 'deb:analyzeDXF: blocksmap_str=\n' , blocksmap_str #------------- + else: + blocksmap_str = '#File contains no section:blocks!' + print "File contains no section:blocks!" + + Window.WaitCursor(False) + output_str = '%s\n%s\n%s\n%s' %(viewsmap_str, vportsmap_str, layersmap_str, blocksmap_str) + infFile = dxfFile[:-4] + '_DXF.INF' # replace last char:'.dxf' with '_DXF.inf' + try: + f = file(infFile, 'w') + f.write(INFFILE_HEADER + '\n# this is a comment line\n\n') + f.write(output_str) + f.close() + Draw.PupMenu('DXF importer: report saved in INF-file:%t|' + '\'%s\'' %infFile) + except: + Draw.PupMenu('DXF importer: ERROR by writing report in INF-file:%t|' + '\'%s\'' %infFile) + finally: + f.close() + + + + +def main(dxfFile): #---------------#############################----------- + #print 'deb:filename:', filename #-------------- + global SCENE + editmode = Window.EditMode() # are we in edit mode? If so ... + if editmode: + Window.EditMode(0) # leave edit mode before + + #SCENE = bpy.data.scenes.active + #SCENE.objects.selected = [] # deselect all + + global cur_COUNTER #counter for progress_bar + cur_COUNTER = 0 + + try: + #print "Getting settings..." + global GUI_A, GUI_B, g_scale_as + if not GUI_A['g_scale_on'].val: + GUI_A['g_scale'].val = 1.0 + + keywords = {} + drawTypes = {} + for k, v in GUI_A.iteritems(): + keywords[k] = v.val + for k, v in GUI_B.iteritems(): + drawTypes[k] = v.val + #print 'deb:startUInew keywords: ', keywords #-------------- + #print 'deb:startUInew drawTypes: ', drawTypes #-------------- + + # The settings object controls how dxf entities are drawn + settings.update(keywords, drawTypes) + #print 'deb:settings.var:\n', settings.var #----------------------- + + if not settings: + #Draw.PupMenu('DXF importer: EXIT!%t') + #print '\nDXF Import: terminated by user!' + print '\nDXF Import: terminated, cause settings failure!' + Window.WaitCursor(False) + if editmode: Window.EditMode(1) # and put things back how we fond them + return None + + #no more used dxfFile = dxfFileName.val + #print 'deb: dxfFile file: ', dxfFile #---------------------- + if dxfFile.lower().endswith('.dxf') and sys.exists(dxfFile): + Window.WaitCursor(True) # Let the user know we are thinking + print 'reading file: %s.' % dxfFile + time1 = Blender.sys.time() #time marker1 + drawing = readDXF(dxfFile, objectify) + print 'reading finished in %.4f sec.' % (Blender.sys.time()-time1) + Window.WaitCursor(False) + else: + if UI_MODE: Draw.PupMenu('DXF importer: Alert!%t| no valid DXF-file selected!') + print "DXF importer: Alert! - no valid DXF-file selected." + Window.WaitCursor(False) + if editmode: Window.EditMode(1) # and put things back how we fond them + return None + + # Draw all the know entity types in the current scene + global oblist + oblist = [] # a list of all created AND linked objects for final f_globalScale + time2 = Blender.sys.time() #time marker2 + + Window.WaitCursor(True) # Let the user know we are thinking + settings.write("\n\nDrawing entities...") + + settings.validate(drawing) + + global activObjectLayer, activObjectName + activObjectLayer, activObjectName = None, None + + drawEntities(drawing.entities, settings) + + #print 'deb:drawEntities after: oblist:', oblist #----------------------- + if oblist: # and settings.var['g_scale'] != 1: + globalScale(oblist, settings.var['g_scale']) + + # Set visibility for all layers on all View3d + #Window.ViewLayers([i+1 for i in range(18)]) # for 2.45 + SCENE.setLayers([i+1 for i in range(18)]) + SCENE.update(1) + SCENE.objects.selected = [i[0] for i in oblist] #select only the imported objects + #SCENE.objects.selected = SCENE.objects #select all objects in current scene + Blender.Redraw() + + time_text = Blender.sys.time() - time2 + Window.WaitCursor(False) + if settings.var['paper_space_on']: space = 'from paper space' + else: space = 'from model space' + ob_len = len(oblist) + message = ' %s objects imported %s in %.4f sec. -----DONE-----' % (ob_len, space, time_text) + settings.progress(1.0/settings.obj_number, 'DXF import done!') + print message + #settings.write(message) + if UI_MODE: Draw.PupMenu('DXF importer: Done!|finished in %.4f sec.' % time_text) + + finally: + # restore state even if things didn't work + #print 'deb:drawEntities finally!' #----------------------- + Window.WaitCursor(False) + if editmode: Window.EditMode(1) # and put things back how we fond them + + + +def getOCS(az): #----------------------------------------------------------------- + """An implimentation of the Arbitrary Axis Algorithm. + """ + #decide if we need to transform our coords + #if az[0] == 0 and az[1] == 0: + if abs(az[0]) < 0.00001 and abs(az[1]) < 0.00001: + if az[2] > 0.0: + return False + elif az[2] < 0.0: + ax = Mathutils.Vector(-1.0, 0, 0) + ay = Mathutils.Vector(0, 1.0, 0) + az = Mathutils.Vector(0, 0, -1.0) + return ax, ay, az + + az = Mathutils.Vector(az) + + cap = 0.015625 # square polar cap value (1/64.0) + if abs(az.x) < cap and abs(az.y) < cap: + ax = Mathutils.CrossVecs(WORLDY, az) + else: + ax = Mathutils.CrossVecs(WORLDZ, az) + ax = ax.normalize() + ay = Mathutils.CrossVecs(az, ax) + ay = ay.normalize() + return ax, ay, az + + + +def transform(normal, rotation, obj): #-------------------------------------------- + """Use the calculated ocs to determine the objects location/orientation in space. + + Quote from dxf docs: + The elevation value stored with an entity and output in DXF files is a sum + of the Z-coordinate difference between the UCS XY plane and the OCS XY + plane, and the elevation value that the user specified at the time the entity + was drawn. + """ + ma = Mathutils.Matrix([1,0,0],[0,1,0],[0,0,1]) + o = Mathutils.Vector(obj.loc) + ocs = getOCS(normal) + if ocs: + ma = Mathutils.Matrix(ocs[0], ocs[1], ocs[2]) + o = ma.invert() * o + ma = Mathutils.Matrix(ocs[0], ocs[1], ocs[2]) + + if rotation != 0: + g = radians(-rotation) + rmat = Mathutils.Matrix([cos(g), -sin(g), 0], [sin(g), cos(g), 0], [0, 0, 1]) + ma = rmat * ma + + obj.setMatrix(ma) + obj.loc = o + #print 'deb:new obj.matrix:\n', obj.getMatrix() #-------------------- + + + +def rotXY_Vec(rotation, vec): #---------------------------------------------------- + """Rotate vector vec in XY-plane. vec must be in radians + """ + if rotation != 0: + o = Mathutils.Vector(vec) + g = radians(-rotation) + vec = o * Mathutils.Matrix([cos(g), -sin(g), 0], [sin(g), cos(g), 0], [0, 0, 1]) + return vec + + + +def getLayersmap(layers): #------------------------------------------------------ + """Build two dictionaries: 1.layername:layer object, and 2.layername:layername_short + """ + layersmap = {} + layernamesmap = {} + for item in layers.data: + if type(item) != list and item.type == 'layer': + layersmap[item.name] = item + layername_short = item.name[:MAX_NAMELENGTH-1] + i = 0 #sufix for layernames cause Blender-objectnames-limits + while layername_short in layernamesmap.keys(): + i += 1 + suffix = str(i) + layername_short = layername_short[:-2] + suffix + layernamesmap[item.name] = layername_short + + #print 'deb:getLayersmap layersmap:\n', layersmap #------------ + #print 'deb:getLayersmap layernamesmap:\n', layernamesmap #------------ + return layersmap, layernamesmap + + + +def getBlocksmap(drawing, layersmap, layFrozen_on=False): #-------------------------------------------------------- + """Build a dictionary of blockname:block_data pairs + """ + usedblocks = {} + for item in drawing.blocks.data: + #print 'deb:getBlocksmap item=%s\n i.entities=%s\n i.data=%s' %(item,item.entities,item.entities.data) + if type(item) != list and item.type == 'block': + childList = [] + used = False + for item2 in item.entities.data: + if type(item2) != list and item2.type == 'insert': + #print 'deb:getBlocksmap dir(item2)=', dir(item2) #---------- + item2str = [item2.name, item2.layer] + childList.append(item2str) + try: usedblocks[item.name] = [used, childList] + except KeyError: print 'Cannot map "%s" - "%s" as Block!' %(item.name, item) + #print 'deb:getBlocksmap: usedblocks=' , usedblocks #------------- + print 'deb:getBlocksmap: layersmap=' , layersmap #------------- + + for item in drawing.entities.data: + if type(item) != list and item.type == 'insert': + if not layersmap or (not layersmap[item.layer].frozen or layFrozen_on): #if insert_layer is not frozen + try: usedblocks[item.name][0] = True + except: pass + + key_list = usedblocks.keys() + key_list.reverse() + for key in key_list: + if usedblocks[key][0]: #if parent used, then set used also all child blocks + for child in usedblocks[key][1]: + if not layersmap or (layersmap and not layersmap[child[1]].frozen): #if insert_layer is not frozen + usedblocks[child[0]][0] = True # marked as used BLOCK + + usedblocks = [i for i in usedblocks.keys() if usedblocks[i][0]] + #print 'deb:getBlocksmap: usedblocks=' , usedblocks #------------- + obj_number = 0 + blocksmap = {} + for item in drawing.blocks.data: + if type(item) != list and item.type == 'block' and item.name in usedblocks: + #if item.name.startswith('*X'): #--todo-- + obj_number += len(item.entities.data) + try: blocksmap[item.name] = item + except KeyError: print 'Cannot map "%s" - "%s" as Block!' %(item.name, item) + + + #print 'deb:getBlocksmap: blocksmap:\n', blocksmap #------------ + return blocksmap, obj_number + + +def getBlocknamesmap(blocksmap): #-------------------------------------------------------- + """Build a dictionary of blockname:blockname_short pairs + """ + #print 'deb:getBlocknamesmap blocksmap:\n', blocksmap #------------ + blocknamesmap = {} + for n in blocksmap.keys(): + blockname_short = n[:MAX_NAMELENGTH-1] + i = 0 #sufix for blockname cause Blender-objectnamelength-limit + while blockname_short in blocknamesmap.keys(): + i += 1 + suffix = str(i) + blockname_short = blockname_short[:-2] + suffix + blocknamesmap[n] = blockname_short + #print 'deb:getBlocknamesmap blocknamesmap:\n', blocknamesmap #------------ + return blocknamesmap + + +def drawEntities(entities, settings, block_def=None): #---------------------------------------- + """Draw every kind of thing in the entity list. + + If provided 'block_def': the entities are to be added to the Blender 'group'. + """ + for _type in type_map.keys(): + #print 'deb:drawEntities_type:', _type #------------------ + # for each known type get a list of that type and call the associated draw function + entities_type = entities.get_type(_type) + if entities_type: drawer(_type, entities_type, settings, block_def) + + +def drawer(_type, entities, settings, block_def): #------------------------------------------ + """Call with a list of entities and a settings object to generate Blender geometry. + + If 'block_def': the entities are to be added to the Blender 'group'. + """ + #print 'deb:drawer _type, entities:\n ', _type, entities #----------------------- + + if entities: + # Break out early if settings says we aren't drawing the current dxf-type + global cur_COUNTER #counter for progress_bar + group = None + #print 'deb:drawer.check:_type: ', _type #-------------------- + if _type == '3dface':_type = 'face' # hack, while python_variable_name can not beginn with a nummber + if not settings.drawTypes[_type] or _type == 'block_record': + message = 'Skipping dxf\'%ss\' entities' %_type + settings.write(message, True) + cur_COUNTER += len(entities) + settings.progress(cur_COUNTER, message) + return + #print 'deb:drawer.todo:_type:', _type #----------------------- + #print 'deb:drawer entities:\n ', entities #----------------------- + + len_temp = len(entities) + # filtering only model-space enitities (no paper-space enitities) + if settings.var['paper_space_on']: + entities = [entity for entity in entities if entity.space != 0] + else: + entities = [entity for entity in entities if entity.space == 0] + + # filtering only objects with color from acceptedColorsList + if settings.var['colorFilter_on']: + entities = [entity for entity in entities if entity.color in settings.acceptedColors] + + # filtering only objects on layers from acceptedLayersList + if settings.var['layerFilter_on']: + #entities = [entity for entity in entities if entity.layer[0] in ['M','3','0'] and not entity.layer.endswith('H')] + entities = [entity for entity in entities if entity.layer in settings.acceptedLayers] + + # filtering only objects on not-frozen layers + if layersmap and not settings.var['layFrozen_on']: + entities = [entity for entity in entities if not layersmap[entity.layer].frozen] + + global activObjectLayer, activObjectName + activObjectLayer = '' + activObjectName = '' + + message = "Drawing dxf\'%ss\'..." %_type + cur_COUNTER += len_temp - len(entities) + settings.write(message, False) + settings.progress(cur_COUNTER, message) + if len(entities) > 0.1 / settings.obj_number: + show_progress = int(0.03 / settings.obj_number) + else: show_progress = 0 + cur_temp = 0 + + #print 'deb:drawer cur_COUNTER: ', cur_COUNTER #----------------------- + + for entity in entities: #----loop------------------------------------- + settings.write('\b.', False) + cur_COUNTER += 1 + if show_progress: + cur_temp += 1 + if cur_temp == show_progress: + settings.progress(cur_COUNTER, message) + cur_temp = 0 + #print 'deb:drawer show_progress=',show_progress #---------------- + + # get the layer group (just to make things a little cleaner) + if settings.var['group_bylayer_on'] and not block_def: + group = getGroup('l:%s' % layernamesmap[entity.layer]) + + if _type == 'insert': #---- INSERT and MINSERT=array -------------------- + if not settings.var['block_nn'] and entity.name.startswith('*X'): #---- support for noname BLOCKs + #print 'deb:drawer entity.name:', entity.name #------------ + continue + elif settings.var['blockFilter_on'] and not settings.accepted_block(entity.name): + continue + + #print 'deb:insert entity.loc:', entity.loc #---------------- + insertFlag = True + columns = entity.columns[0] + coldist = entity.columns[1] + rows = entity.rows[0] + rowdist = entity.rows[1] + deltaloc = [0,0,0] + #print 'deb:insert columns, rows:', columns, rows #----------- + for col in xrange(columns): + deltaloc[0] = col * coldist + for row in xrange(rows): + deltaloc[1] = row * rowdist + #print 'deb:insert col=%s, row=%s,deltaloc=%s' %(col, row, deltaloc) #------ + ob = entity.draw(settings, deltaloc) #-----draw BLOCK---------- + if block_def: + blockFlag = True + bl_loc = block_def[1] + ob.loc = [ob.loc[0]-bl_loc[0],ob.loc[1]-bl_loc[1],ob.loc[2]-bl_loc[2]] + else: blockFlag = False + setObjectProperties(ob, group, entity, settings, block_def) + if ob: + if settings.var['optimization'] <= settings.MIN: + #if settings.var['g_origin_on'] and not block_def: ob.loc = Mathutils.Vector(ob.loc) + settings.g_origin + if settings.var['g_scale_on']: globalScaleOne(ob, insertFlag, blockFlag, settings.var['g_scale']) + settings.redraw() + else: oblist.append((ob, insertFlag, blockFlag)) + + else: #---draw entities except BLOCKs/INSERTs--------------------- + insertFlag = False + alt_obname = activObjectName + ob = entity.draw(settings) + if ob and ob.name != alt_obname: + if block_def: + blockFlag = True + bl_loc = block_def[1] + ob.loc = [ob.loc[0]-bl_loc[0],ob.loc[1]-bl_loc[1],ob.loc[2]-bl_loc[2]] + else: blockFlag = False + setObjectProperties(ob, group, entity, settings, block_def) + if settings.var['optimization'] <= settings.MIN: + #if settings.var['g_origin_on'] and not block_def: ob.loc = Mathutils.Vector(ob.loc) + settings.g_origin + if settings.var['g_scale_on']: globalScaleOne(ob, insertFlag, blockFlag, settings.var['g_scale']) + settings.redraw() + else: oblist.append((ob, insertFlag, blockFlag)) + + #print 'deb:Finished drawing:', entities[0].type #------------------------ + message = "\nDrawing dxf\'%ss\' done!" % _type + settings.write(message, True) + + + +def globalScale(oblist, SCALE): #--------------------------------------------------------- + """Global_scale for list of all imported objects. + + oblist is a list of pairs (ob, insertFlag), where insertFlag=True/False + """ + #print 'deb:globalScale.oblist: ---------%\n', oblist #--------------------- + for l in oblist: + ob, insertFlag, blockFlag = l[0], l[1], l[2] + globalScaleOne(ob, insertFlag, blockFlag, SCALE) + + +def globalScaleOne(ob, insertFlag, blockFlag, SCALE): #--------------------------------------------------------- + """Global_scale imported object. + """ + #print 'deb:globalScaleOne ob: ', ob #--------------------- + if settings.var['g_origin_on'] and not blockFlag: + ob.loc = Mathutils.Vector(ob.loc) + settings.g_origin + + SCALE_MAT= Mathutils.Matrix([SCALE,0,0,0],[0,SCALE,0,0],[0,0,SCALE,0],[0,0,0,1]) + if insertFlag: # by BLOCKs/INSERTs only insert-point coords must be scaled------------ + ob.loc = Mathutils.Vector(ob.loc) * SCALE_MAT + else: # entire scaling for all other imported objects ------------ + if ob.type == 'Mesh': + me = Mesh.Get(ob.name) + # set centers of all objects in (0,0,0) + #me.transform(ob.matrixWorld*SCALE_MAT) + #ob.loc = Mathutils.Vector([0,0,0]) + # preseve centers of all objects + me.transform(SCALE_MAT) + ob.loc = Mathutils.Vector(ob.loc) * SCALE_MAT + else: #--todo-- also for curves: neutral scale factor after import + ob.setMatrix(ob.matrixWorld*SCALE_MAT) + + +def setObjectProperties(ob, group, entity, settings, block_def): #----------------------- + """Link object to scene. + """ + + if not ob: #remi--todo----------------------- + message = "\nObject \'%s\' not found!" %entity + settings.write(message) + return + + if group: + setGroup(group, ob) # if object belongs to group + + if block_def: # if object belongs to BLOCK_def - Move it to layer nr19 + setGroup(block_def[0], ob) + #print 'deb:setObjectProperties \'%s\' set to block_def_group!' %ob.name #--------- + ob.layers = [19] + else: + #ob.layers = [i+1 for i in xrange(20)] #remi--todo------------ + ob.layers = [settings.var['target_layer']] + + # Set material for any objects except empties + if ob.type != 'Empty' and settings.var['material_on']: + setMaterial_from(entity, ob, settings, block_def) + + # Set the visibility + #if settings.layer_isOff(entity.layer): + if layersmap and layersmap[entity.layer].color < 0: # color is negative if layer is off + #ob.layers = [20] #remi--todo------------- + ob.restrictDisplay = True + ob.restrictRender = True + + #print 'deb:\n---------linking Object %s!' %ob.name #---------- + + + +def getGroup(name): #----------------------------------------------------------------- + """Returns a Blender group-object. + """ + try: + group = Group.Get(name) + except: # What is the exception? + group = Group.New(name) + return group + + +def setGroup(group, ob): #------------------------------------------------------------ + """Assigns object to Blender group. + """ + try: + group.objects.link(ob) + except: + group.objects.append(ob) #remi?--------------- + + + +def setMaterial_from(entity, ob, settings, block_def): #------------------------------------------------ + """ Set Blender-material for the object controled by item. + + Set Blender-material for the object + - controlled by settings.var['material_from'] + """ + if settings.var['material_from'] == 1: # 1= material from color + if entity.color_index == BYLAYER or entity.color_index == 256: + mat = settings.colMaterials(entity.layer) + elif entity.color_index == BYBLOCK or entity.color_index == 0: + #--todo-- looking for block.color_index + #mat = settings.colMaterials(block.color_index) + #if block_def: mat = settings.colMaterials(block_def[2]) + mat = settings.colMaterials(3) + else: + mat = settings.colMaterials(entity.color_index) + + elif settings.var['material_from'] == 2: # 2= material from layer_name + mat = settings.layMaterials(layername = entity.layer) + + elif settings.var['material_from'] == 3: # 3= material from layer+color + mat = settings.layMaterials(layername=entity.layer, color=entity.color_index) + +# elif settings.var['material_from'] == 4: # 4= material from block_name + +# elif settings.var['material_from'] == 5: # 5= material from XDATA + +# elif settings.var['material_from'] == 6: # 6= material from INI-file + + else: # set neutral material + try: + mat = Material.Get('dxf-neutral') + except: + mat = Material.New('dxf-neutral') + mat.setRGBCol(color_map[3]) + try:mat.setMode('Shadeless', 'Wire') #work-around for 2.45rc1-bug + except: + mat.mode |= Material.Modes.SHADELESS # + mat.mode |= Material.Modes.WIRE + try: + #print 'deb:material mat:', mat #----------- + ob.setMaterials([mat]) #assigns Blender-material to object + except ValueError: + settings.write("material error - \'%s\'!" %mat) + ob.colbits = 0x01 # Set OB materials. + + + +def calcBulge(p1, p2, arc_res, triples=False): #------------------------------------------------- + """given startpoint, endpoint and bulge of arc, returns points/segments of its representation. + + Needs to take into account bulge sign. + negative = clockwise + positive = counter-clockwise + + to find center given two points, and arc angle + calculate radius + Cord = sqrt(start^2 + end^2) + S = (bulge*Cord)/2 + radius = ((Cord/2)^2+S^2)/2*S + angle of arc = 4*atan( bulge ) + angle from p1 to center is (180-angle)/2 + get vector pointing from p1 to p2 (p2 - p1) + normalize it and multiply by radius + rotate around p1 by angle to center, point to center. + start angle = angle between (center - p1) and worldX + end angle = angle between (center - p2) and worldX + + calculate the center, radius, start angle, and end angle + returns points/segments of its mesh representation + incl.startpoint, without endpoint + """ + + bulge = p1.bulge + p1 = Mathutils.Vector(p1.loc) + p2 = Mathutils.Vector(p2.loc) + cord = p2 - p1 # vector from p1 to p2 + clength = cord.length + s = (bulge * clength)/2.0 # sagitta (height) + radius = abs(((clength/2.0)**2.0 + s**2.0)/(2.0*s)) # magic formula + angle = (degrees(4.0*atan(bulge))) # theta (included angle) + radial = cord.normalize() * radius # a radius length vector aligned with cord + delta = (180.0 - abs(angle))/2.0 # the angle from cord to center + if bulge < 0: delta = -delta + rmat = Mathutils.RotationMatrix(-delta, 3, 'Z') + center = p1 + (rmat * radial) # rotate radial by delta degrees, then add to p1 to find center + #length = radians(abs(angle)) * radius + #print 'deb:calcBulge:\n angle, delta: ', angle, delta #---------------- + #print 'deb:center, radius: ', center, radius #---------------------- + startpoint = p1 - center + endpoint = p2 - center + #print 'deb:calcBulg: startpoint:', startpoint #--------- + #print 'deb:calcBulg: endpoint:', endpoint #--------- + + if not triples: #IF mesh-representation ----------- + if arc_res > 1024: arc_res = 1024 + elif arc_res < 4: arc_res = 4 + pieces = int(abs(angle)/(360.0/arc_res)) # set a fixed step of ARC_RESOLUTION + if pieces < 3: pieces = 3 + else: #IF curve-representation ------------------------------- + if arc_res > 32: arc_res = 32 + elif arc_res < 3: arc_res = 3 + pieces = int(abs(angle)/(360.0/arc_res)) # set a fixed step of ARC_RESOLUTION + if pieces < 2: pieces = 2 + + step = angle/pieces # set step so pieces * step = degrees in arc + stepmatrix = Mathutils.RotationMatrix(-step, 3, "Z") + + if not triples: #IF mesh-representation ----------- + points = [startpoint] + point = startpoint + for i in xrange(int(pieces)-1): #fast (but not so acurate as: vector * RotMatrix(-step*i,3,"Z") + point = stepmatrix * point + points.append(point) + points = [ point+center for point in points] + # vector to point convertion: + points = [list(point) for point in points] + return points, list(center) + + else: #IF curve-representation ------------------------------- + # correct Bezier curves representation for free segmented circles/arcs + step2 = radians(step * 0.5) + bulg = radius * (1 - cos(step2)) + deltaY = 4.0 * bulg / (3.0 * sin(step2) ) + #print 'deb:calcArcCurve: bulg, deltaY:\n', bulg, deltaY #--------- + #print 'deb:calcArcCurve: step:\n', step #--------- + + #org handler0 = Mathutils.Vector(0.0, -deltaY, 0.0) + #handler = startmatrix * handler0 + #endhandler = endmatrix * handler0 + rotMatr90 = Mathutils.Matrix([0, -1, 0], [1, 0, 0], [0, 0, 1]) + handler = rotMatr90 * startpoint + handler = - deltaY * handler.normalize() + endhandler = rotMatr90 * endpoint + endhandler = - deltaY * endhandler.normalize() + + points = [startpoint] + handlers1 = [startpoint + handler] + handlers2 = [startpoint - handler] + point = Mathutils.Vector(startpoint) + for i in xrange(int(pieces)-1): + point = stepmatrix * point + handler = stepmatrix * handler + handler1 = point + handler + handler2 = point - handler + points.append(point) + handlers1.append(handler1) + handlers2.append(handler2) + points.append(endpoint) + handlers1.append(endpoint + endhandler) + handlers2.append(endpoint - endhandler) + + points = [point + center for point in points] + handlers1 = [point + center for point in handlers1] + handlers2 = [point + center for point in handlers2] + + VectorTriples = [list(h1)+list(p)+list(h2) for h1,p,h2 in zip(handlers1, points, handlers2)] + #print 'deb:calcBulgCurve: handlers1:\n', handlers1 #--------- + #print 'deb:calcBulgCurve: points:\n', points #--------- + #print 'deb:calcBulgCurve: handlers2:\n', handlers2 #--------- + #print 'deb:calcBulgCurve: VectorTriples:\n', VectorTriples #--------- + return VectorTriples + + + + +def calcArc(center, radius, start, end, arc_res, triples): #----------------------------------------- + """calculate Points (or BezierTriples) for ARC/CIRCLEs representation. + + Given parameters of the ARC/CIRCLE, + returns points/segments (or BezierTriples) and centerPoint + """ + # center is currently set by object + # if start > end: start = start - 360 + if end > 360: end = end % 360.0 + + startmatrix = Mathutils.RotationMatrix(-start, 3, "Z") + startpoint = startmatrix * Mathutils.Vector(radius, 0, 0) + endmatrix = Mathutils.RotationMatrix(-end, 3, "Z") + endpoint = endmatrix * Mathutils.Vector(radius, 0, 0) + + if end < start: end +=360.0 + angle = end - start + #length = radians(angle) * radius + + if not triples: #IF mesh-representation ----------- + if arc_res > 1024: arc_res = 1024 + elif arc_res < 4: arc_res = 4 + pieces = int(abs(angle)/(360.0/arc_res)) # set a fixed step of ARC_RESOLUTION + if pieces < 3: pieces = 3 + step = angle/pieces # set step so pieces * step = degrees in arc + stepmatrix = Mathutils.RotationMatrix(-step, 3, "Z") + + points = [startpoint] + point = startpoint + for i in xrange(int(pieces)-1): + point = stepmatrix * point + points.append(point) + points.append(endpoint) + + if center: + centerVec = Mathutils.Vector(center) + #points = [point + centerVec for point in points()] + points = [point + centerVec for point in points] + # vector to point convertion: + points = [list(point) for point in points] + return points + + else: #IF curve-representation --------------- + if arc_res > 32: arc_res = 32 + elif arc_res < 3: arc_res = 3 + pieces = int(abs(angle)/(360.0/arc_res)) # set a fixed step of ARC_RESOLUTION + if pieces < 2: pieces = 2 + step = angle/pieces # set step so pieces * step = degrees in arc + stepmatrix = Mathutils.RotationMatrix(-step, 3, "Z") + + # correct Bezier curves representation for free segmented circles/arcs + step2 = radians(step * 0.5) + bulg = radius * (1 - cos(step2)) + deltaY = 4.0 * bulg / (3.0 * sin(step2) ) + #print 'deb:calcArcCurve: bulg, deltaY:\n', bulg, deltaY #--------- + #print 'deb:calcArcCurve: step:\n', step #--------- + handler0 = Mathutils.Vector(0.0, -deltaY, 0.0) + + points = [startpoint] + handler = startmatrix * handler0 + endhandler = endmatrix * handler0 + handlers1 = [startpoint + handler] + handlers2 = [startpoint - handler] + point = Mathutils.Vector(startpoint) + for i in xrange(int(pieces)-1): + point = stepmatrix * point + handler = stepmatrix * handler + handler1 = point + handler + handler2 = point - handler + points.append(point) + handlers1.append(handler1) + handlers2.append(handler2) + points.append(endpoint) + handlers1.append(endpoint + endhandler) + handlers2.append(endpoint - endhandler) + VectorTriples = [list(h1)+list(p)+list(h2) for h1,p,h2 in zip(handlers1, points, handlers2)] + #print 'deb:calcArcCurve: handlers1:\n', handlers1 #--------- + #print 'deb:calcArcCurve: points:\n', points #--------- + #print 'deb:calcArcCurve: handlers2:\n', handlers2 #--------- + #print 'deb:calcArcCurve: VectorTriples:\n', VectorTriples #--------- + return VectorTriples + + +def drawCurveCircle(circle): #--- no more used -------------------------------------------- + """Given a dxf circle object return a blender circle object using curves. + """ + c = Curve.New('circle') # create new curve data + center = circle.loc + radius = circle.radius + + p1 = (0, -radius, 0) + p2 = (radius, 0, 0) + p3 = (0, radius, 0) + p4 = (-radius, 0, 0) + + p1 = BezTriple.New(p1) + p2 = BezTriple.New(p2) + p3 = BezTriple.New(p3) + p4 = BezTriple.New(p4) + + curve = c.appendNurb(p1) + curve.append(p2) + curve.append(p3) + curve.append(p4) + for point in curve: + point.handleTypes = [AUTO, AUTO] + curve.flagU = 1 # Set curve cyclic + c.update() + + ob = Object.New('Curve', 'circle') # make curve object + return ob + + +def drawCurveArc(self): #---- only for ELLIPSE ------------------------------------------------------------- + """Given a dxf ELLIPSE object return a blender_curve. + """ + center = self.loc + radius = self.radius + start = self.start_angle + end = self.end_angle + + if start > end: + start = start - 360.0 + startmatrix = Mathutils.RotationMatrix(start, 3, "Z") + startpoint = startmatrix * Mathutils.Vector((radius, 0, 0)) + endmatrix = Mathutils.RotationMatrix(end, 3, "Z") + endpoint = endmatrix * Mathutils.Vector((radius, 0, 0)) + # Note: handles must be tangent to arc and of correct length... + + a = Curve.New('arc') # create new curve data + + p1 = (0, -radius, 0) + p2 = (radius, 0, 0) + p3 = (0, radius, 0) + p4 = (-radius, 0, 0) + + p1 = BezTriple.New(p1) + p2 = BezTriple.New(p2) + p3 = BezTriple.New(p3) + p4 = BezTriple.New(p4) + + curve = a.appendNurb(p1) + curve.append(p2) + curve.append(p3) + curve.append(p4) + for point in curve: + point.handleTypes = [AUTO, AUTO] + curve.flagU = 1 # Set curve cyclic + a.update() + + ob = Object.New('Curve', 'arc') # make curve object + return ob + + + + +# GUI STUFF -----#################################################----------------- +from Blender.BGL import * + +EVENT_NONE = 1 +EVENT_START = 2 +EVENT_REDRAW = 3 +EVENT_LOAD_INI = 4 +EVENT_SAVE_INI = 5 +EVENT_RESET = 6 +EVENT_CHOOSE_INI = 7 +EVENT_CHOOSE_DXF = 8 +EVENT_HELP = 9 +EVENT_PRESETCURV = 10 +EVENT_PRESETS = 11 +EVENT_DXF_DIR = 12 +EVENT_DXF_NAME = 13 +EVENT_LIST = 14 +EVENT_ORIGIN = 15 +EVENT_SCALE = 16 +EVENT_PRESET2D = 20 +EVENT_PRESET3D = 21 +EVENT_EXIT = 100 +GUI_EVENT = EVENT_NONE + +GUI_A = {} # GUI-buttons dictionary for parameter +GUI_B = {} # GUI-buttons dictionary for drawingTypes + +# settings default, initialize ------------------------ + +points_as_menu = "convert to: %t|empty %x1|mesh.vertex %x2|thin sphere %x3|thin box %x4|*curve.vertex %x5" +lines_as_menu = "convert to: %t|*edge %x1|mesh %x2|*thin cylinder %x3|thin box %x4|curve %x5" +mlines_as_menu = "convert to: %t|*edge %x1|*mesh %x2|*thin cylinder %x3|*thin box %x|*curve %x5" +plines_as_menu = "convert to: %t|*edge %x1|mesh %x2|*thin cylinder %x3|*thin box %x4|curve %x5" +plines3_as_menu = "convert to: %t|*edge %x1|mesh %x2|*thin cylinder %x3|*thin box %x4|curve %x5" +plmesh_as_menu = "convert to: %t|*edge %x1|mesh %x2" +solids_as_menu = "convert to: %t|*edge %x1|mesh %x2" +blocks_as_menu = "convert to: %t|dupliGroup %x1|*real.Group %x2|*exploded %x3" +texts_as_menu = "convert to: %t|text %x1|*mesh %x2|*curve %x5" +material_from_menu= "material from: %t|*LINESTYLE %x7|COLOR %x1|LAYER %x2|*LAYER+COLOR %x3|*BLOCK %x4|*XDATA %x5|*INI-File %x6" +g_scale_list = ''.join(( + 'scale factor: %t', + '|user def. %x12', + '|yard to m %x8', + '|feet to m %x7', + '|inch to m %x6', + '| x 1000 %x3', + '| x 100 %x2', + '| x 10 %x1', + '| x 1 %x0', + '| x 0.1 %x-1', + '| x 0.01 %x-2', + '| x 0.001 %x-3', + '| x 0.0001 %x-4', + '| x 0.00001 %x-5')) + +#print 'deb: g_scale_list', g_scale_list #----------- + +dxfFileName = Draw.Create("") +iniFileName = Draw.Create(INIFILE_DEFAULT_NAME + INIFILE_EXTENSION) +user_preset = 0 +config_UI = Draw.Create(0) #switch_on/off extended config_UI +g_scale_as = Draw.Create(int(log10(G_SCALE))) + + +keywords_org = { + 'curves_on' : 0, + 'optimization': 2, + 'one_mesh_on': 1, + 'vGroup_on' : 1, + 'dummy_on' : 0, + 'views_on' : 0, + 'cams_on' : 0, + 'lights_on' : 0, + 'xref_on' : 1, + 'block_nn': 0, + 'blockFilter_on': 0, + 'newScene_on' : 1, + 'target_layer' : TARGET_LAYER, + 'group_bylayer_on' : GROUP_BYLAYER, + 'g_originX' : G_ORIGIN_X, + 'g_originY' : G_ORIGIN_Y, + 'g_originZ' : G_ORIGIN_Z, + 'g_origin_on': 0, + 'g_scale' : float(G_SCALE), +# 'g_scale_as': int(log10(G_SCALE)), # 0, + 'g_scale_on': 0, + 'thick_on' : 1, + 'thick_min' : float(MIN_THICK), + 'thick_force': 0, + 'width_on' : 1, + 'width_min' : float(MIN_WIDTH), + 'width_force': 0, + 'dist_on' : 1, + 'dist_min' : float(MIN_DIST), + 'dist_force': 0, + 'material_on': 1, + 'material_from': 2, + 'pl_3d' : 1, + 'fill_on' : 1, + 'meshSmooth_on': 1, + 'curve_res' : CURV_RESOLUTION, + 'curve_arc' : CURVARC_RESOLUTION, + 'arc_res' : ARC_RESOLUTION, + 'arc_rad' : ARC_RADIUS, + 'thin_res' : THIN_RESOLUTION, + 'pl_trim_max' : TRIM_LIMIT, + 'pl_trim_on': 1, + 'paper_space_on': 0, + 'layFrozen_on': 0, + 'Z_force_on': 0, + 'Z_elev': float(ELEVATION), + 'points_as' : 2, + 'lines_as' : 2, + 'mlines_as' : 2, + 'plines_as' : 2, + 'plines3_as': 2, + 'plmesh_as' : 2, + 'solids_as' : 2, + 'blocks_as' : 1, + 'texts_as' : 1 + } + +drawTypes_org = { + 'point' : 1, + 'line' : 1, + 'arc' : 1, + 'circle': 1, + 'ellipse': 1, + 'mline' : 0, + 'polyline': 1, + 'plmesh': 1, + 'pline3': 1, + 'lwpolyline': 1, + 'text' : 1, + 'mtext' : 0, + 'block' : 1, + 'insert': 1, + 'solid' : 1, + 'trace' : 1, + 'face' : 1, +# 'view' : 0, + } + +# creating of GUI-buttons +# GUI_A - GUI-buttons dictionary for parameter +# GUI_B - GUI-buttons dictionary for drawingTypes +for k, v in keywords_org.iteritems(): + GUI_A[k] = Draw.Create(v) +for k, v in drawTypes_org.iteritems(): + GUI_B[k] = Draw.Create(v) +#print 'deb:init GUI_A: ', GUI_A #--------------- +#print 'deb:init GUI_B: ', GUI_B #--------------- +# initialize settings-object controls how dxf entities are drawn +settings = Settings(keywords_org, drawTypes_org) + + +def update_RegistryKey(key, item): # + """updates key in Blender.Registry + """ + cache = True # data is also saved to a file + rdict = Registry.GetKey('DXF_Importer', cache) + if not rdict: rdict = {} + if item: + rdict[key] = item + Registry.SetKey('DXF_Importer', rdict, cache) + #print 'deb:update_RegistryKey rdict', rdict #--------------- + + +def check_RegistryKey(key): + """ check if the key is already there (saved on a previous execution of this script) + """ + cache = True # data is also saved to a file + rdict = Registry.GetKey('DXF_Importer', cache) + #print 'deb:check_RegistryKey rdict:', rdict #---------------- + if rdict: # if found, get the values saved there + try: + item = rdict[key] + return item + except: + #update_RegistryKey() # if data isn't valid rewrite it + pass + +def saveConfig(): #--todo----------------------------------------------- + """Save settings/config/materials from GUI to INI-file. + + Write all config data to INI-file. + """ + global iniFileName + + iniFile = iniFileName.val + #print 'deb:saveConfig inifFile: ', inifFile #---------------------- + if iniFile.lower().endswith(INIFILE_EXTENSION): + + #--todo-- sort key.list for output + #key_list = GUI_A.keys().val + #key_list.sort() + #for key in key_list: + # l_name, l_data = key, GUI_A[key].val + # list_A + + output_str = '[%s,%s]' %(GUI_A, GUI_B) + if output_str =='None': + Draw.PupMenu('DXF importer: INI-file: Alert!%t|no config-data present to save!') + else: + #if BPyMessages.Warning_SaveOver(iniFile): #<- remi find it too abstarct + if sys.exists(iniFile): + try: + f = file(iniFile, 'r') + try: header_str = f.readline() + finally: f.close() + except: pass + if header_str.startswith(INIFILE_HEADER[0:13]): + if Draw.PupMenu(' OK ? %t|SAVE OVER: ' + '\'%s\'' %iniFile) == 1: + save_ok = True + else: save_ok = False + elif Draw.PupMenu(' OK ? %t|SAVE OVER: ' + '\'%s\'' %iniFile + + '|Alert: this file has no valid ImportDXF-header| ! it may belong to another aplication !') == 1: + save_ok = True + else: save_ok = False + else: save_ok = True + + if save_ok: + # replace: ',' -> ',\n' + # replace: '{' -> '\n{\n' + # replace: '}' -> '\n}\n' + output_str = ',\n'.join(output_str.split(',')) + output_str = '\n}'.join(output_str.split('}')) + output_str = '{\n'.join(output_str.split('{')) + try: + f = file(iniFile, 'w') + try: + f.write(INIFILE_HEADER + '\n# this is a comment line\n') + f.write(output_str) + finally: f.close() + #Draw.PupMenu('DXF importer: INI-file: Done!%t|config-data saved in ' + '\'%s\'' %iniFile) + except: + Draw.PupMenu('DXF importer: INI-file: Error!%t|failure by writing to ' + '\'%s\'|no config-data saved!' %iniFile) + + else: + Draw.PupMenu('DXF importer: INI-file: Alert!%t|no valid name/extension for INI-file selected!') + print "DXF importer: Alert!: no valid INI-file selected." + if not iniFile: + if dxfFileName.val.lower().endswith('.dxf'): + iniFileName.val = dxfFileName.val[0:-4] + INIFILE_EXTENSION + + +def loadConfig(): #remi--todo----------------------------------------------- + """Load settings/config/materials from INI-file. + + Read material-assignements from config-file. + """ + #070724 buggy Window.FileSelector(loadConfigFile, 'Load config data from INI-file', inifilename) + global iniFileName, GUI_A, GUI_B + + iniFile = iniFileName.val + update_RegistryKey('iniFileName', iniFile) + #print 'deb:loadConfig iniFile: ', iniFile #---------------------- + if iniFile.lower().endswith(INIFILE_EXTENSION) and sys.exists(iniFile): + try: + f = file(iniFile, 'r') + try: + header_str = f.readline() + if header_str.startswith(INIFILE_HEADER): + data_str = f.read() + f.close() + #print 'deb:loadConfig data_str from %s: \n' %iniFile , data_str #----------------- + data = eval(data_str) + for k, v in data[0].iteritems(): + try: GUI_A[k].val = v + except: GUI_A[k] = Draw.Create(v) + for k, v in data[1].iteritems(): + try: GUI_B[k].val = v + except: GUI_B[k] = Draw.Create(v) + else: + Draw.PupMenu('DXF importer: INI-file: Alert!%t|no valid header in INI-file: ' + '\'%s\'' %iniFile) + finally: f.close() + except: pass + else: + Draw.PupMenu('DXF importer: INI-file: Alert!%t|no valid INI-file selected!') + print "DXF importer: Alert!: no valid INI-file selected." + if not iniFileName: + if dxfFileName.val.lower().endswith('.dxf'): + iniFileName.val = dxfFileName.val[0:-4] + INIFILE_EXTENSION + + + +def updateConfig(keywords, drawTypes): #----------------------------------------------- + """updates GUI_settings with given dictionaries + + """ + global GUI_A, GUI_B + #print 'deb:lresetDefaultConfig keywords_org: \n', keywords_org #--------- + for k, v in keywords.iteritems(): + GUI_A[k].val = v + for k, v in drawTypes.iteritems(): + GUI_B[k].val = v + +def resetDefaultConfig(): #----------------------------------------------- + """Resets settings/config/materials to defaults. + + """ + #print 'deb:lresetDefaultConfig keywords_org: \n', keywords_org #--------- + updateConfig(keywords_org, drawTypes_org) + + +def presetConfig_curv(): #----------------------------------------------- + """Sets settings/config/materials for curve representation. + + """ + global GUI_A + if GUI_A['curves_on'].val == 1: + GUI_A['points_as'].val = 5 + GUI_A['lines_as'].val = 5 + GUI_A['mlines_as'].val = 5 + GUI_A['plines_as'].val = 5 + GUI_A['plines3_as'].val = 5 + else: + GUI_A['points_as'].val = 2 + GUI_A['lines_as'].val = 2 + GUI_A['mlines_as'].val = 2 + GUI_A['plines_as'].val = 2 + GUI_A['plines3_as'].val = 2 + + +def resetDefaultConfig_2D(): #----------------------------------------------- + """Sets settings/config/materials to defaults 2D. + + """ +# presetConfig_curv() + keywords2d = { + 'views_on' : 0, + 'cams_on' : 0, + 'lights_on' : 0, + 'vGroup_on' : 1, + 'thick_on' : 0, + 'thick_force': 0, + 'width_on' : 1, + 'width_force': 0, + 'dist_on' : 1, + 'dist_force': 0, + 'pl_3d' : 0, + 'fill_on' : 0, + 'pl_trim_on': 1, + 'Z_force_on': 0, + 'meshSmooth_on': 0, + 'solids_as' : 2, + 'blocks_as' : 1, + 'texts_as' : 1 + } + + drawTypes2d = { + 'point' : 1, + 'line' : 1, + 'arc' : 1, + 'circle': 1, + 'ellipse': 1, + 'mline' : 0, + 'polyline': 1, + 'plmesh': 0, + 'pline3': 0, + 'lwpolyline': 1, + 'text' : 1, + 'mtext' : 0, + 'block' : 1, + 'insert': 1, + 'solid' : 1, + 'trace' : 1, + 'face' : 0, +# 'view' : 0, + } + + updateConfig(keywords2d, drawTypes2d) + +def resetDefaultConfig_3D(): #----------------------------------------------- + """Sets settings/config/materials to defaults 3D. + + """ +# presetConfig_curv() + keywords3d = { + 'vGroup_on' : 1, + 'thick_on' : 1, + 'thick_force': 0, + 'width_on' : 1, + 'width_force': 0, + 'dist_on' : 1, + 'dist_force': 0, + 'pl_3d' : 0, + 'fill_on' : 1, + 'pl_trim_on': 1, + 'Z_force_on': 0, + 'meshSmooth_on': 1, + 'solids_as' : 2, + 'blocks_as' : 1, + 'texts_as' : 1 + } + + drawTypes3d = { + 'point' : 1, + 'line' : 1, + 'arc' : 1, + 'circle': 1, + 'ellipse': 1, + 'mline' : 0, + 'polyline': 1, + 'plmesh': 1, + 'pline3': 1, + 'lwpolyline': 1, + 'text' : 1, + 'mtext' : 0, + 'block' : 1, + 'insert': 1, + 'solid' : 1, + 'trace' : 1, + 'face' : 1, +# 'view' : 0, + } + + updateConfig(keywords3d, drawTypes3d) + + +def inputGlobalScale(): + """Pop-up UI-Block for global scale factor + """ + global GUI_A + #print 'deb:inputGlobalScale ##########' #------------ + x_scale = Draw.Create(GUI_A['g_scale'].val) + block = [] + #block.append("global translation vector:") + block.append(("", x_scale, 0.0, 10000000.0)) + + retval = Draw.PupBlock("set global scale factor:", block) + + GUI_A['g_scale'].val = float(x_scale.val) + + +def inputOriginVector(): + """Pop-up UI-Block for global translation vector + """ + global GUI_A + #print 'deb:inputOriginVector ##########' #------------ + x_origin = Draw.Create(GUI_A['g_originX'].val) + y_origin = Draw.Create(GUI_A['g_originY'].val) + z_origin = Draw.Create(GUI_A['g_originZ'].val) + block = [] + #block.append("global translation vector:") + block.append(("X: ", x_origin, -100000000.0, 100000000.0)) + block.append(("Y: ", y_origin, -100000000.0, 100000000.0)) + block.append(("Z: ", z_origin, -100000000.0, 100000000.0)) + + retval = Draw.PupBlock("set global translation vector:", block) + + GUI_A['g_originX'].val = x_origin.val + GUI_A['g_originY'].val = y_origin.val + GUI_A['g_originZ'].val = z_origin.val + + +def draw_UI(): #----------------------------------------------------------------- + """ Draw startUI and setup Settings. + """ + global GUI_A, GUI_B #__version__ + global user_preset, iniFileName, dxfFileName, config_UI, g_scale_as + + # This is for easy layout changes + but_0c = 70 #button 1.column width + but_1c = 70 #button 1.column width + but_2c = 70 #button 2.column + but_3c = 70 #button 3.column + menu_margin = 10 + butt_margin = 10 + menu_w = (3 * butt_margin) + but_0c + but_1c + but_2c + but_3c #menu width + + simple_menu_h = 80 + extend_menu_h = 350 + y = simple_menu_h # y is menu upper.y + if config_UI.val: y += extend_menu_h + x = 20 #menu left.x + but0c = x + menu_margin #buttons 0.column position.x + but1c = but0c + but_0c + butt_margin + but2c = but1c + but_1c + butt_margin + but3c = but2c + but_2c + butt_margin + but4c = but3c + but_3c + + # Here starts menu ----------------------------------------------------- + #glClear(GL_COLOR_BUFFER_BIT) + #glRasterPos2d(8, 125) + + y += 30 + colorbox(x, y+20, x+menu_w+menu_margin*2, menu_margin) + Draw.Label("DXF-Importer ver." + __version__, but0c, y, menu_w, 20) + + if config_UI.val: + b0, b0_ = but0c, but_0c + butt_margin + b1, b1_ = but1c, but_1c + y -= 10 + + y -= 20 + Draw.BeginAlign() + GUI_B['point'] = Draw.Toggle('POINT', EVENT_REDRAW, b0, y, b0_, 20, GUI_B['point'].val, "support dxf-POINT on/off") + if GUI_B['point'].val: + GUI_A['points_as'] = Draw.Menu(points_as_menu, EVENT_NONE, b1, y, b1_, 20, GUI_A['points_as'].val, "select target Blender-object") +# Draw.Label('-->', but2c, y, but_2c, 20) + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_B['line'] = Draw.Toggle('LINE...etc', EVENT_REDRAW, b0, y, b0_, 20, GUI_B['line'].val, "support dxf-LINE,ARC,CIRCLE,ELLIPSE on/off") + if GUI_B['line'].val: + GUI_A['lines_as'] = Draw.Menu(lines_as_menu, EVENT_NONE, but1c, y, but_1c, 20, GUI_A['lines_as'].val, "select target Blender-object") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_B['mline'] = Draw.Toggle('*MLINE', EVENT_REDRAW, b0, y, b0_, 20, GUI_B['mline'].val, "(*wip)support dxf-MLINE on/off") + if GUI_B['mline'].val: + GUI_A['mlines_as'] = Draw.Menu(mlines_as_menu, EVENT_NONE, but1c, y, but_1c, 20, GUI_A['mlines_as'].val, "select target Blender-object") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_B['polyline'] = Draw.Toggle('2D-PLINE', EVENT_REDRAW, b0, y, b0_, 20, GUI_B['polyline'].val, "support dxf-2D-POLYLINE on/off") + if GUI_B['polyline'].val: + GUI_A['plines_as'] = Draw.Menu(plines_as_menu, EVENT_NONE, but1c, y, but_1c, 20, GUI_A['plines_as'].val, "select target Blender-object") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_B['pline3'] = Draw.Toggle('3D-PLINE', EVENT_REDRAW, b0, y, b0_, 20, GUI_B['pline3'].val, "support dxf-3D-POLYLINE on/off") + if GUI_B['pline3'].val: + GUI_A['plines3_as'] = Draw.Menu(plines3_as_menu, EVENT_NONE, but1c, y, but_1c, 20, GUI_A['plines3_as'].val, "select target Blender-object") + Draw.EndAlign() + + y -= 10 + y -= 20 + Draw.BeginAlign() + GUI_A['material_on'] = Draw.Toggle('material', EVENT_REDRAW, b0, y, b0_-20, 20, GUI_A['material_on'].val, "support for material assignment on/off") + if GUI_A['material_on'].val: + GUI_A['material_from'] = Draw.Menu(material_from_menu, EVENT_NONE, b1-20, y, b1_+20, 20, GUI_A['material_from'].val, "material assignment from?") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_A['g_origin_on'] = Draw.Toggle('glob.reLoc', EVENT_REDRAW, b0, y, b0_, 20, GUI_A['g_origin_on'].val, "global relocate all DXF objects on/off") + if GUI_A['g_origin_on'].val: + tmp = Draw.PushButton('=', EVENT_ORIGIN, b1, y, 20, 20, "edit relocation-vector (x,y,z in DXF units)") + origin_str = '(%.4f, %.4f, %.4f)' % ( + GUI_A['g_originX'].val, + GUI_A['g_originY'].val, + GUI_A['g_originZ'].val + ) + tmp = Draw.Label(origin_str, b1+20, y, 300, 20) + #GUI_A['g_origin'] = Draw.String('', EVENT_ORIGIN, b1, y, b1_, 20, GUI_A['g_origin'].val, "global translation-vector (x,y,z) in DXF units") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_A['g_scale_on'] = Draw.Toggle('glob.Scale', EVENT_REDRAW, b0, y, b0_, 20, GUI_A['g_scale_on'].val, "global scale all DXF objects on/off") + if GUI_A['g_scale_on'].val: + g_scale_as = Draw.Menu(g_scale_list, EVENT_SCALE, b1, y, 45, 20, g_scale_as.val, "factor for scaling the DXFdata") + if g_scale_as.val == 12: + pass + else: + if g_scale_as.val == 6: #scale inches to meters + GUI_A['g_scale'].val = 0.0254000 + elif g_scale_as.val == 7: #scale feets to meters + GUI_A['g_scale'].val = 0.3048000 + elif g_scale_as.val == 8: #scale yards to meters + GUI_A['g_scale'].val = 0.9144000 + else: + GUI_A['g_scale'].val = 10.0 ** int(g_scale_as.val) + scale_float = GUI_A['g_scale'].val + if scale_float < 0.000001 or scale_float > 1000000: + scale_str = ' = %s' % GUI_A['g_scale'].val + else: + scale_str = ' = %.6f' % GUI_A['g_scale'].val + Draw.Label(scale_str, b1+45, y, 200, 20) + Draw.EndAlign() + + y -= 10 + y -= 20 + Draw.BeginAlign() + GUI_A['meshSmooth_on'] = Draw.Toggle('smooth', EVENT_NONE, b0, y, b0_-20, 20, GUI_A['meshSmooth_on'].val, "mesh smooth for circles/arcsegments on/off") + GUI_A['pl_trim_on'] = Draw.Toggle('trim', EVENT_NONE, b1-20, y, 32, 20, GUI_A['pl_trim_on'].val, "intersection of POLYLINE-wide-segments on/off") + GUI_A['pl_trim_max'] = Draw.Number('', EVENT_NONE, b1+12, y, b1_-12, 20, GUI_A['pl_trim_max'].val, 0, 5, "limit for intersection of POLYLINE-wide-segments: 0.0-5.0") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() +# GUI_A['thin_res'] = Draw.Number('thin:', EVENT_NONE, but0c, y, but_0c, 20, GUI_A['thin_res'].val, 4, 64, "thin cylinder resolution - number of segments (4-64)") + GUI_A['arc_rad'] = Draw.Number('bR:', EVENT_NONE, b0, y, b0_, 20, GUI_A['arc_rad'].val, 0.01, 100, "basis radius for arc/circle resolution (0.01-100)") + GUI_A['arc_res'] = Draw.Number('', EVENT_NONE, b1, y, b1_/2, 20, GUI_A['arc_res'].val, 3, 500, "arc/circle resolution - number of segments (3-500)") + GUI_A['fill_on'] = Draw.Toggle('caps', EVENT_NONE, b1+b1_/2, y, b1_/2, 20, GUI_A['fill_on'].val, "draws top and bottom caps of CYLINDERs/closed curves on/off") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_A['curves_on'] = Draw.Toggle('to Curves', EVENT_PRESETCURV, b0, y, b0_, 20, GUI_A['curves_on'].val, "import into curves instead into meshes on/off") + GUI_A['curve_arc'] = Draw.Number('', EVENT_NONE, b1, y, b1_/2, 20, GUI_A['curve_arc'].val, 3, 32, "Bezier circle: amount of segments: 3-32") + GUI_A['curve_res'] = Draw.Number('', EVENT_NONE, b1+b1_/2, y, b1_/2, 20, GUI_A['curve_res'].val, 1, 128, "Set the Curve's U-resolution value: 1-128") + Draw.EndAlign() + + y -= 20 + GUI_A['group_bylayer_on'] = Draw.Toggle('Lay', EVENT_NONE, b0, y, 30, 20, GUI_A['group_bylayer_on'].val, "grouping entities from the same layer on/off") + GUI_A['vGroup_on'] = Draw.Toggle('vGroups', EVENT_NONE, b0+30, y, b1_-10, 20, GUI_A['vGroup_on'].val, "support Blender-VertexGroups on/off") + GUI_A['one_mesh_on'] = Draw.Toggle('oneMesh', EVENT_NONE, b1+10, y, b1_-10, 20, GUI_A['one_mesh_on'].val, "draw DXF-entities into one mesh-object. Recommended for big DXF-files. on/off") + + y -= 30 + Draw.BeginAlign() + GUI_A['paper_space_on'] = Draw.Toggle('paper space', EVENT_NONE, b0, y, b0_+20, 20, GUI_A['paper_space_on'].val, "import only layout (paper space) on/off") + GUI_A['layFrozen_on'] = Draw.Toggle('frozen', EVENT_NONE, b1+20, y, b1_-20, 20, GUI_A['layFrozen_on'].val, "import also from frozen layers on/off") + #GUI_A['dummy_on'] = Draw.Toggle('-', EVENT_NONE, but3c, y, but_3c, 20, GUI_A['dummy_on'].val, "dummy on/off") + Draw.EndAlign() + + y_down = y + # ----------------------------------------------- + y = simple_menu_h + extend_menu_h +20 + b0, b0_ = but2c, but_2c + butt_margin + b1, b1_ = but3c, but_3c + + y -= 20 + Draw.BeginAlign() + GUI_B['plmesh'] = Draw.Toggle('POLY-MESH/FACE', EVENT_NONE, b0, y, b0_+b1_, 20, GUI_B['plmesh'].val, "support dxf-POLYMESH/POLYFACE on/off") +# GUI_A['plmesh_as'] = Draw.Menu(plmesh_as_menu, EVENT_NONE, but1c, y, but_1c, 20, GUI_A['plmesh_as'].val, "select target Blender-object") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_B['solid'] = Draw.Toggle('SOLID', EVENT_NONE, b0, y, b0_, 20, GUI_B['solid'].val, "support dxf-SOLID and TRACE on/off") + GUI_B['face'] = Draw.Toggle('3DFACE', EVENT_NONE, b1, y, b1_, 20, GUI_B['face'].val, "support dxf-3DFACE on/off") +# GUI_A['solids_as'] = Draw.Menu(solids_as_menu, EVENT_NONE, but3c, y, but_3c, 20, GUI_A['solids_as'].val, "select target Blender-object") + Draw.EndAlign() + #print 'deb:support solid, trace', GUI_B['trace'].val, GUI_B['solid'].val # ------------ + + + y -= 20 + Draw.BeginAlign() + GUI_B['text'] = Draw.Toggle('TEXT', EVENT_NONE, b0, y, b0_, 20, GUI_B['text'].val, "support dxf-TEXT on/off") + GUI_B['mtext'] = Draw.Toggle('*MTEXT', EVENT_NONE, b1, y, b1_, 20, GUI_B['mtext'].val, "(*wip)support dxf-MTEXT on/off") +# GUI_A['texts_as'] = Draw.Menu(texts_as_menu, EVENT_NONE, but3c, y, but_3c, 20, GUI_A['texts_as'].val, "select target Blender-object") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_B['block'] = Draw.Toggle('BLOCK', EVENT_REDRAW, b0, y, b0_-30, 20, GUI_B['block'].val, "support dxf-BLOCK and ARRAY on/off") + GUI_B['insert'].val = GUI_B['block'].val + if GUI_B['block'].val: + GUI_A['block_nn'] = Draw.Toggle('n', EVENT_NONE, b1-30, y, 15, 20, GUI_A['block_nn'].val, "support hatch/noname BLOCKs *X... on/off") + GUI_A['blockFilter_on'] = Draw.Toggle('F', EVENT_NONE, b1-15, y, 15, 20, GUI_A['blockFilter_on'].val, "(*wip) support name filtering of BLOCKs on/off") + GUI_A['xref_on'] = Draw.Toggle('Xref', EVENT_NONE, b1, y, 20, 20, GUI_A['xref_on'].val, "support place holder for XREF-BLOCKs on/off") + GUI_A['blocks_as'] = Draw.Menu(blocks_as_menu, EVENT_NONE, b1+20, y, b1_-20, 20, GUI_A['blocks_as'].val, "select target representation for imported BLOCKs") + Draw.EndAlign() + + + y -= 20 + Draw.BeginAlign() + GUI_A['views_on'] = Draw.Toggle('views', EVENT_NONE, b0, y, b0_-25, 20, GUI_A['views_on'].val, "imports VIEWs and VIEWPORTs as cameras on/off") + GUI_A['cams_on'] = Draw.Toggle('*cams', EVENT_NONE, b1-25, y, b1_-25, 20, GUI_A['cams_on'].val, "(*wip) support ASHADE cameras on/off") + GUI_A['lights_on'] = Draw.Toggle('*lights', EVENT_NONE, b1+25, y, b1_-25, 20, GUI_A['lights_on'].val, "(*wip) support AVE_RENDER lights on/off") + Draw.EndAlign() + + y -= 10 + y -= 20 + y -= 20 + y -= 20 + + y -= 10 + y -= 20 + Draw.BeginAlign() + GUI_A['Z_force_on'] = Draw.Toggle('*elevation', EVENT_REDRAW, b0, y, b0_, 20, GUI_A['Z_force_on'].val, "*set objects Z-coordinates to elevation on/off") + if GUI_A['Z_force_on'].val: + GUI_A['Z_elev'] = Draw.Number('', EVENT_NONE, b1, y, b1_, 20, GUI_A['Z_elev'].val, -1000, 1000, "set default elevation(Z-coordinate)") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_A['dist_on'] = Draw.Toggle('dist.:', EVENT_NONE, b0, y, b0_-20, 20, GUI_A['dist_on'].val, "support distance on/off") + GUI_A['dist_force'] = Draw.Toggle('F', EVENT_NONE, b0+b0_-20, y, 20, 20, GUI_A['dist_force'].val, "force minimal distance on/off") + GUI_A['dist_min'] = Draw.Number('', EVENT_NONE, b1, y, b1_, 20, GUI_A['dist_min'].val, 0, 10, "minimal length/distance (double.vertex removing)") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_A['thick_on'] = Draw.Toggle('thick:', EVENT_NONE, b0, y, b0_-20, 20, GUI_A['thick_on'].val, "support thickness on/off") + GUI_A['thick_force'] = Draw.Toggle('F', EVENT_REDRAW, b0+b0_-20, y, 20, 20, GUI_A['thick_force'].val, "force for thickness at least limiter value on/off") + if GUI_A['thick_force'].val: + GUI_A['thick_min'] = Draw.Number('', EVENT_NONE, b1, y, b1_, 20, GUI_A['thick_min'].val, 0, 10, "minimal value for thickness") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + GUI_A['width_on'] = Draw.Toggle('width:', EVENT_NONE, b0, y, b0_-20, 20, GUI_A['width_on'].val, "support width on/off") + GUI_A['width_force'] = Draw.Toggle('F', EVENT_REDRAW, b0+b0_-20, y, 20, 20, GUI_A['width_force'].val, "force for width at least limiter value on/off") + if GUI_A['width_force'].val: + GUI_A['width_min'] = Draw.Number('', EVENT_NONE, b1, y, b1_, 20, GUI_A['width_min'].val, 0, 10, "minimal value for width") + Draw.EndAlign() + + y -= 30 + GUI_A['newScene_on'] = Draw.Toggle('newScene', EVENT_NONE, b0, y, b0_, 20, GUI_A['newScene_on'].val, "creates new Blender-Scene for each import on/off") + GUI_A['target_layer'] = Draw.Number('layer', EVENT_NONE, b1, y, b1_, 20, GUI_A['target_layer'].val, 1, 18, "imports into this Blender-layer (<19> reserved for block_definitions)") + + #-------------------------------------- + if y > y_down: y = y_down + #GUI_A['dummy_on'] = Draw.Toggle(' - ', EVENT_NONE, but0c, y, but_0c, 20, GUI_A['dummy_on'].val, "reserved") + y -= 30 + Draw.BeginAlign() + Draw.PushButton('INI file >', EVENT_CHOOSE_INI, but0c, y, but_0c, 20, 'Select INI-file from project directory') + iniFileName = Draw.String(' :', EVENT_NONE, but1c, y, menu_w-but_0c-butt_margin, 20, iniFileName.val, FILENAME_MAX, "write here the name of the INI-file") + Draw.EndAlign() + + y -= 20 + Draw.BeginAlign() + Draw.PushButton('#', EVENT_PRESETS, but0c, y, 20, 20, "tipist for Preset-INI-files") + Draw.PushButton('Load', EVENT_LOAD_INI, but0c+20, y, but_0c-20, 20, '.Loads configuration from ini-file: %s' % iniFileName.val) + Draw.PushButton('Save', EVENT_SAVE_INI, but1c, y, but_1c-20, 20, 'Saves configuration to ini-file: %s' % iniFileName.val) + Draw.EndAlign() + but_ = (but_2c+but_3c)/4 + but = but2c + Draw.PushButton('reset', EVENT_RESET, but, y, but_, 20, "reset configuration to defaults") + Draw.PushButton('2D', EVENT_PRESET2D, but+but_, y, but_, 20, 'set configuration for 2D import') + Draw.PushButton('3D', EVENT_PRESET3D, but+but_*2, y, but_, 20, 'set configuration for 3D import') + GUI_A['optimization'] = Draw.Number('', EVENT_NONE, but4c-35, y, 35, 20, GUI_A['optimization'].val, 0, 3, "Optimization Level: 0=Debug/directDrawing, 1=Verbose, 2=ProgressBar, 3=silentMode/fastest") + + + y = simple_menu_h + Draw.BeginAlign() + Draw.PushButton('DXFfile >', EVENT_CHOOSE_DXF, but0c, y, but_0c, 20, 'Select DXF-file from project directory') + dxfFileName = Draw.String(' :', EVENT_NONE, but1c, y, but_1c+but_2c+but_3c-20, 20, dxfFileName.val, FILENAME_MAX, "type the name of DXF-file or type *.dxf for multi-import") + Draw.PushButton('*.*', EVENT_DXF_DIR, but3c+but_3c-20, y, 20, 20, 'Set asterisk * as filter') + Draw.EndAlign() + + + y -= 50 + Draw.BeginAlign() + Draw.PushButton('EXIT', EVENT_EXIT, but0c, y, but_0c, 40, '' ) + Draw.PushButton('HELP', EVENT_HELP, but1c, y, but_1c-20, 20, 'calls BlenderWiki for Manual, Updates and Support.') + Draw.PushButton('?', EVENT_LIST, but1c+but_1c-20, y, 20, 20, 'DXF analyze tool: print listing of LAYERs and BLOCKs into the text file .INF') + Draw.PushButton('START IMPORT', EVENT_START, but2c, y, but_2c+but_3c+butt_margin, 40, 'Start the import procedure') + Draw.EndAlign() + + config_UI = Draw.Toggle('CONFIG', EVENT_REDRAW, but1c-butt_margin/2, y+20, but_1c+butt_margin, 20, config_UI.val, 'Advanced configuration on/off' ) + + y -= 20 + Draw.BeginAlign() + Draw.Label(' ', but0c-menu_margin, y, menu_margin, 20) + Draw.Label("*) parts under construction", but0c, y, menu_w, 20) + Draw.Label(' ', but0c+menu_w, y, menu_margin, 20) + Draw.EndAlign() + +#-- END GUI Stuf----------------------------------------------------- + +def colorbox(x,y,xright,bottom): + glColor3f(0.75, 0.75, 0.75) + glRecti(x + 1, y + 1, xright - 1, bottom - 1) + +def dxf_callback(input_filename): + global dxfFileName + dxfFileName.val=input_filename +# dirname == Blender.sys.dirname(Blender.Get('filename')) +# update_RegistryKey('DirName', dirname) +# update_RegistryKey('dxfFileName', input_filename) + +def ini_callback(input_filename): + global iniFileName + iniFileName.val=input_filename + +def event(evt, val): + if evt in (Draw.QKEY, Draw.ESCKEY) and not val: + Blender.Draw.Exit() + +def bevent(evt): +# global EVENT_NONE,EVENT_LOAD_DXF,EVENT_LOAD_INI,EVENT_SAVE_INI,EVENT_EXIT + global config_UI, user_preset + global GUI_A + + ######### Manages GUI events + if (evt==EVENT_EXIT): + Blender.Draw.Exit() + print 'DXF-Importer *** exit ***' #--------------------- + elif (evt==EVENT_CHOOSE_INI): + Window.FileSelector(ini_callback, "INI-file Selection", '*.ini') + elif (evt==EVENT_REDRAW): + Draw.Redraw() + elif (evt==EVENT_RESET): + resetDefaultConfig() + Draw.Redraw() + elif (evt==EVENT_PRESET2D): + resetDefaultConfig_2D() + Draw.Redraw() + elif (evt==EVENT_SCALE): + if g_scale_as.val == 12: + inputGlobalScale() + if GUI_A['g_scale'].val < 0.00000001: + GUI_A['g_scale'].val = 0.00000001 + Draw.Redraw() + elif (evt==EVENT_ORIGIN): + inputOriginVector() + Draw.Redraw() + elif (evt==EVENT_PRESET3D): + resetDefaultConfig_3D() + Draw.Redraw() + elif (evt==EVENT_PRESETCURV): + presetConfig_curv() + Draw.Redraw() + elif (evt==EVENT_PRESETS): + user_preset += 1 + index = str(user_preset) + if user_preset > 5: user_preset = 0; index = '' + iniFileName.val = INIFILE_DEFAULT_NAME + index + INIFILE_EXTENSION + Draw.Redraw() + elif (evt==EVENT_LIST): + dxfFile = dxfFileName.val + update_RegistryKey('dxfFileName', dxfFileName.val) + if dxfFile.lower().endswith('.dxf') and sys.exists(dxfFile): + analyzeDXF(dxfFile) + else: + Draw.PupMenu('DXF importer: Alert!%t|no valid DXF-file selected!') + print "DXF importer: error, no valid DXF-file selected! try again" + Draw.Redraw() + elif (evt==EVENT_HELP): + try: + import webbrowser + webbrowser.open('http://wiki.blender.org/index.php?title=Scripts/Manual/Import/DXF-3D') + except: + Draw.PupMenu('DXF importer: HELP Alert!%t|no connection to manual-page on Blender-Wiki! try:|\ +http://wiki.blender.org/index.php?title=Scripts/Manual/Import/DXF-3D') + Draw.Redraw() + elif (evt==EVENT_LOAD_INI): + loadConfig() + Draw.Redraw() + elif (evt==EVENT_SAVE_INI): + saveConfig() + Draw.Redraw() + elif (evt==EVENT_DXF_DIR): + dxfFile = dxfFileName.val + dxfPathName = '' + if '/' in dxfFile: + dxfPathName = '/'.join(dxfFile.split('/')[:-1]) + '/' + elif '\\' in dxfFile: + dxfPathName = '\\'.join(dxfFile.split('\\')[:-1]) + '\\' + dxfFileName.val = dxfPathName + '*.dxf' +# dirname == Blender.sys.dirname(Blender.Get('filename')) +# update_RegistryKey('DirName', dirname) +# update_RegistryKey('dxfFileName', dxfFileName.val) + GUI_A['newScene_on'].val = 1 + Draw.Redraw() +# elif (evt==EVENT_DXF_NAME): +# dirname == Blender.sys.dirname(Blender.Get('filename')) +# update_RegistryKey('DirName', dirname) +# #print 'deb:EVENT_DXF_NAME dxfFileName.val:', dxfFileName.val #-------------- +# update_RegistryKey('dxfFileName', dxfFileName.val) + elif (evt==EVENT_CHOOSE_DXF): + filename = '' # '*.dxf' + if dxfFileName.val: filename = dxfFileName.val + Window.FileSelector(dxf_callback, "DXF-file Selection", filename) + elif (evt==EVENT_START): + dxfFile = dxfFileName.val + #print 'deb: dxfFile file: ', dxfFile #---------------------- + update_RegistryKey('dxfFileName', dxfFileName.val) + if dxfFile.lower().endswith('*.dxf'): + if Draw.PupMenu('DXF importer: OK?|will import all DXF-files from:|%s' % dxfFile) == 1: + global UI_MODE + UI_MODE = False + multi_import(dxfFile[:-5]) # cut last 5 characters '*.dxf' + Draw.Redraw() + #Draw.Exit() + else: + Draw.Redraw() + elif dxfFile.lower().endswith('.dxf') and sys.exists(dxfFile): + print '\nStandard Mode: active' + if GUI_A['newScene_on'].val: + _dxf_file = dxfFile.split('/')[-1].split('\\')[-1] + _dxf_file = _dxf_file[:-4] # cut last char:'.dxf' + _dxf_file = _dxf_file[:MAX_NAMELENGTH] #? [-MAX_NAMELENGTH:]) + global SCENE + SCENE = Blender.Scene.New(_dxf_file) + SCENE.makeCurrent() + Blender.Redraw() + #or so? Blender.Scene.makeCurrent(_dxf_file) + #sce = bpy.data.scenes.new(_dxf_file) + #bpy.data.scenes.active = sce + else: + SCENE = Blender.Scene.GetCurrent() + SCENE.objects.selected = [] # deselect all + main(dxfFile) + #SCENE.objects.selected = SCENE.objects + #Window.RedrawAll() + #Blender.Redraw() + #Draw.Redraw() + else: + Draw.PupMenu('DXF importer: Alert!%t|no valid DXF-file selected!') + print "DXF importer: error, no valid DXF-file selected! try again" + Draw.Redraw() + + + + +def multi_import(DIR): + """Imports all DXF-files from directory DIR. + + """ + global SCENE + batchTIME = Blender.sys.time() + #if #DIR == "": DIR = os.path.curdir + if DIR == "": DIR = Blender.sys.dirname(Blender.Get('filename')) + print 'Multifiles Import from %s' %DIR + files = \ + [sys.join(DIR, f) for f in os.listdir(DIR) if f.lower().endswith('.dxf')] + if not files: + print '...None DXF-files found. Abort!' + return + + i = 0 + for dxfFile in files: + i += 1 + print '\nDXF-file', i, 'of', len(files) #,'\nImporting', dxfFile + if GUI_A['newScene_on'].val: + _dxf_file = dxfFile.split('/')[-1].split('\\')[-1] + _dxf_file = _dxf_file[:-4] # cut last char:'.dxf' + _dxf_file = _dxf_file[:MAX_NAMELENGTH] #? [-MAX_NAMELENGTH:]) + SCENE = Blender.Scene.New(_dxf_file) + SCENE.makeCurrent() + #or so? Blender.Scene.makeCurrent(_dxf_file) + #sce = bpy.data.scenes.new(_dxf_file) + #bpy.data.scenes.active = sce + else: + SCENE = Blender.Scene.GetCurrent() + SCENE.objects.selected = [] # deselect all + main(dxfFile) + #Blender.Redraw() + + print 'TOTAL TIME: %.6f' % (Blender.sys.time() - batchTIME) + print '\a\r', # beep when done + + if __name__ == "__main__": - Window.FileSelector(main, 'Import a DXF file', '*.dxf') + UI_MODE = True + # recall last used DXF-file and INI-file names + dxffilename = check_RegistryKey('dxfFileName') + #print 'deb:start dxffilename:', dxffilename #---------------- + if dxffilename: dxfFileName.val = dxffilename + else: + dirname = Blender.sys.dirname(Blender.Get('filename')) + #print 'deb:start dirname:', dirname #---------------- + dxfFileName.val = sys.join(dirname, '') + inifilename = check_RegistryKey('iniFileName') + if inifilename: iniFileName.val = inifilename + + Draw.Register(draw_UI, event, bevent) + + +""" +if 1: + # DEBUG ONLY + UI_MODE = False + TIME= Blender.sys.time() + #DIR = '/dxf_r12_testfiles/' + DIR = '/metavr/' + import os + print 'Searching for files' + os.system('find %s -iname "*.dxf" > /tmp/tempdxf_list' % DIR) + # os.system('find /storage/ -iname "*.dxf" > /tmp/tempdxf_list') + print '...Done' + file= open('/tmp/tempdxf_list', 'r') + lines= file.readlines() + file.close() + # sort by filesize for faster testing + lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] + lines_size.sort() + lines = [f[1] for f in lines_size] + + for i, _dxf in enumerate(lines): + if i >= 70: + #if 1: + print 'Importing', _dxf, '\nNUMBER', i, 'of', len(lines) + if True: + _dxf_file= _dxf.split('/')[-1].split('\\')[-1] + _dxf_file = _dxf_file[:-4] # cut last char:'.dxf' + _dxf_file = _dxf_file[:MAX_NAMELENGTH] #? [-MAX_NAMELENGTH:]) + sce = bpy.data.scenes.new(_dxf_file) + bpy.data.scenes.active = sce + dxfFileName.val = _dxf + main(_dxf) + + print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) +"""
\ No newline at end of file diff --git a/release/scripts/import_mdd.py b/release/scripts/import_mdd.py index 8e7b9985d3d..1ee196ab67f 100644 --- a/release/scripts/import_mdd.py +++ b/release/scripts/import_mdd.py @@ -1,11 +1,11 @@ #!BPY - #""" - #Name: 'Load MDD to Mesh RVKs' - #Blender: 242 - #Group: 'Import' - #Tooltip: 'baked vertex animation to active mesh object.' - #""" +""" + Name: 'Load MDD to Mesh RVKs' + Blender: 242 + Group: 'Import' + Tooltip: 'baked vertex animation to active mesh object.' +""" __author__ = "Bill L.Nieuwendorp" __bpydoc__ = """\ This script Imports Lightwaves MotionDesigner format. diff --git a/release/scripts/import_obj.py b/release/scripts/import_obj.py index 753d8816ff9..e5bdc796e16 100644 --- a/release/scripts/import_obj.py +++ b/release/scripts/import_obj.py @@ -535,13 +535,16 @@ def get_float_func(filepath): find the float function for this obj file - weather to replace commas or not ''' - file= open(filepath, 'r') + file= open(filepath, 'rU') for line in file: #.xreadlines(): if line.startswith('v'): # vn vt v if ',' in line: return lambda f: float(f.replace(',', '.')) elif '.' in line: return float + + # incase all vert values were ints + return float def load_obj(filepath, CLAMP_SIZE= 0.0, CREATE_FGONS= True, CREATE_SMOOTH_GROUPS= True, CREATE_EDGES= True, SPLIT_OBJECTS= True, SPLIT_GROUPS= True, SPLIT_MATERIALS= True, IMAGE_SEARCH=True): ''' diff --git a/release/scripts/lightwave_import.py b/release/scripts/lightwave_import.py index 63506a6dc1a..2049fbd2380 100644 --- a/release/scripts/lightwave_import.py +++ b/release/scripts/lightwave_import.py @@ -656,7 +656,12 @@ def read_clip(lwochunk, dir_part): # ima, IMAG, g_IMAG refers to clip dictionary 'ID' entries: refer to blok and surf clip_dict = {} data = cStringIO.StringIO(lwochunk.read()) - image_index, = struct.unpack(">L", data.read(4)) + data_str = data.read(4) + if len(data_str) < 4: # can be zero also??? :/ + # Should not happen but lw can import so we should too + return + + image_index, = struct.unpack(">L", data_str) clip_dict['ID'] = image_index i = 4 while(i < lwochunk.chunksize): @@ -693,7 +698,7 @@ def read_clip(lwochunk, dir_part): i = i + 6 + subchunklen #end loop on surf chunks ###if DEBUG: print "read image:%s" % clip_dict - if clip_dict.has_key('XREF'): + if 'XREF' in clip_dict: # has_key ###if DEBUG: print "Cross-reference: no image pre-allocated." return clip_dict #look for images @@ -985,7 +990,8 @@ def read_surfs(lwochunk, surf_list, tag_list): if uvname: # != "": my_dict['UVNAME'] = uvname #theoretically there could be a number of them: only one used per surf - if not(my_dict.has_key('g_IMAG')) and (rr.has_key('CHAN')) and (rr.has_key('OPAC')) and (rr.has_key('IMAG')): + # all are dictionaries - so testing keys + if not('g_IMAG' in my_dict) and ('CHAN' in rr) and ('OPAC' in rr) and ('IMAG' in rr): if (rr['CHAN'] == 'COLR') and (rr['OPAC'] == 0): my_dict['g_IMAG'] = rr['IMAG'] #do not set anything, just save image object for later assignment subchunklen = 0 #force ending @@ -1005,40 +1011,6 @@ def read_surfs(lwochunk, surf_list, tag_list): ###if DEBUG: print "-> Material pre-allocated." return my_dict - - -def reduce_face(verts, face): - TriangleArea= Blender.Mathutils.TriangleArea - Vector= Blender.Mathutils.Vector - ####if DEBUG: print len(face), face - # wants indicies local to the face - len_face= len(face) - if len_face==3: - return [face] - elif len_face==4: - vecs= [Vector(verts[i]) for i in face] - # Get the convave quad area - a1= TriangleArea(vecs[0], vecs[1], vecs[2]) - a2= TriangleArea(vecs[0], vecs[2], vecs[3]) - - a3= TriangleArea(vecs[0], vecs[1], vecs[3]) - a4= TriangleArea(vecs[1], vecs[2], vecs[3]) - - if abs((a1+a2) - (a3+a4)) < (a1+a2+a3+a4)/100: # Not convace - ####if DEBUG: print 'planer' - return [[0,1,2,3]] - if a1+a2<a3+a4: - return [[0,1,2], [0,2,3]] - else: - return [[0,1,3], [1,2,3]] - - else: # 5+ - ####if DEBUG: print 'SCANFILL...', len(face) - ngons= BPyMesh.ngon(verts, face, PREF_FIX_LOOPS= True) - return ngons - - - # ========================= # === Recalculate Faces === # ========================= @@ -1109,7 +1081,7 @@ def my_create_mesh(clip_list, surf, objspec_list, current_facelist, objname, not pass msh.mode |= Blender.Mesh.Modes.AUTOSMOOTH #smooth it anyway - if surf.has_key('SMAN'): + if 'SMAN' in surf: # has_key #not allowed mixed mode mesh (all the mesh is smoothed and all with the same angle) #only one smoothing angle will be active! => take the max one msh.degr = min(80, int(surf['SMAN']/3.1415926535897932384626433832795*180.0)) #lwo in radians - blender in degrees @@ -1119,12 +1091,8 @@ def my_create_mesh(clip_list, surf, objspec_list, current_facelist, objname, not except: img= None - #uv_flag = ((surf.has_key('UVNAME')) and (uvcoords_dict.has_key(surf['UVNAME'])) and (img != None)) - uv_flag = ((surf.has_key('UVNAME')) and (uvcoords_dict.has_key(surf['UVNAME']))) - - - + uv_flag = (('UVNAME' in surf) and (surf['UVNAME'] in uvcoords_dict)) ###if DEBUG: print "\n#===================================================================#" ###if DEBUG: print "Processing Object: %s" % objname @@ -1186,55 +1154,50 @@ def my_create_mesh(clip_list, surf, objspec_list, current_facelist, objname, not uvs.append(default_uv) return uvs - + cur_face for i in cur_ptag_faces_indexes: cur_face = complete_facelist[i] numfaceverts = len(cur_face) if numfaceverts == 2: edges.append((vertex_map[cur_face[0]], vertex_map[cur_face[1]])) - elif numfaceverts == 3: - rev_face = (cur_face[2], cur_face[1], cur_face[0]) + elif numfaceverts == 3 or numfaceverts == 4: + rev_face = [__i for __i in reversed(cur_face)] face_data.append( [vertex_map[j] for j in rev_face] ) if uv_flag: face_uvs.append(tmp_get_face_uvs(rev_face, i)) - - elif numfaceverts > 3: - meta_faces= reduce_face(complete_vertlist, cur_face) # Indices of triangles + elif numfaceverts > 4: + meta_faces= BPyMesh.ngon(complete_vertlist, cur_face, PREF_FIX_LOOPS= True) edge_face_count = {} for mf in meta_faces: - # ###if DEBUG: print meta_faces + # These will always be tri's since they are scanfill faces + mf = cur_face[mf[2]], cur_face[mf[1]], cur_face[mf[0]] + face_data.append( [vertex_map[j] for j in mf] ) - if len(mf) == 3: #triangle - mf = cur_face[mf[2]], cur_face[mf[1]], cur_face[mf[0]] - face_data.append( [vertex_map[j] for j in mf] ) - - if uv_flag: face_uvs.append(tmp_get_face_uvs(mf, i)) - - #if USE_FGON: - if len(meta_faces) > 1: - mf = face_data[-1] # reuse mf - for i in xrange(3): - v1= mf[i] - v2= mf[i-1] - if v1!=v2: - if v1>v2: - v2,v1= v1,v2 - try: - edge_face_count[v1,v2]+= 1 - except: - edge_face_count[v1,v2]= 0 + if uv_flag: face_uvs.append(tmp_get_face_uvs(mf, i)) + + #if USE_FGON: + if len(meta_faces) > 1: + mf = face_data[-1] # reuse mf + for j in xrange(3): + v1= mf[j] + v2= mf[j-1] + if v1!=v2: + if v1>v2: + v2,v1= v1,v2 + try: + edge_face_count[v1,v2]+= 1 + except: + edge_face_count[v1,v2]= 0 - else: #quads - mf= cur_face[mf[3]], cur_face[mf[2]], cur_face[mf[1]], cur_face[mf[0]] - face_data.append( [vertex_map[j] for j in mf] ) - if uv_flag: face_uvs.append(tmp_get_face_uvs(mf, i)) + if edge_face_count: edges_fgon.extend( [vert_key for vert_key, count in edge_face_count.iteritems() if count] ) - - msh.edges.extend(edges) + if edges: + msh.edges.extend(edges) + face_mapping_removed = msh.faces.extend(face_data, indexList=True) - if surf.has_key('TRAN') or (mat and mat.alpha<1.0): # incase mat is null + if 'TRAN' in surf or (mat and mat.alpha<1.0): # incase mat is null transp_flag = True else: transp_flag = False @@ -1347,7 +1310,7 @@ def create_objects(clip_list, objspec_list, surf_list): def lookup_imag(clip_list, ima_id): for ii in clip_list: if ii and ii['ID'] == ima_id: - if ii.has_key('XREF'): + if 'XREF' in ii: # has_key #cross reference - recursively look for images return lookup_imag(clip_list, ii['XREF']) else: @@ -1415,7 +1378,7 @@ def create_blok(surf, mat, clip_list, obj_size, obj_pos): #if not blok['ENAB']: # ###if DEBUG: print "***Image is not ENABled! Quitting this block" # break - if not(blok.has_key('IMAG')): + if not('IMAG' in blok): # has_key ###if DEBUG: print "***No IMAGE for this block? Quitting" break #extract out the image index within the clip_list if blok['IMAG'] == 0: blok['IMAG'] = lastimag #experimental .... @@ -1434,13 +1397,13 @@ def create_blok(surf, mat, clip_list, obj_size, obj_pos): tname += "+" else: tname += "x" #let's signal when should not be enabled - if blok.has_key('CHAN'): + if 'CHAN' in blok: # has_key tname += blok['CHAN'] newtex = bpy.data.textures.new(tname) newtex.setType('Image') # make it anu image texture newtex.image = img #how does it extends beyond borders - if blok.has_key('WRAP'): + if 'WRAP' in blok: # has_key if (blok['WRAP'] == 3) or (blok['WRAP'] == 2): newtex.setExtend('Extend') elif (blok['WRAP'] == 1): @@ -1457,35 +1420,35 @@ def create_blok(surf, mat, clip_list, obj_size, obj_pos): nega = False mapflag = Blender.Texture.MapTo.COL #default to color maptype = Blender.Texture.Mappings.FLAT - if blok.has_key('CHAN'): - if blok['CHAN'] == 'COLR' and blok.has_key('OPACVAL'): + if 'CHAN' in blok: # has_key + if blok['CHAN'] == 'COLR' and 'OPACVAL' in blok: # has_key colfac = blok['OPACVAL'] # Blender needs this to be clamped colfac = max(0.0, min(1.0, colfac)) ###if DEBUG: print "!!!Set Texture -> MapTo -> Col = %.3f" % colfac if blok['CHAN'] == 'BUMP': mapflag = Blender.Texture.MapTo.NOR - if blok.has_key('OPACVAL'): norfac = blok['OPACVAL'] + if 'OPACVAL' in blok: norfac = blok['OPACVAL'] # has_key ###if DEBUG: print "!!!Set Texture -> MapTo -> Nor = %.3f" % norfac if blok['CHAN'] == 'LUMI': mapflag = Blender.Texture.MapTo.EMIT - if blok.has_key('OPACVAL'): dvar = blok['OPACVAL'] + if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar if blok['CHAN'] == 'DIFF': mapflag = Blender.Texture.MapTo.REF - if blok.has_key('OPACVAL'): dvar = blok['OPACVAL'] + if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar if blok['CHAN'] == 'SPEC': mapflag = Blender.Texture.MapTo.SPEC - if blok.has_key('OPACVAL'): dvar = blok['OPACVAL'] + if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar if blok['CHAN'] == 'TRAN': mapflag = Blender.Texture.MapTo.ALPHA - if blok.has_key('OPACVAL'): dvar = blok['OPACVAL'] + if 'OPACVAL' in blok: dvar = blok['OPACVAL'] # has_key ###if DEBUG: print "!!!Set Texture -> MapTo -> DVar = %.3f" % dvar alphaflag = 1 nega = True - if blok.has_key('NEGA'): + if 'NEGA' in blok: # has_key ###if DEBUG: print "!!!Watch-out: effect of this texture channel must be INVERTED!" nega = not nega @@ -1498,7 +1461,7 @@ def create_blok(surf, mat, clip_list, obj_size, obj_pos): 'Texture Displacement', 'Additive'] set_blendmode = 7 #default additive - if blok.has_key('OPAC'): + if 'OPAC' in blok: # has_key set_blendmode = blok['OPAC'] if set_blendmode == 5: #transparency newtex.imageFlags |= Blender.Texture.ImageFlags.CALCALPHA @@ -1509,7 +1472,7 @@ def create_blok(surf, mat, clip_list, obj_size, obj_pos): axis = [Blender.Texture.Proj.X, Blender.Texture.Proj.Y, Blender.Texture.Proj.Z] size = [1.0] * 3 ofs = [0.0] * 3 - if blok.has_key('PROJ'): + if 'PROJ' in blok: # has_key if blok['PROJ'] == 0: #0 - Planar ###if DEBUG: print "!!!Flat projection" coordflag = Blender.Texture.TexCo.ORCO @@ -1598,44 +1561,44 @@ def update_material(clip_list, objspec, surf_list): break #mat = Blender.Material.New(surf['NAME']) #surf['g_MAT'] = mat - if surf.has_key('COLR'): + if 'COLR' in surf: # has_key mat.rgbCol = surf['COLR'] - if surf.has_key('LUMI'): + if 'LUMI' in surf: mat.setEmit(surf['LUMI']) - if surf.has_key('GVAL'): + if 'GVAL' in surf: # has_key mat.setAdd(surf['GVAL']) - if surf.has_key('SPEC'): - mat.setSpec(surf['SPEC']) #it should be * 2 but seems to be a bit higher lwo [0.0, 1.0] - blender [0.0, 2.0] - if surf.has_key('DIFF'): - mat.setRef(surf['DIFF']) #lwo [0.0, 1.0] - blender [0.0, 1.0] - if surf.has_key('GLOS'): #lwo [0.0, 1.0] - blender [0, 255] - glo = int(371.67 * surf['GLOS'] - 42.334) #linear mapping - seems to work better than exp mapping - if glo <32: glo = 32 #clamped to 32-255 + if 'SPEC' in surf: # has_key + mat.setSpec(surf['SPEC']) #it should be * 2 but seems to be a bit higher lwo [0.0, 1.0] - blender [0.0, 2.0] + if 'DIFF' in surf: # has_key + mat.setRef(surf['DIFF']) #lwo [0.0, 1.0] - blender [0.0, 1.0] + if 'GLOS' in surf: # has_key #lwo [0.0, 1.0] - blender [0, 255] + glo = int(371.67 * surf['GLOS'] - 42.334) #linear mapping - seems to work better than exp mapping + if glo <32: glo = 32 #clamped to 32-255 if glo >255: glo = 255 mat.setHardness(glo) - if surf.has_key('TRNL'): + if 'TRNL' in surf: # has_key mat.setTranslucency(surf['TRNL']) #NOT SURE ABOUT THIS lwo [0.0, 1.0] - blender [0.0, 1.0] - mm = mat.getMode() + mm = mat.mode mm |= Blender.Material.Modes.TRANSPSHADOW - if surf.has_key('REFL'): + if 'REFL' in surf: # has_key mat.setRayMirr(surf['REFL']) #lwo [0.0, 1.0] - blender [0.0, 1.0] mm |= Blender.Material.Modes.RAYMIRROR - if surf.has_key('TRAN'): + if 'TRAN' in surf: # has_key mat.setAlpha(1.0-surf['TRAN']) #lwo [0.0, 1.0] - blender [1.0, 0.0] mm |= Blender.Material.Modes.RAYTRANSP - if surf.has_key('RIND'): + if 'RIND' in surf: # has_key s = surf['RIND'] if s < 1.0: s = 1.0 if s > 3.0: s = 3.0 mat.setIOR(s) #clipped to blender [1.0, 3.0] mm |= Blender.Material.Modes.RAYTRANSP - if surf.has_key('BLOK') and surf['BLOK'] != []: + if 'BLOK' in surf and surf['BLOK'] != []: #update the material according to texture. alphaflag = create_blok(surf, mat, clip_list, obj_size, obj_pos) if alphaflag: mm |= Blender.Material.Modes.RAYTRANSP - mat.setMode(mm) + mat.mode = mm #finished setting up the material #end if exist SURF #end loop on materials (SURFs) @@ -1679,9 +1642,10 @@ def main(): if __name__=='__main__': main() -""" -# Cams debugging lwo loader + +# Cams debugging lwo loader +""" TIME= Blender.sys.time() import os print 'Searching for files' @@ -1690,6 +1654,12 @@ os.system('find /fe/lwo/Objects/ -follow -iname "*.lwo" > /tmp/templwo_list') print '...Done' file= open('/tmp/templwo_list', 'r') lines= file.readlines() + +# sort by filesize for faster testing +lines_size = [(os.path.getsize(f[:-1]), f[:-1]) for f in lines] +lines_size.sort() +lines = [f[1] for f in lines_size] + file.close() def between(v,a,b): @@ -1706,8 +1676,9 @@ for i, _lwo in enumerate(lines): #if between(i, 525, 550): #if i > 1635: #if i != 1519: # 730 - if i>125: - _lwo= _lwo[:-1] + if i>141: + #if 1: + # _lwo= _lwo[:-1] print 'Importing', _lwo, '\nNUMBER', i, 'of', len(lines) _lwo_file= _lwo.split('/')[-1].split('\\')[-1] newScn= bpy.data.scenes.new(_lwo_file) diff --git a/release/scripts/mesh_edges2curves.py b/release/scripts/mesh_edges2curves.py index fdf61298ebc..f65eccae9a4 100644 --- a/release/scripts/mesh_edges2curves.py +++ b/release/scripts/mesh_edges2curves.py @@ -12,7 +12,7 @@ __version__ = "1.0 2006/02/08" __bpydoc__ = """\ Edges to Curves -This script converts open and closed edge loops into polylines +This script converts open and closed edge loops into curve polylines Supported:<br> Polylines where each vert has no more then 2 edges attached to it. @@ -39,17 +39,8 @@ Supported:<br> # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- - from Blender import * -def edkey(ed): - i1 = ed.v1.index - i2 = ed.v2.index - if i1>i2: - return (i2,i1), ed - else: - return (i1,i2), ed - def polysFromMesh(me): # a polyline is 2 #polylines are a list @@ -138,6 +129,7 @@ def mesh2polys(): v= poly[vIdx] cu.appendNurb([v.co.x, v.co.y, v.co.z, w, t]) + vIdx += 1 cu[i].type= 0 # Poly Line # Close the polyline if its closed. diff --git a/release/scripts/mesh_skin.py b/release/scripts/mesh_skin.py index fdb721bc9f3..a554e128b41 100644 --- a/release/scripts/mesh_skin.py +++ b/release/scripts/mesh_skin.py @@ -628,7 +628,7 @@ def main(): try: me.faces.delete(1, [ f for f in me.faces if f.sel ]) except: pass - me.faces.extend(faces) + me.faces.extend(faces, smooth = True) print '\nSkin done in %.4f sec.' % (Blender.sys.time()-time1) diff --git a/release/scripts/mesh_unfolder.py b/release/scripts/mesh_unfolder.py index 8ebf1777c2e..906e0f0a300 100644 --- a/release/scripts/mesh_unfolder.py +++ b/release/scripts/mesh_unfolder.py @@ -1,10 +1,10 @@ #!BPY """ Name: 'Unfold' -Blender: 243 +Blender: 245 Group: 'Mesh' Tip: 'Unfold meshes to create nets' -Version: v2.2.4 +Version: v2.5 Author: Matthew Chadwick """ import Blender @@ -18,16 +18,18 @@ try: from math import * import sys import random - from decimal import * import xml.sax, xml.sax.handler, xml.sax.saxutils + + # annoying but need so classes dont raise errors + xml_sax_handler_ContentHandler = xml.sax.handler.ContentHandler except: - print "One of the Python modules required can't be found." - print sys.exc_info()[1] - traceback.print_exc(file=sys.stdout) - + Draw.PupMenu('Error%t|A full python installation is required to run this script.') + xml = None + xml_sax_handler_ContentHandler = type(0) + __author__ = 'Matthew Chadwick' -__version__ = '2.2.4 24032007' +__version__ = '2.5 06102007' __url__ = ["http://celeriac.net/unfolder/", "blender", "blenderartist"] __email__ = ["post at cele[remove this text]riac.net", "scripts"] __bpydoc__ = """\ @@ -79,7 +81,7 @@ class FacesAndEdges: self.edgeFaces[key].append(face) def findTakenAdjacentFace(self, bface, edge): return self.findAdjacentFace(bface, edge) - # find the first untaken (non-selected) adjacent face in the list of adjacent faces for the given edge + # find the first untaken (non-selected) adjacent face in the list of adjacent faces for the given edge (allows for manifold meshes too) def findAdjacentFace(self, bface, edge): faces = self.edgeFaces[edge.key()] for i in xrange(len(faces)): @@ -107,84 +109,7 @@ class FacesAndEdges: if(bface!=None): bface.sel= True self.nfaces+=1 - - -class IntersectionResult: - def __init__(self, rn, rd, v=None): - self.v = v - self.rd = rd - self.rn = rn - def intersected(self): - return not(not(self.v)) - def isParallel(self): - return (self.rd==0) - def isColinear(self): - return (self.rn==0) - def intersection(self): - return self.v - -# represents a line segment between two points [p1, p2]. the points are [x,y] -class LineSegment: - def __init__(self, p): - self.p = p - def intersects(self, s): - rn = ((self.p[0].y-s.p[0].y)*(s.p[1].x-s.p[0].x)-(self.p[0].x-s.p[0].x)*(s.p[1].y-s.p[0].y)) - rd = ((self.p[1].x-self.p[0].x)*(s.p[1].y-s.p[0].y)-(self.p[1].y-self.p[0].y)*(s.p[1].x-s.p[0].x)) - # need an epsilon closeTo() here - if(rd<0.0000001 or rn==0.0): - return IntersectionResult(rn,rd) - r = rn/rd - s = ((self.p[0].y-s.p[0].y)*(self.p[1].x-self.p[0].x)-(self.p[0].x-s.p[0].x)*(self.p[1].y-self.p[0].y)) / rd - i = (0.0<=r and r<=1.0 and 0.0<=s and s<=1.0) - if not(i): - return None - ix = self.p[0].x + r*(self.p[1].x - self.p[0].x) - iy = self.p[0].y + r*(self.p[1].y - self.p[0].y) - t = 0.0001 - if ( abs(ix-self.p[0].x)>t and abs(iy-self.p[0].x)>t and abs(ix-self.p[1].x)>t and abs(iy-self.p[1].y)>t ): - return IntersectionResult( rn, rd,Vector([ix,iy,0.0])) - else: - return None - -class LineSegments: - def __init__(self, face): - self.face = face - def segmentAt(self, i): - if(i>self.face.nPoints()-1): - return None - if(i==self.face.nPoints()-1): - j = 0 - else: - j = i+1 - return LineSegment([ self.face.v[i], self.face.v[j] ]) - def iterateSegments(self, something): - results = [] - for i in xrange(self.face.nPoints()): - results.extend(something.haveSegment(self.segmentAt(i))) - return results - def compareSegments(self, something, segment): - results = [] - for i in xrange(self.face.nPoints()): - results.append(something.compareSegments([self.segmentAt(i), segment])) - return results - -class FaceOverlapTest: - def __init__(self, face1, face2): - self.faces = [face1, face2] - self.segments = [ LineSegments(self.faces[0]), LineSegments(self.faces[1]) ] - def suspectsOverlap(self): - tests = self.segments[0].iterateSegments(self) - gi = 0 - for i in tests: - if( i!=None and i.intersected() ): - gi+=1 - return gi>0 - def haveSegment(self, segment): - return self.segments[1].compareSegments(self, segment) - def compareSegments(self, segments): - return segments[0].intersects(segments[1]) - # A fold between two faces with a common edge class Fold: @@ -398,7 +323,7 @@ class Net: if(len(ff.v)<3): raise Exception("This mesh contains an isolated edge - it must consist only of faces") testFace = Poly.fromVectors( [ Vector([0.0,0.0,0.0]), Vector([0.0,1.0,0.0]), Vector([1.0,1.0,0.0]) ] ) - # hmmm + # hmmm. I honestly can't remember why this needs to be done, but it does. u=0 v=1 w=2 @@ -412,6 +337,7 @@ class Net: xyFold = Fold(None, xyFace, refFace, Edge(xyFace.v[0], xyFace.v[1] )) self.refFold = Fold(xyFold, refFace, ff, Edge(refFace.v[0], refFace.v[1] )) self.refFold.srcFace = self.firstFace + # prepare to grow the trees trunk = Tree(self, None, self.refFold) trunk.generations = self.generations self.firstPoly = ff @@ -423,6 +349,7 @@ class Net: self.folds.append(self.refFold) trunk.grow() i = 0 + # keep the trees growing while they can while(self.myFacesVisited<len(self.src.faces) and len(self.branches) > 0): if self.edgeIteratorClass==RandomEdgeIterator: i = random.randint(0,len(self.branches)-1) @@ -459,11 +386,12 @@ class Net: for afold in folds: mdf = afold.unfoldedFace() if(afold!=fold): - it1 = FaceOverlapTest(mf, mdf) - it2 = FaceOverlapTest(mdf, mf) - overlap = (it1.suspectsOverlap() or it2.suspectsOverlap()) + # currently need to get agreement from both polys because + # a touch by a vertex of one the other's edge is acceptable & + # they disagree on that + intersects = mf.intersects2D(mdf) and mdf.intersects2D(mf) inside = ( mdf.containsAnyOf(mf) or mf.containsAnyOf(mdf) ) - if( overlap or inside or mdf.overlays(mf)): + if( intersects or inside or mdf.overlays(mf)): c.append(afold) return c def getOverlapsBetweenGL(self, fold, folds): @@ -644,9 +572,9 @@ class Net: overlaps = self.report() attempts+=1 return attempts - def unfoldSelected(feedback=None, netName=None): + def fromSelected(feedback=None, netName=None): return Net.createNet(Blender.Object.GetSelected()[0], feedback, netName) - unfoldSelected = staticmethod(unfoldSelected) + fromSelected = staticmethod(fromSelected) def clone(self, object=None): if(object==None): object = self.object @@ -823,7 +751,7 @@ class Curvature(EdgeIterator): g += f.dihedralAngle() self.gooodness = g - + class Edge: def __init__(self, v1=None, v2=None, mEdge=None, i=-1): self.idx = i @@ -891,6 +819,23 @@ class Edge: return +1 else: return -1 + # Does the given segment intersect this, for overlap detection. + # endpoints are allowed to touch the line segment + def intersects2D(self, s): + if(self.matches(s)): + return False + else: + i = Geometry.LineIntersect2D(self.v1, self.v2, s.v1, s.v2) + if(i!=None): + i.resize4D() + i.z = self.v1.z # hack to put the point on the same plane as this edge for comparison + return(i!=None and not(self.endsWith(i))) + def matches(self, s): + return ( (self.v1==s.v1 and self.v2==s.v2) or (self.v2==s.v1 and self.v1==s.v2) ) + # Is the given point on the end of this segment ? 10-5 seems to an acceptable limit for closeness in Blender + def endsWith(self, aPoint, e=0.0001): + return ( (self.v1-aPoint).length < e or (self.v2-aPoint).length < e ) + class Poly: ids = -1 @@ -899,6 +844,7 @@ class Poly: self.v = [] self.id = Poly.ids self.boundz = None + self.edges = None def getID(self): return self.id def normal(self): @@ -910,6 +856,21 @@ class Poly: q = a-c q.resize3D() return CrossVecs(p,q) + def makeEdges(self): + self.edges = [] + for i in xrange(self.nPoints()): + self.edges.append(Edge( self.v[i % self.nPoints()], self.v[ (i+1) % self.nPoints()] )) + def edgeAt(self, i): + if(self.edges==None): + self.makeEdges() + return self.edges[i] + def intersects2D(self, poly): + for i in xrange(self.nPoints()): + edge = self.edgeAt(i) + for j in xrange(poly.nPoints()): + if edge.intersects2D(poly.edgeAt(j)): + return True + return False def isBad(self): badness = 0 for vv in self.v: @@ -1031,8 +992,7 @@ class Poly: def toString(self): return self.v # This is the BEST algorithm for point-in-polygon detection. - # It's by W. Randolph Franklin. It's also very beautiful (looks even better in C). - # All the others are shite; they give false positives. + # It's by W. Randolph Franklin. # returns 1 for inside, 1 or 0 for edges def contains(self, tp): c = 0 @@ -1114,6 +1074,7 @@ class SVGExporter: def export(self): self.net.unfoldTo(1) bb = self.object.getBoundBox() + print bb self.vxmin = bb[0][0] self.vymin = bb[0][1] self.vxmax = bb[7][0] @@ -1141,14 +1102,14 @@ class SVGExporter: self.addPolys() self.e.endElement("clipPath") def addUVImage(self): - image = Blender.Image.GetCurrent() + image = Blender.Image.GetCurrent() #hmm - how to determine the desired image ? if image==None: return ifn = image.getFilename() - #ifn = self.filename.replace(".svg", ".jpg") - #image.setFilename(ifn) - #ifn = ifn[ifn.rfind("/")+1:] - #image.save() + ifn = self.filename.replace(".svg", ".jpg") + image.setFilename(ifn) + ifn = ifn[ifn.rfind("/")+1:] + image.save() atts = {} atts["clip-path"] = "url(#netClip)" atts["xlink:href"] = ifn @@ -1242,7 +1203,7 @@ class SVGExporter: traceback.print_exc(file=sys.stdout) fileSelected = staticmethod(fileSelected) - +# for importing nets saved by the above exporter class NetHandler(xml.sax.handler.ContentHandler): def __init__(self, net): self.net = net @@ -1413,7 +1374,7 @@ class GUI: while(s):# and search < searchLimit): if(net!=None): name = net.des.name - net = Net.unfoldSelected(self, name) + net = Net.fromSelected(self, name) net.setAvoidsOverlaps(not(self.overlaps.val)) print print "Unfolding selected object" @@ -1516,6 +1477,14 @@ class GUI: else: self.nOverlaps = 0 Draw.Redraw(1) + if(evt==233): + f1 = Poly.fromBlenderFace(Blender.Object.GetSelected()[0].getData().faces[0]) + f2 = Poly.fromBlenderFace(Blender.Object.GetSelected()[1].getData().faces[0]) + print + print Blender.Object.GetSelected()[0].getName() + print Blender.Object.GetSelected()[1].getName() + print f1.intersects2D(f2) + print f2.intersects2D(f1) if(evt==714): Net.unfoldAll(self) Draw.Redraw(1) @@ -1567,6 +1536,7 @@ class GUI: Draw.Button("Unfold", 1, l.nx(), l.ny(), l.cw, l.ch, "Unfold selected mesh to net") Draw.Button("save", 104, l.nx(), l.ny(), l.cw, l.ch, "Save net as SVG") Draw.Button("load", 107, l.nx(), l.ny(), l.cw, l.ch, "Load net from SVG") + #Draw.Button("test", 233, l.nx(), l.ny(), l.cw, l.ch, "test") # unfolding enthusiasts - try uncommenting this self.ancestors = Draw.Number("depth", 654, l.nx(), l.ny(), cw, ch, self.ancestors.val, 0, 9999, "depth of branching 0=diffuse") #self.noise = Draw.Number("noise", 631, l.nx(), l.ny(), cw, ch, self.noise.val, 0.0, 1.0, "noisyness of branching") @@ -1574,7 +1544,7 @@ class GUI: options = "order %t|random %x0|brightest %x1|curvature %x2|winding %x3| 1010 %x4|largest %x5" self.shape = Draw.Menu(options, 713, l.nx(), l.ny(), cw, ch, self.shape.val, "shape of net") Draw.Button("exit", 6, l.nx(), l.ny(), l.cw, l.ch, "exit") - BGL.glClearColor(0.5, 0.5, 0.5, 1) + BGL.glClearColor(0.3, 0.3, 0.3, 1) BGL.glColor3f(0.3,0.3,0.3) l.newLine() BGL.glRasterPos2i(32, 100) @@ -1601,10 +1571,12 @@ class FlowLayout: self.y-=self.ch+self.margin self.x = self.margin -try: - sys.setrecursionlimit(10000) - gui = GUI() - gui.makeStandardGUI() - #gui.makePopupGUI() -except: - traceback.print_exc(file=sys.stdout) +# if xml is None, then dont bother running the script +if xml: + try: + sys.setrecursionlimit(10000) + gui = GUI() + gui.makeStandardGUI() + #gui.makePopupGUI() + except: + traceback.print_exc(file=sys.stdout) diff --git a/release/scripts/mesh_wire.py b/release/scripts/mesh_wire.py index 35cfa325497..bd38c47a9b9 100644 --- a/release/scripts/mesh_wire.py +++ b/release/scripts/mesh_wire.py @@ -44,6 +44,7 @@ from BPyMathutils import angleToLength import mesh_solidify import BPyMessages +reload(BPyMessages) import bpy @@ -219,9 +220,7 @@ def solid_wire(ob_orig, me_orig, sce, PREF_THICKNESS, PREF_SOLID, PREF_SHARP, PR for ii in vusers: co += me.verts[ii].co co /= len(vusers) - - - + me.faces.delete(1, range(len(me.faces))) me.faces.extend(new_faces) @@ -245,6 +244,18 @@ def main(): BPyMessages.Error_NoMeshActive() return + # Saves the editmode state and go's out of + # editmode if its enabled, we cant make + # changes to the mesh data while in editmode. + is_editmode = Window.EditMode() + Window.EditMode(0) + + me = ob_act.getData(mesh=1) # old NMesh api is default + if len(me.faces)==0: + BPyMessages.Error_NoMeshFaces() + if is_editmode: Window.EditMode(1) + return + # Create the variables. PREF_THICK = Blender.Draw.Create(0.005) PREF_SOLID = Blender.Draw.Create(1) @@ -259,16 +270,10 @@ def main(): ] if not Blender.Draw.PupBlock('Solid Wireframe', pup_block): + if is_editmode: Window.EditMode(1) return - # Saves the editmode state and go's out of - # editmode if its enabled, we cant make - # changes to the mesh data while in editmode. - is_editmode = Window.EditMode() - Window.EditMode(0) - Window.WaitCursor(1) - me = ob_act.getData(mesh=1) # old NMesh api is default t = sys.time() # Run the mesh editing function @@ -282,4 +287,4 @@ def main(): # This lets you can import the script without running it if __name__ == '__main__': - main()
\ No newline at end of file + main() diff --git a/release/scripts/object_drop.py b/release/scripts/object_drop.py index 4ea8bde2ca8..63a0bd574fb 100644 --- a/release/scripts/object_drop.py +++ b/release/scripts/object_drop.py @@ -1,7 +1,7 @@ #!BPY """ Name: 'Drop Onto Ground' -Blender: 243 +Blender: 245 Group: 'Object' Tooltip: 'Drop the selected objects onto "ground" objects' """ @@ -221,4 +221,5 @@ def main(): if __name__ == '__main__': main() -GLOBALS.clear()
\ No newline at end of file +GLOBALS.clear() + diff --git a/release/scripts/object_find.py b/release/scripts/object_find.py index 4ce93998f00..0cfd6ad1d92 100644 --- a/release/scripts/object_find.py +++ b/release/scripts/object_find.py @@ -41,20 +41,27 @@ import BPyMessages def get_object_images(ob): # Could optimize this - if ob.type == 'Mesh': - unique_images = {} - me = ob.getData(mesh=1) - orig_uvlayer = me.activeUVLayer - - for uvlayer in me.getUVLayerNames(): - me.activeUVLayer = uvlayer - for f in me.faces: - i = f.image - if i: unique_images[i.name] = i - - me.activeUVLayer = orig_uvlayer - - return unique_images.values() + if ob.type != 'Mesh': + return [] + + me = ob.getData(mesh=1) + + if not me.faceUV: + return [] + + unique_images = {} + + orig_uvlayer = me.activeUVLayer + + for uvlayer in me.getUVLayerNames(): + me.activeUVLayer = uvlayer + for f in me.faces: + i = f.image + if i: unique_images[i.name] = i + + me.activeUVLayer = orig_uvlayer + + return unique_images.values() # Todo, support other object types, materials return [] diff --git a/release/scripts/object_random_loc_sz_rot.py b/release/scripts/object_random_loc_sz_rot.py index 13c42e859d7..1af0dc7218a 100755..100644 --- a/release/scripts/object_random_loc_sz_rot.py +++ b/release/scripts/object_random_loc_sz_rot.py @@ -33,46 +33,67 @@ This script randomizes the selected objects location/size/rotation. # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- +''' +30 Jun 07 - ZanQdo: + - Properly coded axis toggles for Loc random (didn't work at all) + - Made Rot input values meaningful (45 will give max 45 degres of rotation) + - Pumped up the Scale value limit + - Made Scale input values meaningful (15 will give max 15 units bigger model) +''' + from Blender import Draw, Scene from Blender.Mathutils import Rand -def rnd(): - return Rand()-0.5 +def rnd(flag): + Random = Rand() + if flag == "LOC": + return (Random - 0.5) * 2 + elif flag == "ROT": + return (Random - 0.5) * 0.035 + elif flag == "SIZE": + return (Random - 0.5) * 1.8 def randomize(sel, PREF_LOC, PREF_SIZE, PREF_ROT, PREF_LINK_AXIS, PREF_X_AXIS, PREF_Y_AXIS, PREF_Z_AXIS): for ob in sel: if PREF_LOC: if PREF_LINK_AXIS: - rand = PREF_LOC*rnd() + rand = PREF_LOC*rnd("LOC") ob.loc = (ob.LocX+(rand*PREF_X_AXIS), ob.LocY+(rand*PREF_Y_AXIS), ob.LocZ+(rand*PREF_Z_AXIS)) else: - ob.loc = (ob.LocX+(PREF_LOC*rnd()), ob.LocY+(PREF_LOC*rnd()), ob.LocZ+(PREF_LOC*rnd())) - + if PREF_X_AXIS: x= PREF_LOC*rnd("LOC") + else: x= 0 + if PREF_Y_AXIS: y= PREF_LOC*rnd("LOC") + else: y= 0 + if PREF_Z_AXIS: z= PREF_LOC*rnd("LOC") + else: z= 0 + ob.loc = (ob.LocX+x, ob.LocY+y, ob.LocZ+z) + if PREF_SIZE: if PREF_LINK_AXIS: - rand = 1 + (PREF_SIZE*rnd()) + rand = PREF_SIZE*rnd("SIZE") if PREF_X_AXIS: x= rand - else: x= 1 + else: x= 0 if PREF_Y_AXIS: y= rand - else: y= 1 + else: y= 0 if PREF_Z_AXIS: z= rand - else: z= 1 - ob.size = (ob.SizeX*x, ob.SizeY*y, ob.SizeZ*z) + else: z= 0 + else: - if PREF_X_AXIS: x= 1+ PREF_SIZE*rnd() - else: x= 1 - if PREF_Y_AXIS: y= 1+ PREF_SIZE*rnd() - else: y= 1 - if PREF_Z_AXIS: z= 1+ PREF_SIZE*rnd() - else: z= 1 + if PREF_X_AXIS: x= PREF_SIZE*rnd("SIZE") + else: x= 0 + if PREF_Y_AXIS: y= PREF_SIZE*rnd("SIZE") + else: y= 0 + if PREF_Z_AXIS: z= PREF_SIZE*rnd("SIZE") + else: z= 0 - ob.size = (ob.SizeX*x, ob.SizeY*y, ob.SizeZ*z) + ob.size = (abs(ob.SizeX+x), abs(ob.SizeY+y), abs(ob.SizeZ+z)) + if PREF_ROT: if PREF_LINK_AXIS: - rand = PREF_ROT*rnd() + rand = PREF_ROT*rnd("ROT") ob.rot = (ob.RotX+rand, ob.RotY+rand, ob.RotZ+rand) else: - ob.rot = (ob.RotX+(PREF_X_AXIS*PREF_ROT*rnd()), ob.RotY+(PREF_Y_AXIS*PREF_ROT*rnd()), ob.RotZ+(PREF_Z_AXIS*PREF_ROT*rnd())) + ob.rot = (ob.RotX+(PREF_X_AXIS*PREF_ROT*rnd("ROT")), ob.RotY+(PREF_Y_AXIS*PREF_ROT*rnd("ROT")), ob.RotZ+(PREF_Z_AXIS*PREF_ROT*rnd("ROT"))) def main(): @@ -89,9 +110,9 @@ def main(): PREF_Z_AXIS= Draw.Create(1) pup_block = [\ - ('loc:', PREF_LOC, 0.0, 10.0, 'Amount to randomize the location'),\ - ('size:', PREF_SIZE, 0.0, 10.0, 'Amount to randomize the size'),\ - ('rot:', PREF_ROT, 0.0, 10.0, 'Amount to randomize the rotation'),\ + ('loc:', PREF_LOC, 0.0, 100.0, 'Amount to randomize the location'),\ + ('size:', PREF_SIZE, 0.0, 100.0, 'Amount to randomize the size'),\ + ('rot:', PREF_ROT, 0.0, 360.0, 'Amount to randomize the rotation'),\ '',\ ('Link Axis', PREF_LINK_AXIS, 'Use the same random value for each objects XYZ'),\ ('X Axis', PREF_X_AXIS, 'Enable X axis randomization'),\ diff --git a/release/scripts/object_timeofs_follow_act.py b/release/scripts/object_timeofs_follow_act.py new file mode 100644 index 00000000000..83863da7d8f --- /dev/null +++ b/release/scripts/object_timeofs_follow_act.py @@ -0,0 +1,107 @@ +#!BPY +""" +Name: 'TimeOffset follow Active' +Blender: 245 +Group: 'Object' +Tooltip: 'ActObs animated loc sets TimeOffset on other objects at closest frame' +""" +__author__= "Campbell Barton" +__url__= ["blender.org", "blenderartists.org"] +__version__= "1.0" + +__bpydoc__= """ +""" + +# -------------------------------------------------------------------------- +# TimeOffset follow Active v1.0 by Campbell Barton (AKA Ideasman42) +# -------------------------------------------------------------------------- +# ***** 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 ***** +# -------------------------------------------------------------------------- + +import Blender +from Blender import Image, sys, Draw, Window, Scene, Group +import bpy +import BPyMessages + + +def main(): + + sce = Scene.GetCurrent() + + ob_act = sce.objects.active + + if not ob_act: + Draw.PupMenu("Error%t|no active object") + return + + objects = list(sce.objects.context) + + try: objects.remove(ob_act) + except: pass + + if not objects: + Draw.PupMenu("Error%t|no objects selected") + return + + curframe = Blender.Get('curframe') + + FRAME_START= Draw.Create( Blender.Get('staframe') ) + FRAME_END= Draw.Create( Blender.Get('endframe') ) + + # Get USER Options + pup_block= [\ + ('Start:', FRAME_START, 1, 300000, 'Use the active objects position starting at this frame'),\ + ('End:', FRAME_END, 1, 300000, 'Use the active objects position starting at this frame'),\ + ] + + if not Draw.PupBlock('Set timeoffset...', pup_block): + return + + FRAME_START = FRAME_START.val + FRAME_END = FRAME_END.val + + if FRAME_START >= FRAME_END: + Draw.PupMenu("Error%t|frames are not valid") + + + # Ok - all error checking + locls_act = [] + for f in xrange((FRAME_END-FRAME_START)): + i = FRAME_START+f + Blender.Set('curframe', i) + locls_act.append(ob_act.matrixWorld.translationPart()) + + for ob in objects: + loc = ob.matrixWorld.translationPart() + best_frame = -1 + best_dist = 100000000 + for i, loc_act in enumerate(locls_act): + dist = (loc_act-loc).length + if dist < best_dist: + best_dist = dist + best_frame = i + FRAME_START + + ob.timeOffset = float(best_frame) + + # Set the original frame + Blender.Set('curframe', curframe) + +if __name__ == '__main__': + main() + diff --git a/release/scripts/ply_export.py b/release/scripts/ply_export.py index 010ee72ec67..cecde5a0b59 100644 --- a/release/scripts/ply_export.py +++ b/release/scripts/ply_export.py @@ -7,6 +7,7 @@ Group: 'Export' Tooltip: 'Export active object to Stanford PLY format' """ +import bpy import Blender from Blender import Mesh, Scene, Window, sys, Image, Draw import BPyMesh @@ -64,7 +65,7 @@ def file_callback(filename): if not filename.lower().endswith('.ply'): filename += '.ply' - scn= Blender.Scene.GetCurrent() + scn= bpy.data.scenes.active ob= scn.objects.active if not ob: Blender.Draw.PupMenu('Error%t|Select 1 active object') @@ -89,6 +90,10 @@ def file_callback(filename): if not Draw.PupBlock('Export...', pup_block): return + is_editmode = Blender.Window.EditMode() + if is_editmode: + Blender.Window.EditMode(0, '', 0) + Window.WaitCursor(1) EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val @@ -132,7 +137,7 @@ def file_callback(filename): if vertexColors: col = f.col for j, v in enumerate(f): if smooth: - normal= v.no + normal= tuple(v.no) normal_key = rvec3d(normal) if faceUV: @@ -211,12 +216,12 @@ def file_callback(filename): file.write('\n') file.close() - - + + if is_editmode: + Blender.Window.EditMode(1, '', 0) def main(): Blender.Window.FileSelector(file_callback, 'PLY Export', Blender.sys.makename(ext='.ply')) - if __name__=='__main__': main() diff --git a/release/scripts/ply_import.py b/release/scripts/ply_import.py index a1118d8356d..6b96777102a 100644 --- a/release/scripts/ply_import.py +++ b/release/scripts/ply_import.py @@ -199,10 +199,14 @@ def read(filename): obj = obj_spec.load(format_specs[format], file) except IOError, (errno, strerror): - file.close() + try: file.close() + except: pass + return None - file.close() + try: file.close() + except: pass + return (obj_spec, obj); diff --git a/release/scripts/render_save_layers.py b/release/scripts/render_save_layers.py new file mode 100644 index 00000000000..04bedde6e8d --- /dev/null +++ b/release/scripts/render_save_layers.py @@ -0,0 +1,116 @@ +#!BPY + +""" +Name: 'Save Render Layers...' +Blender: 245 +Group: 'Render' +Tooltip: 'Save current renderlayers as a BPython script' +""" + +__author__ = "Campbell Barton" +__url__ = ("blender", "elysiun") +__version__ = "1.0" + +__bpydoc__ = """\ +""" + +# -------------------------------------------------------------------------- +# The scripts generated by this script are put under Public Domain by +# default, but you are free to edit the ones you generate with this script +# and change their license to another one of your choice. +# -------------------------------------------------------------------------- +# ***** 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 ***** +# -------------------------------------------------------------------------- + +import Blender +from Blender.Window import Theme, FileSelector +from Blender import Scene + +sce = Scene.GetCurrent() +rend = sce.render + +# default filename: theme's name + '_theme.py' in user's scripts dir: +default_fname = Blender.Get("scriptsdir") +default_fname = Blender.sys.join(default_fname, sce.name + '_renderlayer.py') +default_fname = default_fname.replace(' ','_') + +def write_renderlayers(filename): + "Write the current renderlayer as a bpython script" + + if not filename.endswith('.py'): filename += '.py' + + fout = file(filename, "w") + + fout.write("""#!BPY + +# \"\"\" +# Name: '%s' +# Blender: 245 +# Group: 'Render' +# Tooltip: '' +# \"\"\" + +__%s__ = "????" +__%s__ = "2.43" +__%s__ = ["blender"] +__%s__ = \"\"\"\\ +You can edit this section to write something about your script that can +be read then with the Scripts Help Browser script in Blender. + +Remember to also set author, version and possibly url(s) above. You can also +define an __email__ tag, check some bundled script's source for examples. +\"\"\" + +# This script was automatically generated by the save_theme.py bpython script. +# By default, these generated scripts are released as Public Domain, but you +# are free to change the license of the scripts you generate with +# save_theme.py before releasing them. + +import Blender +from Blender import Scene + +sce = Scene.GetCurrent() +rend = sce.render +""" % (sce.name, "author", "version", "url", "bpydoc")) + + for lay in rend.renderLayers: # + fout.write("\nlay = rend.addRenderLayer()\n") + fout.write("lay.name = \"%s\"\n" % lay.name) + + exec("vars = dir(lay)") + + for var in vars: + if var.startswith('_'): + continue + + v = "lay.%s" % var + exec("value = %s" % v) + if type(value) == str: + fout.write("%s = '%s'\n" % (v, value)) + else: + fout.write("%s = %s\n" % (v, value)) + + fout.write('\nBlender.Redraw(-1)') + fout.close() + try: + Blender.UpdateMenus() + except: + Blender.Draw.PupMenu("Warning - check console!%t|Menus could not be automatically updated") + +FileSelector(write_renderlayers, "Save RenderLayers", default_fname)
\ No newline at end of file diff --git a/release/scripts/save_theme.py b/release/scripts/save_theme.py index 7b21b5c74f7..a99050b404d 100644 --- a/release/scripts/save_theme.py +++ b/release/scripts/save_theme.py @@ -73,7 +73,11 @@ theme = Theme.Get()[0] # get current theme # default filename: theme's name + '_theme.py' in user's scripts dir: default_fname = Blender.Get("scriptsdir") -default_fname = Blender.sys.join(default_fname, theme.name + '_theme.py') +if (default_fname): + default_fname = Blender.sys.join(default_fname, theme.name + '_theme.py') +else: + default_fname = theme.name + '_theme.py' + default_fname = default_fname.replace(' ','_') def write_theme(filename): diff --git a/release/scripts/scripttemplate_mesh_edit.py b/release/scripts/scripttemplate_mesh_edit.py index e94b139a92d..159fb884925 100644 --- a/release/scripts/scripttemplate_mesh_edit.py +++ b/release/scripts/scripttemplate_mesh_edit.py @@ -70,6 +70,7 @@ def main(): # editmode if its enabled, we cant make # changes to the mesh data while in editmode. is_editmode = Window.EditMode() + if is_editmode: Window.EditMode(0) Window.WaitCursor(1) me = ob_act.getData(mesh=1) # old NMesh api is default diff --git a/release/scripts/scripttemplate_pyconstraint.py b/release/scripts/scripttemplate_pyconstraint.py new file mode 100644 index 00000000000..68aa9194435 --- /dev/null +++ b/release/scripts/scripttemplate_pyconstraint.py @@ -0,0 +1,114 @@ +#!BPY +""" +Name: 'Script Constraint' +Blender: 245 +Group: 'ScriptTemplate' +Tooltip: 'Add a new script for custom constraints' +""" + +from Blender import Window +import bpy + +script_data = \ +"""#BPYCONSTRAINT +''' + PyConstraint template, access this in the "add constraint" scripts submenu. + Add docstring here +''' + +import Blender +from Blender import Draw +from Blender import Mathutils +import math + +''' + This variable specifies the number of targets + that this constraint can use +''' +NUM_TARGETS = 1 + + +''' + This function is called to evaluate the constraint + obmatrix: (Matrix) copy of owner's 'ownerspace' matrix + targetmatrices: (List) list of copies of the 'targetspace' matrices of the targets (where applicable) + idprop: (IDProperties) wrapped data referring to this + constraint instance's idproperties +''' +def doConstraint(obmatrix, targetmatrices, idprop): + # Separate out the tranformation components for easy access. + obloc = obmatrix.translationPart() # Translation + obrot = obmatrix.toEuler() # Rotation + obsca = obmatrix.scalePart() # Scale + + # Define user-settable parameters.\ + # Must also be defined in getSettings(). + if not idprop.has_key('user_toggle'): idprop['user_toggle'] = 1 + if not idprop.has_key('user_slider'): idprop['user_slider'] = 1.0 + + + # Do stuff here, changing obloc, obrot, and obsca. + + + # Convert back into a matrix for loc, scale, rotation, + mtxloc = Mathutils.TranslationMatrix( obloc ) + mtxrot = obrot.toMatrix().resize4x4() + mtxsca = Mathutils.Matrix([obsca[0],0,0,0], [0,obsca[1],0,0], [0,0,obsca[2],0], [0,0,0,1]) + + # Recombine the separate elements into a transform matrix. + outputmatrix = mtxsca * mtxrot * mtxloc + + # Return the new matrix. + return outputmatrix + + + +''' + This function manipulates the matrix of a target prior to sending it to doConstraint() + target_object: wrapped data, representing the target object + subtarget_bone: wrapped data, representing the subtarget pose-bone/vertex-group (where applicable) + target_matrix: (Matrix) the transformation matrix of the target + id_properties_of_constraint: (IDProperties) wrapped idproperties +''' +def doTarget(target_object, subtarget_bone, target_matrix, id_properties_of_constraint): + return target_matrix + + +''' + This function draws a pupblock that lets the user set + the values of custom settings the constraint defines. + This function is called when the user presses the settings button. + idprop: (IDProperties) wrapped data referring to this + constraint instance's idproperties +''' +def getSettings(idprop): + # Define user-settable parameters. + # Must also be defined in getSettings(). + if not idprop.has_key('user_toggle'): idprop['user_toggle'] = 1 + if not idprop.has_key('user_slider'): idprop['user_slider'] = 1.0 + + # create temporary vars for interface + utoggle = Draw.Create(idprop['user_toggle']) + uslider = Draw.Create(idprop['user_slider']) + + + # define and draw pupblock + block = [] + block.append("Buttons: ") + block.append(("Toggle", utoggle, "This is a toggle button.")) + block.append("More buttons: ") + block.append(("Slider", uslider, 0.0000001, 1000.0, "This is a number field.")) + + retval = Draw.PupBlock("Constraint Template", block) + + # update id-property values after user changes settings + if (retval): + idprop['user_toggle']= utoggle.val + idprop['user_slider']= uslider.val + +""" + +new_text = bpy.data.texts.new('pyconstraint_template.py') +new_text.write(script_data) +bpy.data.texts.active = new_text +Window.RedrawAll() diff --git a/release/scripts/uv_export.py b/release/scripts/uv_export.py index f32b5b4fc12..5977a3b76f6 100644 --- a/release/scripts/uv_export.py +++ b/release/scripts/uv_export.py @@ -9,7 +9,7 @@ Tooltip: 'Export the UV face layout of the selected object to a .TGA or .SVG fil __author__ = "Martin 'theeth' Poirier" __url__ = ("http://www.blender.org", "http://blenderartists.org/") -__version__ = "2.4" +__version__ = "2.5" __bpydoc__ = """\ This script exports the UV face layout of the selected mesh object to @@ -96,6 +96,11 @@ Notes:<br>See change logs in scripts for a list of contributors. # Version 2.4 # Port from NMesh to Mesh by Daniel Salazar (zanqdo) # -------------------------- +# Version 2.5 +# Fixed some old off by one rasterizing errors (didn't render points at 1.0 in the UV scale properly). +# Fixed wire drawing for non 1 wire size (didn't wrap or stretch properly +# and would often raise exceptions) +# -------------------------- FullPython = False @@ -178,12 +183,16 @@ def ExportCallback(f): time1= Blender.sys.time() - if obj.type != "Mesh": + if not obj or obj.type != "Mesh": BPyMessages.Error_NoMeshActive() return - + + is_editmode = Blender.Window.EditMode() + if is_editmode: Blender.Window.EditMode(0) + mesh = obj.getData(mesh=1) if not mesh.faceUV: + if is_editmode: Blender.Window.EditMode(1) BPyMessages.Error_NoMeshUvActive() return @@ -202,6 +211,8 @@ def ExportCallback(f): UVFaces = ExtractUVFaces(mesh, bAllFaces.val) + if is_editmode: Blender.Window.EditMode(1) + if not bSVG.val: print "TGA export is running..." UV_Export_TGA(UVFaces, bSize.val, bWSize.val, bWrap.val, name) @@ -322,7 +333,7 @@ def UV_Export_TGA(vList, size, wsize, wrap, file): step = 0 - img = Buffer(size+1,size+1) + img = Buffer(size,size) if wrap: wrapSize = size @@ -333,15 +344,16 @@ def UV_Export_TGA(vList, size, wsize, wrap, file): for f in vList: for v in f: x = int(v[0] * size) - maxx = max (x, maxx) - minx = min (x, minx) + maxx = max (x + wsize - 1, maxx) + minx = min (x - wsize + 1, minx) y = int(v[1] * size) - maxy = max (y, maxy) - miny = min (y, miny) + maxy = max (y + wsize - 1, maxy) + miny = min (y - wsize + 1, miny) wrapSize = max (maxx - minx + 1, maxy - miny + 1) scale = float (size) / float (wrapSize) + max_index = size - 1 # max index of the buffer (height or width) fnum = 0 fcnt = len (vList) @@ -361,31 +373,31 @@ def UV_Export_TGA(vList, size, wsize, wrap, file): if step: try: for t in xrange(step): - x = int(floor((co1[0] + t*(co2[0]-co1[0])/step) * size)) - y = int(floor((co1[1] + t*(co2[1]-co1[1])/step) * size)) + x = int(floor((co1[0] + t*(co2[0]-co1[0])/step) * max_index)) + y = int(floor((co1[1] + t*(co2[1]-co1[1])/step) * max_index)) - if wrap: - x = x % wrapSize - y = y % wrapSize - else: - x = int ((x - minx) * scale) - y = int ((y - miny) * scale) - - co = x * 1 + y * 1 * size; - - img[co] = 0 - if wsize > 1: - for x in range(-1*wsize + 1,wsize): - for y in range(-1*wsize,wsize): - img[co + 1 * x + y * 1 * size] = 0 + for dx in range(-1*wsize + 1, wsize): + if wrap: + wx = (x + dx) % wrapSize + else: + wx = int ((x - minx + dx) * scale) + + for dy in range(-1*wsize + 1, wsize): + if wrap: + wy = (y + dy) % wrapSize + else: + wy = int ((y - miny + dy) * scale) + + co = wx * 1 + wy * 1 * size + img[co] = 0 except OverflowError: if not extreme_warning: print "Skipping extremely long UV edges, check your layout for excentric values" extreme_warning = True for v in f: - x = int(v[0] * size) - y = int(v[1] * size) + x = int(v[0] * max_index) + y = int(v[1] * max_index) if wrap: x = x % wrapSize diff --git a/release/scripts/uv_seams_from_islands.py b/release/scripts/uv_seams_from_islands.py index dd27aab4ce9..241f38fc4aa 100644 --- a/release/scripts/uv_seams_from_islands.py +++ b/release/scripts/uv_seams_from_islands.py @@ -37,7 +37,6 @@ def seams_from_islands(me): # add seams SEAM = Mesh.EdgeFlags.SEAM for ed in me.edges: - print len(set(edge_uvs[ed.key])) if len(set(edge_uvs[ed.key])) > 1: ed.flag |= SEAM @@ -59,7 +58,7 @@ def main(): # editmode if its enabled, we cant make # changes to the mesh data while in editmode. is_editmode = Window.EditMode() - if is_editmode: Window.EditMode(1) + if is_editmode: Window.EditMode(0) Window.WaitCursor(1) @@ -68,8 +67,10 @@ def main(): # Run the mesh editing function seams_from_islands(me) + if is_editmode: Window.EditMode(1) + # Timing the script is a good way to be aware on any speed hits when scripting - print 'My Script finished in %.2f seconds' % (sys.time()-t) + print 'UV Seams from Islands finished in %.2f seconds' % (sys.time()-t) Window.WaitCursor(0) diff --git a/release/scripts/uvcalc_follow_active_coords.py b/release/scripts/uvcalc_follow_active_coords.py index c97e7168d2f..2a79d0d05df 100644 --- a/release/scripts/uvcalc_follow_active_coords.py +++ b/release/scripts/uvcalc_follow_active_coords.py @@ -51,6 +51,11 @@ def extend(): Draw.PupMenu('ERROR: No mesh object.') return + # Toggle Edit mode + is_editmode = Window.EditMode() + if is_editmode: + Window.EditMode(0) + me = ob.getData(mesh=1) me_verts = me.verts @@ -233,7 +238,12 @@ def extend(): face_modes[i] = 2 # dont search again print sys.time() - t - me.update() + + if is_editmode: + Window.EditMode(1) + else: + me.update() + Window.RedrawAll() Window.WaitCursor(0) diff --git a/release/scripts/uvcalc_from_adjacent.py b/release/scripts/uvcalc_from_adjacent.py deleted file mode 100644 index 32bbd9e08e6..00000000000 --- a/release/scripts/uvcalc_from_adjacent.py +++ /dev/null @@ -1,129 +0,0 @@ -#!BPY -""" -Name: 'UVs from unselected adjacent' -Blender: 242 -Group: 'UVCalculation' -Tooltip: 'Assign UVs to selected faces from surrounding unselected faces.' -""" -__author__ = "Campbell Barton" -__url__ = ("blender", "elysiun") -__version__ = "1.0 2006/02/07" - -__bpydoc__ = """\ -This script sets the UV mapping and image of selected faces from adjacent unselected faces. - -Use this script in face select mode for texturing between textured faces. -""" - -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# Script copyright (C) Campbell J Barton -# -# 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 * -import bpy - -def mostUsedImage(imageList): # Returns the image most used in the list. - if not imageList: - return None - elif len(imageList) < 3: - return imageList[0] - - # 3+ Images, Get the most used image for surrounding faces. - imageCount = {} - for image in imageList: - if image: - image_key= image.name - else: - image_key = None - - try: - imageCount[image_key]['imageCount'] +=1 # an extra user of this image - except: - imageCount[image_key] = {'imageCount':1, 'blenderImage':image} # start with 1 user. - - # Now a list of tuples, (imageName, {imageCount, image}) - imageCount = imageCount.items() - - try: imageCount.sort(key=lambda a: a[1]) - except: imageCount.sort(lambda a,b: cmp(a[1], b[1])) - - - return imageCount[-1][1]['blenderImage'] - - -def main(): - scn = bpy.data.scenes.active - ob = scn.objects.active - if ob == None or ob.type != 'Mesh': - Draw.PupMenu('ERROR: No mesh object in face select mode.') - return - me = ob.getData(mesh=1) - - if not me.faceUV: - Draw.PupMenu('ERROR: No mesh object in face select mode.') - return - - selfaces = [f for f in me.faces if f.sel] - unselfaces = [f for f in me.faces if not f.sel if not f.hide] - - # Gather per Vert UV and Image, store in vertUvAverage - vertUvAverage = [[[],[]] for i in xrange(len(me.verts))] - - for f in unselfaces: # Unselected faces only. - fuv = f.uv - for i,v in enumerate(f): - vertUvAverage[v.index][0].append(fuv[i]) - vertUvAverage[v.index][1].append(f.image) - - # Average per vectex UV coords - for vertUvData in vertUvAverage: - uvList = vertUvData[0] - if uvList: - # Convert from a list of vectors into 1 vector. - vertUvData[0] = reduce(lambda a,b: a+b, uvList, Mathutils.Vector(0,0)) * (1.0/len(uvList)) - else: - vertUvData[0] = None - - - - # Assign to selected faces - TEX_FLAG = Mesh.FaceModes['TEX'] - for f in selfaces: - uvlist = [] - imageList = [] - for i,v in enumerate(f): - uv, vImages = vertUvAverage[v.index] - uvlist.append( uv ) - imageList.extend(vImages) - - if None not in uvlist: - # all the faces images used by this faces vert. some faces will be added twice but thats ok. - # Get the most used image and assign to the face. - image = mostUsedImage(imageList) - f.uv = uvlist - - if image: - f.image = image - f.mode |= TEX_FLAG - Window.RedrawAll() - -if __name__ == '__main__': - main()
\ No newline at end of file diff --git a/release/scripts/uvcalc_lightmap.py b/release/scripts/uvcalc_lightmap.py index a4269149203..af9acb09e17 100644 --- a/release/scripts/uvcalc_lightmap.py +++ b/release/scripts/uvcalc_lightmap.py @@ -221,7 +221,11 @@ PREF_MARGIN_DIV= 512): else: face_groups = [] - for me in meshes: + for me in meshes: + # Add face UV if it does not exist. + # All new faces are selected. + me.faceUV = True + if PREF_SEL_ONLY: faces = [f for f in me.faces if f.sel] else: @@ -540,6 +544,12 @@ def main(): Draw.PupMenu('Error%t|No mesh objects selected.') return + # Toggle Edit mode + is_editmode = Window.EditMode() + if is_editmode: + Window.EditMode(0) + + Window.WaitCursor(1) lightmap_uvpack(meshes,\ PREF_SEL_ONLY.val,\ @@ -550,6 +560,9 @@ def main(): PREF_BOX_DIV.val,\ int(1/(PREF_MARGIN_DIV.val/100))) + if is_editmode: + Window.EditMode(1) + Window.WaitCursor(0) if __name__ == '__main__': diff --git a/release/scripts/uvcalc_quad_clickproj.py b/release/scripts/uvcalc_quad_clickproj.py index 67b58885f9d..791fd661cc9 100644 --- a/release/scripts/uvcalc_quad_clickproj.py +++ b/release/scripts/uvcalc_quad_clickproj.py @@ -80,6 +80,10 @@ def main(): if not ob or ob.type!='Mesh': return + is_editmode = Window.EditMode() + if is_editmode: + Window.EditMode(0) + mousedown_wait() # so the menu items clicking dosnt trigger the mouseclick Window.DrawProgressBar (0.0, '') @@ -100,10 +104,8 @@ def main(): mouse_buttons = Window.GetMouseButtons() - Window.DrawProgressBar (0.2, '(2 of 3 ) Click confirms the U coords') - mousedown_wait() obmat= ob.matrixWorld @@ -112,7 +114,7 @@ def main(): if not mouseInView or not OriginA: return - + me = ob.getData(mesh=1) # Get the face under the mouse @@ -255,6 +257,9 @@ def main(): Window.Redraw(Window.Types.VIEW3D) Window.SetCursorPos(*orig_cursor) + if is_editmode: + Window.EditMode(1) + Window.RedrawAll() if __name__=='__main__': diff --git a/release/scripts/uvcalc_smart_project.py b/release/scripts/uvcalc_smart_project.py index b10b69285e0..49d52d12d47 100644 --- a/release/scripts/uvcalc_smart_project.py +++ b/release/scripts/uvcalc_smart_project.py @@ -1121,6 +1121,9 @@ def main(): print "Smart Projection time: %.2f" % (sys.time() - time1) # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec." % (sys.time() - time1)) + if is_editmode: + Window.EditMode(1) + Window.DrawProgressBar(1.0, "") Window.WaitCursor(0) Window.RedrawAll() diff --git a/release/scripts/vertexpaint_selfshadow_ao.py b/release/scripts/vertexpaint_selfshadow_ao.py index c083bc9e8b0..7a6f4f9176d 100644 --- a/release/scripts/vertexpaint_selfshadow_ao.py +++ b/release/scripts/vertexpaint_selfshadow_ao.py @@ -39,6 +39,7 @@ and optionaly blur the shading to remove artifacts from spesific edges. # -------------------------------------------------------------------------- from Blender import Scene, Draw, sys, Window, Mathutils, Mesh +import bpy import BPyMesh @@ -135,10 +136,10 @@ def vertexFakeAO(me, PREF_BLUR_ITERATIONS, PREF_BLUR_RADIUS, PREF_MIN_EDLEN, PRE Window.WaitCursor(0) def main(): - scn= Scene.GetCurrent() - ob= scn.getActiveObject() + sce= bpy.data.scenes.active + ob= sce.objects.active - if not ob or ob.getType() != 'Mesh': + if not ob or ob.type != 'Mesh': Draw.PupMenu('Error, no active mesh object, aborting.') return @@ -174,8 +175,8 @@ def main(): PREF_SHADOW_ONLY= PREF_SHADOW_ONLY.val PREF_SEL_ONLY= PREF_SEL_ONLY.val - if not me.faceUV: - me.faceUV= 1 + if not me.vertexColors: + me.vertexColors= 1 t= sys.time() vertexFakeAO(me, PREF_BLUR_ITERATIONS, PREF_BLUR_RADIUS, PREF_MIN_EDLEN, PREF_CLAMP_CONCAVE, PREF_CLAMP_CONVEX, PREF_SHADOW_ONLY, PREF_SEL_ONLY) diff --git a/release/scripts/vrml97_export.py b/release/scripts/vrml97_export.py index b5afacbe272..57ffd243bfc 100644 --- a/release/scripts/vrml97_export.py +++ b/release/scripts/vrml97_export.py @@ -656,10 +656,10 @@ class VRML2Export: self.writeMaterial(mater, self.cleanStr(mater.name,'')) if (mater.mode & Blender.Material.Modes['TEXFACE']): if image != None: - self.writeImageTexture(image.name) + self.writeImageTexture(image.name, image.filename) else: if image != None: - self.writeImageTexture(image.name) + self.writeImageTexture(image.name, image.filename) self.writeIndented("}\n", -1) @@ -837,7 +837,7 @@ class VRML2Export: self.writeIndented("transparency %s\n" % (round(transp,self.cp))) self.writeIndented("}\n",-1) - def writeImageTexture(self, name): + def writeImageTexture(self, name, filename): if self.texNames.has_key(name): self.writeIndented("texture USE %s\n" % self.cleanStr(name)) self.texNames[name] += 1 @@ -846,7 +846,7 @@ class VRML2Export: self.writeIndented("texture DEF %s ImageTexture {\n" % \ self.cleanStr(name), 1) self.writeIndented('url "%s"\n' % \ - name.split("\\")[-1].split("/")[-1]) + filename.split("\\")[-1].split("/")[-1]) self.writeIndented("}\n",-1) self.texNames[name] = 1 diff --git a/release/scripts/weightpaint_average.py b/release/scripts/weightpaint_average.py new file mode 100644 index 00000000000..4e8830256b2 --- /dev/null +++ b/release/scripts/weightpaint_average.py @@ -0,0 +1,121 @@ +#!BPY +""" +Name: 'Vertex Groups Island Average' +Blender: 243 +Group: 'WeightPaint' +Tooltip: 'Average the vertex weights for each connected set of verts' +""" + +# -------------------------------------------------------------------------- +# ***** 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 ***** +# -------------------------------------------------------------------------- +import Blender +from Blender import Scene, Mesh, Window, sys, Draw +from BPyMesh import meshWeight2List, list2MeshWeight, mesh2linkedFaces + +import BPyMessages +import bpy + +def faceGroups2VertSets(face_groups): + ''' Return the face groups as sets of vert indicies ''' + return [set([v.index for f in fg for v in f]) for fg in face_groups] + + +def vgroup_average(ob_orig, me, sce, PREF_ALL_VGROUPS=True): + if not me.getVertGroupNames(): + return + + weight_names, weight_list = meshWeight2List(me) + + weight_names_len = len(weight_names) + vgroup_dummy = [0.0] * weight_names_len + vgroup_range = range(weight_names_len) + + if not PREF_ALL_VGROUPS: + weight_active_index = weight_names.index(me.activeGroup) + + for vert_set in faceGroups2VertSets( mesh2linkedFaces(me) ): + if not vert_set: + continue + + + if PREF_ALL_VGROUPS: + # We need to average the vgroups + collected_group = vgroup_dummy[:] + for i in vert_set: + vert_group = weight_list[i] # get the original weight + weight_list[i] = collected_group # replace with the collected group + + for j in vgroup_range: # iter through the vgroups + collected_group[j] += vert_group[j] + + for j in vgroup_range: + collected_group[j] /= len(vert_set) + else: + # Active group only + vert_weight = 0.0 + for i in vert_set: + vert_weight += weight_list[i][weight_active_index] + + vert_weight /= len(vert_set) + + for i in vert_set: + weight_list[i][weight_active_index] = vert_weight + + list2MeshWeight(me, weight_names, weight_list) + +def main(): + + # Gets the current scene, there can be many scenes in 1 blend file. + sce = bpy.data.scenes.active + + # Get the active object, there can only ever be 1 + # and the active object is always the editmode object. + ob_act = sce.objects.active + + if not ob_act or ob_act.type != 'Mesh': + BPyMessages.Error_NoMeshActive() + return + + # Saves the editmode state and go's out of + # editmode if its enabled, we cant make + # changes to the mesh data while in editmode. + is_editmode = Window.EditMode() + Window.EditMode(0) + + PREF_ALL_VGROUPS = Draw.PupMenu("All Groups?%t|All Groups%x1|Active Group Only%x0") + if PREF_ALL_VGROUPS==-1: + return + + Window.WaitCursor(1) + me = ob_act.getData(mesh=1) # old NMesh api is default + t = sys.time() + + # Run the mesh editing function + vgroup_average(ob_act, me, sce, PREF_ALL_VGROUPS) + + # Timing the script is a good way to be aware on any speed hits when scripting + print 'Average VGroups in %.2f seconds' % (sys.time()-t) + Window.WaitCursor(0) + if is_editmode: Window.EditMode(1) + + +# This lets you can import the script without running it +if __name__ == '__main__': + main()
\ No newline at end of file diff --git a/release/scripts/weightpaint_invert.py b/release/scripts/weightpaint_invert.py new file mode 100644 index 00000000000..cdae83a9d50 --- /dev/null +++ b/release/scripts/weightpaint_invert.py @@ -0,0 +1,95 @@ +#!BPY +""" +Name: 'Invert Active Group' +Blender: 245 +Group: 'WeightPaint' +Tooltip: 'Invert the active vertex group' +""" + +# -------------------------------------------------------------------------- +# ***** 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 ***** +# -------------------------------------------------------------------------- +import Blender +from Blender import Scene, Mesh, Window, sys + +import BPyMessages +import bpy + +def vgroup_invert(ob_orig, me): + if not me.getVertGroupNames(): + return + group_act = me.activeGroup + if group_act == None: + return + + group_data = me.getVertsFromGroup(group_act, 1) + + weights= [1.0] * len(me.verts) # 1.0 - initialize inverted + + group_data = me.getVertsFromGroup(group_act, 1) # (i,w) tuples. + + me.removeVertGroup(group_act) # messes up the active group. + for i,w in group_data: + weights[i] = 1.0-w + + me.addVertGroup(group_act) + + rep = Blender.Mesh.AssignModes.REPLACE + vertList= [None] + for i,weight in enumerate(weights): + vertList[0] = i + me.assignVertsToGroup(group_act, vertList, weight, rep) + + me.activeGroup = group_act + me.update() + +def main(): + + # Gets the current scene, there can be many scenes in 1 blend file. + sce = bpy.data.scenes.active + + # Get the active object, there can only ever be 1 + # and the active object is always the editmode object. + ob_act = sce.objects.active + + if not ob_act or ob_act.type != 'Mesh': + BPyMessages.Error_NoMeshActive() + return + + # Saves the editmode state and go's out of + # editmode if its enabled, we cant make + # changes to the mesh data while in editmode. + is_editmode = Window.EditMode() + Window.EditMode(0) + + Window.WaitCursor(1) + me = ob_act.getData(mesh=1) # old NMesh api is default + t = sys.time() + + # Run the mesh editing function + vgroup_invert(ob_act, me) + + # Timing the script is a good way to be aware on any speed hits when scripting + print 'Invert VGroup in %.2f seconds' % (sys.time()-t) + Window.WaitCursor(0) + if is_editmode: Window.EditMode(1) + +# This lets you can import the script without running it +if __name__ == '__main__': + main()
\ No newline at end of file diff --git a/release/scripts/weightpaint_normalize.py b/release/scripts/weightpaint_normalize.py index 204868a79fc..1f6c3d201aa 100644 --- a/release/scripts/weightpaint_normalize.py +++ b/release/scripts/weightpaint_normalize.py @@ -40,54 +40,100 @@ proportion of the veighting is unchanged. # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -from Blender import Scene, Draw +from Blender import Scene, Draw, Object, Modifier import BPyMesh SMALL_NUM= 0.000001 -def actWeightNormalize(me, PREF_PEAKWEIGHT, PREF_KEEP_PROPORTION): + +def getArmatureGroups(ob, me): + + arm_obs = [] + + arm = ob.parent + if arm and arm.type == 'Armature' and ob.parentType == Object.ParentTypes.ARMATURE: + arm_obs.append(arm) + + for m in ob.modifiers: + if m.type== Modifier.Types.ARMATURE: + arm = m[Modifier.Settings.OBJECT] + if arm: + arm_obs.append(arm) + + # convert to a dict and back, should be a set! :/ - python 2.3 dosnt like. + return dict([ (bonename, None) for arm in arm_obs for bonename in arm.data.bones.keys() ]).keys() + + + +def actWeightNormalize(me, ob, PREF_PEAKWEIGHT, PREF_ACTIVE_ONLY, PREF_ARMATURE_ONLY, PREF_KEEP_PROPORTION): groupNames, vWeightDict= BPyMesh.meshWeight2Dict(me) new_weight= max_weight= -1.0 act_group= me.activeGroup - vWeightDictUsed=[False] * len(vWeightDict) - - for i, wd in enumerate(vWeightDict): - try: - new_weight= wd[act_group] - if new_weight > max_weight: - max_weight= new_weight - vWeightDictUsed[i]=wd - except: - pass - - if max_weight < SMALL_NUM or new_weight == -1: - Draw.PupMenu('No verts to normalize. exiting.') - return + if PREF_ACTIVE_ONLY: + normalizeGroups = [act_group] + else: + normalizeGroups = groupNames[:] - if abs(max_weight-PREF_PEAKWEIGHT) < SMALL_NUM: - Draw.PupMenu('Vert Weights are alredy normalized.') - return + if PREF_ARMATURE_ONLY: + + armature_groups = getArmatureGroups(ob, me) + + i = len(normalizeGroups) + while i: + i-=1 + if not normalizeGroups[i] in armature_groups: + del normalizeGroups[i] - max_weight= max_weight/PREF_PEAKWEIGHT - if PREF_KEEP_PROPORTION: - # TODO, PROPORTIONAL WEIGHT SCALING. - for wd in vWeightDictUsed: - if wd: # not false. - if len(wd) == 1: - # Only 1 group for thsi vert. Simple - wd[act_group] /= max_weight - else: - # More then 1 group. will need to scale all users evenly. - local_maxweight= max(wd.itervalues()) / PREF_PEAKWEIGHT - for weight in wd.iterkeys(): - wd[weight] /= local_maxweight - + for act_group in normalizeGroups: + vWeightDictUsed=[False] * len(vWeightDict) + + for i, wd in enumerate(vWeightDict): + try: + new_weight= wd[act_group] + if new_weight > max_weight: + max_weight= new_weight + vWeightDictUsed[i]=wd + except: + pass - else: # Simple, just scale the weights up. - for wd in vWeightDictUsed: - if wd: # not false. - wd[act_group] /= max_weight + # These can be skipped for now, they complicate things when using multiple vgroups, + ''' + if max_weight < SMALL_NUM or new_weight == -1: + Draw.PupMenu('No verts to normalize. exiting.') + #return + + if abs(max_weight-PREF_PEAKWEIGHT) < SMALL_NUM: + Draw.PupMenu('Vert Weights are alredy normalized.') + #return + ''' + max_weight= max_weight/PREF_PEAKWEIGHT + + if PREF_KEEP_PROPORTION: + # TODO, PROPORTIONAL WEIGHT SCALING. + for wd in vWeightDictUsed: + if wd: # not false. + if len(wd) == 1: + # Only 1 group for thsi vert. Simple + wd[act_group] /= max_weight + else: + # More then 1 group. will need to scale all users evenly. + if PREF_ARMATURE_ONLY: + local_maxweight= max([v for k, v in wd.iteritems() if k in armature_groups]) / PREF_PEAKWEIGHT + if local_maxweight > 0.0: + # So groups that are not used in any bones are ignored. + for weight in wd.iterkeys(): + if weight in armature_groups: + wd[weight] /= local_maxweight + else: + local_maxweight= max(wd.itervalues()) / PREF_PEAKWEIGHT + for weight in wd.iterkeys(): + wd[weight] /= local_maxweight + + else: # Simple, just scale the weights up. we alredy know this is in an armature group (if needed) + for wd in vWeightDictUsed: + if wd: # not false. + wd[act_group] /= max_weight # Copy weights back to the mesh. BPyMesh.dict2MeshWeight(me, groupNames, vWeightDict) @@ -104,17 +150,21 @@ def main(): me= ob.getData(mesh=1) PREF_PEAKWEIGHT= Draw.Create(1.0) + PREF_ACTIVE_ONLY= Draw.Create(1) PREF_KEEP_PROPORTION= Draw.Create(1) + PREF_ARMATURE_ONLY= Draw.Create(0) pup_block= [\ ('Peak Weight:', PREF_PEAKWEIGHT, 0.01, 1.0, 'Upper weight for normalizing.'),\ + ('Active Only', PREF_ACTIVE_ONLY, 'Only Normalize groups that have matching bones in an armature (when an armature is used).'),\ ('Proportional', PREF_KEEP_PROPORTION, 'Scale other weights so verts (Keep weights with other groups in proportion).'),\ + ('Armature Only', PREF_ARMATURE_ONLY, 'Only Normalize groups that have matching bones in an armature (when an armature is used).'),\ ] if not Draw.PupBlock('Clean Selected Meshes...', pup_block): return - actWeightNormalize(me, PREF_PEAKWEIGHT.val, PREF_KEEP_PROPORTION.val) + actWeightNormalize(me, ob, PREF_PEAKWEIGHT.val, PREF_ACTIVE_ONLY.val, PREF_ARMATURE_ONLY.val, PREF_KEEP_PROPORTION.val) if __name__=='__main__': main()
\ No newline at end of file diff --git a/release/scripts/wizard_curve2tree.py b/release/scripts/wizard_curve2tree.py new file mode 100644 index 00000000000..f11d797636f --- /dev/null +++ b/release/scripts/wizard_curve2tree.py @@ -0,0 +1,4007 @@ +#!BPY +""" +Name: 'Tree from Curves' +Blender: 245 +Group: 'Wizards' +Tip: 'Generate trees from curve shapes' +""" + +__author__ = "Campbell Barton" +__url__ = ['www.blender.org', 'blenderartists.org'] +__version__ = "0.1" + +__bpydoc__ = """\ + +""" + +# -------------------------------------------------------------------------- +# Tree from Curves v0.1 by Campbell Barton (AKA Ideasman42) +# -------------------------------------------------------------------------- +# ***** 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 ***** +# -------------------------------------------------------------------------- + +import bpy +import Blender +import BPyMesh +from Blender.Mathutils import Vector, Matrix, CrossVecs, AngleBetweenVecs, LineIntersect, TranslationMatrix, ScaleMatrix, RotationMatrix, Rand +from Blender.Geometry import ClosestPointOnLine +from Blender.Noise import randuvec + +GLOBALS = {} +GLOBALS['non_bez_error'] = 0 + +def AngleBetweenVecsSafe(a1, a2): + try: + return AngleBetweenVecs(a1,a2) + except: + return 180.0 + +# Copied from blender, we could wrap this! - BKE_curve.c +# But probably not toooo bad in python +def forward_diff_bezier(q0, q1, q2, q3, pointlist, steps, axis): + f= float(steps) + rt0= q0 + rt1= 3.0*(q1-q0)/f + f*= f + rt2= 3.0*(q0-2.0*q1+q2)/f + f*= steps + rt3= (q3-q0+3.0*(q1-q2))/f + + q0= rt0 + q1= rt1+rt2+rt3 + q2= 2*rt2+6*rt3 + q3= 6*rt3 + if axis == None: + for a in xrange(steps+1): + pointlist[a] = q0 + q0+= q1 + q1+= q2 + q2+= q3; + + else: + for a in xrange(steps+1): + pointlist[a][axis] = q0 + q0+= q1 + q1+= q2 + q2+= q3; + +def points_from_bezier_seg(steps, pointlist, radlist, bez1_vec, bez2_vec, radius1, radius2): + + # x,y,z,axis + for ii in (0,1,2): + forward_diff_bezier(bez1_vec[1][ii], bez1_vec[2][ii], bez2_vec[0][ii], bez2_vec[1][ii], pointlist, steps, ii) + + # radius - no axis, Copied from blenders BBone roll interpolation. + forward_diff_bezier(radius1, radius1 + 0.390464*(radius2-radius1), radius2 - 0.390464*(radius2-radius1), radius2, radlist, steps, None) + + +def debug_pt(co): + Blender.Window.SetCursorPos(tuple(co)) + Blender.Window.RedrawAll() + print 'debugging', co + +def freshMesh(mesh): + ''' + Utility function to get a new mesh or clear the existing one, but dont clear everything. + ''' + if mesh: + materials = mesh.materials + mesh.verts = None + for group in mesh.getVertGroupNames(): + mesh.removeVertGroup(group) + + # Add materials back + mesh.materials = materials + else: + mesh = bpy.data.meshes.new() + + return mesh + +def getObFromName(name): + if name: + try: return bpy.data.objects[name] + except: return None + else: + return None + +def getGroupFromName(name): + if name: + try: return bpy.data.groups[name] + except: return None + else: + return None + +def closestVecIndex(vec, vecls): + best= -1 + best_dist = 100000000 + for i, vec_test in enumerate(vecls): + # Dont use yet, we may want to tho + if vec_test: # Seems odd, but use this so we can disable some verts in the list. + dist = (vec-vec_test).length + if dist < best_dist: + best = i + best_dist = dist + + return best + +IRATIONAL_NUM = 22.0/7.0 +def next_random_num(rnd): + ''' + return a random number between 0.0 and 1.0 + ''' + rnd[0] += (rnd[0] * IRATIONAL_NUM) % 1 + # prevent + if rnd[0] > 1000000: + rnd[0]-=1000000 + return rnd[0] % 1 + +eul = 0.00001 + +BRANCH_TYPE_CURVE = 0 +BRANCH_TYPE_GROWN = 1 +BRANCH_TYPE_FILL = 2 + +class tree: + def __init__(self): + self.branches_all = [] + self.branches_root = [] + self.branches_twigs = [] + self.mesh = None + self.armature = None + self.objectCurve = None + self.objectCurveMat = None + self.objectCurveIMat = None + + self.objectTwigBounds = None # use for twigs only at the moment. + self.objectTwigBoundsIMat = None + self.objectTwigBoundsMat = None + self.objectTwigBoundsMesh = None + + self.objectLeafBounds = None + self.objectLeafBoundsIMat = None + self.objectLeafBoundsMesh = None + + self.limbScale = 1.0 + + self.debug_objects = [] + self.steps = 6 # defalt, curve overwrites + + def __repr__(self): + s = '' + s += '[Tree]' + s += ' limbScale: %.6f' % self.limbScale + s += ' object: %s' % self.objectCurve + + for brch in self.branches_root: + s += str(brch) + return s + + def fromCurve(self, objectCurve): + # Now calculate the normals + self.objectCurve = objectCurve + self.objectCurveMat = objectCurve.matrixWorld + self.objectCurveIMat = self.objectCurveMat.copy().invert() + curve = objectCurve.data + self.steps = curve.resolu # curve resolution + + # Set the curve object scale + if curve.bevob: + # A bit of a hack to guess the size of the curve object if you have one. + bb = curve.bevob.boundingBox + # self.limbScale = (bb[0] - bb[7]).length / 2.825 # THIS IS GOOD WHEN NON SUBSURRFED + self.limbScale = (bb[0] - bb[7]).length / 1.8 + elif curve.ext2 != 0.0: + self.limbScale = curve.ext2 * 1.5 + + # forward_diff_bezier will fill in the blanks + # nice we can reuse these for every curve segment :) + pointlist = [[None, None, None] for i in xrange(self.steps+1)] + radlist = [ None for i in xrange(self.steps+1) ] + + for spline in curve: + + if len(spline) < 2: # Ignore single point splines + continue + + if spline.type != 1: # 0 poly, 1 bez, 4 nurbs + GLOBALS['non_bez_error'] = 1 + continue + + + brch = branch() + brch.type = BRANCH_TYPE_CURVE + + + + bez_list = list(spline) + for i in xrange(1, len(bez_list)): + bez1 = bez_list[i-1] + bez2 = bez_list[i] + vec1 = bez1.vec + vec2 = bez2.vec + if abs(vec1[1][0]-vec2[1][0]) > 0.000001 or\ + abs(vec1[1][1]-vec2[1][1]) > 0.000001 or\ + abs(vec1[1][2]-vec2[1][2]) > 0.000001: + + points_from_bezier_seg(self.steps, pointlist, radlist, vec1, vec2, bez1.radius, bez2.radius) + + # remove endpoint for all but the last + len_pointlist = len(pointlist) + if i != len(bez_list)-1: + len_pointlist -= 1 + + brch.bpoints.extend([ bpoint(brch, Vector(pointlist[ii]), Vector(), radlist[ii] * self.limbScale) for ii in xrange(len_pointlist) ]) + + # Finalize once point data is there + if brch.bpoints: + # if all points are in the same location, this is possible + self.branches_all.append(brch) + if brch.bpoints[0].radius < brch.bpoints[-1].radius: # This means we dont have to worry about curve direction. + brch.bpoints.reverse() + brch.calcData() + + # Sort from big to small, so big branches get priority + self.branches_all.sort( key = lambda brch: -brch.bpoints[0].radius ) + + + def closestBranchPt(self, co): + best_brch = None + best_pt = None + best_dist = 10000000000 + for brch in self.branches_all: + for pt in brch.bpoints: + # if pt.inTwigBounds: # only find twigs, give different results for leaves + l = (pt.co-co).length + if l < best_dist: + best_dist = l + best_brch = brch + best_pt = pt + return best_brch, best_pt + + def setTwigBounds(self, objectMesh): + self.objectTwigBounds = objectMesh + self.objectTwigBoundsMesh = objectMesh.getData(mesh=1) + self.objectTwigBoundsMat = objectMesh.matrixWorld.copy() + self.objectTwigBoundsIMat = self.objectTwigBoundsMat.copy().invert() + + for brch in self.branches_all: + brch.calcTwigBounds(self) + + def setLeafBounds(self, objectMesh): + self.objectLeafBounds = objectMesh + self.objectLeafBoundsMesh = objectMesh.getData(mesh=1) + self.objectLeafBoundsIMat = objectMesh.matrixWorld.copy().invert() + + def isPointInTwigBounds(self, co, selected_only=False): + return self.objectTwigBoundsMesh.pointInside(co * self.objectCurveMat * self.objectTwigBoundsIMat, selected_only) + + def isPointInLeafBounds(self, co, selected_only=False): + return self.objectLeafBoundsMesh.pointInside(co * self.objectCurveMat * self.objectLeafBoundsIMat, selected_only) + + def resetTags(self, value): + for brch in self.branches_all: + brch.tag = value + + def buildConnections( self,\ + sloppy = 1.0,\ + connect_base_trim = 1.0,\ + do_twigs = False,\ + twig_ratio = 2.0,\ + twig_select_mode = 0,\ + twig_select_factor = 0.5,\ + twig_scale = 0.8,\ + twig_scale_width = 1.0,\ + twig_random_orientation = 180,\ + twig_random_angle = 33,\ + twig_recursive=True,\ + twig_recursive_limit=3,\ + twig_ob_bounds=None,\ + twig_ob_bounds_prune=True,\ + twig_ob_bounds_prune_taper=1.0,\ + twig_placement_maxradius=10.0,\ + twig_placement_maxtwig=0,\ + twig_follow_parent=0.0,\ + twig_follow_x=0.0,\ + twig_follow_y=0.0,\ + twig_follow_z=0.0,\ + do_variation = 0,\ + variation_seed = 1,\ + variation_orientation = 0.0,\ + variation_scale = 0.0,\ + do_twigs_fill = 0,\ + twig_fill_levels=4,\ + twig_fill_rand_scale=0.0,\ + twig_fill_fork_angle_max=180.0,\ + twig_fill_radius_min=0.1,\ + twig_fill_radius_factor=0.75,\ + twig_fill_shape_type=0,\ + twig_fill_shape_rand=0.0,\ + twig_fill_shape_power=0.3,\ + ): + ''' + build tree data - fromCurve must run first + ''' + + + # Sort the branchs by the first radius, so big branchs get joins first + ### self.branches_all.sort( key = lambda brch: brch.bpoints[0].radius ) + + #self.branches_all.reverse() + + # Connect branches + for i in xrange(len(self.branches_all)): + brch_i = self.branches_all[i] + + for j in xrange(len(self.branches_all)): + if i != j: + # See if any of the points match this branch + # see if Branch 'i' is the child of branch 'j' + + brch_j = self.branches_all[j] + + if not brch_j.inParentChain(brch_i): # So we dont make cyclic tree! + + pt_best_j, dist = brch_j.findClosest(brch_i.bpoints[0].co) + + # Check its in range, allow for a bit out - hense the sloppy + if dist < pt_best_j.radius * sloppy: + brch_i.parent_pt = pt_best_j + pt_best_j.childCount += 1 # dont remove me + + brch_i.baseTrim(connect_base_trim) + + ''' + if pt_best_j.childCount>4: + raise "ERROR" + ''' + + # addas a member of best_j.children later when we have the geometry info available. + + #### print "Found Connection!!!", i, j + break # go onto the next branch + + """ + children = [brch_child for brch_child in pt.children] + if children: + # This pt is one side of the segment, pt.next joins this segment. + # calculate the median point the 2 segments would spanal + # Once this is done we need to adjust 2 things + # 1) move both segments up/down so they match the branches best. + # 2) set the spacing of the segments around the point. + + + # First try to get the ideal some space around each joint + # the spacing shoule be an average of + for brch.bpoints: + """ + + ''' + for brch in self.branches_all: + brch.checkPointList() + ''' + + # Variations - use for making multiple versions of the same tree. + if do_variation: + irational_num = 22.0/7.0 # use to make the random number more odd + rnd = [variation_seed] + + # Add children temporarily + for brch in self.branches_all: + if brch.parent_pt: + rnd_rot = ((next_random_num(rnd) * variation_orientation) - 0.5) * 720 + mat_orientation = RotationMatrix(rnd_rot, 3, 'r', brch.parent_pt.no) + rnd_sca = 1 + ((next_random_num(rnd)-0.5)* variation_scale ) + mat_scale = Matrix([rnd_sca,0,0],[0,rnd_sca,0],[0,0,rnd_sca]) + # mat_orientation = RotationMatrix(0, 3, 'r', brch.parent_pt.no) + brch.transformRecursive(self, mat_scale * mat_orientation, brch.parent_pt.co) + + if (do_twigs or do_twigs_fill) and twig_ob_bounds: # Only spawn twigs inside this mesh + self.setTwigBounds(twig_ob_bounds) + + # Important we so this with existing parent/child but before connecting and calculating verts. + if do_twigs: + + # scale values down + twig_random_orientation= twig_random_orientation/360.0 + twig_random_angle= twig_random_angle/360.0 + + irational_num = 22.0/7.0 # use to make the random number more odd + + if not twig_recursive: + twig_recursive_limit = 0 + + self.buildTwigs(twig_ratio, twig_select_mode, twig_select_factor) + + branches_twig_attached = [] + + # This wont add all! :/ + brch_twig_index = 0 + brch_twig_index_LAST = -1 # use this to prevent in inf loop, since its possible we cant place every branch + while brch_twig_index < len(self.branches_twigs) and brch_twig_index_LAST != brch_twig_index: + ###print "While" + ### print brch_twig_index, len(self.branches_twigs) # if this dosnt change, quit the while + + brch_twig_index_LAST = brch_twig_index + + # new twigs have been added, recalculate + branches_twig_sort = [brch.bestTwigSegment() for brch in self.branches_all] + branches_twig_sort.sort() # this will sort the branches with best braches for adding twigs to at the start of the list + + for tmp_sortval, twig_pt_index, brch_parent in branches_twig_sort: # tmp_sortval is not used. + if twig_pt_index != -1 and \ + (twig_recursive_limit == 0 or brch_parent.generation < twig_recursive_limit) and \ + (twig_placement_maxtwig == 0 or brch_parent.twig_count < twig_placement_maxtwig) and \ + brch_parent.bpoints[twig_pt_index].radius < twig_placement_maxradius: + + if brch_twig_index >= len(self.branches_twigs): + break + + brch_twig = self.branches_twigs[brch_twig_index] + parent_pt = brch_parent.bpoints[twig_pt_index] + + brch_twig.parent_pt = parent_pt + parent_pt.childCount += 1 + + # Scale this twig using this way... + # The size of the parent, scaled by the parent point's radius, + # ...compared to the parent branch;s root point radius. + # Also take into account the length of the parent branch + # Use this for pretend random numbers too. + scale = twig_scale * (parent_pt.branch.bpoints[0].radius / brch_twig.bpoints[0].radius) * (parent_pt.radius / parent_pt.branch.bpoints[0].radius) + + # Random orientation + # THIS IS NOT RANDOM - Dont be real random so we can always get re-produceale results. + if twig_random_orientation: rnd1 = (((irational_num * scale * 10000000) % 360) - 180) * twig_random_orientation + else: rnd1 = 0.0 + if twig_random_angle: rnd2 = (((irational_num * scale * 66666666) % 360) - 180) * twig_random_angle + else: rnd2 = 0.0 + + # Align this with the existing branch + angle = AngleBetweenVecsSafe(zup, parent_pt.no) + cross = CrossVecs(zup, parent_pt.no) + mat_align = RotationMatrix(angle, 3, 'r', cross) + + # Use the bend on the point to work out which way to make the branch point! + if parent_pt.prev: cross = CrossVecs(parent_pt.no, parent_pt.prev.no - parent_pt.no) + else: cross = CrossVecs(parent_pt.no, parent_pt.next.no - parent_pt.no) + + if parent_pt.branch.parent_pt: + angle = AngleBetweenVecsSafe(parent_pt.branch.parent_pt.no, parent_pt.no) + else: + # Should add a UI for this... only happens when twigs come off a root branch + angle = 66 + + mat_branch_angle = RotationMatrix(angle+rnd1, 3, 'r', cross) + mat_scale = Matrix([scale,0,0],[0,scale,0],[0,0,scale]) + + mat_orientation = RotationMatrix(rnd2, 3, 'r', parent_pt.no) + + if twig_scale_width != 1.0: + # adjust length - no radius adjusting + for pt in brch_twig.bpoints: + pt.radius *= twig_scale_width + + brch_twig.transform(mat_scale * mat_branch_angle * mat_align * mat_orientation, parent_pt.co) + + # Follow the parent normal + if twig_follow_parent or twig_follow_x or twig_follow_y or twig_follow_z: + + vecs = [] + brch_twig_len = float(len(brch_twig.bpoints)) + + if twig_follow_parent: + no = parent_pt.no.copy() * twig_follow_parent + else: + no = Vector() + + no.x += twig_follow_x + no.y += twig_follow_y + no.z += twig_follow_z + + for i, pt in enumerate(brch_twig.bpoints): + if pt.prev: + fac = i / brch_twig_len + + # Scale this value + fac_inv = 1-fac + + no_orig = pt.co - pt.prev.co + len_orig = no_orig.length + + no_new = (fac_inv * no_orig) + (fac * no) + no_new.length = len_orig + + # Mix the 2 normals + vecs.append(no_new) + + # Apply the coords + for i, pt in enumerate(brch_twig.bpoints): + if pt.prev: + pt.co = pt.prev.co + vecs[i-1] + + brch_twig.calcPointExtras() + + + # When using a bounding mesh, clip and calculate points in bounds. + #print "Attempting to trim base" + brch_twig.baseTrim(connect_base_trim) + + if twig_ob_bounds and (twig_ob_bounds_prune or twig_recursive): + brch_twig.calcTwigBounds(self) + + # we would not have been but here if the bounds were outside + if twig_ob_bounds_prune: + brch_twig.boundsTrim() + if twig_ob_bounds_prune_taper != 1.0: + # taper to a point. we could use some nice taper algo here - just linear atm. + + brch_twig.taper(twig_ob_bounds_prune_taper) + + # Make sure this dosnt mess up anything else + + brch_twig_index += 1 + + # Add to the branches + #self.branches_all.append(brch_twig) + if len(brch_twig.bpoints) > 2: + branches_twig_attached.append(brch_twig) + brch_twig.generation = brch_parent.generation + 1 + brch_parent.twig_count += 1 + else: + # Dont add the branch + parent_pt.childCount -= 1 + + # Watch This! - move 1 tab down for no recursive twigs + if twig_recursive: + self.branches_all.extend(branches_twig_attached) + branches_twig_attached = [] + + if not twig_recursive: + self.branches_all.extend(branches_twig_attached) + branches_twig_attached = [] + + + if do_twigs_fill and twig_ob_bounds: + self.twigFill(\ + twig_fill_levels,\ + twig_fill_rand_scale,\ + twig_fill_fork_angle_max,\ + twig_fill_radius_min,\ + twig_fill_radius_factor,\ + twig_fill_shape_type,\ + twig_fill_shape_rand,\ + twig_fill_shape_power,\ + ) + + ### self.branches_all.sort( key = lambda brch: brch.parent_pt != None ) + + # Calc points with dependancies + # detect circular loops!!! - TODO + #### self.resetTags(False) # NOT NEEDED NOW + done_nothing = False + while done_nothing == False: + done_nothing = True + + for brch in self.branches_all: + + if brch.tag == False and (brch.parent_pt == None or brch.parent_pt.branch.tag == True): + # Assign this to a spesific side of the parents point + # we know this is a child but not which side it should be attached to. + if brch.parent_pt: + + child_locs = [\ + brch.parent_pt.childPointUnused(0),\ + brch.parent_pt.childPointUnused(1),\ + brch.parent_pt.childPointUnused(2),\ + brch.parent_pt.childPointUnused(3)] + + best_idx = closestVecIndex(brch.bpoints[0].co, child_locs) + + # best_idx could be -1 if all childPoint's are used however we check for this and dont allow it to happen. + #if best_idx==-1: + # raise "Error"z + brch.parent_pt.children[best_idx] = brch + + for pt in brch.bpoints: + pt.calcVerts() + + done_nothing = False + brch.tag = True + + ''' + for i in xrange(len(self.branches_all)): + brch_i = self.branches_all[i] + print brch_i.myindex, + print 'tag', brch_i.tag, + print 'parent is', + if brch_i.parent_pt: + print brch_i.parent_pt.branch.myindex + else: + print None + ''' + + def optimizeSpacing(self, seg_density=0.5, seg_density_angle=20.0, seg_density_radius=0.3, joint_compression=1.0, joint_smooth=1.0): + ''' + Optimize spacing, taking branch hierarchy children into account, + can add or subdivide segments so branch joins dont look horrible. + ''' + for brch in self.branches_all: + brch.evenJointDistrobution(joint_compression) + + # Correct points that were messed up from sliding + # This happens when one point is pushed past another and the branch gets an overlaping line + + for brch in self.branches_all: + brch.fixOverlapError(joint_smooth) + + + # Collapsing + for brch in self.branches_all: + brch.collapsePoints(seg_density, seg_density_angle, seg_density_radius, joint_smooth) + + ''' + for brch in self.branches_all: + brch.branchReJoin() + ''' + + def twigFill(self_tree,\ + twig_fill_levels,\ + twig_fill_rand_scale,\ + twig_fill_fork_angle_max,\ + twig_fill_radius_min,\ + twig_fill_radius_factor,\ + twig_fill_shape_type,\ + twig_fill_shape_rand,\ + twig_fill_shape_power,\ + ): + ''' + Fill with twigs, this function uses its own class 'segment' + + twig_fill_shape_type; + 0 - no child smoothing + 1 - smooth one child + 2 - smooth both children + + ''' + + rnd = [1] + + segments_all = [] + segments_level = [] + + # Only for testing + def preview_curve(): + TWIG_WIDTH_MAX = 1.0 + TWIG_WIDTH_MIN = 0.1 + cu = bpy.data.curves["cu"] + # remove all curves + while len(cu): + del cu[0] + # return + + cu.setFlag(1) + cu.ext2 = 0.01 + + WIDTH_STEP = (TWIG_WIDTH_MAX-TWIG_WIDTH_MIN) / twig_fill_levels + + for i, seg in enumerate(segments_all): + + # 1 is the base and 2 is the tail + + p1_h2 = seg.getHeadHandle() # isnt used + p1_co = seg.headCo + p1_h1 = seg.getHeadHandle() + + p2_h1 = seg.getTailHandle() + + p2_co = seg.tailCo + p2_h2 = seg.tailCo # isnt used + + bez1 = Blender.BezTriple.New([ p1_h1[0], p1_h1[1], p1_h1[2], p1_co[0], p1_co[1], p1_co[2], p1_h2[0], p1_h2[1], p1_h2[2] ]) + bez2 = Blender.BezTriple.New([ p2_h1[0], p2_h1[1], p2_h1[2], p2_co[0], p2_co[1], p2_co[2], p2_h2[0], p2_h2[1], p2_h2[2] ]) + bez1.handleTypes = bez2.handleTypes = [Blender.BezTriple.HandleTypes.FREE, Blender.BezTriple.HandleTypes.FREE] + + bez1.radius = TWIG_WIDTH_MIN + (WIDTH_STEP * (seg.levelFromLeaf+1)) + bez2.radius = TWIG_WIDTH_MIN + (WIDTH_STEP * seg.levelFromLeaf) + + cunurb = cu.appendNurb(bez1) + cunurb.append(bez2) + + # This sucks + for bez in cunurb: + bez.handleTypes = [Blender.BezTriple.HandleTypes.FREE, Blender.BezTriple.HandleTypes.FREE] + + ### cc = sce.objects.new( cu ) + cu.update() + + + def mergeCo(parentCo, ch1Co, ch2Co, twig_fill_shape_rand): + if twig_fill_shape_rand==0.0: + return (parentCo + ch1Co + ch2Co) / 3.0 + else: + + w1 = (next_random_num(rnd)*twig_fill_shape_rand) + (1-twig_fill_shape_rand) + w2 = (next_random_num(rnd)*twig_fill_shape_rand) + (1-twig_fill_shape_rand) + w3 = (next_random_num(rnd)*twig_fill_shape_rand) + (1-twig_fill_shape_rand) + wtot = w1+w2+w3 + w1=w1/wtot + w2=w2/wtot + w3=w3/wtot + + # return (parentCo*w1 + ch1Co*w2 + ch2Co*w2) + co1 = (parentCo * w1) + (ch1Co * (1.0-w1)) + co2 = (ch1Co * w2) + (ch2Co * (1.0-w2)) + co3 = (ch2Co * w3) + (parentCo * (1.0-w3)) + + return (co1 + co2 + co3) / 3.0 + + + + class segment: + def __init__(self, level): + self.headCo = Vector() + self.tailCo = Vector() + self.parent = None + self.mergeCount = 0 + self.levelFromLeaf = level # how far we are from the leaf in levels + self.levelFromRoot = -1 # set later, assume root bone + self.children = [] + segments_all.append(self) + + if level >= len(segments_level): segments_level.append([self]) + else: segments_level[level].append(self) + + self.brothers = [] + self.no = Vector() # only endpoints have these + # self.id = len(segments_all) + + # First value is the bpoint, + # Second value is what to do - + # 0 - dont join + # 1 - Join to parent (tree point) + # 2 - join to parent, point from another fill-twig branch we just created. + + self.bpt = (None, False) # branch point for root segs only + self.new_bpt = None + + self.used = False # use this to tell if we are apart of a branch + + def sibling(self): + i = self.parent.children.index(self) + + if i == 0: + return self.parent.children[ 1 ] + elif i == 1: + return self.parent.children[ 0 ] + else: + raise "error" + + + def getHeadHandle(self): + """ + For Bezier only + """ + + if not self.parent: + return self.headCo + + if twig_fill_shape_type == 0: # no smoothing + return self.headCo + elif twig_fill_shape_type == 1: + if self.parent.children[1] == self: + return self.headCo + # 2 - always do both + + + # Y shape with curve? optional + + # we have a parent but it has no handle direction, easier + if not self.parent.parent: no = self.parent.headCo - self.parent.tailCo + else: no = self.parent.parent.headCo-self.parent.tailCo + + no.length = self.getLength() * twig_fill_shape_power + # Ok we have to account for the parents handle + return self.headCo - no + # return self.headCo - Vector(1, 0,0) + + def getTailHandle(self): + """ + For Bezier only + """ + if self.parent: + no = self.parent.headCo-self.tailCo + no.length = self.getLength() * twig_fill_shape_power + return self.tailCo + no + else: + return self.tailCo # isnt used + + def getRootSeg(self): + seg = self + while seg.parent: + seg = seg.parent + + return seg + + def calcBrothers(self): + # Run on children first + self.brothers.extend( \ + [seg_child_sibling.parent \ + for seg_child in self.children \ + for seg_child_sibling in seg_child.brothers \ + if seg_child_sibling.parent not in (self, None)]\ + ) + #print self.brothers + + def calcLevelFromRoot(self): + if self.parent: + self.levelFromRoot = self.parent.levelFromRoot + 1 + + for seg_child in self.children: + seg_child.calcLevelFromRoot() + + # Dont use for now, but scale worked, transform was never tested. + """ + def transform(self, matrix): + self.headCo = self.headCo * matrix + self.tailCo = self.tailCo * matrix + + if self.children: + ch1 = self.children[0] + ch2 = self.children[1] + + ch1.transform(matrix) + ch2.transform(matrix) + + def scale(self, scale, cent=None): + # scale = 0.9 + #matrix = Matrix([scale,0,0],[0,scale,0],[0,0,scale]).resize4x4() + #self.transform(matrix) + if cent == None: # first iter + cent = self.headCo + self.tailCo = ((self.tailCo-cent) * scale) + cent + else: + self.headCo = ((self.headCo-cent) * scale) + cent + self.tailCo = ((self.tailCo-cent) * scale) + cent + + if self.children: + self.children[0].scale(scale, cent) + self.children[1].scale(scale, cent) + """ + def recalcChildLoc(self): + if not self.children: + return + ch1 = self.children[0] + ch2 = self.children[1] + new_mid = mergeCo(self.headCo, ch1.tailCo, ch2.tailCo, twig_fill_shape_rand) + + self.tailCo[:] = ch1.headCo[:] = ch2.headCo[:] = new_mid + + ch1.recalcChildLoc() + ch2.recalcChildLoc() + + def merge(self, other): + """ + Merge other into self and make a new segment + """ + """ + seg_child = segment(self.levelFromLeaf) + self.levelFromLeaf += 1 + + seg_child.parent = other.parent = self + + # No need, recalcChildLoc sets the other coords + #self.parent.tailCo = (self.headCo + self.tailCo + other.tailCo) / 3.0 + #self.parent.headCo[:] = self.headCo + + seg_child.headCo[:] = self.headCo + + # isect = LineIntersect(self.headCo, self.tailCo, other.headCo, other.tailCo) + # new_head = (isect[0]+isect[1]) * 0.5 + + seg_child.mergeCount += 1 + other.mergeCount += 1 + + self.children.extend([ seg_child, other ]) + + self.recalcChildLoc() + + # print 'merging', self.id, other.id + """ + + #new_head = (self.headCo + self.tailCo + other.headCo + other.tailCo) * 0.25 + + self.parent = other.parent = segment(self.levelFromLeaf + 1) + + # No need, recalcChildLoc sets the self.parent.tailCo + # self.parent.tailCo = (self.headCo + self.tailCo + other.tailCo) / 3.0 + + self.parent.headCo[:] = self.headCo + self.parent.bpt = self.bpt + self.bpt = (None, False) + + # isect = LineIntersect(self.headCo, self.tailCo, other.headCo, other.tailCo) + # new_head = (isect[0]+isect[1]) * 0.5 + + self.mergeCount += 1 + other.mergeCount += 1 + + self.parent.children.extend([ self, other ]) + + self.parent.recalcChildLoc() + # print 'merging', self.id, other.id + + + def findBestMerge(self, twig_fill_fork_angle_max): + # print "findBestMerge" + if self.parent != None: + return + + best_dist = 1000000 + best_seg = None + for seg_list in (self.brothers, segments_level[self.levelFromLeaf]): + #for seg_list in (segments_level[self.levelFromLeaf],): + + # only use all other segments if we cant find any from our brothers + if seg_list == segments_level[self.levelFromLeaf] and best_seg != None: + break + + for seg in seg_list: + # 2 ppoint join + if seg != self and seg.mergeCount == 0 and seg.parent == None: + + # find the point they would join + test_dist = (self.tailCo - seg.tailCo).length + if test_dist < best_dist: + if twig_fill_fork_angle_max > 179: + best_dist = test_dist + best_seg = seg + else: + # Work out if the desired angle range is ok. + mco = mergeCo( self.headCo, self.tailCo, seg.tailCo, 0.0 ) # we dont want the random value for this test + ang = AngleBetweenVecsSafe(self.tailCo-mco, seg.tailCo-mco) + if ang < twig_fill_fork_angle_max: + best_dist = test_dist + best_seg = seg + return best_seg + + def getNormal(self): + return (self.headCo - self.tailCo).normalize() + + def getLength(self): + return (self.headCo - self.tailCo).length + """ + def toMatrix(self, LEAF_SCALE, LEAF_RANDSCALE, LEAF_RANDVEC): + if LEAF_RANDSCALE: scale = LEAF_SCALE * Rand(1.0-LEAF_RANDSCALE, 1.0+LEAF_RANDSCALE) + else: scale = LEAF_SCALE * 1.0 + + if LEAF_RANDVEC: rand_vec = Vector( Rand(-1, 1), Rand(-1, 1), Rand(-1, 1) ).normalize() * LEAF_RANDVEC + else: rand_vec = Vector( ) + + return Matrix([scale,0,0],[0,scale,0],[0,0,scale]).resize4x4() * (self.no + rand_vec).toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(self.tailCo) + """ + def distripute_seg_on_mesh(me__, face_group): + """ + add segment endpoints + """ + + vert_segment_mapping = {} + for f in face_group: + for v in f: + i = v.index + if i not in vert_segment_mapping: + vert_segment_mapping[i] = len(segments_all) + v.sel = True + seg = segment(0) + # seg.tailCo = v.co.copy() # headCo undefined atm. + seg.tailCo = v.co.copy() * self_tree.objectTwigBoundsMat * self_tree.objectCurveIMat + + # self_tree.objectCurveMat + + seg.no = v.no + + # Build connectivity + for ed in me__.edges: + if ed.v1.sel and ed.v2.sel: + i1,i2 = ed.key + i1 = vert_segment_mapping[i1] + i2 = vert_segment_mapping[i2] + + segments_all[i1].brothers.append( segments_all[i2] ) + segments_all[i2].brothers.append( segments_all[i1] ) + + # Dont need to return anything, added when created. + + def set_seg_attach_point(seg, interior_points, twig_fill_rand_scale): + """ + Can only run on end nodes that have normals set + """ + best_dist = 1000000000.0 + best_point = None + + co = seg.tailCo + + for pt in interior_points: + # line from the point to the seg endpoint + + line_normal = seg.tailCo - pt.nextMidCo + l = line_normal.length + + + cross1 = CrossVecs( seg.no, line_normal ) + cross2 = CrossVecs( pt.no, line_normal ) + + angle_line = min(AngleBetweenVecsSafe(cross1, cross2), AngleBetweenVecsSafe(cross1, -cross2)) + angle_leaf_no_diff = min(AngleBetweenVecsSafe(line_normal, seg.no), AngleBetweenVecsSafe(line_normal, -seg.no)) + + # BEST_ANG=66.0 + # angle = 66.0 # min(AngleBetweenVecs(v2_co-v1_co, leaf.co-cc), AngleBetweenVecs(v1_co-v2_co, leaf.co-cc)) + # print angle, angle2 + # l = (l * ((1+abs(angle-BEST_ANG))**2 )) / (1+angle_line) + l = (1+(angle_leaf_no_diff/180)) * (1+(angle_line/180)) * l + + if l < best_dist: + best_pt = pt + best_co = pt.nextMidCo + + best_dist = l + + # twig_fill_rand_scale + seg.headCo = best_co.copy() + + if twig_fill_rand_scale: + seg_dir = seg.tailCo - seg.headCo + + seg_dir.length = seg_dir.length * ( 1.0 - (next_random_num(rnd)*twig_fill_rand_scale) ) + seg.tailCo = seg.headCo + seg_dir + + + if best_pt.childCount < 4: + # Watch this!!! adding a user before its attached and the branch is created! + # make sure if its not added later on, this isnt left added + best_pt.childCount += 1 + + # True/False denotes weather we try to connect to our parent branch + seg.bpt = (best_pt, True) + else: + seg.bpt = (best_pt, False) + + return True + + + # END Twig code, next add them + + + """ + Uses a reversed approch, fill in twigs from a bounding mesh + """ + # print "twig_fill_fork_angle_max" + # twig_fill_fork_angle_max = 60.0 # + # forward_diff_bezier will fill in the blanks + # nice we can reuse these for every curve segment :) + pointlist = [[None, None, None] for i in xrange(self_tree.steps+1)] + radlist = [ None for i in xrange(self_tree.steps+1) ] + + orig_branch_count = len(self_tree.branches_all) + + for face_group in BPyMesh.mesh2linkedFaces(self_tree.objectTwigBoundsMesh): + # Set the selection to do point inside. + self_tree.objectTwigBoundsMesh.sel = False + for f in face_group: f.sel = True + + interior_points = [] + interior_normal = Vector() + for i, brch in enumerate(self_tree.branches_all): + + if i == orig_branch_count: + break # no need to check new branches are inside us + + for pt in brch.bpoints: + if pt.next and pt.childCount < 4: # cannot attach to the last points + if self_tree.isPointInTwigBounds(pt.co, True): # selected_only + interior_points.append(pt) + interior_normal += pt.no * pt.radius + + segments_all[:] = [] + segments_level[:] = [] + + if interior_points: + # Ok, we can add twigs now + distripute_seg_on_mesh( self_tree.objectTwigBoundsMesh, face_group ) + + for seg in segments_level[0]: # only be zero segments + # Warning, increments the child count for bpoints we attach to!! + set_seg_attach_point(seg, interior_points, twig_fill_rand_scale) + + # Try sorting by other properties! this is ok for now + for segments_level_current in segments_level: + segments_level_current.sort( key = lambda seg: -(seg.headCo-seg.tailCo).length ) + + for level in xrange(twig_fill_levels): + if len(segments_level) > level: + for seg in segments_level[level]: + # print level, seg.brothers + if seg.mergeCount == 0: + seg_merge = seg.findBestMerge(twig_fill_fork_angle_max) + if seg_merge: + seg.merge( seg_merge ) + + if len(segments_level) > level+1: + for seg in segments_level[level+1]: + seg.calcBrothers() + + for seg in segments_all: + if seg.parent == None: + seg.levelFromRoot = 0 + seg.calcLevelFromRoot() + + ''' + for i, seg in enumerate(segments_all): + # Make a branch from this data! + + brch = branch() + brch.type = BRANCH_TYPE_FILL + self_tree.branches_all.append(brch) + + # ============================= do this per bez pair + # 1 is the base and 2 is the tail + + #p1_h1 = seg.getHeadHandle() + p1_co = seg.headCo.copy() + p1_h2 = seg.getHeadHandle() # isnt used + + p2_h1 = seg.getTailHandle() + p2_co = seg.tailCo.copy() + #p2_h2 = seg.getTailHandle() # isnt used + + + bez1_vec = (None, p1_co, p1_h2) + bez2_vec = (p2_h1, p2_co, None) + + seg_root = seg.getRootSeg() + + radius_root = seg_root.bpt.radius * twig_fill_radius_factor + # Clamp so the head is never smaller then the tail + if radius_root < twig_fill_radius_min: radius_root = twig_fill_radius_min + + if seg_root.levelFromLeaf: + # print seg_root.levelFromLeaf, seg.levelFromRoot + WIDTH_STEP = (radius_root - twig_fill_radius_min) / (seg_root.levelFromLeaf+1) + + radius1 = twig_fill_radius_min + (WIDTH_STEP * (seg.levelFromLeaf+1)) + if seg.levelFromLeaf: radius2 = twig_fill_radius_min + (WIDTH_STEP * seg.levelFromLeaf) + else: radius2 = twig_fill_radius_min + else: + radius1 = radius_root + radius2 = twig_fill_radius_min + + + points_from_bezier_seg(self_tree.steps, pointlist, radlist, bez1_vec, bez2_vec, radius1, radius2) + + # dont apply self_tree.limbScale here! - its alredy done + bpoints = [ bpoint(brch, Vector(pointlist[ii]), Vector(), radlist[ii]) for ii in xrange(len(pointlist)) ] + + # remove endpoint for all but the last + #if i != len(bez_list)-1: + # bpoints.pop() + + brch.bpoints.extend(bpoints) + # ============================= + + # Finalize once point data is there + brch.calcData() + # + #preview_curve() + ''' + + for segments_level_current in reversed(segments_level): + for seg in segments_level_current: + if seg.used == False and (seg.parent == None or seg.parent.used == True): + + # The root segment for this set of links. + # seg_root_linked = seg + + brch = branch() + brch.type = BRANCH_TYPE_FILL + self_tree.branches_all.append(brch) + + # Can we attach to a real branch? + if seg.parent == None: + if seg.bpt[1]: # we can do a real join into the attach point + brch.parent_pt = seg.bpt[0] + # brch.parent_pt.childCount # this has alredy changed from + + ''' + if seg.parent: + if seg.bpt[1] == 2: + #if seg.bpt[1]: + # print "Making Connection" + if seg.bpt[0] == None: + raise "Error" + if seg.bpt[1] != 2: + print seg.bpt[1] + raise "Error" + + brch.parent_pt = seg.bpt[1] + brch.parent_pt.childCount += 1 + if brch.parent_pt.childCount > 4: + raise "Aeeae" + print "\n\nM<aking Joint!!" + ''' + + if seg.parent: + sibling = seg.sibling() + if sibling.new_bpt: + if sibling.new_bpt.childCount < 4: + brch.parent_pt = sibling.new_bpt + brch.parent_pt.childCount +=1 + + # Go down the hierarhy + is_first = True + while seg != None: + seg.used = True + + # ============================================== + + #p1_h1 = seg.getHeadHandle() + p1_co = seg.headCo.copy() + p1_h2 = seg.getHeadHandle() # isnt used + + p2_h1 = seg.getTailHandle() + p2_co = seg.tailCo.copy() + #p2_h2 = seg.getTailHandle() # isnt used + + + bez1_vec = (None, p1_co, p1_h2) + bez2_vec = (p2_h1, p2_co, None) + + seg_root = seg.getRootSeg() + + radius_root = seg_root.bpt[0].radius * twig_fill_radius_factor + # Clamp so the head is never smaller then the tail + if radius_root < twig_fill_radius_min: radius_root = twig_fill_radius_min + + if seg_root.levelFromLeaf: + # print seg_root.levelFromLeaf, seg.levelFromRoot + widthStep = (radius_root - twig_fill_radius_min) / (seg_root.levelFromLeaf+1) + + radius1 = twig_fill_radius_min + (widthStep * (seg.levelFromLeaf+1)) + if seg.levelFromLeaf: radius2 = twig_fill_radius_min + (widthStep * seg.levelFromLeaf) + else: radius2 = twig_fill_radius_min + else: + radius1 = radius_root + radius2 = twig_fill_radius_min + + points_from_bezier_seg(self_tree.steps, pointlist, radlist, bez1_vec, bez2_vec, radius1, radius2) + + + start_pointlist = 0 + + # This is like baseTrim, (remove the base points to make nice joins, accounting for radius of parent point) + # except we do it before the branch is made + + if brch.parent_pt: + while len(pointlist) - start_pointlist > 2 and (Vector(pointlist[start_pointlist]) - brch.parent_pt.co).length < (brch.parent_pt.radius*2): + start_pointlist +=1 + + if is_first and brch.parent_pt: + # We need to move the base point to a place where it looks good on the parent branch + # to do this. move the first point, then remove the following points that look horrible (double back on themself) + + no = Vector(pointlist[0]) - Vector(pointlist[-1]) + no.length = brch.parent_pt.radius*2 + pointlist[0] = list(Vector(pointlist[0]) - no) + + """ + pointlist[1][0] = (pointlist[0][0] + pointlist[2][0])/2.0 + pointlist[1][1] = (pointlist[0][1] + pointlist[2][1])/2.0 + pointlist[1][2] = (pointlist[0][2] + pointlist[2][2])/2.0 + + pointlist[2][0] = (pointlist[1][0] + pointlist[3][0])/2.0 + pointlist[2][1] = (pointlist[1][1] + pointlist[3][1])/2.0 + pointlist[2][2] = (pointlist[1][2] + pointlist[3][2])/2.0 + """ + + + # Done setting the start point + + + len_pointlist = len(pointlist) + if seg.children: + len_pointlist -= 1 + + # dont apply self_tree.limbScale here! - its alredy done + bpoints = [ bpoint(brch, Vector(pointlist[ii]), Vector(), radlist[ii]) for ii in xrange(start_pointlist, len_pointlist) ] + brch.bpoints.extend( bpoints ) + # ============================================== + + seg.new_bpt = bpoints[0] + + if seg.children: + seg = seg.children[0] + else: + seg = None + + is_first = False + + # done adding points + brch.calcData() + + + + + def buildTwigs(self, twig_ratio, twig_select_mode, twig_select_factor): + + ratio_int = int(len(self.branches_all) * twig_ratio) + if ratio_int == 0: + return + + # So we only mix branches of similar lengths + branches_sorted = self.branches_all[:] + + # Get the branches based on our selection method! + if twig_select_mode==0: + branches_sorted.sort( key = lambda brch: brch.getLength()) + elif twig_select_mode==1: + branches_sorted.sort( key = lambda brch:-brch.getLength()) + elif twig_select_mode==2: + branches_sorted.sort( key = lambda brch:brch.getStraightness()) + elif twig_select_mode==3: + branches_sorted.sort( key = lambda brch:-brch.getStraightness()) + + factor_int = int(len(self.branches_all) * twig_select_factor) + branches_sorted[factor_int:] = [] # remove the last part of the list + + branches_sorted.sort( key = lambda brch: len(brch.bpoints)) + + branches_new = [] + #for i in xrange(ratio_int): + tot_twigs = 0 + + step = 1 + while tot_twigs < ratio_int and step < len(branches_sorted): + # Make branches from the existing + for j in xrange(step, len(branches_sorted)): + brch = branches_sorted[j-step].mixToNew(branches_sorted[j], None) + branches_new.append( brch ) + tot_twigs +=1 + + if tot_twigs > ratio_int: + break + + ### print "TwigCount", len(branches_new), ratio_int + + self.branches_twigs = branches_new + + def toDebugDisplay(self): + ''' + Should be able to call this at any time to see whats going on, dosnt work so nice ATM. + ''' + sce = bpy.data.scenes.active + + for ob in self.debug_objects: + for ob in sce.objects: + sce.objects.unlink(ob) + + for branch_index, brch in enumerate(self.branches_all): + pt_index = 0 + for pt_index, pt in enumerate(brch.bpoints): + name = '%.3d_%.3d' % (branch_index, pt_index) + if pt.next==None: + name += '_end' + if pt.prev==None: + name += '_start' + + ob = sce.objects.new('Empty', name) + self.debug_objects.append(ob) + mat = ScaleMatrix(pt.radius, 4) * TranslationMatrix(pt.co) + ob.setMatrix(mat) + ob.setDrawMode(8) # drawname + Blender.Window.RedrawAll() + + + + def toMesh(self, mesh=None,\ + do_uv=True,\ + do_uv_keep_vproportion=True,\ + do_uv_vnormalize=False,\ + do_uv_uscale=False,\ + uv_image = None,\ + uv_x_scale=1.0,\ + uv_y_scale=4.0,\ + do_uv_blend_layer= False,\ + do_cap_ends=False,\ + ): + + self.mesh = freshMesh(mesh) + totverts = 0 + + for brch in self.branches_all: + totverts += len(brch.bpoints) + + self.mesh.verts.extend( [ (0.0,0.0,0.0) ] * ((totverts * 4)+1) ) # +1 is a dummy vert + verts = self.mesh.verts + + # Assign verts to points, 4 verts for each point. + i = 1 # dummy vert, should be 0 + for brch in self.branches_all: + for pt in brch.bpoints: + pt.verts[0] = verts[i] + pt.verts[1] = verts[i+1] + pt.verts[2] = verts[i+2] + pt.verts[3] = verts[i+3] + i+=4 + + # Do this again because of collapsing + # pt.calcVerts(brch) + + # roll the tube so quads best meet up to their branches. + for brch in self.branches_all: + #for pt in brch.bpoints: + if brch.parent_pt: + + # Use temp lists for gathering an average + if brch.parent_pt.roll_angle == None: + brch.parent_pt.roll_angle = [brch.getParentQuadAngle()] + # More then 2 branches use this point, add to the list + else: + brch.parent_pt.roll_angle.append( brch.getParentQuadAngle() ) + + # average the temp lists into floats + for brch in self.branches_all: + #for pt in brch.bpoints: + if brch.parent_pt and type(brch.parent_pt.roll_angle) == list: + # print brch.parent_pt.roll_angle + f = 0.0 + for val in brch.parent_pt.roll_angle: + f += val + brch.parent_pt.roll_angle = f/len(brch.parent_pt.roll_angle) + + # set the roll of all the first segments that have parents, + # this is because their roll is set from their parent quad and we dont want them to roll away from that. + for brch in self.branches_all: + if brch.parent_pt: + # if the first joint has a child then apply half the roll + # theres no correct solition here, but this seems ok + if brch.bpoints[0].roll_angle != None: + #brch.bpoints[0].roll_angle *= 0.5 + #brch.bpoints[0].roll_angle = 0.0 + #brch.bpoints[1].roll_angle = 0.0 + brch.bpoints[0].roll_angle = 0.0 + pass + else: + # our roll was set from the branches parent and needs no changing + # set it to zero so the following functions know to interpolate. + brch.bpoints[0].roll_angle = 45.0 + #brch.bpoints[1].roll_angle = 0.0 + + ''' + Now interpolate the roll! + The method used here is a little odd. + + * first loop up the branch and set each points value to the "last defined" value and record the steps + since the last defined value + * Do the same again but backwards + + now for each undefined value we have 1 or 2 values, if its 1 its simple we just use that value + ( no interpolation ), if there are 2 then we use the offsets from each end to work out the interpolation. + + one up, one back, and another to set the values, so 3 loops all up. + ''' + #### print "scan up the branch..." + for brch in self.branches_all: + last_value = None + last_index = -1 + for i in xrange(len(brch.bpoints)): + pt = brch.bpoints[i] + if type(pt.roll_angle) in (float, int): + last_value = pt.roll_angle + last_index = i + else: + if type(last_value) in (float, int): + # Assign a list, because there may be a connecting roll value from another joint + pt.roll_angle = [(last_value, i-last_index)] + + #### print "scan down the branch..." + last_value = None + last_index = -1 + for i in xrange(len(brch.bpoints)-1, -1, -1): # same as above but reverse + pt = brch.bpoints[i] + if type(pt.roll_angle) in (float, int): + last_value = pt.roll_angle + last_index = i + else: + if last_value != None: + if type(pt.roll_angle) == list: + pt.roll_angle.append((last_value, last_index-i)) + else: + #pt.roll_angle = [(last_value, last_index-i)] + + # Dont bother assigning a list because we wont need to add to it later + pt.roll_angle = last_value + + # print "looping ,...." + ### print "assigning/interpolating roll values" + for pt in brch.bpoints: + + # print "this roll IS", pt.roll_angle + + if pt.roll_angle == None: + continue + elif type(pt.roll_angle) in (float, int): + pass + elif len(pt.roll_angle) == 1: + pt.roll_angle = pt.roll_angle[0][0] + else: + # interpolate + tot = pt.roll_angle[0][1] + pt.roll_angle[1][1] + pt.roll_angle = \ + (pt.roll_angle[0][0] * (tot - pt.roll_angle[0][1]) +\ + pt.roll_angle[1][0] * (tot - pt.roll_angle[1][1])) / tot + + #### print pt.roll_angle, 'interpolated roll' + + pt.roll(pt.roll_angle) + + # Done with temp average list. now we know the best roll for each branch. + + # mesh the data + for brch in self.branches_all: + for pt in brch.bpoints: + pt.toMesh(self.mesh) + + #faces_extend = [ face for brch in self.branches_all for pt in brch.bpoints for face in pt.faces if face ] + + + + faces_extend = [] + for brch in self.branches_all: + if brch.parent_pt: + faces_extend.extend(brch.faces) + for pt in brch.bpoints: + for face in pt.faces: + if face: + faces_extend.append(face) + + if do_cap_ends: + # TODO - UV map and image? + faces_extend.extend([ brch.bpoints[-1].verts for brch in self.branches_all ]) + + faces = self.mesh.faces + + faces.extend(faces_extend, smooth=True) + + if do_uv: + # Assign the faces back + face_index = 0 + for brch in self.branches_all: + if brch.parent_pt: + for i in (0,1,2,3): + face = brch.faces[i] = faces[face_index+i] + face_index +=4 + + for pt in brch.bpoints: + for i in (0,1,2,3): + if pt.faces[i]: + pt.faces[i] = faces[face_index] + face_index +=1 + + #if self.mesh.faces: + # self.mesh.faceUV = True + mesh.addUVLayer( 'base' ) + + # rename the uv layer + #mesh.renameUVLayer(mesh.getUVLayerNames()[0], 'base') + + for brch in self.branches_all: + + uv_x_scale_branch = 1.0 + if do_uv_uscale: + uv_x_scale_branch = 0.0 + for pt in brch.bpoints: + uv_x_scale_branch += pt.radius + + uv_x_scale_branch = uv_x_scale_branch / len(brch.bpoints) + # uv_x_scale_branch = brch.bpoints[0].radius + + if do_uv_vnormalize: + uv_normalize = [] + + def uvmap_faces(my_faces, y_val, y_size): + ''' + Accept a branch or pt faces + ''' + uv_ls = [None, None, None, None] + for i in (0,1,2,3): + if my_faces[i]: + if uv_image: + my_faces[i].image = uv_image + uvs = my_faces[i].uv + else: + # Use these for calculating blending values + uvs = [Vector(0,0), Vector(0,0), Vector(0,0), Vector(0,0)] + + uv_ls[i] = uvs + + x1 = i*0.25 * uv_x_scale * uv_x_scale_branch + x2 = (i+1)*0.25 * uv_x_scale * uv_x_scale_branch + + uvs[3].x = x1; + uvs[3].y = y_val+y_size + + uvs[0].x = x1 + uvs[0].y = y_val + + uvs[1].x = x2 + uvs[1].y = y_val + + uvs[2].x = x2 + uvs[2].y = y_val+y_size + + if do_uv_vnormalize: + uv_normalize.extend(uvs) + + return uv_ls + + # Done uvmap_faces + + y_val = 0.0 + + if brch.parent_pt: + y_size = (brch.getParentFaceCent() - brch.bpoints[0].co).length + + if do_uv_keep_vproportion: + y_size = y_size / ((brch.bpoints[0].radius + brch.parent_pt.radius)/2) * uv_y_scale + + brch.uv = uvmap_faces(brch.faces, 0.0, y_size) + + y_val += y_size + + for pt in brch.bpoints: + if pt.next: + y_size = (pt.co-pt.next.co).length + # scale the uvs by the radius, avoids stritching. + if do_uv_keep_vproportion: + y_size = y_size / pt.radius * uv_y_scale + pt.uv = uvmap_faces(pt.faces, y_val, y_size) + y_val += y_size + + + if do_uv_vnormalize and uv_normalize: + # Use yscale here so you can choose to have half the normalized value say. + vscale = (1/uv_normalize[-1].y) * uv_y_scale + for uv in uv_normalize: + uv.y *= vscale + + + # Done with UV mapping the first layer! now map the blend layers + if do_uv_blend_layer: + # Set up the blend UV layer - this is simply the blending for branch joints + mesh.addUVLayer( 'blend' ) + mesh.activeUVLayer = 'blend' + + # Set all faces to be on full blend + for f in mesh.faces: + for uv in f.uv: + uv.y = uv.x = 0.0 + + for brch in self.branches_all: + if brch.parent_pt: + for f in brch.faces: + if f: + uvs = f.uv + uvs[0].x = uvs[1].x = uvs[2].x = uvs[3].x = 0.0 + uvs[0].y = uvs[1].y = 1.0 # swap these? - same as inverting the blend + uvs[2].y = uvs[3].y = 0.0 + + # Set up the join UV layer, this overlays nice blended + mesh.addUVLayer( 'join' ) + mesh.activeUVLayer = 'join' + + # Set all faces to be on full blend + for f in mesh.faces: + for uv in f.uv: + uv.y = uv.x = 0.0 + + for brch in self.branches_all: + if brch.parent_pt: + # The UV's that this branch would cover if it was a face, + uvs_base = brch.parent_pt.uv[brch.getParentQuadIndex()] + + uvs_base_mid = Vector(0,0) + for uv in uvs_base: + uvs_base_mid += uv + + uvs_base_mid *= 0.25 + + # TODO - Factor scale and distance in here + ## uvs_base_small = [(uv+uvs_base_mid)*0.5 for uv in uvs_base] + uvs_base_small = [uvs_base_mid, uvs_base_mid, uvs_base_mid, uvs_base_mid] + + if brch.faces[0]: + f = brch.faces[0] + uvs = f.uv + uvs[0][:] = uvs_base[0] + uvs[1][:] = uvs_base[1] + + uvs[2][:] = uvs_base_small[1] + uvs[3][:] = uvs_base_small[0] + + if brch.faces[1]: + f = brch.faces[1] + uvs = f.uv + uvs[0][:] = uvs_base[1] + uvs[1][:] = uvs_base[2] + + uvs[2][:] = uvs_base_small[2] + uvs[3][:] = uvs_base_small[1] + + if brch.faces[2]: + f = brch.faces[2] + uvs = f.uv + uvs[0][:] = uvs_base[2] + uvs[1][:] = uvs_base[3] + + uvs[2][:] = uvs_base_small[3] + uvs[3][:] = uvs_base_small[2] + + if brch.faces[3]: + f = brch.faces[3] + uvs = f.uv + uvs[0][:] = uvs_base[3] + uvs[1][:] = uvs_base[0] + + uvs[2][:] = uvs_base_small[0] + uvs[3][:] = uvs_base_small[3] + + mesh.activeUVLayer = 'base' # just so people dont get worried the texture is not there - dosnt effect rendering. + else: + # no UV's + pass + + if do_cap_ends: + # de-select end points for + i = len(faces)-1 + + cap_end_face_start = len(faces) - len(self.branches_all) + + j = 0 + for i in xrange(cap_end_face_start, len(faces)): + self.branches_all[j].face_cap = faces[i] + faces[i].sel = 0 + + # default UV's are ok for now :/ + if do_uv and uv_image: + faces[i].image = uv_image + + j +=1 + + # set edge crease for capped ends. + for ed in self.mesh.edges: + if ed.v1.sel==False and ed.v2.sel==False: + ed.crease = 255 + ed.sel = True # so its all selected still + + del faces_extend + + return self.mesh + + def toLeafMesh(self, mesh_leaf,\ + leaf_branch_limit = 0.5,\ + leaf_branch_limit_rand = 0.8,\ + leaf_branch_limit_type_curve = False,\ + leaf_branch_limit_type_grow = False,\ + leaf_branch_limit_type_fill = False,\ + leaf_size = 0.5,\ + leaf_size_rand = 0.5,\ + leaf_branch_density = 0.2,\ + leaf_branch_pitch_angle = 0.0,\ + leaf_branch_pitch_rand = 0.2,\ + leaf_branch_roll_rand = 0.2,\ + leaf_branch_angle = 75.0,\ + leaf_rand_seed = 1.0,\ + leaf_object=None,\ + ): + + ''' + return a mesh with leaves seperate from the tree + + Add to the existing mesh. + ''' + + #radius = [(pt.radius for pt in self.branches_all for pt in brch.bpoints for pt in brch.bpoints] + mesh_leaf = freshMesh(mesh_leaf) + self.mesh_leaf = mesh_leaf + + # if not leaf_object: return # make the dupli anyway :/ - they can do it later or the script could complain + + if leaf_branch_limit == 1.0: + max_radius = 1000000.0 + else: + # We wont place leaves on all branches so... + # first collect stats, we want to know the average radius and total segments + totpoints = 0 + radius = 0.0 + max_radius = 0.0 + for brch in self.branches_all: + for pt in brch.bpoints: + radius += pt.radius + if pt.radius > max_radius: + max_radius = pt.radius + + #totpoints += len(brch.bpoints) + + radius_max = max_radius * leaf_branch_limit + + verts_extend = [] + faces_extend = [] + + co1 = Vector(0.0, -0.5, -0.5) + co2 = Vector(0.0, -0.5, 0.5) + co3 = Vector(0.0, 0.5, 0.5) + co4 = Vector(0.0, 0.5, -0.5) + + rnd_seed = [leaf_rand_seed] # could have seed as an input setting + + for brch in self.branches_all: + + # quick test, do we need leaves on this branch? + if leaf_branch_limit != 1.0 and brch.bpoints[-1].radius > radius_max: + continue + + + for pt in brch.bpoints: + + # For each point we can add 2 leaves + for odd_even in (0,1): + + + if (pt == brch.bpoints[-1] and odd_even==1) or \ + (leaf_branch_density != 1.0 and leaf_branch_density < next_random_num(rnd_seed)): + pass + else: + if leaf_branch_limit_rand: + # (-1 : +1) * leaf_branch_limit_rand + rnd = 1 + (((next_random_num(rnd_seed) - 0.5) * 2 ) * leaf_branch_limit_rand) + else: + rnd = 1.0 + + if pt.childCount == 0 and (leaf_branch_limit == 1.0 or (pt.radius * rnd) < radius_max): + leaf_size_tmp = leaf_size * (1.0-(next_random_num(rnd_seed)*leaf_size_rand)) + + # endpoints dont rotate + if pt.next != None: + cross1 = CrossVecs(zup, pt.no) # use this to offset the leaf later + cross2 = CrossVecs(cross1, pt.no) + if odd_even ==0: + mat_yaw = RotationMatrix(leaf_branch_angle, 3, 'r', cross2) + else: + mat_yaw = RotationMatrix(-leaf_branch_angle, 3, 'r', cross2) + + leaf_no = (pt.no * mat_yaw) + + # Correct upwards pointing from changing the yaw + #my_up = zup * mat + + # correct leaf location for branch width + cross1.length = pt.radius/2 + leaf_co = pt.co + cross1 + else: + # no correction needed, we are at the end of the branch + leaf_no = pt.no + leaf_co = pt.co + + mat = Matrix([leaf_size_tmp,0,0],[0,leaf_size_tmp,0],[0,0,leaf_size_tmp]) * leaf_no.toTrackQuat('x', 'z').toMatrix() + + # Randomize pitch and roll for the leaf + + # work out the axis to pitch and roll + cross1 = CrossVecs(zup, leaf_no) # use this to offset the leaf later + if leaf_branch_pitch_rand or leaf_branch_pitch_angle: + + angle = -leaf_branch_pitch_angle + if leaf_branch_pitch_rand: + angle += leaf_branch_pitch_rand * ((next_random_num(rnd_seed)-0.5)*360) + + mat_pitch = RotationMatrix( angle, 3, 'r', cross1) + mat = mat * mat_pitch + if leaf_branch_roll_rand: + mat_roll = RotationMatrix( leaf_branch_roll_rand * ((next_random_num(rnd_seed)-0.5)*360), 3, 'r', leaf_no) + mat = mat * mat_roll + + mat = mat.resize4x4() * TranslationMatrix(leaf_co) + + i = len(verts_extend) + faces_extend.append( (i,i+1,i+2,i+3) ) + verts_extend.extend([tuple(co4*mat), tuple(co3*mat), tuple(co2*mat), tuple(co1*mat)]) + #count += 1 + + + # setup dupli's + + self.mesh_leaf.verts.extend(verts_extend) + self.mesh_leaf.faces.extend(faces_extend) + + return self.mesh_leaf + + + def toArmature(self, ob_arm, armature): + + armature.drawType = Blender.Armature.STICK + armature.makeEditable() # enter editmode + + # Assume toMesh has run + self.armature = armature + for bonename in armature.bones.keys(): + del armature.bones[bonename] + + + group_names = [] + + for i, brch in enumerate(self.branches_all): + + # get a list of parent points to make into bones. use parents and endpoints + bpoints_parent = [pt for pt in brch.bpoints if pt.childCount or pt.prev == None or pt.next == None] + bpbone_last = None + for j in xrange(len(bpoints_parent)-1): + + # bone container class + bpoints_parent[j].bpbone = bpbone = bpoint_bone() + bpbone.name = '%i_%i' % (i,j) # must be unique + group_names.append(bpbone.name) + + bpbone.editbone = Blender.Armature.Editbone() # automatically added to the armature + self.armature.bones[bpbone.name] = bpbone.editbone + + bpbone.editbone.head = bpoints_parent[j].co + bpbone.editbone.head = bpoints_parent[j].co + bpbone.editbone.tail = bpoints_parent[j+1].co + + # parent the chain. + if bpbone_last: + bpbone.editbone.parent = bpbone_last.editbone + bpbone.editbone.options = [Blender.Armature.CONNECTED] + + bpbone_last = bpbone + + for brch in self.branches_all: + if brch.parent_pt: # We must have a parent + + # find the bone in the parent chain to use for the parent of this + parent_pt = brch.parent_pt + bpbone_parent = None + while parent_pt: + bpbone_parent = parent_pt.bpbone + if bpbone_parent: + break + + parent_pt = parent_pt.prev + + + if bpbone_parent: + brch.bpoints[0].bpbone.editbone.parent = bpbone_parent.editbone + else: # in rare cases this may not work. should be verry rare but check anyway. + print 'this is really odd... look into the bug.' + + self.armature.update() # exit editmode + + # Skin the mesh + if self.mesh: + for group in group_names: + self.mesh.addVertGroup(group) + + for brch in self.branches_all: + vertList = [] + group = '' # dummy + + for pt in brch.bpoints: + if pt.bpbone: + if vertList: + self.mesh.assignVertsToGroup(group, vertList, 1.0, Blender.Mesh.AssignModes.ADD) + + vertList = [] + group = pt.bpbone.name + + vertList.extend( [v.index for v in pt.verts] ) + + if vertList: + self.mesh.assignVertsToGroup(group, vertList, 1.0, Blender.Mesh.AssignModes.ADD) + + return self.armature + + def toAction(self, ob_arm, texture, anim_speed=1.0, anim_magnitude=1.0, anim_speed_size_scale=True, anim_offset_scale=1.0): + # Assume armature + action = ob_arm.action + if not action: + action = bpy.data.actions.new() + action.fakeUser = False # so we dont get masses of bad data + ob_arm.action = action + + # Blender.Armature.NLA.ob_arm. + pose = ob_arm.getPose() + + for pose_bone in pose.bones.values(): + pose_bone.insertKey(ob_arm, 0, [Blender.Object.Pose.ROT], True) + + # Now get all the IPO's + + ipo_dict = action.getAllChannelIpos() + # print ipo_dict + + # Sicne its per frame, it increases very fast. scale it down a bit + anim_speed = anim_speed/100 + + # When we have the same trees next to eachother, they will animate the same way unless we give each its own texture or offset settings. + # We can use the object's location as a factor - this also will have the advantage? of seeing the animation move across the tree's + # allow a scale so the difference between tree textures can be adjusted. + anim_offset = self.objectCurve.matrixWorld.translationPart() * anim_offset_scale + + anim_speed_final = anim_speed + # Assign drivers to them all + for name, ipo in ipo_dict.iteritems(): + tex_str = 'b.Texture.Get("%s")' % texture.name + + if anim_speed_size_scale: + # Adjust the speed by the bone size. + # get the point from the name. a bit ugly but works fine ;) - Just dont mess the index up! + lookup = [int(val) for val in name.split('_')] + pt = self.branches_all[ lookup[0] ].bpoints[ lookup[1] ] + anim_speed_final = anim_speed / (1+pt.radius) + + cu = ipo[Blender.Ipo.PO_QUATX] + try: cu.delBezier(0) + except: pass + cu.driver = 2 # Python expression + cu.driverExpression = '%.3f*(%s.evaluate(((b.Get("curframe")*%.3f)+%.3f,%.3f,%.3f)).w-0.5)' % (anim_magnitude, tex_str, anim_speed_final, anim_offset.x, anim_offset.y, anim_offset.z) + + cu = ipo[Blender.Ipo.PO_QUATY] + try: cu.delBezier(0) + except: pass + cu.driver = 2 # Python expression + cu.driverExpression = '%.3f*(%s.evaluate((%.3f,(b.Get("curframe")*%.3f)+%.3f,%.3f)).w-0.5)' % (anim_magnitude, tex_str, anim_offset.x, anim_speed_final, anim_offset.y, anim_offset.z) + + cu = ipo[Blender.Ipo.PO_QUATZ] + try: cu.delBezier(0) + except: pass + cu.driver = 2 # Python expression + cu.driverExpression = '%.3f*(%s.evaluate((%.3f,%.3f,(b.Get("curframe")*%.3f)+%.3f)).w-0.5)' % (anim_magnitude, tex_str, anim_offset.x, anim_offset.y, anim_speed_final, anim_offset.z) + +xyzup = Vector(1,1,1).normalize() +xup = Vector(1,0,0) +yup = Vector(0,1,0) +zup = Vector(0,0,1) + +class bpoint_bone: + def __init__(self): + self.name = None + self.editbone = None + self.blenbone = None + self.posebone = None + +class bpoint(object): + ''' The point in the middle of the branch, not the mesh points + ''' + __slots__ = 'branch', 'co', 'no', 'radius', 'vecs', 'verts', 'children', 'faces', 'uv', 'next', 'prev', 'childCount', 'bpbone', 'roll_angle', 'nextMidCo', 'childrenMidCo', 'childrenMidRadius', 'targetCos', 'inTwigBounds' + def __init__(self, brch, co, no, radius): + self.branch = brch + self.co = co + self.no = no + self.radius = radius + self.vecs = [None, None, None, None] # 4 for now + self.verts = [None, None, None, None] + self.children = [None, None, None, None] # child branches, dont fill in faces here + self.faces = [None, None, None, None] + self.uv = None # matching faces, except - UV's are calculated even if there is no face, this is so we can calculate the blending UV's + self.next = None + self.prev = None + self.childCount = 0 + self.bpbone = None # bpoint_bone instance + + # when set, This is the angle we need to roll to best face our branches + # the roll that is set may be interpolated if we are between 2 branches that need to roll. + # Set to None means that the roll will be left default (from parent) + self.roll_angle = None + + + # The location between this and the next point, + # if we want to be tricky we can try make this not just a simple + # inbetween and use the normals to have some curvature + self.nextMidCo = None + + # Similar to above, median point of all children + self.childrenMidCo = None + + # Similar as above, but for radius + self.childrenMidRadius = None + + # Target locations are used when you want to move the point to a new location but there are + # more then 1 influence, build up a list and then apply + self.targetCos = [] + + # When we use twig bounding mesh, store if this point is in the bounding mesh. Assume true unless we set to false and do the test + self.inTwigBounds = True + + def __repr__(self): + s = '' + s += '\t\tco:', self.co + s += '\t\tno:', self.no + s += '\t\tradius:', self.radius + s += '\t\tchildren:', [(child != False) for child in self.children] + return s + + def makeLast(self): + self.next = None + self.nextMidCo = None + self.childrenMidCo = None + + def setCo(self, co): + self.co[:] = co + self.calcNextMidCo() + self.calcNormal() + + if self.prev: + self.prev.calcNextMidCo() + self.prev.calcNormal() + self.prev.calcChildrenMidData() + + if self.next: + self.prev.calcNormal() + + self.calcChildrenMidData() + + + def nextLength(self): + return (self.co-self.next.co).length + def prevLength(self): + return (self.co-self.prev.co).length + + def hasOverlapError(self): + if self.prev == None: + return False + if self.next == None: + return False + ''' + # see if this point sits on the line between its siblings. + co, fac = ClosestPointOnLine(self.co, self.prev.co, self.next.co) + + if fac >= 0.0 and fac <= 1.0: + return False # no overlap, we are good + else: + return True # error, some overlap + ''' + + + # Alternate method, maybe better + ln = self.nextLength() + lp = self.prevLength() + ls = (self.prev.co-self.next.co).length + + # Are we overlapping? the length from our next or prev is longer then the next-TO-previous? + if ln>ls or lp>ls: + return True + else: + return False + + + def applyTargetLocation(self): + if not self.targetCos: + return False + elif len(self.targetCos) == 1: + co_all = self.targetCos[0] + else: + co_all = Vector() + for co in self.targetCos: + co_all += co + co_all = co_all / len(self.targetCos) + + self.targetCos[:] = [] + + length = (self.co-co_all).length + # work out if we are moving up or down + if AngleBetweenVecsSafe(self.no, self.co - co_all) < 90: + + # Up + while length > (self.co-self.prev.co).length: + if not self.collapseUp(): + break + + else: + # Down + while length*2 > (self.co-self.next.co).length: + if not self.collapseDown(): + break + + self.setCo(co_all) + + return True + + def calcNextMidCo(self): + if not self.next: + return None + + # be tricky later. + self.nextMidCo = (self.co + self.next.co) * 0.5 + + def calcNormal(self): + if self.prev == None: + self.no = (self.next.co - self.co).normalize() + elif self.next == None: + self.no = (self.co - self.prev.co).normalize() + else: + self.no = (self.next.co - self.prev.co).normalize() + + def calcChildrenMidData(self): + ''' + Calculate childrenMidCo & childrenMidRadius + This is a bit tricky, we need to find a point between this and the next, + the medium of all children, this point will be on the line between this and the next. + ''' + if not self.next: + return None + + # factor between this and the next point + radius = factor = factor_i = 0.0 + + count = 0 + for brch in self.children: + if brch: # we dont need the co at teh moment. + co, fac = ClosestPointOnLine(brch.bpoints[0].co, self.co, self.next.co) + factor_i += fac + count += 1 + + radius += brch.bpoints[0].radius + + if not count: + return + + # interpolate points + factor_i = factor_i/count + factor = 1-factor_i + + self.childrenMidCo = (self.co * factor) + (self.next.co * factor_i) + self.childrenMidRadius = radius + + #debug_pt(self.childrenMidCo) + + def getAbsVec(self, index): + # print self.vecs, index + return self.co + self.vecs[index] + + def slide(self, factor): + ''' + Slides the segment up and down using the prev and next points + ''' + self.setCo(self.slideCo(factor)) + + def slideCo(self, factor): + if self.prev == None or self.next == None or factor==0.0: + return + + if factor < 0.0: + prev_co = self.prev.co + co = self.co + + ofs = co-prev_co + ofs.length = abs(factor) + self.co - ofs + + return self.co - ofs + else: + next_co = self.next.co + co = self.co + + ofs = co-next_co + ofs.length = abs(factor) + + return self.co - ofs + + + def collapseDown(self): + ''' + Collapse the next point into this one + ''' + + # self.next.next == None check is so we dont shorten the final length of branches. + if self.next == None or self.next.next == None or self.childCount or self.next.childCount: + return False + + self.branch.bpoints.remove(self.next) + self.next = self.next.next # skip + self.next.prev = self + + # Watch this place - must update all data thats needed. roll is not calculaetd yet. + self.calcNextMidCo() + return True + + def collapseUp(self): + ''' + Collapse the previous point into this one + ''' + + # self.next.next == None check is so we dont shorten the final length of branches. + if self.prev == None or self.prev.prev == None or self.prev.childCount or self.prev.prev.childCount: + return False + + self.branch.bpoints.remove(self.prev) + self.prev = self.prev.prev # skip + self.prev.next = self + + # Watch this place - must update all data thats needed. roll is not calculaetd yet. + self.prev.calcNextMidCo() + return True + + + def smooth(self, factor, factor_joint): + ''' + Blend this point into the other 2 points + ''' + if self.next == None or self.prev == None: + return False + + if self.childCount or self.prev.childCount: + factor = factor_joint; + + if factor==0.0: + return False; + + radius = (self.next.radius + self.prev.radius)/2.0 + no = (self.next.no + self.prev.no).normalize() + + # do a line intersect to work out the best location + ''' + cos = LineIntersect( self.next.co, self.next.co+self.next.no,\ + self.prev.co, self.prev.co+self.prev.no) + if cos == None: + co = (self.prev.co + self.next.co)/2.0 + else: + co = (cos[0]+cos[1])/2.0 + ''' + # Above can give odd results every now and then + co = (self.prev.co + self.next.co)/2.0 + + # Now apply + factor_i = 1.0-factor + self.setCo(self.co*factor_i + co*factor) + self.radius = self.radius*factor_i + radius*factor + + return True + + def childPoint(self, index): + ''' + Returns the middle point for any children between this and the next edge + ''' + if self.next == None: + raise 'Error' + + if index == 0: return (self.getAbsVec(0) + self.next.getAbsVec(1)) / 2 + if index == 1: return (self.getAbsVec(1) + self.next.getAbsVec(2)) / 2 + if index == 2: return (self.getAbsVec(2) + self.next.getAbsVec(3)) / 2 + if index == 3: return (self.getAbsVec(3) + self.next.getAbsVec(0)) / 2 + + def childPointUnused(self, index): + ''' + Same as above but return None when the point is alredy used. + ''' + if self.children[index]: + return None + return self.childPoint(index) + + + def roll(self, angle): + ''' + Roll the quad about its normal + use for aurienting the sides of a quad to meet a branch that stems from here... + ''' + + mat = RotationMatrix(angle, 3, 'r', self.no) + for i in xrange(4): + self.vecs[i] = self.vecs[i] * mat + + + def toMesh(self, mesh): + self.verts[0].co = self.getAbsVec(0) + self.verts[1].co = self.getAbsVec(1) + self.verts[2].co = self.getAbsVec(2) + self.verts[3].co = self.getAbsVec(3) + + if not self.next: + return + + if self.prev == None and self.branch.parent_pt: + # join from parent branch + + # which side are we of the parents quad + index = self.branch.parent_pt.children.index(self.branch) + + # collect the points we are to merge into between the parent its next point + if index==0: verts = [self.branch.parent_pt.verts[0], self.branch.parent_pt.verts[1], self.branch.parent_pt.next.verts[1], self.branch.parent_pt.next.verts[0]] + if index==1: verts = [self.branch.parent_pt.verts[1], self.branch.parent_pt.verts[2], self.branch.parent_pt.next.verts[2], self.branch.parent_pt.next.verts[1]] + if index==2: verts = [self.branch.parent_pt.verts[2], self.branch.parent_pt.verts[3], self.branch.parent_pt.next.verts[3], self.branch.parent_pt.next.verts[2]] + if index==3: verts = [self.branch.parent_pt.verts[3], self.branch.parent_pt.verts[0], self.branch.parent_pt.next.verts[0], self.branch.parent_pt.next.verts[3]] + + + # Watchout for overlapping faces! + self.branch.faces[:] =\ + [verts[0], verts[1], self.verts[1], self.verts[0]],\ + [verts[1], verts[2], self.verts[2], self.verts[1]],\ + [verts[2], verts[3], self.verts[3], self.verts[2]],\ + [verts[3], verts[0], self.verts[0], self.verts[3]] + + # normal join, parents or no parents + if not self.children[0]: self.faces[0] = [self.verts[0], self.verts[1], self.next.verts[1], self.next.verts[0]] + if not self.children[1]: self.faces[1] = [self.verts[1], self.verts[2], self.next.verts[2], self.next.verts[1]] + if not self.children[2]: self.faces[2] = [self.verts[2], self.verts[3], self.next.verts[3], self.next.verts[2]] + if not self.children[3]: self.faces[3] = [self.verts[3], self.verts[0], self.next.verts[0], self.next.verts[3]] + + def calcVerts(self): + if self.prev == None: + if self.branch.parent_pt: + cross = CrossVecs(self.no, self.branch.getParentFaceCent() - self.branch.parent_pt.getAbsVec( self.branch.getParentQuadIndex() )) + else: + # parentless branch - for best results get a cross thats not the same as the normal, in rare cases this happens. + + # Was just doing + # cross = zup + # which works most of the time, but no verticle lines + + if AngleBetweenVecsSafe(self.no, zup) > 1.0: cross = zup + elif AngleBetweenVecsSafe(self.no, yup) > 1.0: cross = yup + else: cross = xup + + else: + cross = CrossVecs(self.prev.vecs[0], self.no) + + self.vecs[0] = Blender.Mathutils.CrossVecs(self.no, cross) + self.vecs[0].length = abs(self.radius) + mat = RotationMatrix(90, 3, 'r', self.no) + self.vecs[1] = self.vecs[0] * mat + self.vecs[2] = self.vecs[1] * mat + self.vecs[3] = self.vecs[2] * mat + + def hasChildren(self): + ''' + Use .childCount where possible, this does the real check + ''' + if self.children.count(None) == 4: + return False + else: + return True + +class branch: + def __init__(self): + self.bpoints = [] + self.parent_pt = None + self.tag = False # have we calculated our points + self.face_cap = None + self.length = -1 + # self.totchildren = 0 + # Bones per branch + self.faces = [None, None, None, None] + self.uv = None # face uvs can be fake, always 4 + self.bones = [] + self.generation = 0 # use to limit twig reproduction + self.twig_count = 0 # count the number of twigs - so as to limit how many twigs a branch gets + # self.myindex = -1 + ### self.segment_spacing_scale = 1.0 # use this to scale up the spacing - so small twigs dont get WAY too many polys + self.type = None + + def __repr__(self): + s = '' + s += '\tbranch' + s += '\tbpoints:', len(self.bpoints) + for pt in brch.bpoints: + s += str(self.pt) + + def getNormal(self): + return (self.bpoints[-1].co - self.bpoints[0].co).normalize() + + def getParentAngle(self): + if self.parent_pt: + return AngleBetweenVecsSafe(self.parent_pt.no, self.bpoints[0].no ) + else: + return 45.0 + + def getParentRadiusRatio(self): + if self.parent_pt: + return self.bpoints[0].radius / self.parent_pt.radius + else: + return 0.8 + + def getLength(self): + return (self.bpoints[0].co - self.bpoints[-1].co).length + + def getStraightness(self): + straight = 0.0 + pt = self.bpoints[0] + while pt.next: + straight += AngleBetweenVecsSafe(pt.no, pt.next.no) + pt = pt.next + return straight + + + ''' + def calcTotChildren(self): + for pt in self.bpoints: + self.totchildren += pt.childCount + ''' + def calcData(self): + ''' + Finalize once point data is there + ''' + self.calcPointLinkedList() + self.calcPointExtras() + + def calcPointLinkedList(self): + for i in xrange(1, len(self.bpoints)-1): + self.bpoints[i].next = self.bpoints[i+1] + self.bpoints[i].prev = self.bpoints[i-1] + + self.bpoints[0].next = self.bpoints[1] + self.bpoints[-1].prev = self.bpoints[-2] + + def calcPointExtras(self): + ''' + Run on a new branch or after transforming an existing one. + ''' + for pt in self.bpoints: + pt.calcNormal() + pt.calcNextMidCo() + + def calcTwigBounds(self, tree): + ''' + Check if out points are + ''' + for pt in self.bpoints: + pt.inTwigBounds = tree.isPointInTwigBounds(pt.co) + #if pt.inTwigBounds: + # debug_pt(pt.co) + + def baseTrim(self, connect_base_trim): + # if 1) dont remove the whole branch, maybe an option but later + # if 2) we are alredy a parent, cant remove me now.... darn :/ not nice... + # could do this properly but it would be slower and its a corner case. + # + # if 3) this point is within the branch, remove it. + # Scale this value by the difference in radius, a low trim looks better when the parent is a lot bigger.. + # + + while len(self.bpoints)>2 and\ + self.bpoints[0].childCount == 0 and\ + (self.parent_pt.nextMidCo - self.bpoints[0].co).length < ((self.parent_pt.radius + self.parent_pt.next.radius)/4) + (self.bpoints[0].radius * connect_base_trim): + # Note /4 - is a bit odd, since /2 is correct, but /4 lets us have more tight joints by default + + + del self.bpoints[0] + self.bpoints[0].prev = None + + def boundsTrim(self): + ''' + depends on calcTwigBounds running first. - also assumes no children assigned yet! make sure this is always the case. + ''' + trim = False + for i, pt in enumerate(self.bpoints): + if not pt.inTwigBounds: + trim = True + break + + # We must have at least 2 points to be a valid branch. this will be a stump :/ + if not trim or i < 3: + self.bpoints = [] # + return + + # Shorten the point list + self.bpoints = self.bpoints[:i] + self.bpoints[-1].makeLast() + + def taper(self, twig_ob_bounds_prune_taper = 0.0): + l = float(len( self.bpoints )) + for i, pt in enumerate(self.bpoints): + pt.radius *= (((l-i)/l) + (twig_ob_bounds_prune_taper*(i/l)) ) + + def getParentBranch(self): + if not self.parent_pt: + return None + return self.parent_pt.branch + + def getParentQuadAngle(self): + ''' + The angle off we are from our parent quad, + ''' + # used to roll the parent so its faces us better + + # Warning this can be zero sometimes, see the try below for the error + parent_normal = self.getParentFaceCent() - self.parent_pt.nextMidCo + + + self_normal = self.bpoints[1].co - self.parent_pt.co + # We only want the angle in relation to the parent points normal + # modify self_normal to make this so + cross = CrossVecs(self_normal, self.parent_pt.no) + self_normal = CrossVecs(self.parent_pt.no, cross) # CHECK + + #try: angle = AngleBetweenVecs(parent_normal, self_normal) + #except: return 0.0 + angle = AngleBetweenVecsSafe(parent_normal, self_normal) + + + # see if we need to rotate positive or negative + # USE DOT PRODUCT! + cross = CrossVecs(parent_normal, self_normal) + if AngleBetweenVecsSafe(cross, self.parent_pt.no) > 90: + angle = -angle + + return angle + + def getParentQuadIndex(self): + return self.parent_pt.children.index(self) + def getParentFaceCent(self): + return self.parent_pt.childPoint( self.getParentQuadIndex() ) + + def findClosest(self, co): + ''' + Find the closest point that can bare a child + ''' + + + ''' # this dosnt work, but could. + best = None + best_dist = 100000000 + for pt in self.bpoints: + if pt.next: + co_on_line, fac = ClosestPointOnLine(co, pt.co, pt.next.co) + print fac + if fac >= 0.0 and fac <= 1.0: + return pt, (co-co_on_line).length + + return best, best_dist + ''' + best = None + best_dist = 100000000 + for pt in self.bpoints: + if pt.nextMidCo and pt.childCount < 4: + dist = (pt.nextMidCo-co).length + if dist < best_dist: + best = pt + best_dist = dist + + return best, best_dist + + def inParentChain(self, brch): + ''' + See if this branch is a parent of self or in the chain + ''' + + self_parent_lookup = self.getParentBranch() + while self_parent_lookup: + if self_parent_lookup == brch: + return True + self_parent_lookup = self_parent_lookup.getParentBranch() + + return False + + def transform(self, mat, loc=None, scale=None): + if scale==None: + scale = (xyzup * mat).length + + for pt in self.bpoints: + if loc: + pt.co = (pt.co * mat) + loc + else: + pt.co = pt.co * mat + pt.radius *= scale + + for pt in self.bpoints: + self.calcPointExtras() + + def translate(self, co): + ''' + Simply move the twig on the branch + ''' + ofs = self.bpoints[0].co-co + for pt in self.bpoints: + pt.co -= ofs + + def transformRecursive(self, tree, mat3x3, cent, scale=None): + + if scale==None: + # incase this is a translation matrix + scale = ((xyzup * mat3x3) - (Vector(0,0,0) * mat3x3)).length + + for pt in self.bpoints: pt.co = ((pt.co-cent) * mat3x3) + cent + #for pt in self.bpoints: pt.co = (pt.co * mat3x3) + for pt in self.bpoints: self.calcPointExtras() + + + for brch in tree.branches_all: + if brch.parent_pt: + if brch.parent_pt.branch == self: + + brch.transformRecursive(tree, mat3x3, cent, scale) + + ''' + for pt in self.bpoints: + for brch in pt.children: + if brch: + brch.transformRecursive(mat3x3, cent, scale) + ''' + def bestTwigSegment(self): + ''' + Return the most free part on the branch to place a new twig + return (sort_value, best_index, self) + ''' + + # loop up and down the branch - counding how far from the last parent segment we are + spacing1 = [0] * (len(self.bpoints)-1) + spacing2 = spacing1[:] + + step_from_parent = 0 + for i in xrange(len(spacing1)): # -1 because the last pt cant have kits + + if self.bpoints[i].childCount or self.bpoints[i].inTwigBounds==False: + step_from_parent = 0 + else: + step_from_parent += 1 + + spacing1[i] += step_from_parent # -1 because the last pt cant have kits + + best_index = -1 + best_val = -1 + step_from_parent = 0 + for i in xrange(len(spacing1)-1, -1, -1): + + if self.bpoints[i].childCount or self.bpoints[i].inTwigBounds==False: + step_from_parent = 0 + else: + step_from_parent += 1 + + spacing2[i] += step_from_parent + + # inTwigBounds is true by default, when twigBounds are used it can be false + if self.bpoints[i].childCount < 4 and self.bpoints[i].inTwigBounds: + # Dont allow to assign more verts then 4 + val = spacing1[i] * spacing2[i] + if val > best_val: + best_val = val + best_index = i + + #if best_index == -1: + # raise "Error" + + # This value is only used for sorting, so the lower the value - the sooner it gets a twig. + #sort_val = -best_val + (1/self.getLength()) + sort_val=self.getLength() + + return sort_val, best_index, self + + def evenPointDistrobution(self, factor=1.0, factor_joint=1.0): + ''' + Redistribute points that are not evenly distributed + factor is between 0.0 and 1.0 + ''' + + for pt in self.bpoints: + if pt.next and pt.prev and pt.childCount == 0 and pt.prev.childCount == 0: + w1 = pt.nextLength() + w2 = pt.prevLength() + wtot = w1+w2 + if wtot > 0.0: + w1=w1/wtot + #w2=w2/wtot + w1 = abs(w1-0.5)*2 # make this from 0.0 to 1.0, where 0 is the middle and 1.0 is as far out of the middle as possible. + # print "%.6f" % w1 + pt.smooth(w1*factor, w1*factor_joint) + + def fixOverlapError(self, joint_smooth=1.0): + # Keep fixing until no hasOverlapError left to fix. + + error = True + while error: + error = False + for pt in self.bpoints: + if pt.prev and pt.next: + if pt.hasOverlapError(): + if pt.smooth(1.0, joint_smooth): # if we cant fix then dont bother trying again. + error = True + + def evenJointDistrobution(self, joint_compression = 1.0): + # See if we need to evaluate this branch at all + if len(self.bpoints) <= 2: # Rare but in this case we cant do anything + return + has_children = False + for pt in self.bpoints: + if pt.childCount: + has_children = True + break + + if not has_children: + return + + # OK, we have children, so we have some work to do... + # center each segment + + # work out the median location of all points children. + for pt in self.bpoints: + pt.calcChildrenMidData() + pt.targetCos[:] = [] + + for pt in self.bpoints: + + if pt.childrenMidCo: + # Move this and the next segment to be around the child point. + # TODO - factor in the branch angle, be careful with this - close angles can have extreme values. + slide_dist = (pt.childrenMidCo - pt.co).length - (pt.childrenMidRadius * joint_compression) + co = pt.slideCo( slide_dist ) + if co: + pt.targetCos.append( co ) + + slide_dist = (pt.childrenMidRadius * joint_compression) - (pt.childrenMidCo - pt.next.co).length + co = pt.next.slideCo( slide_dist ) + if co: + pt.next.targetCos.append( co ) + + for pt in reversed(self.bpoints): + pt.applyTargetLocation() + + def collapsePoints(self, seg_density=0.5, seg_density_angle=20.0, seg_density_radius=0.3, smooth_joint=1.0): + + collapse = True + while collapse: + collapse = False + pt = self.bpoints[0] + while pt: + # Collapse angles greater then 90. they are useually artifacts + + if pt.prev and pt.next and pt.prev.childCount == 0: + if (pt.radius + pt.prev.radius) != 0.0 and abs(pt.radius - pt.prev.radius) / (pt.radius + pt.prev.radius) < seg_density_radius: + ang = AngleBetweenVecsSafe(pt.no, pt.prev.no) + if seg_density_angle == 180 or ang > 90 or ang < seg_density_angle: + ## if (pt.prev.nextMidCo-pt.co).length < ((pt.radius + pt.prev.radius)/2) * seg_density: + if (pt.prev.nextMidCo-pt.co).length < seg_density or ang > 90: + pt_save = pt.prev + if pt.next.collapseUp(): # collapse this point + collapse = True + pt = pt_save # so we never reference a removed point + + if pt.childCount == 0 and pt.next: #if pt.childrenMidCo == None: + if (pt.radius + pt.next.radius) != 0.0 and abs(pt.radius - pt.next.radius) / (pt.radius + pt.next.radius) < seg_density_radius: + ang = AngleBetweenVecsSafe(pt.no, pt.next.no) + if seg_density_angle == 180 or ang > 90 or ang < seg_density_angle: + # do here because we only want to run this on points with no children, + # Are we closer theto eachother then the radius? + ## if (pt.nextMidCo-pt.co).length < ((pt.radius + pt.next.radius)/2) * seg_density: + if (pt.nextMidCo-pt.co).length < seg_density or ang > 90: + if pt.collapseDown(): + collapse = True + + pt = pt.next + ## self.checkPointList() + self.evenPointDistrobution(1.0, smooth_joint) + + for pt in self.bpoints: + pt.calcNormal() + pt.calcNextMidCo() + + # This is a bit dodgy - moving the branches around after placing can cause problems + """ + def branchReJoin(self): + ''' + Not needed but nice to run after collapsing incase segments moved a lot + ''' + if not self.parent_pt: + return # nothing to do + + # see if the next segment is closer now (after collapsing) + parent_pt = self.parent_pt + root_pt = self.bpoints[0] + + #try: + index = parent_pt.children.index(self) + #except: + #print "This is bad!, but not being able to re-join isnt that big a deal" + + current_dist = (parent_pt.nextMidCo - root_pt.co).length + + # TODO - Check size of new area is ok to move into + + if parent_pt.next and parent_pt.next.next and parent_pt.next.children[index] == None: + # We can go here if we want, see if its better + if current_dist > (parent_pt.next.nextMidCo - root_pt.co).length: + self.parent_pt.children[index] = None + self.parent_pt.childCount -= 1 + + self.parent_pt = parent_pt.next + self.parent_pt.children[index] = self + self.parent_pt.childCount += 1 + return + + if parent_pt.prev and parent_pt.prev.children[index] == None: + # We can go here if we want, see if its better + if current_dist > (parent_pt.prev.nextMidCo - root_pt.co).length: + self.parent_pt.children[index] = None + self.parent_pt.childCount -= 1 + + self.parent_pt = parent_pt.prev + self.parent_pt.children[index] = self + self.parent_pt.childCount += 1 + return + """ + + def checkPointList(self): + ''' + Error checking. use to check if collapsing worked. + ''' + p_link = self.bpoints[0] + i = 0 + while p_link: + if self.bpoints[i] != p_link: + raise "Error" + + if p_link.prev and p_link.prev != self.bpoints[i-1]: + raise "Error Prev" + + if p_link.next and p_link.next != self.bpoints[i+1]: + raise "Error Next" + + p_link = p_link.next + i+=1 + + def mixToNew(self, other, BLEND_MODE): + ''' + Generate a new branch based on 2 existing ones + These branches will point 'zup' - aurient 'xup' and have a tip length of 1.0 + ''' + + # Lets be lazy! - if the branches are different sizes- use the shortest. + # brch1 is always smaller + + brch1 = self + brch2 = other + if len(brch1.bpoints) > len(brch2.bpoints): + brch1, brch2 = brch2, brch1 + + if len(brch1.bpoints) == 1: + return None + + co_start = brch1.bpoints[0].co + cos1 = [ pt.co - co_start for pt in brch1.bpoints ] + + co_start = brch2.bpoints[0].co + if len(brch1.bpoints) == len(brch2.bpoints): + cos2 = [ pt.co - co_start for pt in brch2.bpoints ] + else: # truncate the points + cos2 = [ brch2.bpoints[i].co - co_start for i in xrange(len(brch1.bpoints)) ] + + scales = [] + for cos_ls in (cos1, cos2): + cross = CrossVecs(cos_ls[-1], zup) + mat = RotationMatrix(AngleBetweenVecsSafe(cos_ls[-1], zup), 3, 'r', cross) + cos_ls[:] = [co*mat for co in cos_ls] + + # point z-up + + # Now they are both pointing the same way aurient the curves to be rotated the same way + xy_nor = Vector(0,0,0) + for co in cos_ls: + xy_nor.x += co.x + xy_nor.y += co.y + cross = CrossVecs(xy_nor, xup) + + # Also scale them here so they are 1.0 tall always + scale = 1.0/(cos_ls[0]-cos_ls[-1]).length + mat = RotationMatrix(AngleBetweenVecsSafe(xy_nor, xup), 3, 'r', cross) * Matrix([scale,0,0],[0,scale,0],[0,0,scale]) + cos_ls[:] = [co*mat for co in cos_ls] + + scales.append(scale) + + # Make the new branch + new_brch = branch() + new_brch.type = BRANCH_TYPE_GROWN + for i in xrange(len(cos1)): + new_brch.bpoints.append( bpoint(new_brch, (cos1[i]+cos2[i])*0.5, Vector(), (brch1.bpoints[i].radius*scales[0] + brch2.bpoints[i].radius*scales[1])/2) ) + + new_brch.calcData() + return new_brch + + ''' + def toMesh(self): + pass + ''' + + + + +# No GUI code above this! ------------------------------------------------------ + +# PREFS - These can be saved on the object's id property. use 'tree2curve' slot +from Blender import Draw +import BPyWindow +ID_SLOT_NAME = 'Curve2Tree' + +EVENT_NONE = 0 +EVENT_EXIT = 1 +EVENT_UPDATE = 2 +EVENT_UPDATE_AND_UI = 2 +EVENT_REDRAW = 3 + + +# Prefs for each tree +PREFS = {} +PREFS['connect_sloppy'] = Draw.Create(1.0) +PREFS['connect_base_trim'] = Draw.Create(1.0) +PREFS['seg_density'] = Draw.Create(0.5) +PREFS['seg_density_angle'] = Draw.Create(20.0) +PREFS['seg_density_radius'] = Draw.Create(0.3) +PREFS['seg_joint_compression'] = Draw.Create(1.0) +PREFS['seg_joint_smooth'] = Draw.Create(2.0) +PREFS['image_main'] = Draw.Create('') +PREFS['do_uv'] = Draw.Create(0) +PREFS['uv_x_scale'] = Draw.Create(4.0) +PREFS['uv_y_scale'] = Draw.Create(1.0) +PREFS['do_material'] = Draw.Create(0) +PREFS['material_use_existing'] = Draw.Create(1) +PREFS['material_texture'] = Draw.Create(1) +PREFS['material_stencil'] = Draw.Create(1) +PREFS['do_subsurf'] = Draw.Create(1) +PREFS['do_cap_ends'] = Draw.Create(0) +PREFS['do_uv_keep_vproportion'] = Draw.Create(1) +PREFS['do_uv_vnormalize'] = Draw.Create(0) +PREFS['do_uv_uscale'] = Draw.Create(0) +PREFS['do_armature'] = Draw.Create(0) +PREFS['do_anim'] = Draw.Create(1) +try: PREFS['anim_tex'] = Draw.Create([tex for tex in bpy.data.textures][0].name) +except: PREFS['anim_tex'] = Draw.Create('') + +PREFS['anim_speed'] = Draw.Create(0.2) +PREFS['anim_magnitude'] = Draw.Create(0.2) +PREFS['anim_speed_size_scale'] = Draw.Create(1) +PREFS['anim_offset_scale'] = Draw.Create(1.0) + +PREFS['do_twigs_fill'] = Draw.Create(0) +PREFS['twig_fill_levels'] = Draw.Create(4) + +PREFS['twig_fill_rand_scale'] = Draw.Create(0.1) +PREFS['twig_fill_fork_angle_max'] = Draw.Create(180.0) +PREFS['twig_fill_radius_min'] = Draw.Create(0.001) +PREFS['twig_fill_radius_factor'] = Draw.Create(0.75) +PREFS['twig_fill_shape_type'] = Draw.Create(1) +PREFS['twig_fill_shape_rand'] = Draw.Create(0.5) +PREFS['twig_fill_shape_power'] = Draw.Create(0.5) + +PREFS['do_twigs'] = Draw.Create(0) +PREFS['twig_ratio'] = Draw.Create(2.0) +PREFS['twig_select_mode'] = Draw.Create(0) +PREFS['twig_select_factor'] = Draw.Create(0.5) +PREFS['twig_scale'] = Draw.Create(0.8) +PREFS['twig_scale_width'] = Draw.Create(1.0) +PREFS['twig_random_orientation'] = Draw.Create(180) +PREFS['twig_random_angle'] = Draw.Create(33) +PREFS['twig_recursive'] = Draw.Create(1) +PREFS['twig_recursive_limit'] = Draw.Create(3) +PREFS['twig_ob_bounds'] = Draw.Create('') # WATCH out, used for do_twigs_fill AND do_twigs +PREFS['twig_ob_bounds_prune'] = Draw.Create(1) +PREFS['twig_ob_bounds_prune_taper'] = Draw.Create(1.0) +PREFS['twig_placement_maxradius'] = Draw.Create(10.0) +PREFS['twig_placement_maxtwig'] = Draw.Create(4) +PREFS['twig_follow_parent'] = Draw.Create(0.0) +PREFS['twig_follow_x'] = Draw.Create(0.0) +PREFS['twig_follow_y'] = Draw.Create(0.0) +PREFS['twig_follow_z'] = Draw.Create(0.0) + +PREFS['do_leaf'] = Draw.Create(0) + +PREFS['leaf_branch_limit'] = Draw.Create(0.25) +PREFS['leaf_branch_limit_rand'] = Draw.Create(0.1) +PREFS['leaf_branch_density'] = Draw.Create(0.1) +PREFS['leaf_branch_pitch_angle'] = Draw.Create(0.0) +PREFS['leaf_branch_pitch_rand'] = Draw.Create(0.2) +PREFS['leaf_branch_roll_rand'] = Draw.Create(0.2) +PREFS['leaf_branch_angle'] = Draw.Create(75.0) +PREFS['leaf_rand_seed'] = Draw.Create(1.0) +PREFS['leaf_size'] = Draw.Create(0.5) +PREFS['leaf_size_rand'] = Draw.Create(0.5) + +PREFS['leaf_object'] = Draw.Create('') + +PREFS['do_variation'] = Draw.Create(0) +PREFS['variation_seed'] = Draw.Create(1) +PREFS['variation_orientation'] = Draw.Create(0.0) +PREFS['variation_scale'] = Draw.Create(0.0) + +GLOBAL_PREFS = {} +GLOBAL_PREFS['realtime_update'] = Draw.Create(0) + + +def getContextCurveObjects(): + sce = bpy.data.scenes.active + objects = [] + ob_act = sce.objects.active + for ob in sce.objects.context: + if ob == ob_act: ob_act = None + + if ob.type != 'Curve': + ob = ob.parent + if not ob or ob.type != 'Curve': + continue + objects.append(ob) + + # Alredy delt with + + + # Add the active, important when using localview or local layers + if ob_act: + ob = ob_act + if ob.type != 'Curve': + ob = ob.parent + if not ob or ob.type != 'Curve': + pass + else: + objects.append(ob) + + return objects + + +def Prefs2Dict(prefs, new_prefs): + ''' + Make a copy with no button settings + ''' + new_prefs.clear() + for key, val in prefs.items(): + try: new_prefs[key] = val.val + except: new_prefs[key] = val + return new_prefs + +def Dict2Prefs(prefs, new_prefs): + ''' + Make a copy with button settings + ''' + for key in prefs: # items would be nice for id groups + val = prefs[key] + ok = True + + try: + # If we have this setting allredy but its a different type, use the old setting (converting int's to floats for instance) + new_val = new_prefs[key] # this may fail, thats ok + if (type(new_val)==Blender.Types.ButtonType) and (type(new_val.val) != type(val)): + ok = False + except: + pass + + if ok: + try: + new_prefs[key] = Blender.Draw.Create( val ) + except: + new_prefs[key] = val + + return new_prefs + +def Prefs2IDProp(idprop, prefs): + new_prefs = {} + Prefs2Dict(prefs, new_prefs) + try: del idprop[ID_SLOT_NAME] + except: pass + + idprop[ID_SLOT_NAME] = new_prefs + +def IDProp2Prefs(idprop, prefs): + try: + prefs = idprop[ID_SLOT_NAME] + except: + return False + Dict2Prefs(prefs, PREFS) + return True + +def buildTree(ob_curve, single=False): + ''' + Must be a curve object, write to a child mesh + Must check this is a curve object! + ''' + print 'Curve2Tree, starting...' + # if were only doing 1 object, just use the current prefs + prefs = {} + + if single or not (IDProp2Prefs(ob_curve.properties, prefs)): + prefs = PREFS + + + # Check prefs are ok. + + + sce = bpy.data.scenes.active + + def getObChild(parent, obtype): + try: + return [ _ob for _ob in sce.objects if _ob.type == obtype if _ob.parent == parent ][0] + except: + return None + + def newObChild(parent, obdata): + + ob_new = bpy.data.scenes.active.objects.new(obdata) + # ob_new.Layers = parent.Layers + + # new object settings + parent.makeParent([ob_new]) + ob_new.setMatrix(Matrix()) + ob_new.sel = 0 + return ob_new + + def hasModifier(modtype): + return len([mod for mod in ob_mesh.modifiers if mod.type == modtype]) > 0 + + + sce = bpy.data.scenes.active + + if PREFS['image_main'].val: + try: image = bpy.data.images[PREFS['image_main'].val] + except: image = None + else: image = None + + # Get the mesh child + + print '\treading blenders curves...', + time1 = Blender.sys.time() + + t = tree() + t.fromCurve(ob_curve) + if not t.branches_all: + return # Empty curve? - may as well not throw an error + + time2 = Blender.sys.time() # time print + """ + print '%.4f sec' % (time2-time1) + if PREFS['do_twigs'].val: + print '\tbuilding twigs...', + t.buildTwigs(ratio=PREFS['twig_ratio'].val) + time3 = Blender.sys.time() # time print + print '%.4f sec' % (time3 - time2) + """ + if 0: pass + else: + time3 = Blender.sys.time() # time print + + print '\tconnecting branches...', + + twig_ob_bounds = getObFromName(PREFS['twig_ob_bounds'].val) + + t.buildConnections(\ + sloppy = PREFS['connect_sloppy'].val,\ + connect_base_trim = PREFS['connect_base_trim'].val,\ + do_twigs = PREFS['do_twigs'].val,\ + twig_ratio = PREFS['twig_ratio'].val,\ + twig_select_mode = PREFS['twig_select_mode'].val,\ + twig_select_factor = PREFS['twig_select_factor'].val,\ + twig_scale = PREFS['twig_scale'].val,\ + twig_scale_width = PREFS['twig_scale_width'].val,\ + twig_random_orientation = PREFS['twig_random_orientation'].val,\ + twig_random_angle = PREFS['twig_random_angle'].val,\ + twig_recursive = PREFS['twig_recursive'].val,\ + twig_recursive_limit = PREFS['twig_recursive_limit'].val,\ + twig_ob_bounds = twig_ob_bounds,\ + twig_ob_bounds_prune = PREFS['twig_ob_bounds_prune'].val,\ + twig_ob_bounds_prune_taper = PREFS['twig_ob_bounds_prune_taper'].val,\ + twig_placement_maxradius = PREFS['twig_placement_maxradius'].val,\ + twig_placement_maxtwig = PREFS['twig_placement_maxtwig'].val,\ + twig_follow_parent = PREFS['twig_follow_parent'].val,\ + twig_follow_x = PREFS['twig_follow_x'].val,\ + twig_follow_y = PREFS['twig_follow_y'].val,\ + twig_follow_z = PREFS['twig_follow_z'].val,\ + do_variation = PREFS['do_variation'].val,\ + variation_seed = PREFS['variation_seed'].val,\ + variation_orientation = PREFS['variation_orientation'].val,\ + variation_scale = PREFS['variation_scale'].val,\ + do_twigs_fill = PREFS['do_twigs_fill'].val,\ + twig_fill_levels = PREFS['twig_fill_levels'].val,\ + twig_fill_rand_scale = PREFS['twig_fill_rand_scale'].val,\ + twig_fill_fork_angle_max = PREFS['twig_fill_fork_angle_max'].val,\ + twig_fill_radius_min = PREFS['twig_fill_radius_min'].val,\ + twig_fill_radius_factor = PREFS['twig_fill_radius_factor'].val,\ + twig_fill_shape_type = PREFS['twig_fill_shape_type'].val,\ + twig_fill_shape_rand = PREFS['twig_fill_shape_rand'].val,\ + twig_fill_shape_power = PREFS['twig_fill_shape_power'].val,\ + ) + + time4 = Blender.sys.time() # time print + print '%.4f sec' % (time4-time3) + print '\toptimizing point spacing...', + + t.optimizeSpacing(\ + seg_density=PREFS['seg_density'].val,\ + seg_density_angle=PREFS['seg_density_angle'].val,\ + seg_density_radius=PREFS['seg_density_radius'].val,\ + joint_compression = PREFS['seg_joint_compression'].val,\ + joint_smooth = PREFS['seg_joint_smooth'].val\ + ) + + time5 = Blender.sys.time() # time print + print '%.4f sec' % (time5-time4) + print '\tbuilding mesh...', + + ob_mesh = getObChild(ob_curve, 'Mesh') + if not ob_mesh: + # New object + mesh = bpy.data.meshes.new('tree_' + ob_curve.name) + ob_mesh = newObChild(ob_curve, mesh) + # do subsurf later + + else: + # Existing object + mesh = ob_mesh.getData(mesh=1) + ob_mesh.setMatrix(Matrix()) + + # Do we need a do_uv_blend_layer? + if PREFS['do_material'].val and PREFS['material_stencil'].val and PREFS['material_texture'].val: + do_uv_blend_layer = True + else: + do_uv_blend_layer = False + + mesh = t.toMesh(mesh,\ + do_uv = PREFS['do_uv'].val,\ + uv_image = image,\ + do_uv_keep_vproportion = PREFS['do_uv_keep_vproportion'].val,\ + do_uv_vnormalize = PREFS['do_uv_vnormalize'].val,\ + do_uv_uscale = PREFS['do_uv_uscale'].val,\ + uv_x_scale = PREFS['uv_x_scale'].val,\ + uv_y_scale = PREFS['uv_y_scale'].val,\ + do_uv_blend_layer = do_uv_blend_layer,\ + do_cap_ends = PREFS['do_cap_ends'].val + ) + + if PREFS['do_leaf'].val: + ob_leaf_dupliface = getObChild(ob_mesh, 'Mesh') + if not ob_leaf_dupliface: # New object + mesh_leaf = bpy.data.meshes.new('leaf_' + ob_curve.name) + ob_leaf_dupliface = newObChild(ob_mesh, mesh_leaf) + else: + mesh_leaf = ob_leaf_dupliface.getData(mesh=1) + ob_leaf_dupliface.setMatrix(Matrix()) + + leaf_object = getObFromName(PREFS['leaf_object'].val) + + mesh_leaf = t.toLeafMesh(mesh_leaf,\ + leaf_branch_limit = PREFS['leaf_branch_limit'].val,\ + leaf_branch_limit_rand = PREFS['leaf_branch_limit_rand'].val,\ + leaf_size = PREFS['leaf_size'].val,\ + leaf_size_rand = PREFS['leaf_size_rand'].val,\ + leaf_branch_density = PREFS['leaf_branch_density'].val,\ + leaf_branch_pitch_angle = PREFS['leaf_branch_pitch_angle'].val,\ + leaf_branch_pitch_rand = PREFS['leaf_branch_pitch_rand'].val,\ + leaf_branch_roll_rand = PREFS['leaf_branch_roll_rand'].val,\ + leaf_branch_angle = PREFS['leaf_branch_angle'].val,\ + leaf_rand_seed = PREFS['leaf_rand_seed'].val,\ + leaf_object = leaf_object,\ + ) + + if leaf_object: + ob_leaf_dupliface.enableDupFaces = True + ob_leaf_dupliface.enableDupFacesScale = True + ob_leaf_dupliface.makeParent([leaf_object]) + else: + ob_leaf_dupliface.enableDupFaces = False + + mesh.calcNormals() + + if PREFS['do_material'].val: + + materials = mesh.materials + if PREFS['material_use_existing'].val and materials: + t.material = materials[0] + else: + t.material = bpy.data.materials.new(ob_curve.name) + mesh.materials = [t.material] + + if PREFS['material_texture'].val: + + # Set up the base image texture + t.texBase = bpy.data.textures.new('base_' + ob_curve.name) + t.material.setTexture(0, t.texBase, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.COL) + t.texBase.type = Blender.Texture.Types.IMAGE + if image: + t.texBase.image = image + t.texBaseMTex = t.material.getTextures()[0] + t.texBaseMTex.uvlayer = 'base' + + if PREFS['material_stencil'].val: + # Set up the blend texture + t.texBlend = bpy.data.textures.new('blend_' + ob_curve.name) + t.material.setTexture(1, t.texBlend, Blender.Texture.TexCo.UV, 0) # map to None + t.texBlend.type = Blender.Texture.Types.BLEND + t.texBlend.flags |= Blender.Texture.Flags.FLIPBLEND + t.texBlendMTex = t.material.getTextures()[1] + t.texBlendMTex.stencil = True + t.texBlendMTex.uvlayer = 'blend' + + + # Now make the texture for the stencil to blend, can reuse texBase here, jus tdifferent settings for the mtex + t.material.setTexture(2, t.texBase, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.COL) + t.texJoinMTex = t.material.getTextures()[2] + t.texJoinMTex.uvlayer = 'join' + + # Add a UV layer for blending + + + + + time6 = Blender.sys.time() # time print + print '%.4f sec' % (time6-time5) + + # Do armature stuff.... + if PREFS['do_armature'].val: + + print '\tbuilding armature & animation...', + + ob_arm = getObChild(ob_curve, 'Armature') + if ob_arm: + armature = ob_arm.data + ob_arm.setMatrix(Matrix()) + else: + armature = bpy.data.armatures.new() + ob_arm = newObChild(ob_curve, armature) + + t.toArmature(ob_arm, armature) + + # Add the modifier. + if not hasModifier(Blender.Modifier.Types.ARMATURE): + mod = ob_mesh.modifiers.append(Blender.Modifier.Types.ARMATURE) + + # TODO - assigne object anyway, even if an existing modifier exists. + mod[Blender.Modifier.Settings.OBJECT] = ob_arm + + if PREFS['do_anim'].val: + try: + tex = bpy.data.textures[PREFS['anim_tex'].val] + except: + tex = None + Blender.Draw.PupMenu('error no texture, cannot animate bones') + + if tex: + t.toAction(ob_arm, tex,\ + anim_speed = PREFS['anim_speed'].val,\ + anim_magnitude = PREFS['anim_magnitude'].val,\ + anim_speed_size_scale= PREFS['anim_speed_size_scale'].val,\ + anim_offset_scale=PREFS['anim_offset_scale'].val + ) + + time7 = Blender.sys.time() # time print + print '%.4f sec\n' % (time7-time6) + else: + time7 = Blender.sys.time() # time print + + print 'done in %.4f sec' % (time7 - time1) + + # Add subsurf last it needed. so armature skinning is done first. + # Do subsurf? + if PREFS['do_subsurf'].val: + if not hasModifier(Blender.Modifier.Types.SUBSURF): + mod = ob_mesh.modifiers.append(Blender.Modifier.Types.SUBSURF) + + #ob_mesh.makeDisplayList() + #mesh.update() + bpy.data.scenes.active.update() + +def do_pref_read(e=0,v=0, quiet=False): + ''' + We dont care about e and v values, only there because its a callback + ''' + sce = bpy.data.scenes.active + ob = sce.objects.active + + if not ob: + if not quiet: + Blender.Draw.PupMenu('No active curve object') + return + + if ob.type != 'Curve': + ob = ob.parent + + if ob == None or ob.type != 'Curve': + if not quiet: + Blender.Draw.PupMenu('No active curve object') + return + + if not IDProp2Prefs(ob.properties, PREFS): + if not quiet: + Blender.Draw.PupMenu('Curve object has no settings stored on it') + return + + Blender.Draw.Redraw() + +def do_pref_write(e,v): + + objects = getContextCurveObjects() + if not objects: + Blender.Draw.PupMenu('No curve objects selected') + return + + for ob in objects: + Prefs2IDProp(ob.properties, PREFS) + +def do_pref_clear(e,v): + objects = getContextCurveObjects() + if not objects: + Blender.Draw.PupMenu('No curve objects selected') + return + + for ob in objects: + try: del idprop[ID_SLOT_NAME] + except: pass + +def do_tex_check(e,v): + if not v: return + try: + bpy.data.textures[v] + except: + PREFS['anim_tex'].val = '' + Draw.PupMenu('Texture dosnt exist!') + Draw.Redraw() + +def do_ob_check(e,v): + if not v: return + try: + bpy.data.objects[v] + except: + # PREFS['twig_ob_bounds'].val = '' + Draw.PupMenu('Object dosnt exist!') + Draw.Redraw() + +def do_group_check(e,v): + if not v: return + try: + bpy.data.groups[v] + except: + # PREFS['leaf_object'].val = '' + Draw.PupMenu('dosnt exist!') + Draw.Redraw() + +# Button callbacks +def do_active_image(e,v): + img = bpy.data.images.active + if img: + PREFS['image_main'].val = img.name + else: + PREFS['image_main'].val = '' + +# Button callbacks +def do_tree_generate__real(): + sce = bpy.data.scenes.active + objects = getContextCurveObjects() + + if not objects: + Draw.PupMenu('Select one or more curve objects or a mesh/armature types with curve parents') + + is_editmode = Blender.Window.EditMode() + if is_editmode: + Blender.Window.EditMode(0, '', 0) + Blender.Window.WaitCursor(1) + + for ob in objects: + buildTree(ob, len(objects)==1) + + if is_editmode: + Blender.Window.EditMode(1, '', 0) + + Blender.Window.RedrawAll() + + Blender.Window.WaitCursor(0) + + +# Profile +# Had to do this to get it to work in ubuntu "sudo aptitude install python-profiler" +''' +import hotshot +import profile +from hotshot import stats +''' +def do_tree_generate(e,v): + + do_tree_generate__real() + ''' + prof = hotshot.Profile("hotshot_edi_stats") + prof.runcall(do_tree_generate__real) + prof.close() + s = stats.load("hotshot_edi_stats") + s.sort_stats("time").print_stats() + ''' + if GLOBALS['non_bez_error']: + Blender.Draw.PupMenu('Error%t|Nurbs and Poly curve types cant be used!') + GLOBALS['non_bez_error'] = 0 + + + +def evt(e,val): + pass + +def bevt(e): + + if e==EVENT_NONE: + return + + if e == EVENT_UPDATE or e == EVENT_UPDATE_AND_UI: + if GLOBAL_PREFS['realtime_update'].val: + do_tree_generate(0,0) # values dont matter + + if e == EVENT_REDRAW or e == EVENT_UPDATE_AND_UI: + Draw.Redraw() + if e == EVENT_EXIT: + Draw.Exit() + pass + +def gui(): + MARGIN = 4 + rect = BPyWindow.spaceRect() + but_width = int((rect[2]-MARGIN*2)/4.0) # 72 + # Clamp + if but_width>100: but_width = 100 + but_height = 17 + + x=MARGIN + y=rect[3]-but_height-MARGIN + xtmp = x + + Blender.Draw.BeginAlign() + PREFS['do_twigs_fill'] = Draw.Toggle('Fill Twigs',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['do_twigs_fill'].val, 'Generate child branches based existing branches'); xtmp += but_width*2; + if PREFS['do_twigs_fill'].val: + + PREFS['twig_fill_levels'] = Draw.Number('Generations', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_levels'].val, 1, 32, 'How many generations to make for filled twigs'); xtmp += but_width*2; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + # WARNING USED IN 2 PLACES!! - see below + PREFS['twig_ob_bounds'] = Draw.String('OB Bound: ', EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['twig_ob_bounds'].val, 64, 'Only grow twigs inside this mesh object', do_ob_check); xtmp += but_width*2; + PREFS['twig_fill_rand_scale'] = Draw.Number('Randomize Scale', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_rand_scale'].val, 0.0, 1.0, 'Randomize twig scale from the bounding mesh'); xtmp += but_width*2; + + y-=but_height + xtmp = x + + PREFS['twig_fill_radius_min'] = Draw.Number('Min Radius', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_radius_min'].val, 0.0, 1.0, 'Radius at endpoints of all twigs'); xtmp += but_width*2; + PREFS['twig_fill_radius_factor'] = Draw.Number('Inherit Scale', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_radius_factor'].val, 0.0, 1.0, 'What attaching to branches, scale the radius by this value for filled twigs, 0.0 for fixed width twigs.'); xtmp += but_width*2; + + y-=but_height + xtmp = x + + #PREFS['twig_fill_shape_type'] = Draw.Number('Shape Type', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_shape_type'].val, 0.0, 1.0, 'Shape used for the fork'); xtmp += but_width*2; + PREFS['twig_fill_shape_type'] = Draw.Menu('Join Type%t|Even%x0|Smooth One Child%x1|Smooth Both Children%x2',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['twig_fill_shape_type'].val, 'Select the wat twigs '); xtmp += but_width*2; + PREFS['twig_fill_fork_angle_max'] = Draw.Number('Shape Max Ang', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_fork_angle_max'].val, 0.0, 180.0, 'Maximum fork angle'); xtmp += but_width*2; + + y-=but_height + xtmp = x + + PREFS['twig_fill_shape_rand'] = Draw.Number('Shape Rand', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_shape_rand'].val, 0.0, 1.0, 'Randomize the shape of forks'); xtmp += but_width*2; + PREFS['twig_fill_shape_power'] = Draw.Number('Shape Strength', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_fill_shape_power'].val, 0.0, 1.0, 'Strength of curves'); xtmp += but_width*2; + + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + + + # ---------- ---------- ---------- ---------- + Blender.Draw.BeginAlign() + PREFS['do_twigs'] = Draw.Toggle('Grow Twigs',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['do_twigs'].val, 'Generate child branches based existing branches'); xtmp += but_width*2; + if PREFS['do_twigs'].val: + + PREFS['twig_ratio'] = Draw.Number('Twig Multiply', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_ratio'].val, 0.01, 500.0, 'How many twigs to generate per branch'); xtmp += but_width*2; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + PREFS['twig_select_mode'] = Draw.Menu('Branch Selection Method%t|From Short%x0|From Long%x1|From Straight%x2|From Bent%x3|',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['twig_select_mode'].val, 'Select branches to use as twigs based on this attribute'); xtmp += but_width*2; + PREFS['twig_select_factor'] = Draw.Number('From Factor', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_select_factor'].val, 0.0, 16, 'Select branches, lower value is more strict and will give you less variation'); xtmp += but_width*2; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + PREFS['twig_recursive'] = Draw.Toggle('Recursive Twigs',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['twig_recursive'].val, 'Recursively add twigs into eachother'); xtmp += but_width*2; + PREFS['twig_recursive_limit'] = Draw.Number('Generations', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_recursive_limit'].val, 0.0, 16, 'Number of generations allowed, 0 is inf'); xtmp += but_width*2; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + + PREFS['twig_scale'] = Draw.Number('Twig Scale', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_scale'].val, 0.01, 10.0, 'Scale down twigs in relation to their parents each generation'); xtmp += but_width*2; + PREFS['twig_scale_width'] = Draw.Number('Twig Scale Width', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_scale_width'].val, 0.01, 20.0, 'Scale the twig length only (not thickness)'); xtmp += but_width*2; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + + PREFS['twig_random_orientation'] = Draw.Number('Rand Orientation', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_random_orientation'].val, 0.0, 360.0, 'Random rotation around the parent'); xtmp += but_width*2; + PREFS['twig_random_angle'] = Draw.Number('Rand Angle', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_random_angle'].val, 0.0, 360.0, 'Random rotation to the parent joint'); xtmp += but_width*2; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + + PREFS['twig_placement_maxradius'] = Draw.Number('Place Max Radius', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_placement_maxradius'].val, 0.0, 50.0, 'Only place twigs on branches below this radius'); xtmp += but_width*2; + PREFS['twig_placement_maxtwig'] = Draw.Number('Place Max Count', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['twig_placement_maxtwig'].val, 0.0, 50.0, 'Limit twig placement to this many per branch'); xtmp += but_width*2; + + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['twig_follow_parent'] = Draw.Number('ParFollow', EVENT_UPDATE, xtmp, y, but_width, but_height, PREFS['twig_follow_parent'].val, 0.0, 10.0, 'Follow the parent branch'); xtmp += but_width; + PREFS['twig_follow_x'] = Draw.Number('Grav X', EVENT_UPDATE, xtmp, y, but_width, but_height, PREFS['twig_follow_x'].val, -10.0, 10.0, 'Twigs gravitate on the X axis'); xtmp += but_width; + PREFS['twig_follow_y'] = Draw.Number('Grav Y', EVENT_UPDATE, xtmp, y, but_width, but_height, PREFS['twig_follow_y'].val, -10.0, 10.0, 'Twigs gravitate on the Y axis'); xtmp += but_width; + PREFS['twig_follow_z'] = Draw.Number('Grav Z', EVENT_UPDATE, xtmp, y, but_width, but_height, PREFS['twig_follow_z'].val, -10.0, 10.0, 'Twigs gravitate on the Z axis'); xtmp += but_width; + + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + # WARNING USED IN 2 PLACES!! + PREFS['twig_ob_bounds'] = Draw.String('OB Bound: ', EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['twig_ob_bounds'].val, 64, 'Only grow twigs inside this mesh object', do_ob_check); xtmp += but_width*2; + + if PREFS['twig_ob_bounds_prune'].val: + but_width_tmp = but_width + else: + but_width_tmp = but_width*2 + + PREFS['twig_ob_bounds_prune'] = Draw.Toggle('Prune',EVENT_UPDATE_AND_UI, xtmp, y, but_width_tmp, but_height, PREFS['twig_ob_bounds_prune'].val, 'Prune twigs to the mesh object bounds'); xtmp += but_width_tmp; + if PREFS['twig_ob_bounds_prune'].val: + PREFS['twig_ob_bounds_prune_taper'] = Draw.Number('Taper', EVENT_UPDATE_AND_UI, xtmp, y, but_width, but_height, PREFS['twig_ob_bounds_prune_taper'].val, 0.0, 1.0, 'Taper pruned branches to a point'); xtmp += but_width; + + #PREFS['image_main'] = Draw.String('IM: ', EVENT_UPDATE, xtmp, y, but_width*3, but_height, PREFS['image_main'].val, 64, 'Image to apply to faces'); xtmp += but_width*3; + #Draw.PushButton('Use Active', EVENT_UPDATE, xtmp, y, but_width, but_height, 'Get the image from the active image window', do_active_image); xtmp += but_width; + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + + + Blender.Draw.BeginAlign() + PREFS['do_leaf'] = Draw.Toggle('Generate Leaves',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['do_leaf'].val, 'Generate leaves using duplifaces'); xtmp += but_width*2; + + if PREFS['do_leaf'].val: + + PREFS['leaf_object'] = Draw.String('OB: ', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_object'].val, 64, 'Use this object as a leaf', do_ob_check); xtmp += but_width*2; + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + PREFS['leaf_size'] = Draw.Number('Size', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_size'].val, 0.001, 10.0, 'size of the leaf'); xtmp += but_width*2; + PREFS['leaf_size_rand'] = Draw.Number('Randsize', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_size_rand'].val, 0.0, 1.0, 'randomize the leaf size'); xtmp += but_width*2; + + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + # Dont use yet + PREFS['leaf_branch_limit'] = Draw.Number('Branch Limit', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_limit'].val, 0.0, 1.0, 'Maximum thichness where a branch can bare leaves, higher value to place leaves on bigger branches'); xtmp += but_width*2; + PREFS['leaf_branch_limit_rand'] = Draw.Number('Limit Random', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_limit_rand'].val, 0.0, 1.0, 'Randomize the allowed minimum branch width to place leaves'); xtmp += but_width*2; + + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + PREFS['leaf_branch_density'] = Draw.Number('Density', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_density'].val, 0.0, 1.0, 'Chance each segment has of baring a leaf, use a high value for more leaves'); xtmp += but_width*2; + PREFS['leaf_branch_angle'] = Draw.Number('Angle From Branch', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_angle'].val, 0.0, 90.0, 'angle the leaf is from the branch direction'); xtmp += but_width*2; + + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + PREFS['leaf_rand_seed'] = Draw.Number('Random Seed', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_rand_seed'].val, 0.0, 10000.0, 'Set the seed for leaf random values'); xtmp += but_width*2; + PREFS['leaf_branch_pitch_angle'] = Draw.Number('Pitch Angle', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_pitch_angle'].val, -180, 180.0, 'Change the pitch rotation of leaves, negative angle to point down'); xtmp += but_width*2; + + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + PREFS['leaf_branch_pitch_rand'] = Draw.Number('Random Pitch', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_pitch_rand'].val, 0.0, 1.0, 'Randomize the leaf rotation (up-down/pitch)'); xtmp += but_width*2; + PREFS['leaf_branch_roll_rand'] = Draw.Number('Random Roll', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['leaf_branch_roll_rand'].val, 0.0, 1.0, 'Randomize the leaf rotation (roll/tilt/yaw)'); xtmp += but_width*2; + + + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + + Blender.Draw.BeginAlign() + if PREFS['do_uv'].val == 0: but_width_tmp = but_width*2 + else: but_width_tmp = but_width*4 + PREFS['do_uv'] = Draw.Toggle('Generate UVs',EVENT_UPDATE_AND_UI, xtmp, y, but_width_tmp, but_height, PREFS['do_uv'].val, 'Calculate UVs coords'); xtmp += but_width_tmp; + + if PREFS['do_uv'].val: + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + PREFS['do_uv_uscale'] = Draw.Toggle('U-Scale', EVENT_UPDATE, xtmp, y, but_width, but_height, PREFS['do_uv_uscale'].val, 'Scale the width according to the face size (will NOT tile)'); xtmp += but_width; + PREFS['do_uv_keep_vproportion'] = Draw.Toggle('V-Aspect', EVENT_UPDATE, xtmp, y, but_width, but_height, PREFS['do_uv_keep_vproportion'].val, 'Correct the UV aspect with the branch width'); xtmp += but_width; + PREFS['do_uv_vnormalize'] = Draw.Toggle('V-Normaize', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['do_uv_vnormalize'].val, 'Scale the UVs to fit onto the image verticaly'); xtmp += but_width*2; + + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['uv_x_scale'] = Draw.Number('Scale U', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['uv_x_scale'].val, 0.01, 10.0, 'Edge loop spacing around branch join, lower value for less webed joins'); xtmp += but_width*2; + PREFS['uv_y_scale'] = Draw.Number('Scale V', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['uv_y_scale'].val, 0.01, 10.0, 'Edge loop spacing around branch join, lower value for less webed joins'); xtmp += but_width*2; + + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['image_main'] = Draw.String('IM: ', EVENT_UPDATE, xtmp, y, but_width*3, but_height, PREFS['image_main'].val, 64, 'Image to apply to faces'); xtmp += but_width*3; + Draw.PushButton('Use Active', EVENT_UPDATE, xtmp, y, but_width, but_height, 'Get the image from the active image window', do_active_image); xtmp += but_width; + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + Blender.Draw.BeginAlign() + PREFS['do_material'] = Draw.Toggle('Generate Material',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['do_material'].val, 'Create material and textures (for seamless joints)'); xtmp += but_width*2; + + if PREFS['do_material'].val: + PREFS['material_use_existing'] = Draw.Toggle('ReUse Existing',EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['material_use_existing'].val, 'Modify the textures of the existing material'); xtmp += but_width*2; + + # ---------- ---------- ---------- ---------- + y-=but_height + xtmp = x + + PREFS['material_texture'] = Draw.Toggle('Texture', EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['material_texture'].val, 'Create an image texture for this material to use'); xtmp += but_width*2; + PREFS['material_stencil'] = Draw.Toggle('Blend Joints', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['material_stencil'].val, 'Use a 2 more texture and UV layers to blend the seams between joints'); xtmp += but_width*2; + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + Blender.Draw.BeginAlign() + if PREFS['do_armature'].val == 0: + but_width_tmp = but_width*2 + else: + but_width_tmp = but_width*4 + + Blender.Draw.BeginAlign() + PREFS['do_armature'] = Draw.Toggle('Generate Motion', EVENT_UPDATE_AND_UI, xtmp, y, but_width_tmp, but_height, PREFS['do_armature'].val, 'Generate Armatuer animation and apply to branches'); xtmp += but_width_tmp; + + # ---------- ---------- ---------- ---------- + if PREFS['do_armature'].val: + y-=but_height + xtmp = x + + PREFS['do_anim'] = Draw.Toggle('Texture Anim', EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['do_anim'].val, 'Use a texture to animate the bones'); xtmp += but_width*2; + + if PREFS['do_anim'].val: + + PREFS['anim_tex'] = Draw.String('TEX: ', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['anim_tex'].val, 64, 'Texture to use for the IPO Driver animation', do_tex_check); xtmp += but_width*2; + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['anim_speed'] = Draw.Number('Speed', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['anim_speed'].val, 0.001, 10.0, 'Animate the movement faster with a higher value'); xtmp += but_width*2; + PREFS['anim_magnitude'] = Draw.Number('Magnitude', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['anim_magnitude'].val, 0.001, 10.0, 'Animate with more motion with a higher value'); xtmp += but_width*2; + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['anim_offset_scale'] = Draw.Number('Unique Offset Scale', EVENT_UPDATE, xtmp, y, but_width*4, but_height, PREFS['anim_offset_scale'].val, 0.001, 10.0, 'Use the curve object location as input into the texture so trees have more unique motion, a low value is less unique'); xtmp += but_width*4; + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + + PREFS['anim_speed_size_scale'] = Draw.Toggle('Branch Size Scales Speed', EVENT_UPDATE, xtmp, y, but_width*4, but_height, PREFS['anim_speed_size_scale'].val, 'Use the branch size as a factor when calculating speed'); xtmp += but_width*4; + + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + + + + + # ---------- ---------- ---------- ---------- + + Blender.Draw.BeginAlign() + PREFS['do_variation'] = Draw.Toggle('Generate Variation', EVENT_UPDATE_AND_UI, xtmp, y, but_width*2, but_height, PREFS['do_variation'].val, 'Create a variant by moving the branches'); xtmp += but_width*2; + + # ---------- ---------- ---------- ---------- + if PREFS['do_variation'].val: + PREFS['variation_seed'] = Draw.Number('Rand Seed', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['variation_seed'].val, 1, 100000, 'Change this to get a different variation'); xtmp += but_width*2; + y-=but_height + xtmp = x + + + PREFS['variation_orientation'] = Draw.Number('Orientation', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['variation_orientation'].val, 0, 1.0, 'Randomize rotation of the branch around its parent'); xtmp += but_width*2; + PREFS['variation_scale'] = Draw.Number('Scale', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['variation_scale'].val, 0.0, 1.0, 'Randomize the scale of branches'); xtmp += but_width*2; + + Blender.Draw.EndAlign() + + y-=but_height+(MARGIN*2) + xtmp = x + + + + # ---------- ---------- ---------- ---------- + Blender.Draw.BeginAlign() + PREFS['seg_density'] = Draw.Number('Segment Spacing',EVENT_UPDATE, xtmp, y, but_width*4, but_height, PREFS['seg_density'].val, 0.05, 10.0, 'Scale the limit points collapse, that are closer then the branch width'); xtmp += but_width*4; + + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + PREFS['seg_density_angle'] = Draw.Number('Angle Spacing', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['seg_density_angle'].val, 0.0, 180.0, 'Segments above this angle will not collapse (lower value for more detail)'); xtmp += but_width*2; + PREFS['seg_density_radius'] = Draw.Number('Radius Spacing', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['seg_density_radius'].val, 0.0, 1.0, 'Segments above this difference in radius will not collapse (lower value for more detail)'); xtmp += but_width*2; + + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['seg_joint_compression'] = Draw.Number('Joint Width', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['seg_joint_compression'].val, 0.1, 2.0, 'Edge loop spacing around branch join, lower value for less webed joins'); xtmp += but_width*2; + PREFS['seg_joint_smooth'] = Draw.Number('Joint Smooth', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['seg_joint_smooth'].val, 0.0, 1.0, 'Edge loop spacing around branch join, lower value for less webed joins'); xtmp += but_width*2; + + y-=but_height + xtmp = x + # ---------- ---------- ---------- ---------- + + PREFS['connect_sloppy'] = Draw.Number('Connect Limit',EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['connect_sloppy'].val, 0.1, 2.0, 'Strictness when connecting branches'); xtmp += but_width*2; + PREFS['connect_base_trim'] = Draw.Number('Joint Bevel', EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['connect_base_trim'].val, 0.0, 2.0, 'low value for a tight join, hi for a smoother bevel'); xtmp += but_width*2; + Blender.Draw.EndAlign() + y-=but_height+MARGIN + xtmp = x + + # ---------- ---------- ---------- ---------- + Blender.Draw.BeginAlign() + PREFS['do_cap_ends'] = Draw.Toggle('Cap Ends',EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['do_cap_ends'].val, 'Add faces onto branch endpoints'); xtmp += but_width*2; + PREFS['do_subsurf'] = Draw.Toggle('SubSurf',EVENT_UPDATE, xtmp, y, but_width*2, but_height, PREFS['do_subsurf'].val, 'Enable subsurf for newly generated objects'); xtmp += but_width*2; + Blender.Draw.EndAlign() + y-=but_height+MARGIN + xtmp = x + + + # ---------- ---------- ---------- ---------- + Blender.Draw.BeginAlign() + Draw.PushButton('Read Active Prefs', EVENT_REDRAW, xtmp, y, but_width*2, but_height, 'Read the ID Property settings from the active curve object', do_pref_read); xtmp += but_width*2; + Draw.PushButton('Write Prefs to Sel', EVENT_NONE, xtmp, y, but_width*2, but_height, 'Save these settings in the ID Properties of all selected curve objects', do_pref_write); xtmp += but_width*2; + + y-=but_height + xtmp = x + + # ---------- ---------- ---------- ---------- + Draw.PushButton('Clear Prefs from Sel', EVENT_NONE, xtmp, y, but_width*4, but_height, 'Remove settings from the selected curve aaobjects', do_pref_clear); xtmp += but_width*4; + Blender.Draw.EndAlign() + + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + Blender.Draw.BeginAlign() + Draw.PushButton('Exit', EVENT_EXIT, xtmp, y, but_width, but_height, '', do_active_image); xtmp += but_width; + Draw.PushButton('Generate from selection', EVENT_REDRAW, xtmp, y, but_width*3, but_height, 'Generate mesh', do_tree_generate); xtmp += but_width*3; + Blender.Draw.EndAlign() + y-=but_height+MARGIN + xtmp = x + # ---------- ---------- ---------- ---------- + + GLOBAL_PREFS['realtime_update'] = Draw.Toggle('Automatic Update', EVENT_UPDATE, xtmp, y, but_width*4, but_height, GLOBAL_PREFS['realtime_update'].val, 'Update automatically when settings change'); xtmp += but_width*4; + + + +if __name__ == '__main__': + # Read the active objects prefs on load. if they exist + do_pref_read(quiet=True) + + Draw.Register(gui, evt, bevt) diff --git a/release/scripts/x3d_export.py b/release/scripts/x3d_export.py index 36359f1d122..eb9d5d35d6c 100644 --- a/release/scripts/x3d_export.py +++ b/release/scripts/x3d_export.py @@ -162,7 +162,8 @@ class VRML2Export: ########################################################## def writeHeader(self): - bfile = sys.expandpath(Blender.Get('filename')) + #bfile = sys.expandpath( Blender.Get('filename') ).replace('<', '<').replace('>', '>') + bfile = self.filename.replace('<', '<').replace('>', '>') # use outfile name self.file.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n") self.file.write("<!DOCTYPE X3D PUBLIC \"ISO//Web3D//DTD X3D 3.0//EN\" \"http://www.web3d.org/specifications/x3d-3.0.dtd\">\n") self.file.write("<X3D version=\"3.0\" profile=\"Immersive\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema-instance\" xsd:noNamespaceSchemaLocation=\"http://www.web3d.org/specifications/x3d-3.0.xsd\">\n") @@ -246,17 +247,17 @@ class VRML2Export: def writeNavigationInfo(self, scene): allObj = [] allObj = list(scene.objects) - headlight = "TRUE" + headlight = "true" vislimit = 0.0 for thisObj in allObj: objType=thisObj.type if objType == "Camera": vislimit = thisObj.data.clipEnd elif objType == "Lamp": - headlight = "FALSE" + headlight = "false" self.file.write("<NavigationInfo headlight=\"%s\" " % headlight) self.file.write("visibilityLimit=\"%s\" " % (round(vislimit,self.cp))) - self.file.write("type=\"EXAMINE, ANY\" avatarSize=\"0.25, 1.75, 0.75\" />\n\n") + self.file.write("type=\"EXAMINE\", \"ANY\" avatarSize=\"0.25, 1.75, 0.75\" />\n\n") def writeSpotLight(self, ob, lamp): safeName = self.cleanStr(ob.name) @@ -404,7 +405,7 @@ class VRML2Export: self.writeIndented("<Appearance>\n", 1) # right now this script can only handle a single material per mesh. if len(maters) >= 1: - mat=Blender.Material.Get(maters[0].name) + mat=maters[0] matFlags = mat.getMode() if not matFlags & Blender.Material.Modes['TEXFACE']: self.writeMaterial(mat, self.cleanStr(maters[0].name,'')) @@ -452,6 +453,7 @@ class VRML2Export: for face in mesh.faces: if face.smooth: issmooth=1 + break if issmooth==1 and self.wire == 0: creaseAngle=(mesh.getMaxSmoothAngle())*(math.pi/180.0) self.file.write("creaseAngle=\"%s\" " % (round(creaseAngle,self.cp))) diff --git a/release/windows/installer/00.sconsblender.nsi b/release/windows/installer/00.sconsblender.nsi index ff7a198d0b9..5875be289b6 100644 --- a/release/windows/installer/00.sconsblender.nsi +++ b/release/windows/installer/00.sconsblender.nsi @@ -3,6 +3,8 @@ ; ; Blender Self-Installer for Windows (NSIS - http://nsis.sourceforge.net) ; +; Requires the MoreInfo plugin - http://nsis.sourceforge.net/MoreInfo_plug-in +; !include "MUI.nsh" !include "FileFunc.nsh" @@ -209,13 +211,13 @@ Function .onInit Call GetWindowsVersion Pop $R0 Strcpy $winversion $R0 - !insertmacro MUI_INSTALLOPTIONS_EXTRACT "RELDIR\data.ini" + !insertmacro MUI_INSTALLOPTIONS_EXTRACT_AS "RELDIR\data.ini" "data.ini" FunctionEnd !define DLL_VER "8.00.50727.42" +!define DLL_VER2 "7.10.3052.4" -Function LocateCallback - +Function LocateCallback_80 MoreInfo::GetProductVersion "$R9" Pop $0 @@ -236,12 +238,38 @@ Function LocateCallback FunctionEnd +Function LocateCallback_71 + MoreInfo::GetProductVersion "$R9" + Pop $0 + + ${VersionCompare} "$0" "${DLL_VER2}" $R1 + + StrCmp $R1 0 0 new + new: + StrCmp $R1 1 0 old + old: + StrCmp $R1 2 0 end + ; Found DLL is older + Call PythonInstall + + end: + StrCpy "$0" StopLocate + StrCpy $DLL_found "true" + Push "$0" + +FunctionEnd + Function DownloadDLL MessageBox MB_OK "You will need to download the Microsoft Visual C++ 2005 Redistributable Package in order to run Blender. Pressing OK will take you to the download page, please follow the instructions on the page that appears." StrCpy $0 "http://www.microsoft.com/downloads/details.aspx?familyid=32BC1BEE-A3F9-4C13-9C99-220B62A191EE&displaylang=en" Call openLinkNewWindow FunctionEnd +Function PythonInstall + MessageBox MB_OK "You will need to install python 2.5 in order to run blender. Pressing OK will take you to the python.org website." + StrCpy $0 "http://www.python.org" + Call openLinkNewWindow +FunctionEnd Var HWND Var DLGITEM @@ -344,9 +372,14 @@ Section "Blender-VERSION (required)" SecCopyUI MessageBox MB_OK "The installer will now check your system for the required system dlls." StrCpy $1 $WINDIR StrCpy $DLL_found "false" - ${Locate} "$1" "/L=F /M=MSVCR80.DLL /S=0B" "LocateCallback" + ${Locate} "$1" "/L=F /M=MSVCR80.DLL /S=0B" "LocateCallback_80" StrCmp $DLL_found "false" 0 +2 Call DownloadDLL + StrCpy $1 $WINDIR + StrCpy $DLL_found "false" + ${Locate} "$1" "/L=F /M=MSVCR71.DLL /S=0B" "LocateCallback_71" + StrCmp $DLL_found "false" 0 +2 + Call PythonInstall SectionEnd |